2
2
from xml .etree .ElementTree import Element
3
3
4
4
from django .conf import settings
5
- from django .http .response import HttpResponse
6
5
from django .test .utils import ContextList , override_settings
7
6
from html5lib .constants import E
8
7
from html5lib .html5parser import HTMLParser
9
8
9
+ from debug_toolbar .toolbar import DebugToolbar
10
+
10
11
from .base import BaseTestCase
11
12
12
13
13
- def _get_ns (element : Element ) -> Dict [str , str ]:
14
+ def get_namespaces (element : Element ) -> Dict [str , str ]:
14
15
"""
15
16
Return the default `xmlns`. See
16
17
https://docs.python.org/3/library/xml.etree.elementtree.html#parsing-xml-with-namespaces
@@ -20,10 +21,12 @@ def _get_ns(element: Element) -> Dict[str, str]:
20
21
return {"" : element .tag [1 :].split ("}" , maxsplit = 1 )[0 ]}
21
22
22
23
24
+ @override_settings (DEBUG = True )
23
25
class CspRenderingTestCase (BaseTestCase ):
24
- "Testing if `csp-nonce` renders."
26
+ """ Testing if `csp-nonce` renders."" "
25
27
26
- panel_id = "StaticFilesPanel"
28
+ def setUp (self ):
29
+ self .parser = HTMLParser ()
27
30
28
31
def _fail_if_missing (
29
32
self , root : Element , path : str , namespaces : Dict [str , str ], nonce : str
@@ -46,50 +49,90 @@ def _fail_if_found(self, root: Element, path: str, namespaces: Dict[str, str]):
46
49
raise self .failureException (f"{ item } has no nonce attribute." )
47
50
48
51
def _fail_on_invalid_html (self , content : bytes , parser : HTMLParser ):
49
- "Fail if the passed HTML is invalid."
52
+ """ Fail if the passed HTML is invalid."" "
50
53
if parser .errors :
51
54
default_msg = ["Content is invalid HTML:" ]
52
55
lines = content .split (b"\n " )
53
- for position , errorcode , datavars in parser .errors :
54
- default_msg .append (" %s" % E [errorcode ] % datavars )
56
+ for position , error_code , data_vars in parser .errors :
57
+ default_msg .append (" %s" % E [error_code ] % data_vars )
55
58
default_msg .append (" %r" % lines [position [0 ] - 1 ])
56
59
msg = self ._formatMessage (None , "\n " .join (default_msg ))
57
60
raise self .failureException (msg )
58
61
59
62
@override_settings (
60
- DEBUG = True , MIDDLEWARE = settings .MIDDLEWARE + ["csp.middleware.CSPMiddleware" ]
63
+ MIDDLEWARE = settings .MIDDLEWARE + ["csp.middleware.CSPMiddleware" ]
61
64
)
62
65
def test_exists (self ):
63
- "A `nonce` should exists when using the `CSPMiddleware`."
66
+ """ A `nonce` should exist when using the `CSPMiddleware`."" "
64
67
response = self .client .get (path = "/regular/basic/" )
65
- if not isinstance (response , HttpResponse ):
66
- raise self .failureException (f"{ response !r} is not a HttpResponse" )
67
68
self .assertEqual (response .status_code , 200 )
68
- parser = HTMLParser ()
69
- el_htmlroot : Element = parser .parse (stream = response .content )
70
- self ._fail_on_invalid_html (content = response .content , parser = parser )
69
+
70
+ html_root : Element = self . parser .parse (stream = response .content )
71
+ self ._fail_on_invalid_html (content = response .content , parser = self . parser )
71
72
self .assertContains (response , "djDebug" )
72
- namespaces = _get_ns (element = el_htmlroot )
73
- context : ContextList = response .context # pyright: ignore[reportAttributeAccessIssue]
73
+
74
+ namespaces = get_namespaces (element = html_root )
75
+ toolbar = list (DebugToolbar ._store .values ())[0 ]
76
+ nonce = str (toolbar .request .csp_nonce )
77
+ self ._fail_if_missing (
78
+ root = html_root , path = ".//link" , namespaces = namespaces , nonce = nonce
79
+ )
80
+ self ._fail_if_missing (
81
+ root = html_root , path = ".//script" , namespaces = namespaces , nonce = nonce
82
+ )
83
+
84
+ @override_settings (
85
+ DEBUG_TOOLBAR_CONFIG = {"DISABLE_PANELS" : set ()},
86
+ MIDDLEWARE = settings .MIDDLEWARE + ["csp.middleware.CSPMiddleware" ],
87
+ )
88
+ def test_redirects_exists (self ):
89
+ response = self .client .get ("/redirect/" )
90
+ self .assertEqual (response .status_code , 200 )
91
+
92
+ html_root : Element = self .parser .parse (stream = response .content )
93
+ self ._fail_on_invalid_html (content = response .content , parser = self .parser )
94
+ self .assertContains (response , "djDebug" )
95
+
96
+ namespaces = get_namespaces (element = html_root )
97
+ context : ContextList = response .context
74
98
nonce = str (context ["toolbar" ].request .csp_nonce )
75
99
self ._fail_if_missing (
76
- root = el_htmlroot , path = ".//link" , namespaces = namespaces , nonce = nonce
100
+ root = html_root , path = ".//link" , namespaces = namespaces , nonce = nonce
77
101
)
78
102
self ._fail_if_missing (
79
- root = el_htmlroot , path = ".//script" , namespaces = namespaces , nonce = nonce
103
+ root = html_root , path = ".//script" , namespaces = namespaces , nonce = nonce
80
104
)
81
105
82
- @override_settings (DEBUG = True )
106
+ @override_settings (
107
+ MIDDLEWARE = settings .MIDDLEWARE + ["csp.middleware.CSPMiddleware" ]
108
+ )
109
+ def test_panel_content_nonce_exists (self ):
110
+ response = self .client .get ("/regular/basic/" )
111
+ self .assertEqual (response .status_code , 200 )
112
+
113
+ toolbar = list (DebugToolbar ._store .values ())[0 ]
114
+ panels_to_check = ["HistoryPanel" , "TimerPanel" ]
115
+ for panel in panels_to_check :
116
+ content = toolbar .get_panel_by_id (panel ).content
117
+ html_root : Element = self .parser .parse (stream = content )
118
+ namespaces = get_namespaces (element = html_root )
119
+ nonce = str (toolbar .request .csp_nonce )
120
+ self ._fail_if_missing (
121
+ root = html_root , path = ".//link" , namespaces = namespaces , nonce = nonce
122
+ )
123
+ self ._fail_if_missing (
124
+ root = html_root , path = ".//script" , namespaces = namespaces , nonce = nonce
125
+ )
126
+
83
127
def test_missing (self ):
84
- "A `nonce` should not exist when not using the `CSPMiddleware`."
128
+ """ A `nonce` should not exist when not using the `CSPMiddleware`."" "
85
129
response = self .client .get (path = "/regular/basic/" )
86
- if not isinstance (response , HttpResponse ):
87
- raise self .failureException (f"{ response !r} is not a HttpResponse" )
88
130
self .assertEqual (response .status_code , 200 )
89
- parser = HTMLParser ()
90
- el_htmlroot : Element = parser .parse (stream = response .content )
91
- self ._fail_on_invalid_html (content = response .content , parser = parser )
131
+
132
+ html_root : Element = self . parser .parse (stream = response .content )
133
+ self ._fail_on_invalid_html (content = response .content , parser = self . parser )
92
134
self .assertContains (response , "djDebug" )
93
- namespaces = _get_ns (element = el_htmlroot )
94
- self ._fail_if_found (root = el_htmlroot , path = ".//link" , namespaces = namespaces )
95
- self ._fail_if_found (root = el_htmlroot , path = ".//script" , namespaces = namespaces )
135
+
136
+ namespaces = get_namespaces (element = html_root )
137
+ self ._fail_if_found (root = html_root , path = ".//link" , namespaces = namespaces )
138
+ self ._fail_if_found (root = html_root , path = ".//script" , namespaces = namespaces )
0 commit comments