fix: harden SSR cache guard against case-sensitivity bypass (CVE-2026-53721)#56
fix: harden SSR cache guard against case-sensitivity bypass (CVE-2026-53721)#56frederikprijck wants to merge 1 commit into
Conversation
CVE-2026-53721) Nitro's route-rule matcher is case-sensitive, but vue-router matches routes case-insensitively. A mixed-case request (e.g. /Cacheable) therefore renders the /cacheable page while resolving NO route rule — bypassing the shared-cacheable cache guard and writing the authenticated user into SSR HTML that the consumer marked publicly cacheable. Nuxt's own fix (4.4.7 / 3.21.7) lowercases the path only in the app-level and nitro-server matchers; the 'nitropack/runtime' getRouteRules path this middleware uses is not covered, so the bypass still reproduces on patched Nuxt. Harden in our own code: after checking the request path as-is, re-resolve the route rules for the lowercased path (via a synthetic event with a fresh context so getRouteRules recomputes) and skip the write if either is shared-cacheable. Skipping is always fail-safe — the client hydrates the user. - middleware: dual lookup (request path + lowercased path) - unit test: shared-cacheable bypass via casing skips the write - e2e: /Cacheable renders anonymous SSR HTML (leaked before the fix)
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Closing — this fix belongs as a commit directly on the issue-52 branch, not a separate PR. Folding it in there. |
Summary
The #52 cache guard reads route rules via
getRouteRulesto decide whether to write the authenticated user into the SSR payload. Nitro's route-rule matcher is case-sensitive, but vue-router matches routes case-insensitively — so a mixed-case request (e.g./Cacheable) renders the/cacheablepage while resolving no route rule. The guard therefore doesn't fire, and the user is written into SSR HTML the consumer markedCache-Control: public, s-maxage. This is CVE-2026-53721.Confirmed live in this repo (reproduced before the fix):
/Cacheablereturns the user'ssub/emailin the raw SSR HTML;/cacheabledoes not.Why patched Nuxt doesn't cover us
Nuxt's fix (4.4.7 / 3.21.7) lowercases the path only in its app-level matcher and a
nitro-serverintegration. Thenitropack/runtimegetRouteRulesthat this middleware uses is a different code path the patch does not touch, so the bypass still reproduces on patched Nuxt (verified on 4.4.8).Fix
After checking the request path as-is, re-resolve route rules for the lowercased path (via a synthetic event with a fresh context so
getRouteRulesrecomputes rather than returning the cached result) and skip the write if either is shared-cacheable. Skipping is always fail-safe — the client hydrates the user.Tests
caching.test.ts):/Cacheablerenders anonymous SSR HTML (it leaked before the fix).Scope / base
Based on
issue-52because it hardens that branch's cache guard directly. The same fix (extended to cover theauth0.ssrUseropt-out) is also part of #55; this PR ensures the core #52 guard is protected independently of #55's merge timing.