fix(auth0-express): harden createRouteUrl against path injection attacks - #23
Merged
Merged
Conversation
cschetan77
force-pushed
the
fix/route-url-path-validation
branch
from
July 13, 2026 05:22
3e822ff to
f6e10b6
Compare
Strip leading backslashes in addition to forward slashes to prevent WHATWG URL parser normalisation bypasses, and add an explicit scheme check before URL construction to reject javascript:, data:, file: and other scheme-based injection attempts as defence-in-depth. toSafeRedirect is updated to branch on whether input carries a scheme: absolute URLs are validated via origin check directly, while relative paths continue through the strict createRouteUrl validation.
…ardening - Fix scheme regex from /^[a-zA-Z0-9.+-]*:/ to /^[a-zA-Z][a-zA-Z0-9.+-]*:/ so paths with a digit-start first segment (e.g. /2024-report:summary) or a bare leading colon (:param) are not misidentified as URL schemes - Extract SCHEME_REGEX to a named constant shared by createRouteUrl and toSafeRedirect to prevent validation drift - Fix ensureNoLeadingSlash: change * to + and remove the dead g flag - Move new URL(safeBaseUrl).origin inside the try block in toSafeRedirect so a malformed safeBaseUrl returns undefined instead of throwing uncaught - Replace createRouteUrl(req.url, appBaseUrl) in callback-handler with new URL(req.url, appBaseUrl) + origin check, so absolute-form request targets forwarded by reverse proxies are handled correctly - Add tests for single leading backslash, digit-start colon path, colon in non-first segment, toSafeRedirect backslash cases, and malformed safeBaseUrl
createRouteUrl now accepts absolute same-origin URLs (e.g. from a reverse
proxy) by branching on whether the input carries a scheme: absolute inputs
are parsed directly and validated by origin check, relative inputs go through
the existing slash-stripping and multi-leading check. This fixes the subpath
deployment regression where new URL('/auth/callback', base) was dropping the
subpath, causing redirect_uri mismatches and invalid_grant errors.
callback-handler.ts reverts to using createRouteUrl so subpath handling is
preserved and there is a single authoritative URL resolution helper.
toSafeRedirect is simplified to new URL(input, base) + origin check —
new URL() already handles both absolute and relative inputs natively, making
the previous scheme-branching logic redundant.
Simplifying toSafeRedirect to new URL(input, base) dropped the subpath
when resolving relative paths — new URL('/auth/callback', 'https://myapp.com/subapp')
ignores the subpath, producing https://myapp.com/auth/callback instead of
https://myapp.com/subapp/auth/callback. Restore the scheme-branching so
relative paths go through createRouteUrl which strips the leading slash
before resolving against the full base, preserving the subpath.
cschetan77
force-pushed
the
fix/route-url-path-validation
branch
from
July 27, 2026 11:47
5d84d83 to
d3cba15
Compare
…ardening - Replace SCHEME_REGEX with AUTHORITY_URL_REGEX (:// required) so bare first-segment-colon paths like `report:summary` and `javascript:alert(1)` are not misclassified as absolute URLs — they now resolve safely as relative paths via the `./` prefix trick - Add SCHEME_PREFIX_REGEX to detect first-segment colons on the relative branch and prepend `./` so the WHATWG parser treats them as relative paths - Wrap `new URL(url)` in a try/catch to surface a descriptive Error instead of a raw TypeError for malformed absolute inputs - Simplify toSafeRedirect to delegate entirely to createRouteUrl, removing the duplicated AUTHORITY_URL_REGEX test and redundant origin re-check - Add tests: bare first-segment colon paths, javascript:/data: as relative, toSafeRedirect subpath preservation, toSafeRedirect file:// rejection, toSafeRedirect javascript: safe resolution
…outeUrl The ./prefix trick (introduced to fix bare first-segment colon paths like report:summary) inadvertently allowed javascript:, data:, and vbscript: inputs to resolve as same-origin paths. While harmless for res.redirect() today, these schemes must never be valid route or redirect destinations. Add UNSAFE_SCHEME_REGEX denylist check before the ./prefix step so these three schemes are explicitly rejected. Legitimate first-segment colon paths like report:summary and news:today are unaffected since they do not match the denylist. Tests updated accordingly.
frederikprijck
approved these changes
Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
`createRouteUrl` is a utility that combines a route path with the application base URL. When the path argument can be influenced by an attacker, insufficient validation allows the base URL origin to be overwritten — enabling open redirect and URL injection attacks.
What was fixed in a prior commit (7c69b9b)
Commit `7c69b9b` introduced two protections:
What remained unmitigated
1. Backslashes not stripped
`ensureNoLeadingSlash` only handled forward slashes. The WHATWG URL parser normalises backslashes to forward slashes, so inputs like `\\evil.com` or `\/evil.com` were treated as protocol-relative URLs (`//evil.com`) — overriding the base host.
2. No defence-in-depth before URL construction
Inputs like `javascript:alert(1)`, `data:text/html,...`, or `file:///etc/passwd` had no leading slashes so the multi-slash check was skipped entirely. The origin check caught these as a single safety net, but that was insufficient defence-in-depth.
What this PR fixes
`ensureNoLeadingSlash`
Strips both leading forward slashes and backslashes using `/^[/\\]+/`, closing the backslash normalisation bypass.
`AUTHORITY_URL_REGEX` — absolute URL detection
Detects inputs with an authority component (`://`) and routes them to a dedicated absolute-URL branch. This correctly handles absolute-form `req.url` values forwarded by reverse proxies. Inputs without `://` (including `javascript:`, `data:`, `report:summary`) are treated as relative paths.
`SCHEME_PREFIX_REGEX` — relative path colon guard
When a relative path's first segment contains a colon (e.g. `report:summary`, `news:today`), the WHATWG parser would misinterpret it as a scheme. Prepending `./` forces correct relative resolution. Legitimately dangerous schemes are blocked first by the denylist below.
`UNSAFE_SCHEME_REGEX` — denylist for unsafe schemes
Explicitly rejects `javascript:`, `data:`, and `vbscript:` before any URL construction. These are the only schemes that can execute code or render arbitrary content in a browser as a navigation target.
`createRouteUrl`
Validation now runs in three sequential gates:
`toSafeRedirect`
Simplified to a try/catch wrapper around `createRouteUrl` — all validation lives in one place, eliminating duplicated regex logic and the previous double base-URL parse.
Attack vectors guarded against
Testing
New and updated test cases (`packages/auth0-express/src/utils.spec.ts`):