@netlify/plugin-csp-nonce 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,101 @@
1
1
  # @netlify/plugin-csp-nonce
2
2
 
3
- Use a nonce for the script-src and style-src directives of your Content Security Policy.
3
+ 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.
4
4
 
5
- This package deploys an edge function to add a header and transform the HTML response body, and a function to log CSP violations.
5
+ 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.
6
+
7
+ Scripts that do not contain a matching `nonce` attribute, or that were not created from a trusted script (see [strict-dynamic](https://content-security-policy.com/strict-dynamic/)), will not be allowed to run.
8
+
9
+ You can use this plugin whether or not your site already has a CSP in place. If your site already has a CSP, the nonce will merge with your existing directives.
10
+
11
+ ## Installation
12
+
13
+ First, add the dependency:
14
+
15
+ ```
16
+ npm i @netlify/plugin-csp-nonce
17
+ ```
18
+
19
+ Then, include the plugin inside your `netlify.toml`:
20
+
21
+ ```
22
+ # netlify.toml
23
+ [[plugins]]
24
+ package = "@netlify/plugin-csp-nonce"
25
+ ```
6
26
 
7
27
  ## Configuration options
8
28
 
9
- - `reportOnly`: When true, uses the Content-Security-Policy-Report-Only header instead of the Content-Security-Policy header.
10
- - `unsafeEval`: When true, adds 'unsafe-eval' to CSP for easier adoption. Set to false to have a safer policy if your code and code dependencies does not use eval().
11
- - `path`: The glob expressions of path(s) that should invoke the CSP nonce edge function. Can be a string or array of strings.
12
- - `excludedPath`: 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)
29
+ Sample configuration:
30
+
31
+ ```
32
+ # netlify.toml
33
+ [[plugins]]
34
+ package = "@netlify/plugin-csp-nonce"
35
+ [plugins.inputs]
36
+ reportOnly = false
37
+ excludedPath = [
38
+ "/api/*",
39
+ "**/*.bespoke.extension"
40
+ ]
41
+ ```
42
+
43
+ #### `reportOnly`
44
+
45
+ _Default: `true`_.
46
+
47
+ When true, uses the `Content-Security-Policy-Report-Only` header instead of the `Content-Security-Policy` header. Setting `reportOnly` to `true` is useful for testing the CSP with real production traffic without actually blocking resources. Be sure to monitor your logging function to observe potential violations.
48
+
49
+ #### `reportUri`
50
+
51
+ _Default: `undefined`_.
52
+
53
+ The relative or absolute URL to report any violations. If left undefined, violations are reported to the `__csp-violations` function, which this plugin deploys. If your site already has a `report-uri` directive defined in its CSP header, then that value will take precedence.
54
+
55
+ #### `unsafeEval`
56
+
57
+ _Default: `true`._
58
+
59
+ When true, adds `'unsafe-eval'` to the CSP for easier adoption. Set to `false` to have a safer policy if your code and code dependencies does not use `eval()`.
60
+
61
+ #### `path`
62
+
63
+ _Default: `"/_"`.\*
64
+
65
+ The glob expressions of path(s) that should invoke the CSP nonce edge function. Can be a string or array of strings.
66
+
67
+ #### `excludedPath`
68
+
69
+ _Default: `[]`_
70
+
71
+ 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).
72
+
73
+ ## Debugging
74
+
75
+ ### Limiting edge function invocations
76
+
77
+ By default, the edge function that inserts the nonce will be invoked on all requests whose path
78
+
79
+ - does not begin with `/.netlify/`
80
+ - does not end with common non-HTML filetype extensions
81
+
82
+ To further limit invocations, add globs to the `excludedPath` configuration option that are specific to your site.
83
+
84
+ Requests that invoke the nonce edge function will contain a `x-debug-csp-nonce: invoked` response header. Use this to determine if unwanted paths are invoking the edge function, and add those paths to the `excludedPath` array.
85
+
86
+ Also, monitor the edge function logs in the Netlify UI. If the edge function is invoked but the response is not transformed, the request's path will be logged.
87
+
88
+ ### Not transforming as expected
89
+
90
+ If your HTML does not contain the `nonce` attribute on the `<script>` tags that you expect, ensure that all of these criteria are met:
91
+
92
+ - The request method is `GET`
93
+ - The `accept` request header starts with `text/html`, or the `user-agent` request header starts with `curl/`
94
+ - The `content-type` response header starts with `text/html`
95
+ - The path of the request is satisfied by the `path` config option, and not included in the `excludedPath` config option
96
+
97
+ ### Quickly enabling and disabling
98
+
99
+ You may want to quickly enable/disable the plugin while monitoring violation reports. You can do so without modifying code.
100
+
101
+ Simply set the `DISABLE_CSP_NONCE` environment variable to `true`, and your next deploy will skip running the plugin. Setting to `false` will re-enable the plugin. The environment variable needs to be scoped to `Builds`.
package/index.js CHANGED
@@ -18,25 +18,29 @@ export const onPreBuild = async ({ inputs, netlifyConfig, utils }) => {
18
18
  ? "./src"
19
19
  : "./node_modules/@netlify/plugin-csp-nonce/src";
20
20
 
21
- const functionsDir = build.functions || "./netlify/functions";
22
- // make the directory in case it actually doesn't exist yet
23
- await utils.run.command(`mkdir -p ${functionsDir}`);
24
- console.log(` Copying function to ${functionsDir}...`);
25
- copyFileSync(
26
- `${basePath}/__csp-violations.ts`,
27
- `${functionsDir}/__csp-violations.ts`
28
- );
29
-
30
21
  const edgeFunctionsDir = build.edge_functions || "./netlify/edge-functions";
31
22
  // make the directory in case it actually doesn't exist yet
32
23
  await utils.run.command(`mkdir -p ${edgeFunctionsDir}`);
33
- console.log(` Copying edge function to ${edgeFunctionsDir}...`);
24
+ console.log(` Writing nonce edge function to ${edgeFunctionsDir}...`);
34
25
  copyFileSync(
35
26
  `${basePath}/__csp-nonce.ts`,
36
27
  `${edgeFunctionsDir}/__csp-nonce.ts`
37
28
  );
38
- console.log(` Copying config inputs to ${edgeFunctionsDir}...`);
39
29
  fs.writeFileSync(`${edgeFunctionsDir}/__csp-nonce-inputs.json`, config);
40
30
 
41
- console.log(` Complete.`);
31
+ // if no reportUri in config input, deploy function on site's behalf
32
+ if (!inputs.reportUri) {
33
+ const functionsDir = build.functions || "./netlify/functions";
34
+ // make the directory in case it actually doesn't exist yet
35
+ await utils.run.command(`mkdir -p ${functionsDir}`);
36
+ console.log(` Writing violations logging function to ${functionsDir}...`);
37
+ copyFileSync(
38
+ `${basePath}/__csp-violations.ts`,
39
+ `${functionsDir}/__csp-violations.ts`
40
+ );
41
+ } else {
42
+ console.log(` Using ${inputs.reportUri} as report-uri directive...`);
43
+ }
44
+
45
+ console.log(` Done.`);
42
46
  };
package/manifest.yml CHANGED
@@ -3,6 +3,8 @@ inputs:
3
3
  - name: reportOnly
4
4
  description: When true, uses the Content-Security-Policy-Report-Only header instead of the Content-Security-Policy header.
5
5
  default: true
6
+ - name: reportUri
7
+ description: The relative or absolute URL to report any violations. If not defined, violations are reported to the __csp-violations function, which this plugin deploys.
6
8
  - name: unsafeEval
7
9
  description: When true, adds 'unsafe-eval' to CSP for easier adoption. Set to false to have a safer policy if your code and code dependencies does not use eval().
8
10
  default: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-csp-nonce",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Use a nonce for the script-src and style-src directives of your Content Security Policy.",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -1,5 +1 @@
1
- {
2
- "reportOnly": false,
3
- "path": "/*",
4
- "excludedPath": []
5
- }
1
+ {}
@@ -8,6 +8,7 @@ import inputs from "./__csp-nonce-inputs.json" assert { type: "json" };
8
8
 
9
9
  type Params = {
10
10
  reportOnly: boolean;
11
+ reportUri?: string;
11
12
  unsafeEval: boolean;
12
13
  path: string | string[];
13
14
  excludedPath: string[];
@@ -34,6 +35,8 @@ const handler = async (request: Request, context: Context) => {
34
35
  .startsWith("text/html");
35
36
  const shouldTransformResponse = isGET && isHTMLRequest && isHTMLResponse;
36
37
  if (!shouldTransformResponse) {
38
+ // @ts-expect-error
39
+ console.log(`Unnecessary invocation for ${request.path || request.url}`);
37
40
  return response;
38
41
  }
39
42
 
@@ -52,7 +55,9 @@ const handler = async (request: Request, context: Context) => {
52
55
  `http:`,
53
56
  ].filter(Boolean);
54
57
  const scriptSrc = `script-src ${rules.join(" ")}`;
55
- const reportUri = `report-uri /.netlify/functions/__csp-violations`;
58
+ const reportUri = `report-uri ${
59
+ params.reportUri || "/.netlify/functions/__csp-violations"
60
+ }`;
56
61
 
57
62
  const csp = response.headers.get(header);
58
63
  if (csp) {