fix(security): open redirect in BarcodeScanner + overly-broad regex in models.py#12701
Conversation
… 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).
There was a problem hiding this comment.
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
returnToinBarcodeScanner.vueusing theURLconstructor + 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. |
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.
Three-stage Docker verification completeThis PR has been tested against a running Docker instance (worktree `openlibrary-security-medium`, mounted at port 8080). BarcodeScanner.vue — open redirect fixStage 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):
Stage 3 (Regression):
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 fixStage 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:
Stage 3 (Regression): Valid Solr variable names (`$q.op`, `$boost_query`, `$q-boost`, `$qFilter`) all accepted correctly ✓ Full test suite
|
|
We can add exceptions in the future, I want to get this one off our plate. |
Summary
Fixes two medium-severity CodeQL findings.
Fix 1: BarcodeScanner.vue — open redirect (CodeQL #1,
js/client-side-unvalidated-url-redirection)The
returnToquery parameter was validated with a regex that allowed anyhttps://URL, including off-origin domains. This made the barcode scanner an open redirect.Before:
After:
The
URLconstructor rejectsjavascript:URIs, and the origin comparison rejects off-originhttps://URLs. The now-redundant warn-but-not-blockwindow.alertbranch 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.htmlloads swagger-ui-dist fromunpkg.comwithout 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)