From faf1bbd10a3718151a81a1a8172a699184d722e4 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Sun, 17 May 2026 12:45:54 +0300 Subject: [PATCH] Add CSP for HTML5 responses --- .../api-server/src/api_server/html5_responses.py | 15 ++++++++++++++- services/api-server/tests/test_api.py | 9 +++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/services/api-server/src/api_server/html5_responses.py b/services/api-server/src/api_server/html5_responses.py index 148e36a..019929c 100644 --- a/services/api-server/src/api_server/html5_responses.py +++ b/services/api-server/src/api_server/html5_responses.py @@ -6,6 +6,15 @@ from fastapi.responses import Response, StreamingResponse from fastapi.staticfiles import StaticFiles HTML5_SECURITY_HEADERS = {"X-Content-Type-Options": "nosniff"} +HTML5_CONTENT_SECURITY_POLICY = ( + "default-src 'self'; " + "script-src 'self'; " + "style-src 'self'; " + "connect-src 'self'; " + "img-src 'self' data:; " + "base-uri 'self'; " + "form-action 'self'" +) class Html5StaticFiles(StaticFiles): @@ -29,7 +38,11 @@ def html5_response(fragment: str) -> Response: return Response( fragment, media_type="text/html; charset=utf-8", - headers={"Cache-Control": "no-cache, no-transform", **HTML5_SECURITY_HEADERS}, + headers={ + "Cache-Control": "no-cache, no-transform", + "Content-Security-Policy": HTML5_CONTENT_SECURITY_POLICY, + **HTML5_SECURITY_HEADERS, + }, ) diff --git a/services/api-server/tests/test_api.py b/services/api-server/tests/test_api.py index 3215bac..e3ecf6a 100644 --- a/services/api-server/tests/test_api.py +++ b/services/api-server/tests/test_api.py @@ -57,6 +57,15 @@ def assert_html5_response_contract(response, *markers: str, full_page: bool = Fa assert "text/html" in response.headers["content-type"] assert response.headers["cache-control"] == "no-cache, no-transform" assert response.headers["x-content-type-options"] == "nosniff" + assert response.headers["content-security-policy"] == ( + "default-src 'self'; " + "script-src 'self'; " + "style-src 'self'; " + "connect-src 'self'; " + "img-src 'self' data:; " + "base-uri 'self'; " + "form-action 'self'" + ) assert_html5_contract(response.text, *markers, full_page=full_page)