From e22d95b2e8627f92d56a735a38bf002beb661c3a Mon Sep 17 00:00:00 2001 From: Christoph Werner Date: Sat, 6 Jul 2024 23:38:12 +0200 Subject: [PATCH 1/5] feat: add the ability to define style-src --- src/__csp-nonce.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/__csp-nonce.ts b/src/__csp-nonce.ts index a38e4c9..44216b6 100644 --- a/src/__csp-nonce.ts +++ b/src/__csp-nonce.ts @@ -15,6 +15,7 @@ type Params = { unsafeEval: boolean; path: string | string[]; excludedPath: string[]; + styleSrc: boolean; }; const params = inputs as Params; @@ -78,7 +79,10 @@ const handler = async (request: Request, context: Context) => { `https:`, `http:`, ].filter(Boolean); - const scriptSrc = `script-src ${rules.join(" ")}`; + + const joinedRules = ${rules.join(" "); + const scriptSrc = `script-src ${joinedRules}`; + const styleSrc = `style-src ${joinedRules}`; const reportUri = `report-uri ${ params.reportUri || "/.netlify/functions/__csp-violations" }`; @@ -96,6 +100,12 @@ const handler = async (request: Request, context: Context) => { // https://github.com/netlify/plugin-csp-nonce/issues/72 return d.replace("script-src ", `${scriptSrc} `).trim(); } + // intentionally add trailing space to avoid mangling `style-src-elem` + if (params.styleSrc && d.startsWith("style-src ")) { + // append with trailing space to include any user-supplied values + // https://github.com/netlify/plugin-csp-nonce/issues/72 + return d.replace("style-src ", `${styleSrc} `).trim(); + } // intentionally omit report-uri: theirs should take precedence return d; }) @@ -104,6 +114,9 @@ const handler = async (request: Request, context: Context) => { if (!directives.find((d) => d.startsWith("script-src "))) { directives.push(scriptSrc); } + if (params.styleSrc && !directives.find((d) => d.startsWith("style-src "))) { + directives.push(styleSrc); + } if (!directives.find((d) => d.startsWith("report-uri"))) { directives.push(reportUri); } @@ -111,7 +124,7 @@ const handler = async (request: Request, context: Context) => { response.headers.set(header, value); } else { // make a new ruleset of directives if no CSP present - const value = [scriptSrc, reportUri].join("; "); + const value = [scriptSrc, params.styleSrc && styleSrc, reportUri].filter(Boolean).join("; "); response.headers.set(header, value); } From 9add6e90d9cafaaf69a0e4ea8f0ef682eec35521 Mon Sep 17 00:00:00 2001 From: Christoph Werner Date: Sat, 6 Jul 2024 23:59:01 +0200 Subject: [PATCH 2/5] fix: update manifest.yml with styleSrc --- manifest.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/manifest.yml b/manifest.yml index b5ab60d..a1e9028 100644 --- a/manifest.yml +++ b/manifest.yml @@ -14,3 +14,6 @@ inputs: - name: excludedPath description: The glob expressions of path(s) that *should not* invoke the CSP nonce edge function. Must be an array of strings. This value gets spread with common non-html filetype extensions (*.css, *.js, *.svg, etc) default: [] + - name: styleSrc + description: When true, adds nonce to the `style-src` directive of your Content Security Policy to prevent attackers from modifying the contents or appearance of your page. + default: false From 81fef24633112660e056690c170cc017770a06c4 Mon Sep 17 00:00:00 2001 From: Christoph Werner Date: Sun, 7 Jul 2024 00:13:25 +0200 Subject: [PATCH 3/5] fix: syntax error --- src/__csp-nonce.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__csp-nonce.ts b/src/__csp-nonce.ts index 44216b6..ba298b1 100644 --- a/src/__csp-nonce.ts +++ b/src/__csp-nonce.ts @@ -80,7 +80,7 @@ const handler = async (request: Request, context: Context) => { `http:`, ].filter(Boolean); - const joinedRules = ${rules.join(" "); + const joinedRules = rules.join(" "); const scriptSrc = `script-src ${joinedRules}`; const styleSrc = `style-src ${joinedRules}`; const reportUri = `report-uri ${ From 12a6f54bc6ea7978b61ae7a463c35c16430a0413 Mon Sep 17 00:00:00 2001 From: Christoph Werner Date: Sun, 7 Jul 2024 00:21:15 +0200 Subject: [PATCH 4/5] fix: add nonce to style tags --- src/__csp-nonce.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/__csp-nonce.ts b/src/__csp-nonce.ts index ba298b1..8d8c465 100644 --- a/src/__csp-nonce.ts +++ b/src/__csp-nonce.ts @@ -129,6 +129,11 @@ const handler = async (request: Request, context: Context) => { } const querySelectors = ["script", 'link[rel="preload"][as="script"]']; + if (params.styleSrc) { + querySelectors.push("style"); + querySelectors.push('link[rel="preload"][as="style"]'); + } + return new HTMLRewriter() .on(querySelectors.join(","), { element(element: HTMLElement) { From 0c884106d4563a05c1806e58fef7132bf255d0f1 Mon Sep 17 00:00:00 2001 From: Christoph Werner Date: Sun, 7 Jul 2024 00:37:30 +0200 Subject: [PATCH 5/5] docs: update README.md to include `styleSrc` --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 358510c..feeed82 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # @netlify/plugin-csp-nonce -Use a [nonce](https://content-security-policy.com/nonce/) for the `script-src` directive of your Content Security Policy (CSP) to help prevent [cross-site scripting (XSS)](https://developer.mozilla.org/en-US/docs/Web/Security/Types_of_attacks#cross-site_scripting_xss) attacks. +Use a [nonce](https://content-security-policy.com/nonce/) for the `script-src` (and optionally `style-src`) directive of your Content Security Policy (CSP) to help prevent [cross-site scripting (XSS)](https://developer.mozilla.org/en-US/docs/Web/Security/Types_of_attacks#cross-site_scripting_xss) attacks. This plugin deploys an edge function that adds a response header and transforms the HTML response body to contain a unique nonce on every request, along with an optional function to log CSP violations. @@ -70,6 +70,12 @@ _Default: `[]`_ The glob expressions of path(s) that _should not_ invoke the CSP nonce edge function. Must be an array of strings. This value gets spread with common non-html filetype extensions (`*.css`, `*.js`, `*.svg`, etc). +#### `styleSrc` + +_Default: `false`_ + +When true, adds nonce to the `style-src` directive of your Content Security Policy to prevent attackers from modifying the contents or appearance of your page. + ## Debugging ### Limiting edge function invocations