Skip to content

Commit 65c60df

Browse files
committed
fix(webapp): reject CSP-delimiter characters in the img-src allowlist
A CSP_IMG_SRC_ALLOWLIST entry containing ';' passed URL parsing and landed verbatim in the space-joined img-src directive, injecting or truncating a directive. Reject entries containing ';' or ',' alongside the existing wildcard and whitespace checks.
1 parent 502e1bd commit 65c60df

2 files changed

Lines changed: 19 additions & 0 deletions

File tree

apps/webapp/app/utils/cspImageOrigins.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,26 @@ describe("parseCspImageOrigins", () => {
5050
["https://example.com#frag", "must be an origin only, with no path, query or hash"],
5151
["example.com", "is not a valid absolute URL"],
5252
["https://user:pw@example.com", "must not contain credentials"],
53+
["https://a.com;script-src", "must not contain ';' or ',' — these delimit CSP directives"],
5354
])("rejects %s and says why", (value, reason) => {
5455
const { origins, rejected } = parseCspImageOrigins(value);
5556
expect(origins).toEqual([]);
5657
expect(rejected).toEqual([{ value, reason }]);
5758
});
5859

60+
it("does not let a ';' smuggle a second directive into img-src", () => {
61+
const { origins } = parseCspImageOrigins("https://a.com;script-src");
62+
expect(origins).toEqual([]);
63+
expect(buildImgSrcDirective(origins)).not.toContain("script-src");
64+
});
65+
66+
it("splits on ',' so a comma can never ride inside a single origin", () => {
67+
// "https://a.com" is valid; the "x" fragment after the comma is rejected on its own.
68+
const { origins } = parseCspImageOrigins("https://a.com,x");
69+
expect(origins).toEqual(["https://a.com"]);
70+
expect(origins.some((origin) => origin.includes(","))).toBe(false);
71+
});
72+
5973
it("keeps the valid entries when a sibling entry is rejected", () => {
6074
const { origins, rejected } = parseCspImageOrigins(
6175
"https://*.evil.com,https://sso.example.com"

apps/webapp/app/utils/cspImageOrigins.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ function rejectionReason(value: string, allowHttp: boolean): string | undefined
7373
if (/\s/.test(value)) {
7474
return "contains whitespace";
7575
}
76+
// `;` and `,` delimit CSP directives / source lists; an entry containing one would
77+
// land verbatim in the space-joined img-src and inject or truncate a directive.
78+
if (/[;,]/.test(value)) {
79+
return "must not contain ';' or ',' — these delimit CSP directives";
80+
}
7681

7782
let url: URL;
7883
try {

0 commit comments

Comments
 (0)