9 |
10 |
--------------------------------------------------------------------------------
/strict-csp/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: '@typescript-eslint/parser',
4 | plugins: [
5 | '@typescript-eslint',
6 | ],
7 | extends: [
8 | 'eslint:recommended',
9 | 'plugin:@typescript-eslint/recommended',
10 | ],
11 | };
12 |
--------------------------------------------------------------------------------
/strict-csp/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "lib": ["es6"],
6 | "allowJs": true,
7 | "outDir": "build",
8 | "rootDir": ".",
9 | "strict": true,
10 | "declaration": true,
11 | "skipLibCheck": true,
12 | "noImplicitAny": true,
13 | "esModuleInterop": true,
14 | "resolveJsonModule": true
15 | },
16 | "include": [
17 | "index.ts"
18 | ],
19 | "exclude": [
20 | "**/*.test.ts",
21 | "jest.config.js"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 |
4 | # dependencies
5 | strict-csp-html-webpack-plugin/node_modules
6 | react-app/node_modules
7 | strict-csp/node_modules
8 |
9 | # testing
10 | react-app/coverage
11 |
12 | # built
13 | strict-csp/build
14 | react-app/build
15 | react-app/dist
16 |
17 | # misc
18 | .DS_Store
19 | .env.local
20 | .env.development.local
21 | .env.test.local
22 | .env.production.local
23 | .eslintcache
24 | react-app/.pnp
25 | react-app/.pnp.js
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 | react-app/
--------------------------------------------------------------------------------
/.github/workflows/npm-publish-webpack.yml:
--------------------------------------------------------------------------------
1 | name: Node.js webpack Package
2 | on:
3 | workflow_dispatch:
4 | release:
5 | types: [created]
6 | defaults:
7 | run:
8 | working-directory: strict-csp-html-webpack-plugin
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | # Setup .npmrc file to publish to npm
15 | - uses: actions/setup-node@v2
16 | with:
17 | node-version: '20.x'
18 | registry-url: 'https://registry.npmjs.org'
19 | - run: npm install
20 | - run: npm publish
21 | env:
22 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/npm-publish-strict-csp.yml:
--------------------------------------------------------------------------------
1 | name: Node.js strict-csp Package
2 | on:
3 | workflow_dispatch:
4 | release:
5 | types: [created]
6 | defaults:
7 | run:
8 | working-directory: strict-csp
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | # Setup .npmrc file to publish to npm
15 | - uses: actions/setup-node@v2
16 | with:
17 | node-version: '20.x'
18 | registry-url: 'https://registry.npmjs.org'
19 | - run: npm install
20 | - run: npm run build
21 | - run: npm publish
22 | env:
23 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
--------------------------------------------------------------------------------
/strict-csp-html-webpack-plugin/test-fixture/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 | const StrictCspHtmlWebpackPlugin = require(path.resolve(__dirname, '..'));
4 |
5 | module.exports = {
6 | context: __dirname,
7 | mode: 'production',
8 | entry: {
9 | library1: './src/library1.js',
10 | app: './src/app.js',
11 | library2: './src/library2.js',
12 | },
13 | output: {
14 | path: path.resolve(__dirname, 'dist'),
15 | filename: '[name].bundle.js',
16 | },
17 | plugins: [
18 | new HtmlWebpackPlugin({
19 | template: './src/template.html',
20 | }),
21 | new StrictCspHtmlWebpackPlugin(HtmlWebpackPlugin),
22 | ],
23 | };
24 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Node.js CI
2 |
3 | on:
4 | pull_request:
5 | branches: [ main ]
6 |
7 | jobs:
8 | build-and-test:
9 | runs-on: ubuntu-latest
10 |
11 | strategy:
12 | matrix:
13 | node-version: [20.x, 22.x]
14 |
15 | steps:
16 | - uses: actions/checkout@v3
17 |
18 | - name: Use Node.js ${{ matrix.node-version }}
19 | uses: actions/setup-node@v3
20 | with:
21 | node-version: ${{ matrix.node-version }}
22 |
23 | - name: Set up development environment
24 | run: sh ./dev-setup.sh
25 |
26 | - name: Run tests for strict-csp
27 | run: npm test
28 | working-directory: ./strict-csp
29 |
30 | - name: Run integration tests for webpack-plugin
31 | run: npm test
32 | working-directory: ./strict-csp-html-webpack-plugin
33 |
34 | - name: Tear down development environment
35 | # Always run this step, even if tests fail
36 | if: always()
37 | run: bash ./undo-dev-setup.sh
--------------------------------------------------------------------------------
/strict-csp-html-webpack-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "strict-csp-html-webpack-plugin",
3 | "version": "1.0.2",
4 | "description": "A webpack plugin that adds a hash-based strict CSP to help protect your site against XSS attacks.",
5 | "main": "plugin.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/google/strict-csp.git",
9 | "directory": "strict-csp-html-webpack-plugin"
10 | },
11 | "devDependencies": {
12 | "cheerio": "^1.1.2",
13 | "html-webpack-plugin": "^5.6.4",
14 | "jest": "^30.1.3",
15 | "webpack": "^5.101.3",
16 | "webpack-cli": "^6.0.1"
17 | },
18 | "scripts": {
19 | "test": "cd .. && bash ./dev-setup.sh && cd ./strict-csp-html-webpack-plugin && jest && cd .. && bash ./undo-dev-setup.sh"
20 | },
21 | "author": "Lukas Weichselbaum ",
22 | "contributors": [
23 | "Maud Nalpas "
24 | ],
25 | "keywords": [
26 | "csp",
27 | "content-security-policy",
28 | "security"
29 | ],
30 | "license": "Apache-2.0"
31 | }
32 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement (CLA). You (or your employer) retain the copyright to your
10 | contribution; this simply gives us permission to use and redistribute your
11 | contributions as part of the project. Head over to
12 | to see your current agreements on file or
13 | to sign a new one.
14 |
15 | You generally only need to submit a CLA once, so if you've already submitted one
16 | (even if it was for a different project), you probably don't need to do it
17 | again.
18 |
19 | ## Code Reviews
20 |
21 | All submissions, including submissions by project members, require review. We
22 | use GitHub pull requests for this purpose. Consult
23 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
24 | information on using pull requests.
25 |
26 | ## Community Guidelines
27 |
28 | This project follows
29 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/).
30 |
--------------------------------------------------------------------------------
/strict-csp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "strict-csp",
3 | "version": "1.1.2",
4 | "description": "Enables a hash-based strict Content Security Policy for static HTML files and single page applications.",
5 | "main": "build/index.js",
6 | "types": "build/index.d.ts",
7 | "scripts": {
8 | "build": "rimraf build && tsc",
9 | "auto-csp": "npm run build && node build/index.js",
10 | "lint": "eslint . --ext .ts",
11 | "tsc": "tsc",
12 | "test": "npm run build && tsc --noEmit && jest"
13 | },
14 | "keywords": [
15 | "csp",
16 | "content-security-policy",
17 | "security"
18 | ],
19 | "author": "Lukas Weichselbaum ",
20 | "contributors": [
21 | "Maud Nalpas "
22 | ],
23 | "license": "Apache-2.0",
24 | "prepublish": "tsc",
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/google/strict-csp.git",
28 | "directory": "strict-csp"
29 | },
30 | "dependencies": {
31 | "@types/cheerio": "^0.22.23",
32 | "cheerio": "^1.1.2"
33 | },
34 | "devDependencies": {
35 | "@types/jest": "^30.0.0",
36 | "@types/node": "^20.0.0",
37 | "@typescript-eslint/eslint-plugin": "^4.10.0",
38 | "@typescript-eslint/parser": "^4.10.0",
39 | "eslint": "^7.15.0",
40 | "jest": "^30.1.3",
41 | "rimraf": "^3.0.2",
42 | "ts-jest": "^29.4.1",
43 | "typescript": "^4.1.3"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Glossary
2 |
3 | - CSP (content-security-policy): A layer of security that can be added to web apps as an HTTP header or meta tag. [Source: MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
4 | - Strict CSP: A specific set of CSP directives that has been identified as an effective and deployable mitigation against XSS (cross-site scripting). XSS is one of the most widespread sedcurity exploits. [Source: w3c](https://w3c.github.io/webappsec-csp/#strict-csp).
5 | - SPA (single-page application): a web app implementation that loads a single web document. When different content needs to be shown, it updates the body content of that document. [Source: MDN](https://developer.mozilla.org/en-US/docs/Glossary/SPA)
6 |
7 | ## About this repo
8 |
9 | Two codebases are in this repo:
10 |
11 | - `strict-csp`: a **bundler-agnostic library**, that can be used to generate a CSP. It now includes support for Trusted Types and violation reporting. [Go to strict-csp](/strict-csp)
12 |
13 | - `strict-csp-html-webpack-plugin`: a **webpack plugin** that configures a strict, hash-based CSP for an SPA. It uses the `strict-csp` library to form a CSP and hooks into the popular `HtmlWebpackPlugin` to set up this CSP as a `meta` HTML tag. [Go to strict-csp-html-webpack-plugin](/strict-csp-html-webpack-plugin)
14 |
15 | Both of these are available as separate npm packages.
16 |
17 | ## Setup for development purposes
18 |
19 | See [DEVELOP.md](/DEVELOP.md).
20 |
21 | ## Resources
22 | * [Mitigate cross-site scripting (XSS) with a strict Content Security Policy (CSP)](https://web.dev/strict-csp/)
23 |
--------------------------------------------------------------------------------
/strict-csp-html-webpack-plugin/plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2021 Google LLC
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * https://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | const strictCspLib = require('strict-csp');
18 |
19 | const defaultOptions = {
20 | enabled: true,
21 | trustedTypes: false,
22 | unsafeEval: false,
23 | reportUri: '',
24 | };
25 |
26 | class StrictCspHtmlWebpackPlugin {
27 | /**
28 | *
29 | * @param {object} options Additional options for this module.
30 | */
31 | constructor(htmlWebpackPlugin, options = {}) {
32 | this.htmlWebpackPlugin = htmlWebpackPlugin;
33 | this.options = { ...defaultOptions, ...options };
34 | }
35 |
36 | /**
37 | * Processes HtmlWebpackPlugin's html data by adding the CSP
38 | * @param htmlPluginData
39 | * @param compileCb
40 | */
41 | processCsp(compilation, htmlPluginData, compileCb) {
42 | if (this.options.enabled) {
43 | const {
44 | trustedTypes,
45 | enableTrustedTypes,
46 | enableTrustedTypesReportOnly,
47 | enableUnsafeEval,
48 | reportUri,
49 | } = this.options;
50 |
51 | const processor = new strictCspLib.StrictCsp(htmlPluginData.html, {
52 | trustedTypes:
53 | enableTrustedTypesReportOnly || trustedTypes === 'report-only'
54 | ? 'report-only'
55 | : enableTrustedTypes || trustedTypes,
56 | unsafeEval: enableUnsafeEval,
57 | reportUri: reportUri,
58 | browserFallbacks: true, // Fallbacks are always enabled in the plugin
59 | });
60 |
61 | const { csp } = processor.process();
62 | htmlPluginData.html = processor.serializeDomWithStrictCspMetaTag(csp);
63 | }
64 |
65 | return compileCb(null, htmlPluginData);
66 | }
67 |
68 | /**
69 | * Hooks into webpack to collect assets and hash them, build the policy, and add it into our HTML template
70 | * @param compiler
71 | */
72 | apply(compiler) {
73 | compiler.hooks.compilation.tap(
74 | 'StrictCspHtmlWebpackPlugin',
75 | (compilation) => {
76 | const hook =
77 | typeof this.htmlWebpackPlugin.getHooks === 'function'
78 | ? this.htmlWebpackPlugin.getHooks(compilation).beforeEmit // html-webpack-plugin v4 and above
79 | : compilation.hooks.htmlWebpackPluginAfterHtmlProcessing; // html-webpack-plugin v3
80 |
81 | hook.tapAsync(
82 | 'StrictCspHtmlWebpackPlugin',
83 | this.processCsp.bind(this, compilation)
84 | );
85 | }
86 | );
87 | }
88 | }
89 |
90 | module.exports = StrictCspHtmlWebpackPlugin;
91 |
--------------------------------------------------------------------------------
/strict-csp-html-webpack-plugin/integration.test.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const fs = require('fs');
4 | const cheerio = require('cheerio');
5 | const HtmlWebpackPlugin = require('html-webpack-plugin');
6 | const StrictCspHtmlWebpackPlugin = require(path.resolve(__dirname, './plugin.js'));
7 |
8 | const runWebpack = (options, done) => {
9 | const config = {
10 | context: __dirname,
11 | mode: 'production',
12 | entry: {
13 | library1: './test-fixture/src/library1.js',
14 | app: './test-fixture/src/app.js',
15 | library2: './test-fixture/src/library2.js',
16 | },
17 | output: {
18 | path: path.resolve(__dirname, 'test-fixture/dist'),
19 | filename: '[name].bundle.js',
20 | },
21 | plugins: [
22 | new HtmlWebpackPlugin({
23 | template: './test-fixture/src/template.html',
24 | }),
25 | new StrictCspHtmlWebpackPlugin(HtmlWebpackPlugin, options),
26 | ],
27 | };
28 |
29 | webpack(config, (err, stats) => {
30 | if (err) {
31 | return done(err);
32 | }
33 | if (stats.hasErrors()) {
34 | return done(new Error(stats.toJson().errors.map((e) => e.message).join('\n')));
35 | }
36 |
37 | const outputFile = path.resolve(
38 | __dirname,
39 | 'test-fixture/dist/index.html'
40 | );
41 | const outputHtml = fs.readFileSync(outputFile, 'utf8');
42 | done(null, outputHtml);
43 | });
44 | };
45 |
46 | describe('StrictCspHtmlWebpackPlugin Integration Test', () => {
47 | it('should build successfully without Trusted Types', (done) => {
48 | runWebpack({}, (err, outputHtml) => {
49 | if (err) return done(err);
50 | const $ = cheerio.load(outputHtml);
51 | const metaTag = $('meta[http-equiv="Content-Security-Policy"]');
52 | expect(metaTag.length).toBe(1);
53 | const cspContent = metaTag.attr('content');
54 | expect(cspContent).not.toContain('require-trusted-types-for');
55 | expect(outputHtml).toMatchSnapshot();
56 | done();
57 | });
58 | });
59 |
60 | it('should build successfully with Trusted Types enabled', (done) => {
61 | runWebpack({ trustedTypes: true }, (err, outputHtml) => {
62 | if (err) return done(err);
63 | const $ = cheerio.load(outputHtml);
64 | const metaTag = $('meta[http-equiv="Content-Security-Policy"]');
65 | expect(metaTag.length).toBe(1);
66 | const cspContent = metaTag.attr('content');
67 | expect(cspContent).toContain("require-trusted-types-for 'script'");
68 | expect(outputHtml).toMatchSnapshot();
69 | done();
70 | });
71 | });
72 |
73 | it('should build successfully with Trusted Types in report-only mode', (done) => {
74 | runWebpack(
75 | {
76 | trustedTypes: 'report-only',
77 | reportUri: 'https://example.com/report',
78 | },
79 | (err, outputHtml) => {
80 | if (err) return done(err);
81 | const $ = cheerio.load(outputHtml);
82 | const metaTag = $('meta[http-equiv="Content-Security-Policy"]');
83 | expect(metaTag.length).toBe(1);
84 | const cspContent = metaTag.attr('content');
85 | expect(cspContent).toContain("require-trusted-types-for 'script'");
86 | const loaderScript = $('script:not([src])').html();
87 | expect(loaderScript).toContain('const generateAndSendReport');
88 | expect(outputHtml).toMatchSnapshot();
89 | done();
90 | }
91 | );
92 | });
93 | });
--------------------------------------------------------------------------------
/strict-csp-html-webpack-plugin/test-fixture/dist/index.html:
--------------------------------------------------------------------------------
1 | Complex Test
Hello World
--------------------------------------------------------------------------------
/DEVELOP.md:
--------------------------------------------------------------------------------
1 | # DEVELOP
2 |
3 | ## Quickstart
4 |
5 | 1. Dev setup:
6 |
7 | ```bash
8 | sh ./dev-setup.sh
9 | ```
10 |
11 | This will create some of the local symlinks you need (see details below).
12 |
13 | 2. Create a test React app and link it with your local plugin
14 |
15 | ```bash
16 | npx create-react-app react-app && cd react-app
17 | # make the local CSP plugin instance available in the React app
18 | # note that the symlink 'strict-csp-html-webpack-plugin' has been creaed at Step 1
19 | npm i --save strict-csp-html-webpack-plugin@beta
20 | npm link 'strict-csp-html-webpack-plugin'
21 | # eject so you can edit the webpack config to add CSP functionality to the React app
22 | npm run eject
23 | ```
24 |
25 | (Note: if you need to unlink later: `cd react-app && npm unlink 'strict-csp-html-webpack-plugin' && cd ..`)
26 |
27 | 3. Add CSP functionality to the React app
28 |
29 | In react-app's `webpack.config.js`:
30 |
31 | ```javascript
32 | const HtmlWebpackPlugin = require('html-webpack-plugin');
33 | const StrictCspHtmlWebpackPlugin = require('strict-csp-html-webpack-plugin');
34 |
35 | module.exports = function (webpackEnv) {
36 | return {
37 | // ...
38 | plugins: [
39 | new HtmlWebpackPlugin(
40 | Object.assign(
41 | {}
42 | // ... HtmlWebpackPlugin config
43 | )
44 | ),
45 | new StrictCspHtmlWebpackPlugin(HtmlWebpackPlugin),
46 | ],
47 | };
48 | };
49 | ```
50 |
51 | 4. Startup:
52 |
53 | ```bash
54 | cd react-app && npm start
55 | ```
56 |
57 | ✨ That's it. Open `http://localhost:{port}` and inspect `index.html`. Observe that react app's `index.html` includes a valid hash-based CSP in a meta tag.
58 |
59 | ### Developing
60 |
61 | #### When changing the library code (strict-csp)
62 |
63 | 🚨 Every time you change strict-csp code, you need to **rebuild it** so that the changes are picked up by the strict csp webpack plugin. Build like this:
64 |
65 | `cd strict-csp && npm run-script build && cd ..`
66 |
67 | Note:
68 |
69 | - If you've added new dependencies to strict-csp, also run `npm install` (as follows: `cd strict-csp && npm i && npm run-script build && cd ..`).
70 | - No need to `link` again here, this only needs to be done once.
71 |
72 | #### When changing the plugin code
73 |
74 | Every time you change locally the plugin code (`strictCspWebpackPlugin.js`), you need to restart the react app with `npm start` to see the changes.
75 |
76 | ## How the development setup works
77 |
78 | To develop this plugin locally, you need to create the symlinks as illustrated below. `dev-setup.sh` does this for you.
79 | `undo-dev-setup.sh` undoes this (this is convenient if you need to debug `dev-setup.sh` itself).
80 |
81 | 
82 |
83 | Note: the **exact `html-webpack-plugin` instance** that `strict-csp-webpack-plugin` hooks into **must be referenced** by `strict-csp-webpack-plugin`, otherwise the hooking won't work and the CSP won't be set. It's a known thing with webpack and it's also the way other plugins that use `html-webpack-plugin` work. [Details](https://github.com/jantimon/html-webpack-plugin/issues/1091).
84 |
85 | ## What `dev-setup.sh` does
86 |
87 | - Builds the library, so that there's something to link to:
88 | `cd strict-csp && npm install && npm run-script build && cd ..`
89 | - Creates a symlink to the library:
90 | `cd strict-csp && npm link && cd ..`
91 | - Links to the library where needed:
92 | `cd strict-csp-html-webpack-plugin && npm link 'strict-csp' && cd ..`
93 |
94 | This is done only once.
95 |
96 | 🧐 Troubleshooting: if you get an error like "linked library not found", ensure that `main` in strict-csp's `package.json` points to a file that exists.
97 |
98 | ## To reset linking
99 |
100 | `sh ./undo-dev-setup.sh`
101 |
102 | ## To troubleshoot individual linking issues
103 |
104 | `npm uninstall`
105 |
106 | `npm ls --depth=0 --link=true`
107 |
--------------------------------------------------------------------------------
/strict-csp/README.md:
--------------------------------------------------------------------------------
1 | # strict-csp
2 |
3 | [Available on npm](https://www.npmjs.com/package/strict-csp)
4 |
5 | ⚠️ This is experimental. Make sure to check [what's not supported](https://github.com/google/strict-csp/issues?q=is%3Aissue+is%3Aopen+label%3Afeature).
6 |
7 | ## What this library does: defense-in-depth against XSS 🛡
8 |
9 | _💡 Are you using webpack? Head over to [strict-csp-html-webpack-plugin](https://github.com/google/strict-csp/tree/main/strict-csp-html-webpack-plugin) instead. It uses this library under the hood to generate a CSP you can use in your webpack project!_
10 |
11 | Cross-site scripting (XSS)—the ability to inject malicious scripts into a web application—has been one of the biggest web security vulnerabilities for over a decade.
12 |
13 | strict-csp is a **bundler-agnostic** library that helps protect your single-page application against XSS attacks. It does so by generating a [strict, hash-based Content-Security-Policy (CSP)](https://web.dev/strict-csp) for your web application.
14 |
15 | A strict CSP helps protect your site against XSS by preventing browsers from executing malicious scripts.
16 |
17 | ## Usage
18 |
19 | This library offers two primary workflows for applying a strict CSP.
20 |
21 | ### Workflow 1: HTTP Header (Recommended)
22 |
23 | The recommended and most secure approach is to set the CSP as an HTTP response header. The `.process()` method returns both the modified HTML and the CSP string needed for the header.
24 |
25 | ```javascript
26 | // Let's say `htmlString` is your SPA's html as a string.
27 | const processor = new StrictCsp(htmlString, {
28 | // Configuration options go here
29 | browserFallbacks: true,
30 | });
31 |
32 | // Process the HTML and generate the CSP.
33 | const { html, csp } = processor.process();
34 |
35 | // In your server:
36 | // 1. Set the CSP as an HTTP Header.
37 | // response.setHeader('Content-Security-Policy', csp);
38 | // 2. Serve the modified HTML.
39 | // response.send(html);
40 | ```
41 |
42 | ### Workflow 2: Meta Tag (Alternative)
43 |
44 | If you cannot set HTTP headers, you can inject the CSP into a `` tag.
45 |
46 | ```javascript
47 | const processor = new StrictCsp(htmlString);
48 |
49 | // 1. Process the HTML to get the CSP string.
50 | // (We ignore the 'html' returned here as it will be outdated).
51 | const { csp } = processor.process();
52 |
53 | // 2. Add the meta tag and get the final HTML.
54 | const finalHtml = processor.serializeDomWithStrictCspMetaTag(csp);
55 |
56 | // Serve the finalHtml.
57 | ```
58 |
59 | ## Example with Trusted Types
60 |
61 | You can also use this library to configure [Trusted Types](https://web.dev/trusted-types) and set up violation reporting. The `.process()` method automatically handles injecting the necessary reporting scripts into the HTML and adding the required directives to the CSP.
62 |
63 | ```javascript
64 | const processor = new StrictCsp(htmlString, {
65 | // Enable Trusted Types in report-only mode
66 | trustedTypes: 'report-only',
67 | // Specify an endpoint for violation reports
68 | reportUri: 'https://your-reporting-endpoint.com/report',
69 | });
70 |
71 | const { html, csp } = processor.process();
72 |
73 | // The `html` now contains the TT reporting scripts.
74 | // The `csp` now contains the 'require-trusted-types-for' directive.
75 | ```
76 |
77 | **Note on Report-Only Mode:** The `trustedTypes: 'report-only'` option works by injecting a script that simulates this mode on the client-side by creating a **default policy** (`trustedTypes.createPolicy('default', ...)`). This policy intercepts calls to dangerous DOM sinks, reports violations, but ultimately allows them to proceed. This is especially useful for static deployments (e.g., with a meta tag) where you cannot set the standard `Content-Security-Policy-Report-Only` HTTP header.
78 |
79 | ## Configuration Options
80 |
81 | You can pass a configuration object to the `StrictCsp` constructor:
82 |
83 | | Option | Type | What it does |
84 | | :--- | :--- | :--- |
85 | | `browserFallbacks` | `boolean` | (Default: `true`) When `true`, enables fallbacks for older browsers. This does not weaken the policy for modern browsers. |
86 | | `trustedTypes` | `boolean` \| `'report-only'` | (Default: `false`) When `true`, enforces [Trusted Types](https://web.dev/trusted-types). When `'report-only'`, it enables violation reporting without enforcement. |
87 | | `reportUri` | `string` | A URL where CSP and Trusted Types violation reports should be sent. |
88 | | `unsafeEval` | `boolean` | (Default: `false`) When `true`, adds `'unsafe-eval'` to the policy in case you cannot remove all uses of `eval()`. |
89 |
90 | ## How does this library work?
91 |
92 | Here's what the library does:
93 |
94 | 1. It finds all externally sourced scripts (`