mirror of
https://github.com/django/django.git
synced 2025-06-30 15:59:11 +00:00
Refs #15727 -- Updated AdminSeleniumTestCase to use ContentSecurityPolicyMiddleware.
Replaced the custom CSP middleware previously used in the admin's AdminSeleniumTestCase with the official ContentSecurityPolicyMiddleware. This change ensures alignment with Django's built-in CSP support. Also updates the test logic to inspect browser console logs to assert that no CSP violations are triggered during Selenium admin tests.
This commit is contained in:
parent
d63241ebc7
commit
ff0ff98d42
@ -1,24 +1,27 @@
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
|
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
|
||||||
from django.test import modify_settings
|
from django.test import modify_settings, override_settings
|
||||||
from django.test.selenium import SeleniumTestCase
|
from django.test.selenium import SeleniumTestCase
|
||||||
from django.utils.deprecation import MiddlewareMixin
|
from django.utils.csp import CSP
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
# Make unittest ignore frames in this module when reporting failures.
|
# Make unittest ignore frames in this module when reporting failures.
|
||||||
__unittest = True
|
__unittest = True
|
||||||
|
|
||||||
|
|
||||||
class CSPMiddleware(MiddlewareMixin):
|
@modify_settings(
|
||||||
"""The admin's JavaScript should be compatible with CSP."""
|
MIDDLEWARE={"append": "django.middleware.csp.ContentSecurityPolicyMiddleware"}
|
||||||
|
)
|
||||||
def process_response(self, request, response):
|
@override_settings(
|
||||||
response.headers["Content-Security-Policy"] = "default-src 'self'"
|
SECURE_CSP={
|
||||||
return response
|
"default-src": [CSP.NONE],
|
||||||
|
"connect-src": [CSP.SELF],
|
||||||
|
"img-src": [CSP.SELF],
|
||||||
@modify_settings(MIDDLEWARE={"append": "django.contrib.admin.tests.CSPMiddleware"})
|
"script-src": [CSP.SELF],
|
||||||
|
"style-src": [CSP.SELF],
|
||||||
|
},
|
||||||
|
)
|
||||||
class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase):
|
class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase):
|
||||||
available_apps = [
|
available_apps = [
|
||||||
"django.contrib.admin",
|
"django.contrib.admin",
|
||||||
@ -28,6 +31,11 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase):
|
|||||||
"django.contrib.sites",
|
"django.contrib.sites",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
# Ensure that no CSP violations were logged in the browser.
|
||||||
|
self.assertEqual(self.get_browser_logs(source="security"), [])
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
def wait_until(self, callback, timeout=10):
|
def wait_until(self, callback, timeout=10):
|
||||||
"""
|
"""
|
||||||
Block the execution of the tests until the specified callback returns a
|
Block the execution of the tests until the specified callback returns a
|
||||||
|
@ -77,7 +77,11 @@ class SeleniumTestCaseBase(type(LiveServerTestCase)):
|
|||||||
def get_capability(cls, browser):
|
def get_capability(cls, browser):
|
||||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||||
|
|
||||||
return getattr(DesiredCapabilities, browser.upper())
|
caps = getattr(DesiredCapabilities, browser.upper())
|
||||||
|
if browser == "chrome":
|
||||||
|
caps["goog:loggingPrefs"] = {"browser": "ALL"}
|
||||||
|
|
||||||
|
return caps
|
||||||
|
|
||||||
def create_options(self):
|
def create_options(self):
|
||||||
options = self.import_options(self.browser)()
|
options = self.import_options(self.browser)()
|
||||||
@ -237,6 +241,19 @@ class SeleniumTestCase(LiveServerTestCase, metaclass=SeleniumTestCaseBase):
|
|||||||
path.parent.mkdir(exist_ok=True, parents=True)
|
path.parent.mkdir(exist_ok=True, parents=True)
|
||||||
self.selenium.save_screenshot(path)
|
self.selenium.save_screenshot(path)
|
||||||
|
|
||||||
|
def get_browser_logs(self, source=None, level="ALL"):
|
||||||
|
"""Return Chrome console logs filtered by level and optionally source."""
|
||||||
|
try:
|
||||||
|
logs = self.selenium.get_log("browser")
|
||||||
|
except AttributeError:
|
||||||
|
logs = []
|
||||||
|
return [
|
||||||
|
log
|
||||||
|
for log in logs
|
||||||
|
if (level == "ALL" or log["level"] == level)
|
||||||
|
and (source is None or log["source"] == source)
|
||||||
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _quit_selenium(cls):
|
def _quit_selenium(cls):
|
||||||
# quit() the WebDriver before attempting to terminate and join the
|
# quit() the WebDriver before attempting to terminate and join the
|
||||||
|
Loading…
x
Reference in New Issue
Block a user