➕(back) install and configure django csp (#1085)
We want to protect all requests from django with content security policy header. We use the djang-csp library and configure it with default values. Fixes #1000
This commit is contained in:
@@ -25,6 +25,8 @@ from django.utils.translation import gettext_lazy as _
|
||||
import requests
|
||||
import rest_framework as drf
|
||||
from botocore.exceptions import ClientError
|
||||
from csp.constants import NONE
|
||||
from csp.decorators import csp_update
|
||||
from lasuite.malware_detection import malware_detection
|
||||
from rest_framework import filters, status, viewsets
|
||||
from rest_framework import response as drf_response
|
||||
@@ -1412,6 +1414,7 @@ class DocumentViewSet(
|
||||
name="",
|
||||
url_path="cors-proxy",
|
||||
)
|
||||
@csp_update({"img-src": [NONE, "data:"]})
|
||||
def cors_proxy(self, request, *args, **kwargs):
|
||||
"""
|
||||
GET /api/v1.0/documents/<resource_id>/cors-proxy
|
||||
@@ -1452,7 +1455,6 @@ class DocumentViewSet(
|
||||
content_type=content_type,
|
||||
headers={
|
||||
"Content-Disposition": "attachment;",
|
||||
"Content-Security-Policy": "default-src 'none'; img-src 'none' data:;",
|
||||
},
|
||||
status=response.status_code,
|
||||
)
|
||||
|
||||
@@ -23,10 +23,25 @@ def test_api_docs_cors_proxy_valid_url():
|
||||
assert response.status_code == 200
|
||||
assert response.headers["Content-Type"] == "image/png"
|
||||
assert response.headers["Content-Disposition"] == "attachment;"
|
||||
assert (
|
||||
response.headers["Content-Security-Policy"]
|
||||
== "default-src 'none'; img-src 'none' data:;"
|
||||
)
|
||||
policy_list = sorted(response.headers["Content-Security-Policy"].split("; "))
|
||||
assert policy_list == [
|
||||
"base-uri 'none'",
|
||||
"child-src 'none'",
|
||||
"connect-src 'none'",
|
||||
"default-src 'none'",
|
||||
"font-src 'none'",
|
||||
"form-action 'none'",
|
||||
"frame-ancestors 'none'",
|
||||
"frame-src 'none'",
|
||||
"img-src 'none' data:",
|
||||
"manifest-src 'none'",
|
||||
"media-src 'none'",
|
||||
"object-src 'none'",
|
||||
"prefetch-src 'none'",
|
||||
"script-src 'none'",
|
||||
"style-src 'none'",
|
||||
"worker-src 'none'",
|
||||
]
|
||||
assert response.streaming_content
|
||||
|
||||
|
||||
@@ -77,10 +92,25 @@ def test_api_docs_cors_proxy_authenticated_user_accessing_protected_doc():
|
||||
assert response.status_code == 200
|
||||
assert response.headers["Content-Type"] == "image/png"
|
||||
assert response.headers["Content-Disposition"] == "attachment;"
|
||||
assert (
|
||||
response.headers["Content-Security-Policy"]
|
||||
== "default-src 'none'; img-src 'none' data:;"
|
||||
)
|
||||
policy_list = sorted(response.headers["Content-Security-Policy"].split("; "))
|
||||
assert policy_list == [
|
||||
"base-uri 'none'",
|
||||
"child-src 'none'",
|
||||
"connect-src 'none'",
|
||||
"default-src 'none'",
|
||||
"font-src 'none'",
|
||||
"form-action 'none'",
|
||||
"frame-ancestors 'none'",
|
||||
"frame-src 'none'",
|
||||
"img-src 'none' data:",
|
||||
"manifest-src 'none'",
|
||||
"media-src 'none'",
|
||||
"object-src 'none'",
|
||||
"prefetch-src 'none'",
|
||||
"script-src 'none'",
|
||||
"style-src 'none'",
|
||||
"worker-src 'none'",
|
||||
]
|
||||
assert response.streaming_content
|
||||
|
||||
|
||||
|
||||
@@ -62,6 +62,25 @@ def test_api_config(is_authenticated):
|
||||
"AI_FEATURE_ENABLED": False,
|
||||
"theme_customization": {},
|
||||
}
|
||||
policy_list = sorted(response.headers["Content-Security-Policy"].split("; "))
|
||||
assert policy_list == [
|
||||
"base-uri 'none'",
|
||||
"child-src 'none'",
|
||||
"connect-src 'none'",
|
||||
"default-src 'none'",
|
||||
"font-src 'none'",
|
||||
"form-action 'none'",
|
||||
"frame-ancestors 'none'",
|
||||
"frame-src 'none'",
|
||||
"img-src 'none'",
|
||||
"manifest-src 'none'",
|
||||
"media-src 'none'",
|
||||
"object-src 'none'",
|
||||
"prefetch-src 'none'",
|
||||
"script-src 'none'",
|
||||
"style-src 'none'",
|
||||
"worker-src 'none'",
|
||||
]
|
||||
|
||||
|
||||
@override_settings(
|
||||
|
||||
@@ -18,9 +18,12 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import sentry_sdk
|
||||
from configurations import Configuration, values
|
||||
from csp.constants import NONE
|
||||
from sentry_sdk.integrations.django import DjangoIntegration
|
||||
from sentry_sdk.integrations.logging import ignore_logger
|
||||
|
||||
# pylint: disable=too-many-lines
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
DATA_DIR = os.getenv("DATA_DIR", os.path.join("/", "data"))
|
||||
@@ -289,6 +292,7 @@ class Base(Configuration):
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"dockerflow.django.middleware.DockerflowMiddleware",
|
||||
"csp.middleware.CSPMiddleware",
|
||||
]
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
@@ -322,6 +326,7 @@ class Base(Configuration):
|
||||
# OIDC third party
|
||||
"mozilla_django_oidc",
|
||||
"lasuite.malware_detection",
|
||||
"csp",
|
||||
]
|
||||
|
||||
# Cache
|
||||
@@ -721,6 +726,38 @@ class Base(Configuration):
|
||||
environ_prefix=None,
|
||||
)
|
||||
|
||||
# Content Security Policy
|
||||
# See https://content-security-policy.com/ for more information.
|
||||
CONTENT_SECURITY_POLICY = {
|
||||
"EXCLUDE_URL_PREFIXES": values.ListValue(
|
||||
[],
|
||||
environ_name="CONTENT_SECURITY_POLICY_EXCLUDE_URL_PREFIXES",
|
||||
environ_prefix=None,
|
||||
),
|
||||
"DIRECTIVES": values.DictValue(
|
||||
default={
|
||||
"default-src": [NONE],
|
||||
"script-src": [NONE],
|
||||
"style-src": [NONE],
|
||||
"img-src": [NONE],
|
||||
"connect-src": [NONE],
|
||||
"font-src": [NONE],
|
||||
"object-src": [NONE],
|
||||
"media-src": [NONE],
|
||||
"frame-src": [NONE],
|
||||
"child-src": [NONE],
|
||||
"form-action": [NONE],
|
||||
"frame-ancestors": [NONE],
|
||||
"base-uri": [NONE],
|
||||
"worker-src": [NONE],
|
||||
"manifest-src": [NONE],
|
||||
"prefetch-src": [NONE],
|
||||
},
|
||||
environ_name="CONTENT_SECURITY_POLICY_DIRECTIVES",
|
||||
environ_prefix=None,
|
||||
),
|
||||
}
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
@property
|
||||
def ENVIRONMENT(self):
|
||||
|
||||
@@ -32,6 +32,7 @@ dependencies = [
|
||||
"django-configurations==2.5.1",
|
||||
"django-cors-headers==4.7.0",
|
||||
"django-countries==7.6.1",
|
||||
"django-csp==4.0",
|
||||
"django-filter==25.1",
|
||||
"django-lasuite[all]==0.0.9",
|
||||
"django-parler==2.3",
|
||||
|
||||
Reference in New Issue
Block a user