-
Notifications
You must be signed in to change notification settings - Fork 723
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Normalize workers assets path (#7318)
* Normalize double slashes into single in Workers Assets * Remove multiple slashes from asset pathing --------- Co-authored-by: Daniel Walsh <walshy@cloudflare.com>
- Loading branch information
1 parent
3154114
commit 6ba5903
Showing
4 changed files
with
176 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@cloudflare/workers-shared": minor | ||
--- | ||
|
||
Prevent same-schema attacks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { SELF } from "cloudflare:test"; | ||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; | ||
import { applyConfigurationDefaults } from "../../packages/workers-shared/asset-worker/src/configuration"; | ||
import Worker from "../../packages/workers-shared/asset-worker/src/index"; | ||
import { getAssetWithMetadataFromKV } from "../../packages/workers-shared/asset-worker/src/utils/kv"; | ||
import type { AssetMetadata } from "../../packages/workers-shared/asset-worker/src/utils/kv"; | ||
|
||
const IncomingRequest = Request<unknown, IncomingRequestCfProperties>; | ||
|
||
vi.mock("../../packages/workers-shared/asset-worker/src/utils/kv.ts"); | ||
vi.mock("../../packages/workers-shared/asset-worker/src/configuration"); | ||
const existsMock = (fileList: Set<string>) => { | ||
vi.spyOn(Worker.prototype, "unstable_exists").mockImplementation( | ||
async (pathname: string) => { | ||
if (fileList.has(pathname)) { | ||
return pathname; | ||
} | ||
} | ||
); | ||
}; | ||
const BASE_URL = "http://example.com"; | ||
|
||
describe("[Asset Worker] `test url normalization`", () => { | ||
afterEach(() => { | ||
vi.mocked(getAssetWithMetadataFromKV).mockRestore(); | ||
}); | ||
beforeEach(() => { | ||
vi.mocked(getAssetWithMetadataFromKV).mockImplementation( | ||
() => | ||
Promise.resolve({ | ||
value: "no-op", | ||
metadata: { | ||
contentType: "no-op", | ||
}, | ||
}) as unknown as Promise< | ||
KVNamespaceGetWithMetadataResult<ReadableStream, AssetMetadata> | ||
> | ||
); | ||
|
||
vi.mocked(applyConfigurationDefaults).mockImplementation(() => { | ||
return { | ||
html_handling: "none", | ||
not_found_handling: "none", | ||
}; | ||
}); | ||
}); | ||
|
||
it("returns 404 for non matched encoded url", async () => { | ||
const files = ["/christmas/starts/november/first.html"]; | ||
const requestPath = "/%2f%2fbad.example.com%2f"; | ||
|
||
existsMock(new Set(files)); | ||
const request = new IncomingRequest(BASE_URL + requestPath); | ||
let response = await SELF.fetch(request, { redirect: "manual" }); | ||
expect(response.status).toBe(404); | ||
}); | ||
|
||
it("returns 200 for matched non encoded url", async () => { | ||
const files = ["/you/lost/the/game.bin"]; | ||
const requestPath = "/you/lost/the/game.bin"; | ||
|
||
existsMock(new Set(files)); | ||
const request = new IncomingRequest(BASE_URL + requestPath); | ||
let response = await SELF.fetch(request, { redirect: "manual" }); | ||
expect(response.status).toBe(200); | ||
}); | ||
|
||
it("returns redirect for matched encoded url", async () => { | ||
const files = ["/awesome/file.bin"]; | ||
const requestPath = "/awesome/file%2ebin"; | ||
const finalPath = "/awesome/file.bin"; | ||
|
||
existsMock(new Set(files)); | ||
const request = new IncomingRequest(BASE_URL + requestPath); | ||
let response = await SELF.fetch(request, { redirect: "manual" }); | ||
expect(response.status).toBe(307); | ||
expect(response.headers.get("location")).toBe(finalPath); | ||
}); | ||
|
||
it("returns 200 for matched non encoded url", async () => { | ||
const files = ["/mylittlepony.png"]; | ||
const requestPath = "/mylittlepony.png"; | ||
|
||
existsMock(new Set(files)); | ||
const request = new IncomingRequest(BASE_URL + requestPath); | ||
let response = await SELF.fetch(request, { redirect: "manual" }); | ||
expect(response.status).toBe(200); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { SELF } from "cloudflare:test"; | ||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; | ||
import { applyConfigurationDefaults } from "../../packages/workers-shared/asset-worker/src/configuration"; | ||
import Worker from "../../packages/workers-shared/asset-worker/src/index"; | ||
import { getAssetWithMetadataFromKV } from "../../packages/workers-shared/asset-worker/src/utils/kv"; | ||
import type { AssetMetadata } from "../../packages/workers-shared/asset-worker/src/utils/kv"; | ||
|
||
const IncomingRequest = Request<unknown, IncomingRequestCfProperties>; | ||
|
||
vi.mock("../../packages/workers-shared/asset-worker/src/utils/kv.ts"); | ||
vi.mock("../../packages/workers-shared/asset-worker/src/configuration"); | ||
const existsMock = (fileList: Set<string>) => { | ||
vi.spyOn(Worker.prototype, "unstable_exists").mockImplementation( | ||
async (pathname: string) => { | ||
if (fileList.has(pathname)) { | ||
return pathname; | ||
} | ||
} | ||
); | ||
}; | ||
const BASE_URL = "http://example.com"; | ||
|
||
describe("[Asset Worker] `test redirects`", () => { | ||
afterEach(() => { | ||
vi.mocked(getAssetWithMetadataFromKV).mockRestore(); | ||
}); | ||
beforeEach(() => { | ||
vi.mocked(getAssetWithMetadataFromKV).mockImplementation( | ||
() => | ||
Promise.resolve({ | ||
value: "no-op", | ||
metadata: { | ||
contentType: "no-op", | ||
}, | ||
}) as unknown as Promise< | ||
KVNamespaceGetWithMetadataResult<ReadableStream, AssetMetadata> | ||
> | ||
); | ||
|
||
vi.mocked(applyConfigurationDefaults).mockImplementation(() => { | ||
return { | ||
html_handling: "none", | ||
not_found_handling: "none", | ||
}; | ||
}); | ||
}); | ||
|
||
it("returns 200 leading encoded double slash", async () => { | ||
const files = ["/blog/index.html"]; | ||
const requestPath = "/%2fblog/index.html"; | ||
|
||
existsMock(new Set(files)); | ||
const request = new IncomingRequest(BASE_URL + requestPath); | ||
let response = await SELF.fetch(request); | ||
expect(response.status).toBe(200); | ||
}); | ||
|
||
it("returns 200 leading non encoded double slash", async () => { | ||
const files = ["/blog/index.html"]; | ||
const requestPath = "//blog/index.html"; | ||
|
||
existsMock(new Set(files)); | ||
const request = new IncomingRequest(BASE_URL + requestPath); | ||
let response = await SELF.fetch(request); | ||
expect(response.status).toBe(200); | ||
}); | ||
|
||
it("returns 404 for non matched url", async () => { | ||
const files = ["/blog/index.html"]; | ||
const requestPath = "/%2fexample.com/"; | ||
|
||
existsMock(new Set(files)); | ||
const request = new IncomingRequest(BASE_URL + requestPath); | ||
let response = await SELF.fetch(request); | ||
expect(response.status).toBe(404); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters