1
+ from __future__ import annotations
1
2
import base64
2
3
import http .client as http_client
3
4
import os
4
5
from functools import partial
6
+ from typing import TYPE_CHECKING
5
7
6
8
from django .conf import settings
7
9
from django .utils .deprecation import MiddlewareMixin
10
12
from csp .constants import HEADER , HEADER_REPORT_ONLY
11
13
from csp .utils import build_policy
12
14
15
+ if TYPE_CHECKING :
16
+ from django .http import HttpRequest , HttpResponseBase
17
+
13
18
14
19
class CSPMiddleware (MiddlewareMixin ):
15
20
"""
@@ -21,17 +26,20 @@ class CSPMiddleware(MiddlewareMixin):
21
26
22
27
"""
23
28
24
- def _make_nonce (self , request ) :
29
+ def _make_nonce (self , request : HttpRequest ) -> str :
25
30
# Ensure that any subsequent calls to request.csp_nonce return the same value
26
- if not getattr (request , "_csp_nonce" , None ):
27
- request ._csp_nonce = base64 .b64encode (os .urandom (16 )).decode ("ascii" )
28
- return request ._csp_nonce
31
+ stored_nonce = getattr (request , "_csp_nonce" , None )
32
+ if isinstance (stored_nonce , str ):
33
+ return stored_nonce
34
+ nonce = base64 .b64encode (os .urandom (16 )).decode ("ascii" )
35
+ setattr (request , "_csp_nonce" , nonce )
36
+ return nonce
29
37
30
- def process_request (self , request ) :
38
+ def process_request (self , request : HttpRequest ) -> None :
31
39
nonce = partial (self ._make_nonce , request )
32
- request . csp_nonce = SimpleLazyObject (nonce )
40
+ setattr ( request , " csp_nonce" , SimpleLazyObject (nonce ) )
33
41
34
- def process_response (self , request , response ) :
42
+ def process_response (self , request : HttpRequest , response : HttpResponseBase ) -> HttpResponseBase :
35
43
# Check for debug view
36
44
exempted_debug_codes = (
37
45
http_client .INTERNAL_SERVER_ERROR ,
@@ -45,8 +53,9 @@ def process_response(self, request, response):
45
53
# Only set header if not already set and not an excluded prefix and not exempted.
46
54
is_not_exempt = getattr (response , "_csp_exempt" , False ) is False
47
55
no_header = HEADER not in response
48
- prefixes = getattr (settings , "CONTENT_SECURITY_POLICY" , {}).get ("EXCLUDE_URL_PREFIXES" , ())
49
- is_not_excluded = not request .path_info .startswith (prefixes )
56
+ policy = getattr (settings , "CONTENT_SECURITY_POLICY" , None ) or {}
57
+ prefixes = policy .get ("EXCLUDE_URL_PREFIXES" , None ) or ()
58
+ is_not_excluded = not request .path_info .startswith (tuple (prefixes ))
50
59
if all ((no_header , is_not_exempt , is_not_excluded )):
51
60
response [HEADER ] = csp
52
61
@@ -55,21 +64,22 @@ def process_response(self, request, response):
55
64
# Only set header if not already set and not an excluded prefix and not exempted.
56
65
is_not_exempt = getattr (response , "_csp_exempt_ro" , False ) is False
57
66
no_header = HEADER_REPORT_ONLY not in response
58
- prefixes = getattr (settings , "CONTENT_SECURITY_POLICY_REPORT_ONLY" , {}).get ("EXCLUDE_URL_PREFIXES" , ())
59
- is_not_excluded = not request .path_info .startswith (prefixes )
67
+ policy = getattr (settings , "CONTENT_SECURITY_POLICY_REPORT_ONLY" , None ) or {}
68
+ prefixes = policy .get ("EXCLUDE_URL_PREFIXES" , None ) or ()
69
+ is_not_excluded = not request .path_info .startswith (tuple (prefixes ))
60
70
if all ((no_header , is_not_exempt , is_not_excluded )):
61
71
response [HEADER_REPORT_ONLY ] = csp_ro
62
72
63
73
return response
64
74
65
- def build_policy (self , request , response ) :
75
+ def build_policy (self , request : HttpRequest , response : HttpResponseBase ) -> str :
66
76
config = getattr (response , "_csp_config" , None )
67
77
update = getattr (response , "_csp_update" , None )
68
78
replace = getattr (response , "_csp_replace" , None )
69
79
nonce = getattr (request , "_csp_nonce" , None )
70
80
return build_policy (config = config , update = update , replace = replace , nonce = nonce )
71
81
72
- def build_policy_ro (self , request , response ) :
82
+ def build_policy_ro (self , request : HttpRequest , response : HttpResponseBase ) -> str :
73
83
config = getattr (response , "_csp_config_ro" , None )
74
84
update = getattr (response , "_csp_update_ro" , None )
75
85
replace = getattr (response , "_csp_replace_ro" , None )
0 commit comments