@@ -21,8 +21,7 @@ class CSPMiddleware(MiddlewareMixin):
21
21
"""
22
22
23
23
def _make_nonce (self , request ):
24
- # Ensure that any subsequent calls to request.csp_nonce return the
25
- # same value
24
+ # Ensure that any subsequent calls to request.csp_nonce return the same value
26
25
if not getattr (request , "_csp_nonce" , None ):
27
26
request ._csp_nonce = base64 .b64encode (os .urandom (16 )).decode ("ascii" )
28
27
return request ._csp_nonce
@@ -32,32 +31,36 @@ def process_request(self, request):
32
31
request .csp_nonce = SimpleLazyObject (nonce )
33
32
34
33
def process_response (self , request , response ):
35
- if getattr (response , "_csp_exempt" , False ):
36
- return response
37
-
38
- # Check for ignored path prefix.
39
- prefixes = getattr (settings , "CSP_EXCLUDE_URL_PREFIXES" , ())
40
- if request .path_info .startswith (prefixes ):
41
- return response
42
-
43
34
# Check for debug view
44
- status_code = response .status_code
45
35
exempted_debug_codes = (
46
36
http_client .INTERNAL_SERVER_ERROR ,
47
37
http_client .NOT_FOUND ,
48
38
)
49
- if status_code in exempted_debug_codes and settings .DEBUG :
39
+ if response . status_code in exempted_debug_codes and settings .DEBUG :
50
40
return response
51
41
52
42
header = "Content-Security-Policy"
53
- if getattr (settings , "CSP_REPORT_ONLY" , False ):
54
- header += "-Report-Only"
55
-
56
- if header in response :
57
- # Don't overwrite existing headers.
58
- return response
59
-
60
- response [header ] = self .build_policy (request , response )
43
+ header_ro = "Content-Security-Policy-Report-Only"
44
+
45
+ csp = self .build_policy (request , response )
46
+ if csp :
47
+ # Only set header if not already set and not an excluded prefix and not exempted.
48
+ is_not_exempt = getattr (response , "_csp_exempt" , False ) is False
49
+ no_header = header not in response
50
+ prefixes = getattr (settings , "CONTENT_SECURITY_POLICY" , {}).get ("EXCLUDE_URL_PREFIXES" , ())
51
+ is_not_excluded = not request .path_info .startswith (prefixes )
52
+ if all ((no_header , is_not_exempt , is_not_excluded )):
53
+ response [header ] = csp
54
+
55
+ csp_ro = self .build_policy_ro (request , response )
56
+ if csp_ro :
57
+ # Only set header if not already set and not an excluded prefix and not exempted.
58
+ is_not_exempt = getattr (response , "_csp_exempt_ro" , False ) is False
59
+ no_header = header_ro not in response
60
+ prefixes = getattr (settings , "CONTENT_SECURITY_POLICY_REPORT_ONLY" , {}).get ("EXCLUDE_URL_PREFIXES" , ())
61
+ is_not_excluded = not request .path_info .startswith (prefixes )
62
+ if all ((no_header , is_not_exempt , is_not_excluded )):
63
+ response [header_ro ] = csp_ro
61
64
62
65
return response
63
66
@@ -67,3 +70,10 @@ def build_policy(self, request, response):
67
70
replace = getattr (response , "_csp_replace" , None )
68
71
nonce = getattr (request , "_csp_nonce" , None )
69
72
return build_policy (config = config , update = update , replace = replace , nonce = nonce )
73
+
74
+ def build_policy_ro (self , request , response ):
75
+ config = getattr (response , "_csp_config_ro" , None )
76
+ update = getattr (response , "_csp_update_ro" , None )
77
+ replace = getattr (response , "_csp_replace_ro" , None )
78
+ nonce = getattr (request , "_csp_nonce" , None )
79
+ return build_policy (config = config , update = update , replace = replace , nonce = nonce , report_only = True )
0 commit comments