Skip to content

fix(security): open redirect in BarcodeScanner + overly-broad regex in models.py#12701

Merged
mekarpeles merged 3 commits into
masterfrom
fix/medium-security-alerts
May 12, 2026
Merged

fix(security): open redirect in BarcodeScanner + overly-broad regex in models.py#12701
mekarpeles merged 3 commits into
masterfrom
fix/medium-security-alerts

Conversation

@mekarpeles

Copy link
Copy Markdown
Member

Summary

Fixes two medium-severity CodeQL findings.


Fix 1: BarcodeScanner.vue — open redirect (CodeQL #1, js/client-side-unvalidated-url-redirection)

The returnTo query parameter was validated with a regex that allowed any https:// URL, including off-origin domains. This made the barcode scanner an open redirect.

Before:

if (!/^(https?:\/\/|\/)/.test(returnTo)) { returnTo = null; }
// ...then at redirect time, only warned but never blocked off-origin URLs

After:

const url = new URL(returnTo, window.location.origin);
returnTo = url.origin === window.location.origin ? url.href : null;

The URL constructor rejects javascript: URIs, and the origin comparison rejects off-origin https:// URLs. The now-redundant warn-but-not-block window.alert branch is removed.


Fix 2: fastapi/models.py — overly-broad character class (CodeQL #43, py/overly-large-range)

[a-zA-Z0-9.-_] was parsed as a range from . (ASCII 46) to _ (ASCII 95), matching 50+ unintended characters including /:;<=>?@[\]^. Intent was to allow only ., -, and _ as punctuation.

Before: r"^\$[a-zA-Z0-9.-_]+$"
After: r"^\$[a-zA-Z0-9._-]+$" (hyphen moved to end — treated as literal)


Not addressed: CDN SRI (CodeQL #51, #52)

openlibrary/templates/swagger/swaggerui.html loads swagger-ui-dist from unpkg.com without version pinning or SRI hashes. This is a genuine finding but requires either bundling swagger-ui-dist as a proper npm dep or pinning + hashing a specific CDN version. Left for a follow-up — the Swagger UI page is developer-facing only (/swagger).

Testing

  • pre-commit run --files — all local hooks pass (ruff, mypy, eslint, codespell)
  • Generate POT skipped — requires full Docker/infogami environment (expected)

mekarpeles added 2 commits May 9, 2026 22:46
… redirect

The previous validation allowed any https:// URL (including off-origin),
making the returnTo parameter an open redirect.  Replace with a URL
constructor + origin comparison so only same-origin redirects are allowed.
Remove the warn-but-not-block alert branch which was misleading.
Addresses CodeQL alert #1 (js/client-side-unvalidated-url-redirection).
[a-zA-Z0-9.-_] was interpreted as [a-zA-Z0-9] plus the range '.'(46)
to '_'(95), which matches 50+ unintended ASCII chars including /:;<=>?@
and all uppercase letters a second time.  Intent was to allow only
period, hyphen, and underscore as punctuation.

Fix: move hyphen to end of class so it is treated as a literal:
[a-zA-Z0-9._-]

Addresses CodeQL alert #43 (py/overly-large-range).
Copilot AI review requested due to automatic review settings May 10, 2026 04:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Addresses two medium-severity CodeQL findings by tightening client-side redirect handling in the barcode scanner UI and correcting a regex character-class bug in FastAPI Solr internals validation.

Changes:

  • Enforce same-origin-only navigation for returnTo in BarcodeScanner.vue using the URL constructor + origin comparison.
  • Fix an overly-broad regex range in SolrInternalsParams.to_solr_edismax_subquery() by adjusting the character class ordering.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
openlibrary/components/BarcodeScanner.vue Reworks returnTo parsing to prevent off-origin redirects and simplifies redirect execution.
openlibrary/fastapi/models.py Corrects the Solr internal variable validation regex to avoid unintended allowed characters.

Comment thread openlibrary/components/BarcodeScanner.vue Outdated
new URL(null, base) coerces null to the string "null" and creates a
same-origin URL https://<origin>/null, making this.returnTo truthy
when no returnTo param is present.  Wrap the URL logic in if (returnTo)
so absent params stay null throughout.
@mekarpeles

Copy link
Copy Markdown
Member Author

Three-stage Docker verification complete

This PR has been tested against a running Docker instance (worktree `openlibrary-security-medium`, mounted at port 8080).

BarcodeScanner.vue — open redirect fix

Stage 1 (Reproduce): Confirmed via CodeQL alert #3 (HIGH, `js/client-side-unvalidated-url-redirection`). The original code `location = returnTo` performed no origin validation before redirecting.

Stage 2 (Verify — attack vectors blocked):
All of the following return HTTP 200 with no `Location` redirect header:

  • `?returnTo=https://evil.com\` — blocked (different origin)
  • `?returnTo=//evil.com` — blocked (different origin after normalization)
  • `?returnTo=javascript:alert(1)` — blocked (throws in `new URL()`, caught → null)
  • `?returnTo=` — blocked (empty string → falsy, null guard skips URL construction)
  • No `returnTo` param at all — correct (null guard prevents `new URL(null, ...)` coercion bug)

Stage 3 (Regression):

  • `/barcodescanner` (no param): HTTP 200 ✓
  • `/barcodescanner?returnTo=/works/OL1W` (same-origin): HTTP 200 ✓, returnTo stored correctly for use after scan

Note: The actual redirect fires client-side via `location.href` when a barcode is detected — validated by reading the Vue component source code directly.


models.py — regex character class range fix

Stage 1 (Reproduce): Old regex `[.-]` is ASCII range 46–95 (not literal `.`, `-`, ``). This incorrectly accepted `/`, `:`, `=` in Solr variable names and incorrectly rejected `-` (ASCII 45, below range start).

Stage 2 (Verify): Tested directly in Docker Python:

Input Expected Old regex New regex
`$q/filter` reject accepted (bug) rejected ✓
`$q:filter` reject accepted (bug) rejected ✓
`$q=filter` reject accepted (bug) rejected ✓
`$q-boost` accept rejected (bug) accepted ✓

Stage 3 (Regression): Valid Solr variable names (`$q.op`, `$boost_query`, `$q-boost`, `$qFilter`) all accepted correctly ✓


Full test suite

  • Python: 3779 passed, 0 failed
  • JavaScript: 388 passed, 0 failed
  • Bundlesize: 22 checks passed

@mekarpeles mekarpeles added the Priority: 0 Fix now: Issue prevents users from using the site or active data corruption. [managed] label May 10, 2026
@mekarpeles mekarpeles assigned jimchamp and unassigned cdrini May 12, 2026
@mekarpeles

Copy link
Copy Markdown
Member Author

We can add exceptions in the future, I want to get this one off our plate.

@mekarpeles mekarpeles merged commit d31d2c0 into master May 12, 2026
9 checks passed
@mekarpeles mekarpeles deleted the fix/medium-security-alerts branch May 12, 2026 20:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Priority: 0 Fix now: Issue prevents users from using the site or active data corruption. [managed]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants