Skip to content

Commit 2e78812

Browse files
authored
fix: better handler malformed paths (#7612)
* fix: better handler malformed paths * chore: changeset * fix: pass-through malformed paths better
1 parent b9533a3 commit 2e78812

File tree

3 files changed

+59
-2
lines changed

3 files changed

+59
-2
lines changed

.changeset/empty-cars-provide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cloudflare/workers-shared": patch
3+
---
4+
5+
fix: resolves an issue where a malformed path such as `https://example.com/%A0` would cause an unhandled error

packages/workers-shared/asset-worker/src/handler.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,14 @@ const safeRedirect = async (
682682
export const decodePath = (pathname: string) => {
683683
return pathname
684684
.split("/")
685-
.map((x) => decodeURIComponent(x))
685+
.map((x) => {
686+
try {
687+
const decoded = decodeURIComponent(x);
688+
return decoded;
689+
} catch {
690+
return x;
691+
}
692+
})
686693
.join("/");
687694
};
688695
/**
@@ -692,6 +699,13 @@ export const decodePath = (pathname: string) => {
692699
const encodePath = (pathname: string) => {
693700
return pathname
694701
.split("/")
695-
.map((x) => encodeURIComponent(x))
702+
.map((x) => {
703+
try {
704+
const encoded = encodeURIComponent(x);
705+
return encoded;
706+
} catch {
707+
return x;
708+
}
709+
})
696710
.join("/");
697711
};

packages/workers-shared/asset-worker/tests/handler.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,42 @@ describe("[Asset Worker] `handleRequest`", () => {
150150

151151
expect(response.status).toBe(404);
152152
});
153+
154+
it("returns expected responses for malformed path", async () => {
155+
const assets: Record<string, string> = {
156+
"/index.html": "aaaaaaaaaa",
157+
"/%A0%A0.html": "bbbbbbbbbb",
158+
};
159+
const configuration: Required<AssetConfig> = {
160+
html_handling: "drop-trailing-slash",
161+
not_found_handling: "none",
162+
serve_directly: true,
163+
};
164+
165+
const exists = async (pathname: string) => {
166+
return assets[pathname] ?? null;
167+
};
168+
const getByEtag = async (_: string) => ({
169+
readableStream: new ReadableStream(),
170+
contentType: "text/html",
171+
});
172+
173+
// first malformed URL should return 404 as no match above
174+
const response = await handleRequest(
175+
new Request("https://example.com/%A0"),
176+
configuration,
177+
exists,
178+
getByEtag
179+
);
180+
expect(response.status).toBe(404);
181+
182+
// but second malformed URL should return 307 as it matches and then redirects
183+
const response2 = await handleRequest(
184+
new Request("https://example.com/%A0%A0"),
185+
configuration,
186+
exists,
187+
getByEtag
188+
);
189+
expect(response2.status).toBe(307);
190+
});
153191
});

0 commit comments

Comments
 (0)