security(proxy-path): set Vary on public routes that echo x-proxy-path (cache poisoning)#8072
security(proxy-path): set Vary on public routes that echo x-proxy-path (cache poisoning)#8072JohnMcLear wants to merge 2 commits into
Conversation
…h (cache poisoning) The home page, pad page and timeslider (and the legacy timeslider redirect) echo the sanitised x-proxy-path prefix into rendered URLs, social-preview metadata, manifest links and the redirect target, but did not advertise Vary. A shared cache/CDN keyed on URL alone could store a prefix injected by one client and serve it to others. The admin routes already emit Vary: x-proxy-path (GHSA-fjgc-3mj7-8rg8); this extends the same protection to the public routes the earlier fix left uncovered. The value is still passed through sanitizeProxyPath (quotes/angle-brackets/protocol-relative/.. already blocked), so this is cache-correctness hardening, not XSS/open-redirect. Adds a varyOnProxyPath() helper applied at every sanitizeProxyPath call site, plus a regression test asserting Vary: x-proxy-path on /, /p/:pad, the timeslider embed page and the legacy redirect. Reported by meifukun. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
PR Summary by Qodosecurity(proxy-path): add Vary on public routes that echo x-proxy-path
AI Description
Diagram
High-Level Assessment
Files changed (2)
|
Code Review by Qodo
1.
|
There was a problem hiding this comment.
Pull request overview
This PR hardens Etherpad’s public HTML/redirect responses against cache poisoning when a reverse proxy injects a per-request subpath via x-proxy-path, by ensuring caches correctly key responses that reflect the sanitized proxy-path into rendered content and redirects.
Changes:
- Add a
varyOnProxyPath(res)helper and apply it at eachsanitizeProxyPath()call site inspecialpages.ts(dev-watch and production route blocks). - Add a new backend spec to assert
Vary: x-proxy-pathis present on key public routes (/,/p/:pad, timeslider embed, and the legacy timeslider redirect).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/node/hooks/express/specialpages.ts | Adds Vary behavior on public routes that render/redirect using the sanitized proxy-path prefix. |
| src/tests/backend/specs/proxyPathVary.ts | Adds regression tests asserting Vary: x-proxy-path on relevant public endpoints. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const PROXY_PATH_VARY_HEADERS = ['x-proxy-path', 'x-forwarded-prefix', 'x-ingress-path']; | ||
| const varyOnProxyPath = (res: any) => res.vary(PROXY_PATH_VARY_HEADERS); |
…y is enabled Addresses Qodo review on #8072. varyOnProxyPath() unconditionally varied on x-forwarded-prefix and x-ingress-path, but sanitizeProxyPath() ignores those two headers unless settings.trustProxy is true. Advertising them in Vary when trustProxy is false does not reflect a real dependency and only fragments shared caches on attacker-supplied header values. Always vary on x-proxy-path (always honored); add the two standard headers only when trustProxy is enabled. Test asserts the exclusion when trustProxy=false and inclusion when true. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks @qodo-free-for-open-source-projects — agreed. |
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
Thanks for the update. The implementation and coverage now correctly keep |
Follow-up hardening to GHSA-fjgc-3mj7-8rg8 (
x-proxy-pathadmin XSS/open-redirect, fixed in #7784). Reported privately by meifukun (https://github.com/meifukun).That advisory added
Vary: x-proxy-pathto the admin routes but left the public routes uncovered. The home page, pad page and timeslider (plus the legacy timeslider redirect) echo the sanitisedx-proxy-pathprefix into rendered URLs, social-preview metadata, manifest links and the redirect target — but advertised noVary. A shared cache/CDN keyed on URL alone could store a prefix injected by one client and serve it to others.Scope
This is cache-correctness hardening, not XSS/open-redirect — the value is still passed through
sanitizeProxyPath(quotes / angle-brackets / protocol-relative///..already blocked by #7784). Deliberately does not changex-proxy-path's trust model: it stays honored undertrustProxy: falsebecause subpath / Home-Assistant-ingress deployments rely on that default. The fix is purely additive (Vary).Change
Adds a
varyOnProxyPath(res)helper applied at everysanitizeProxyPath()call site inspecialpages.ts(both dev-watched and prod route blocks).Test
New
proxyPathVary.tsassertsVary: x-proxy-pathon/,/p/:pad, the timeslider embed page, and the legacy redirect. ExistingproxyPathRedirect.tsstill passes;tsc --noEmitclean.🤖 Generated with Claude Code