From f4611c338a1e564a28307ec08f241a21c7304596 Mon Sep 17 00:00:00 2001 From: Dale Cannon Date: Tue, 21 Jan 2025 14:28:29 +0000 Subject: [PATCH 1/6] Bump django-csp to 3.8 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 562305540..5cabe8fe8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ defusedxml==0.7.* dj-database-url==0.5.0 django-chunk-upload-handlers==0.0.13 django-crispy-forms==1.14.0 -django-csp==3.6 +django-csp==3.8 django-cte==1.3.1 django-extensions==3.2.3 django-filter==23.5 From 4221a2068978bb99e204534b17f4cf47403e7782 Mon Sep 17 00:00:00 2001 From: Dale Cannon Date: Wed, 22 Jan 2025 11:29:47 +0000 Subject: [PATCH 2/6] Add missing object-src CSP directive --- settings/common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/settings/common.py b/settings/common.py index 1b785b30a..792163c9a 100644 --- a/settings/common.py +++ b/settings/common.py @@ -231,6 +231,7 @@ "ajax.googleapis.com/", ) CSP_FONT_SRC = ("'self'", "'unsafe-inline'") +CSP_OBJECT_SRC = ("'none'",) CSP_INCLUDE_NONCE_IN = ("script-src",) CSP_REPORT_ONLY = False From 4694ddd0f1458af1f0e84cf6b3e3ac92ef2c970e Mon Sep 17 00:00:00 2001 From: Dale Cannon Date: Wed, 22 Jan 2025 11:30:45 +0000 Subject: [PATCH 3/6] Add missing base-uri CSP directive --- settings/common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/settings/common.py b/settings/common.py index 792163c9a..fd9c4cedd 100644 --- a/settings/common.py +++ b/settings/common.py @@ -232,6 +232,7 @@ ) CSP_FONT_SRC = ("'self'", "'unsafe-inline'") CSP_OBJECT_SRC = ("'none'",) +CSP_BASE_URI = ("'none'",) CSP_INCLUDE_NONCE_IN = ("script-src",) CSP_REPORT_ONLY = False From e1231a03ac6605a4c18a95af0059dda12e629f04 Mon Sep 17 00:00:00 2001 From: Dale Cannon Date: Wed, 22 Jan 2025 11:32:54 +0000 Subject: [PATCH 4/6] Add missing trusted-types CSP directive --- settings/common.py | 2 ++ webpack.config.js | 3 +++ 2 files changed, 5 insertions(+) diff --git a/settings/common.py b/settings/common.py index fd9c4cedd..d1d50e35a 100644 --- a/settings/common.py +++ b/settings/common.py @@ -233,6 +233,8 @@ CSP_FONT_SRC = ("'self'", "'unsafe-inline'") CSP_OBJECT_SRC = ("'none'",) CSP_BASE_URI = ("'none'",) +CSP_REQUIRE_TRUSTED_TYPES_FOR = ("'script'",) +CSP_TRUSTED_TYPES = ("tap#webpack",) CSP_INCLUDE_NONCE_IN = ("script-src",) CSP_REPORT_ONLY = False diff --git a/webpack.config.js b/webpack.config.js index 5c98a53be..3198e4648 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -18,6 +18,9 @@ module.exports = { // (they are picked up by `collectstatic`) publicPath: "/assets/webpack_bundles/", filename: "[name]-[hash].js", + trustedTypes: { + policyName: "tap#webpack", + }, }, plugins: [ From 503d8905aaddfcf5473b49530a4b1466b9ee0f30 Mon Sep 17 00:00:00 2001 From: Dale Cannon Date: Wed, 22 Jan 2025 12:05:39 +0000 Subject: [PATCH 5/6] Add missing strict-dynamic CSP directive --- requirements-dev.txt | 2 +- settings/common.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index f5448caa1..c09a7734e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ -r requirements.txt -django_debug_toolbar +django_debug_toolbar==5.0.1 pre-commit diff --git a/settings/common.py b/settings/common.py index d1d50e35a..50ea23268 100644 --- a/settings/common.py +++ b/settings/common.py @@ -223,6 +223,7 @@ "https://tagmanager.google.com/", ) CSP_SCRIPT_SRC = ( + "'strict-dynamic'", "'self'", "'unsafe-eval'", "'unsafe-inline'", From c5ea7dff12fa4d14b6bfeca9cf75d21bda415a9e Mon Sep 17 00:00:00 2001 From: Dale Cannon Date: Thu, 23 Jan 2025 15:57:39 +0000 Subject: [PATCH 6/6] Create default Trusted Types policy --- common/static/common/js/application.js | 1 + common/static/common/js/trustedTypes.js | 12 ++++++++++++ package-lock.json | 17 +++++++++++++++++ package.json | 3 ++- settings/common.py | 2 +- 5 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 common/static/common/js/trustedTypes.js diff --git a/common/static/common/js/application.js b/common/static/common/js/application.js index d2fa9f75a..91bc1f7d1 100644 --- a/common/static/common/js/application.js +++ b/common/static/common/js/application.js @@ -9,6 +9,7 @@ const imagePath = (name) => images(name, true); require.context("govuk-frontend/govuk/assets"); +import "./trustedTypes"; import { initAll } from "govuk-frontend"; import showHideCheckboxes from "./showHideCheckboxes"; diff --git a/common/static/common/js/trustedTypes.js b/common/static/common/js/trustedTypes.js new file mode 100644 index 000000000..4fd8cae1d --- /dev/null +++ b/common/static/common/js/trustedTypes.js @@ -0,0 +1,12 @@ +import DOMPurify from "dompurify"; + +/** + * Creates a default Trusted Types policy that serves as a fallback policy + * to sanitise direct sink usage in third-party dependencies. + */ +if (typeof window.trustedTypes !== "undefined") { + window.trustedTypes.createPolicy("default", { + createHTML: (to_escape) => + DOMPurify.sanitize(to_escape, { RETURN_TRUSTED_TYPE: true }), + }); +} diff --git a/package-lock.json b/package-lock.json index 3d7e5a1fd..f27475bd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "chart.js": "^3.9.1", "chartjs-adapter-moment": "^1.0.0", "css-loader": "^5.2.6", + "dompurify": "^3.2.3", "file-loader": "^6.2.0", "govuk-frontend": "^3.15.0", "govuk-react": "^0.10.6", @@ -4160,6 +4161,13 @@ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "dev": true }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/unist": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", @@ -6297,6 +6305,15 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.3.tgz", + "integrity": "sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", diff --git a/package.json b/package.json index ae163f067..521a26a5d 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "chart.js": "^3.9.1", "chartjs-adapter-moment": "^1.0.0", "css-loader": "^5.2.6", + "dompurify": "^3.2.3", "file-loader": "^6.2.0", "govuk-frontend": "^3.15.0", "govuk-react": "^0.10.6", @@ -78,4 +79,4 @@ "pre-commit": "^1.2.2", "react-test-renderer": "^18.2.0" } -} \ No newline at end of file +} diff --git a/settings/common.py b/settings/common.py index 50ea23268..00f385d22 100644 --- a/settings/common.py +++ b/settings/common.py @@ -235,7 +235,7 @@ CSP_OBJECT_SRC = ("'none'",) CSP_BASE_URI = ("'none'",) CSP_REQUIRE_TRUSTED_TYPES_FOR = ("'script'",) -CSP_TRUSTED_TYPES = ("tap#webpack",) +CSP_TRUSTED_TYPES = ("tap#webpack", "dompurify", "default") CSP_INCLUDE_NONCE_IN = ("script-src",) CSP_REPORT_ONLY = False