-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add rendering tests & nonces on link/style tags
- Loading branch information
Showing
8 changed files
with
115 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,3 +14,4 @@ geckodriver.log | |
coverage.xml | ||
.direnv/ | ||
.envrc | ||
venv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
from typing import Dict | ||
from xml.etree.ElementTree import Element | ||
|
||
from django.conf import settings | ||
from django.http.response import HttpResponse | ||
from django.test.utils import ContextList, override_settings | ||
from html5lib.constants import E | ||
from html5lib.html5parser import HTMLParser | ||
|
||
from .base import BaseTestCase | ||
|
||
|
||
def _get_ns(element: Element) -> dict[str, str]: | ||
""" | ||
Return the default `xmlns`. See | ||
https://docs.python.org/3/library/xml.etree.elementtree.html#parsing-xml-with-namespaces | ||
""" | ||
if not element.tag.startswith('{'): | ||
return dict() | ||
return {'': element.tag[1:].split('}', maxsplit=1)[0]} | ||
|
||
|
||
class CspRenderingTestCase(BaseTestCase): | ||
'Testing if `csp-nonce` renders.' | ||
panel_id = "StaticFilesPanel" | ||
|
||
# def setUp(self): | ||
# self.factory = RequestFactory() | ||
# self.async_factory = AsyncRequestFactory() | ||
|
||
def _fail_if_missing( | ||
self, root: Element, path: str, namespaces: Dict[str, str], | ||
nonce: str): | ||
""" | ||
Search elements, fail if a `nonce` attribute is missing on them. | ||
""" | ||
elements = root.findall(path=path, namespaces=namespaces) | ||
for item in elements: | ||
if item.attrib.get('nonce') != nonce: | ||
raise self.failureException(f'{item} has no nonce attribute.') | ||
|
||
def _fail_if_found( | ||
self, root: Element, path: str, namespaces: Dict[str, str]): | ||
""" | ||
Search elements, fail if a `nonce` attribute is found on them. | ||
""" | ||
elements = root.findall(path=path, namespaces=namespaces) | ||
for item in elements: | ||
if 'nonce' in item.attrib: | ||
raise self.failureException(f'{item} has no nonce attribute.') | ||
|
||
def _fail_on_invalid_html(self, content: bytes, parser: HTMLParser): | ||
'Fail if the passed HTML is invalid.' | ||
if parser.errors: | ||
default_msg = ['Content is invalid HTML:'] | ||
lines = content.split(b'\n') | ||
for position, errorcode, datavars in parser.errors: | ||
default_msg.append(' %s' % E[errorcode] % datavars) | ||
default_msg.append(' %r' % lines[position[0] - 1]) | ||
msg = self._formatMessage(None, '\n'.join(default_msg)) | ||
raise self.failureException(msg) | ||
|
||
@override_settings( | ||
DEBUG=True, MIDDLEWARE=settings.MIDDLEWARE + [ | ||
'csp.middleware.CSPMiddleware' | ||
]) | ||
def test_exists(self): | ||
'A `nonce` should exists when using the `CSPMiddleware`.' | ||
response = self.client.get(path='/regular/basic/') | ||
if not isinstance(response, HttpResponse): | ||
raise self.failureException(f'{response!r} is not a HttpResponse') | ||
self.assertEqual(response.status_code, 200) | ||
parser = HTMLParser() | ||
el_htmlroot: Element = parser.parse(stream=response.content) | ||
self._fail_on_invalid_html(content=response.content, parser=parser) | ||
self.assertContains(response, 'djDebug') | ||
namespaces = _get_ns(element=el_htmlroot) | ||
context: ContextList = \ | ||
response.context # pyright: ignore[reportAttributeAccessIssue] | ||
nonce = str(context['toolbar'].request.csp_nonce) | ||
self._fail_if_missing( | ||
root=el_htmlroot, path='.//link', namespaces=namespaces, | ||
nonce=nonce) | ||
self._fail_if_missing( | ||
root=el_htmlroot, path='.//script', namespaces=namespaces, | ||
nonce=nonce) | ||
|
||
@override_settings(DEBUG=True) | ||
def test_missing(self): | ||
'A `nonce` should not exist when not using the `CSPMiddleware`.' | ||
response = self.client.get(path='/regular/basic/') | ||
if not isinstance(response, HttpResponse): | ||
raise self.failureException(f'{response!r} is not a HttpResponse') | ||
self.assertEqual(response.status_code, 200) | ||
parser = HTMLParser() | ||
el_htmlroot: Element = parser.parse(stream=response.content) | ||
self._fail_on_invalid_html(content=response.content, parser=parser) | ||
self.assertContains(response, 'djDebug') | ||
namespaces = _get_ns(element=el_htmlroot) | ||
self._fail_if_found( | ||
root=el_htmlroot, path='.//link', namespaces=namespaces) | ||
self._fail_if_found( | ||
root=el_htmlroot, path='.//script', namespaces=namespaces) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ deps = | |
pygments | ||
selenium>=4.8.0 | ||
sqlparse | ||
django-csp | ||
passenv= | ||
CI | ||
COVERAGE_ARGS | ||
|