﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
37419	Include CSP nonces in headers for streaming responses	Sarah Boyce	Sarah Boyce	"Background:
The security team received a report from Michael Codispoti (codispoti) on HackerOne. This report was agreed to be handled in public as a hardening as the CSP middleware doesn't absolve developer responsibility to escape untrusted input.

Issue:
For a StreamingHttpResponse, the response headers are constructed before the streaming iterator is consumed.
If the iterator first accesses csp_nonce while producing the response body, the HTML receives a generated nonce after the CSP header has already been finalized.

This causes the streamed HTML to contain a nonce that is absent from the corresponding Content-Security-Policy header.

Regression test:
{{{#!diff
diff --git a/tests/middleware/test_csp.py b/tests/middleware/test_csp.py
index f0a4af2a7c..c64754c221 100644
--- a/tests/middleware/test_csp.py
+++ b/tests/middleware/test_csp.py
@@ -2,7 +2,11 @@ from playwright_tests import PlaywrightTestCase
 from utils_tests.test_csp import basic_config, basic_policy
 
 from django.contrib.staticfiles.testing import StaticLiveServerTestCase
-from django.test import SimpleTestCase
+from django.http import StreamingHttpResponse
+from django.middleware.csp import ContentSecurityPolicyMiddleware
+from django.template import Context, Template
+from django.template.context_processors import csp
+from django.test import RequestFactory, SimpleTestCase
 from django.test.utils import modify_settings, override_settings
 from django.utils.csp import CSP
 
@@ -173,6 +177,32 @@ class CSPMiddlewareWithDecoratedViewsTest(SimpleTestCase):
         )
 
 
+class CSPStreamingTests(SimpleTestCase):
+    @override_settings(
+        SECURE_CSP={
+            ""default-src"": [CSP.SELF],
+            ""script-src"": [CSP.NONCE],
+        },
+        SECURE_CSP_REPORT_ONLY={},
+    )
+    def test_nonce_generated_during_streaming_is_in_header(self):
+        request = RequestFactory().get(""/"")
+        template = Template('<script nonce=""{{ csp_nonce }}""></script>')
+
+        def view(request):
+            def content():
+                yield template.render(Context(csp(request)))
+
+            return StreamingHttpResponse(content())
+
+        response = ContentSecurityPolicyMiddleware(view)(request)
+        body = b"""".join(response.streaming_content).decode()
+
+        nonce = body.split('nonce=""', 1)[1].split('""', 1)[0]
+        self.assertTrue(nonce)
+        self.assertIn(f""'nonce-{nonce}'"", response[""Content-Security-Policy""])
+
+
 @override_settings(
     ROOT_URLCONF=""middleware.urls"",
}}}
"	Bug	assigned	Core (Other)	6.1	Normal			Rob Hudson Jake Howard	Unreviewed	1	0	0	0	0	0
