mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 08:25:34 +02:00
[PR #888] [MERGED] fix: centralize protocol-relative URL guard to handle percent-encoded delimiters #920
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#920
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/cloudflare/vinext/pull/888
Author: @southpolesteve
Created: 4/24/2026
Status: ✅ Merged
Merged: 4/24/2026
Merged by: @southpolesteve
Base:
main← Head:fix/normalize-encoded-delimiters-in-protocol-guard📝 Commits (1)
4c8d73dfix: centralize protocol-relative URL guard to handle percent-encoded delimiters📊 Changes
9 files changed (+244 additions, -43 deletions)
View changed files
📝
packages/vinext/src/deploy.ts(+21 -4)📝
packages/vinext/src/index.ts(+7 -5)📝
packages/vinext/src/server/app-router-entry.ts(+7 -7)📝
packages/vinext/src/server/app-ssr-entry.ts(+4 -1)📝
packages/vinext/src/server/prod-server.ts(+28 -19)📝
packages/vinext/src/server/request-pipeline.ts(+58 -3)📝
tests/deploy.test.ts(+8 -3)📝
tests/request-pipeline.test.ts(+108 -0)📝
tests/shims.test.ts(+3 -1)📄 Description
Summary
Six places in the request pipeline inlined a variation of:
to reject protocol-relative paths before they reach the trailing-slash redirect emitter (which would otherwise echo them back into a
Locationheader). The check handles literal delimiters but not their percent-encoded forms —/%5Cevil.com/survives the guard, thennormalizePathnameForRouteMatchStrictre-encodes the decoded backslash back to%5C(preserving it as part of a single path segment), so the 308 trailing-slash redirect ends up withLocation: /%5Cevil.com. Browsers percent-decodeLocationheaders and WHATWG URL treats\as/, so the browser resolves that to a protocol-relative host.Changes
Factor the leading-segment shape check into a new
isOpenRedirectShapedhelper inserver/request-pipeline.tsthat recognises literal (//,/\) and percent-encoded (%5C,%2F) variants, and swap each call site over to it. Also add a defense-in-depth check innormalizeTrailingSlashso a future caller that skips the upstream guard still can't emit a badLocation.Updated call sites:
server/request-pipeline.ts—guardProtocolRelativeUrl+normalizeTrailingSlashserver/prod-server.ts— App Router and Pages Router Node handlersserver/app-router-entry.ts— default Cloudflare Worker entryserver/app-ssr-entry.ts— SSR environment entryindex.ts— Pages Router Vite dev middlewaredeploy.ts— generated Pages Router Cloudflare Worker (inlined copy kept in sync)Tests
Regression tests in
tests/request-pipeline.test.tscover the encoded variants plus the defense-in-depth path. Updates the pinned string assertion intests/deploy.test.tsthat was checking for the old inlined pattern.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.