Skip to content

Commit 22c4670

Browse files
committed
fix(route-rules): reject out-of-scope redirect requests
Mirrors the scope check added for proxy rules. An encoded traversal like `..%2f` bypasses the `/**` scope at match time but can escape the base once the redirect target decodes `%2f` → `/`, letting a victim's browser reach a sibling scope on the redirect host.
1 parent 850e95c commit 22c4670

2 files changed

Lines changed: 7 additions & 2 deletions

File tree

src/runtime/internal/route-rules.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ export const redirect: RouteRuleCtor<"redirect"> = ((m) =>
2929
let targetPath = event.url.pathname + event.url.search;
3030
const strpBase = (m.options as any)._redirectStripBase;
3131
if (strpBase) {
32+
if (!isPathInScope(event.url.pathname, strpBase)) {
33+
throw new HTTPError({ status: 400, message: "Invalid request path" });
34+
}
3235
targetPath = withoutBase(targetPath, strpBase);
3336
}
3437
target = joinURL(target.slice(0, -3), targetPath);
@@ -103,7 +106,8 @@ export const basicAuth: RouteRuleCtor<"auth"> = /* @__PURE__ */ Object.assign(
103106
// Check whether `pathname`, after canonicalization, stays within `base`.
104107
// Prevents match/forward differentials where an encoded traversal like `..%2f`
105108
// bypasses the `/**` scope at match time but escapes the base once the
106-
// upstream decodes `%2f` → `/` (GHSA-5w89-w975-hf9q).
109+
// downstream (proxy upstream or redirect target) decodes `%2f` → `/`
110+
// (GHSA-5w89-w975-hf9q).
107111
//
108112
// WHATWG URL keeps `%2F` and `%5C` opaque in paths, so we pre-decode those,
109113
// then let `new URL` resolve `.`/`..`/`%2E%2E` segments and normalize `\`.

test/unit/route-rules.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ describe("normalizeRouteRules - swr", () => {
3232
});
3333

3434
// Regression for GHSA-5w89-w975-hf9q: an encoded traversal like `..%2f` must
35-
// not let a request escape a `/**` proxy scope once the upstream decodes it.
35+
// not let a request escape a `/**` proxy/redirect scope once the downstream
36+
// decodes it.
3637
describe("isPathInScope", () => {
3738
it("accepts in-scope paths", () => {
3839
expect(isPathInScope("/api/orders/list.json", "/api/orders")).toBe(true);

0 commit comments

Comments
 (0)