diff --git a/poetry.lock b/poetry.lock index 830601a..5a091d9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -198,6 +198,21 @@ python-versions = ">=3.7" [package.dependencies] Django = ">=3.2" +[[package]] +name = "django-csp" +version = "3.7" +description = "Django Content Security Policy support." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +Django = ">=1.8" + +[package.extras] +jinja2 = ["jinja2 (>=2.9.6)"] +tests = ["jinja2 (>=2.9.6)", "mock (==1.0.1)", "pep8 (==1.4.6)", "pytest (<4.0)", "pytest-django", "pytest-flakes (==1.0.1)", "pytest-pep8 (==1.0.6)", "six (==1.12.0)"] + [[package]] name = "django-debug-toolbar" version = "3.7.0" @@ -1372,7 +1387,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "1.1" python-versions = "^3.10" -content-hash = "2fa205bd17be3d23d23481f3e6d6b7d95b19fd925398dcb5c599ac66b4cf6578" +content-hash = "8d2f240eaa055939613b5fcd9f364df73c8488de9fe3aa9e68c691ff7ad7c3d5" [metadata.files] anyascii = [ @@ -1570,6 +1585,10 @@ django-cors-headers = [ {file = "django-cors-headers-3.13.0.tar.gz", hash = "sha256:f9dc6b4e3f611c3199700b3e5f3398c28757dcd559c2f82932687f3d0443cfdf"}, {file = "django_cors_headers-3.13.0-py3-none-any.whl", hash = "sha256:37e42883b5f1f2295df6b4bba96eb2417a14a03270cb24b2a07f021cd4487cf4"}, ] +django-csp = [ + {file = "django_csp-3.7-py2.py3-none-any.whl", hash = "sha256:01443a07723f9a479d498bd7bb63571aaa771e690f64bde515db6cdb76e8041a"}, + {file = "django_csp-3.7.tar.gz", hash = "sha256:01eda02ad3f10261c74131cdc0b5a6a62b7c7ad4fd017fbefb7a14776e0a9727"}, +] django-debug-toolbar = [ {file = "django-debug-toolbar-3.7.0.tar.gz", hash = "sha256:1e3acad24e3d351ba45c6fa2072e4164820307332a776b16c9f06d1f89503465"}, {file = "django_debug_toolbar-3.7.0-py3-none-any.whl", hash = "sha256:80de23066b624d3970fd296cf02d61988e5d56c31aa0dc4a428970b46e2883a8"}, diff --git a/pyproject.toml b/pyproject.toml index 542a614..14bd125 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ django3-cache-decorator = "^0.5.2" django-cors-headers = "^3.13.0" uritemplate = "^4.1.1" PyYAML = "^6.0" +django-csp = "^3.7" [tool.poetry.group.dev.dependencies] diff --git a/website/settings.py b/website/settings.py index 4810d58..f1d25ef 100644 --- a/website/settings.py +++ b/website/settings.py @@ -103,6 +103,7 @@ MIDDLEWARE = [ "django.contrib.messages.middleware.MessageMiddleware", "wagtail.contrib.redirects.middleware.RedirectMiddleware", "django_htmx.middleware.HtmxMiddleware", + "csp.middleware.CSPMiddleware", ] ROOT_URLCONF = "website.urls" @@ -212,10 +213,7 @@ WAGTAILSEARCH_BACKENDS = { WAGTAILADMIN_BASE_URL = f"https://{BASE_HOSTNAME}" -CORS_ALLOWED_ORIGINS = [ - WAGTAILADMIN_BASE_URL, - "https://editor.swagger.io" -] +CORS_ALLOWED_ORIGINS = [WAGTAILADMIN_BASE_URL, "https://editor.swagger.io"] WAGTAIL_ENABLE_UPDATE_CHECK = False @@ -397,6 +395,9 @@ SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") if not DEBUG: SECURE_HSTS_SECONDS = 2592000 # 30 days + CSP_BLOCK_ALL_MIXED_CONTENT = True + CSP_UPGRADE_INSECURE_REQUESTS = True + if sentry_dsn := env("SENTRY_DSN"): import sentry_sdk from sentry_sdk.integrations.django import DjangoIntegration