diff --git a/src/application/javascript/JavaScriptArtifactPathResolution.ts b/src/application/javascript/JavaScriptArtifactPathResolution.ts index c2485a11d..bb93419d7 100644 --- a/src/application/javascript/JavaScriptArtifactPathResolution.ts +++ b/src/application/javascript/JavaScriptArtifactPathResolution.ts @@ -264,6 +264,12 @@ const htmlCandidate = ( return unresolvedOutcome(input, "external", [ "The document base href is external, so its script reference is not a local artifact path.", ]); + // Removing URL tabs and newlines can join an encoded dot or separator that + // the raw declaration split, so admit the parsed URL text again. + if (!admitsCanonicalPathSyntax(declared)) + return unresolvedOutcome(input, "rejected", [ + "Encoded dot or separator bytes are rejected before artifact path resolution.", + ]); const declaredPath = percentDecodeUrlPath(declared); if (declaredPath === null) return unresolvedOutcome(input, "rejected", [ diff --git a/src/application/javascript/JavaScriptHtmlBaseResolution.test.ts b/src/application/javascript/JavaScriptHtmlBaseResolution.test.ts index 49a5f7ad2..681d79099 100644 --- a/src/application/javascript/JavaScriptHtmlBaseResolution.test.ts +++ b/src/application/javascript/JavaScriptHtmlBaseResolution.test.ts @@ -150,6 +150,12 @@ describe("HTML reference URL text", () => { }); }, ); + it("rejects a base href whose URL whitespace splits an encoded separator", () => { + expect(resolve("app.js", "a%2\nf/")).toMatchObject({ + resolution_status: "rejected", + resolved_path: null, + }); + }); it("rejects a base href that decodes to NUL", () => { expect(resolve("app.js", "/a%00/")).toMatchObject({ resolution_status: "rejected", @@ -164,7 +170,7 @@ describe("HTML reference URL text", () => { }); }, ); - it.each(["%2e%2e/app.js", "a%2fapp.js"])( + it.each(["%2e%2e/app.js", "a%2fapp.js", "%2\nfapp.js", "%2\ne%2\te/app.js"])( "still rejects encoded dot and separator bytes in %j", (declared) => { expect(resolve(declared, "")).toMatchObject({