diff --git a/README.md b/README.md
index 81b673a..5a972c1 100644
--- a/README.md
+++ b/README.md
@@ -47,7 +47,6 @@ Pre-Django 1.10, middleware modules can be added to `MIDDLEWARE_CLASSES` list in
...
'security.middleware.DoNotTrackMiddleware',
'security.middleware.ContentNoSniff',
- 'security.middleware.XssProtectMiddleware',
'security.middleware.XFrameOptionsMiddleware',
)
@@ -57,7 +56,6 @@ After Django 1.10, middleware modules can be added to `MIDDLEWARE` list in setti
...
'security.middleware.DoNotTrackMiddleware',
'security.middleware.ContentNoSniff',
- 'security.middleware.XssProtectMiddleware',
'security.middleware.XFrameOptionsMiddleware',
)
@@ -139,11 +137,6 @@ or minimum configuration.
-XssProtectMiddleware
- | DEPRECATED: Will be removed in future releases, consider django.middleware.security.SecurityMiddleware via SECURE_BROWSER_XSS_FILTER setting. Enforce browser's Cross Site Scripting protection. Recommended.
- | None.
-
## Views
diff --git a/security/middleware.py b/security/middleware.py
index d441a0a..ab5e2a1 100644
--- a/security/middleware.py
+++ b/security/middleware.py
@@ -23,10 +23,11 @@
logger = logging.getLogger(__name__)
DJANGO_SECURITY_MIDDLEWARE_URL = (
"https://docs.djangoproject.com/en/1.11/ref"
- "/middleware/#django.middleware.security.SecurityMiddleware")
+ "/middleware/#django.middleware.security.SecurityMiddleware"
+)
DJANGO_CLICKJACKING_MIDDLEWARE_URL = (
- "https://docs.djangoproject.com/en/1.11/"
- "ref/clickjacking/")
+ "https://docs.djangoproject.com/en/1.11/" "ref/clickjacking/"
+)
class CustomLogoutMixin(object):
@@ -36,20 +37,25 @@ class CustomLogoutMixin(object):
"""
class Messages(object):
- NOT_A_MODULE_PATH = (u"Invalid CUSTOM_LOGOUT_MODULE setting '{0}'. "
- u"Expected module path to a function")
- FAILED_TO_LOAD = (u"Invalid CUSTOM_LOGOUT_MODULE setting. "
- u"Failed to load module '{0}': {1}")
- MISSING_FUNCTION = (u"Invalid CUSTOM_LOGOUT_MODULE setting. "
- u"Could not find function '{0}' in module '{1}'")
+ NOT_A_MODULE_PATH = (
+ "Invalid CUSTOM_LOGOUT_MODULE setting '{0}'. "
+ "Expected module path to a function"
+ )
+ FAILED_TO_LOAD = (
+ "Invalid CUSTOM_LOGOUT_MODULE setting. " "Failed to load module '{0}': {1}"
+ )
+ MISSING_FUNCTION = (
+ "Invalid CUSTOM_LOGOUT_MODULE setting. "
+ "Could not find function '{0}' in module '{1}'"
+ )
def perform_logout(self, request):
- if not getattr(self, 'CUSTOM_LOGOUT_MODULE', None):
+ if not getattr(self, "CUSTOM_LOGOUT_MODULE", None):
logout(request)
return
try:
- module_path, func_name = self.CUSTOM_LOGOUT_MODULE.rsplit('.', 1)
+ module_path, func_name = self.CUSTOM_LOGOUT_MODULE.rsplit(".", 1)
except ValueError:
err = self.Messages.NOT_A_MODULE_PATH
raise Exception(err.format(self.CUSTOM_LOGOUT_MODULE))
@@ -113,9 +119,7 @@ def __init__(self, get_response=None):
)
for key in self.OPTIONAL_SETTINGS:
- self.load_setting(
- key, getattr(django.conf.settings, key, None)
- )
+ self.load_setting(key, getattr(django.conf.settings, key, None))
setting_changed.connect(self._on_setting_changed)
@@ -169,8 +173,8 @@ def process_request(self, request):
Read DNT header from browser request and create request attribute
"""
request.dnt = None
- if 'HTTP_DNT' in request.META:
- request.dnt = request.META['HTTP_DNT'] == '1'
+ if "HTTP_DNT" in request.META:
+ request.dnt = request.META["HTTP_DNT"] == "1"
# returns None in normal conditions
def process_response(self, request, response):
@@ -178,84 +182,8 @@ def process_response(self, request, response):
Echo DNT header in response per section 8.4 of draft-mayer-do-not-
track-00
"""
- if 'HTTP_DNT' in request.META:
- response['DNT'] = request.META['HTTP_DNT']
- return response
-
-
-class XssProtectMiddleware(BaseMiddleware):
- """
- DEPRECATED: Will be removed in future releases. Consider
- django.middleware.security.SecurityMiddleware as a replacement for this via
- SECURE_BROWSER_XSS_FILTER setting.
-
- Sends X-XSS-Protection HTTP header that controls Cross-Site Scripting
- filter on MSIE. Use XSS_PROTECT option in settings file with the following
- values:
-
- ``sanitize`` enable XSS filter that tries to sanitize requests instead
- of blocking (*default*)
-
- ``on`` enable full XSS filter blocking XSS requests (may `leak
- document.referrer `_)
-
- ``off`` completely disable XSS filter
-
- **Note:** As of 1.8, Django's `SECURE_BROWSER_XSS_FILTER
- `_
- controls the X-XSS-Protection header.
-
- Reference:
-
- - `Controlling the XSS Filter
- `_
- """
-
- OPTIONAL_SETTINGS = ("XSS_PROTECT",)
-
- OPTIONS = {
- 'on': '1; mode=block',
- 'off': '0',
- 'sanitize': '1',
- }
-
- DEFAULT = 'sanitize'
-
- def __init__(self, get_response=None):
- super().__init__(get_response)
- warnings.warn((
- 'DEPRECATED: The middleware "{name}" will no longer be '
- 'supported in future releases of this library. Refer to {url} for '
- 'an alternative approach with regards to the settings: {settings}'
- ).format(
- name=self.__class__.__name__,
- url=DJANGO_SECURITY_MIDDLEWARE_URL,
- settings="SECURE_BROWSER_XSS_FILTER"))
-
- def load_setting(self, setting, value):
- if not value:
- self.option = self.DEFAULT
- return
-
- value = value.lower()
-
- if value in self.OPTIONS.keys():
- self.option = value
- return
-
- raise ImproperlyConfigured(
- self.__class__.__name__ + " invalid option for XSS_PROTECT."
- )
-
- def process_response(self, request, response):
- """
- Add X-XSS-Protection to the response header.
- """
- header = self.OPTIONS[self.option]
- response['X-XSS-Protection'] = header
+ if "HTTP_DNT" in request.META:
+ response["DNT"] = request.META["HTTP_DNT"]
return response
@@ -274,22 +202,18 @@ class ClearSiteDataMiddleware(BaseMiddleware):
`_
"""
- REQUIRED_SETTINGS = ('CLEAR_SITE_DATA_URL_WHITELIST',)
- OPTIONAL_SETTINGS = ('CLEAR_SITE_DATA_DIRECTIVES')
+ REQUIRED_SETTINGS = ("CLEAR_SITE_DATA_URL_WHITELIST",)
+ OPTIONAL_SETTINGS = "CLEAR_SITE_DATA_DIRECTIVES"
- DEFAULT_DIRECTIVES = ['cookies', 'storage']
- ALLOWED_DIRECTIVES = (
- 'cache', 'cookies', 'storage', 'executionContexts', '*'
- )
+ DEFAULT_DIRECTIVES = ["cookies", "storage"]
+ ALLOWED_DIRECTIVES = ("cache", "cookies", "storage", "executionContexts", "*")
def load_setting(self, setting, value):
- if setting == 'CLEAR_SITE_DATA_URL_WHITELIST':
+ if setting == "CLEAR_SITE_DATA_URL_WHITELIST":
self.clear_site_urls = value
directives = getattr(
- django.conf.settings,
- 'CLEAR_SITE_DATA_DIRECTIVES',
- self.DEFAULT_DIRECTIVES
+ django.conf.settings, "CLEAR_SITE_DATA_DIRECTIVES", self.DEFAULT_DIRECTIVES
)
directives = [
@@ -298,9 +222,8 @@ def load_setting(self, setting, value):
if directive.strip() in self.ALLOWED_DIRECTIVES
]
- self.clear_site_directives = ', '.join(
- '"{0}"'.format(directive)
- for directive in directives
+ self.clear_site_directives = ", ".join(
+ '"{0}"'.format(directive) for directive in directives
)
def process_response(self, request, response):
@@ -310,7 +233,7 @@ def process_response(self, request, response):
"""
if request.path in self.clear_site_urls:
- response['Clear-Site-Data'] = self.clear_site_directives
+ response["Clear-Site-Data"] = self.clear_site_directives
return response
@@ -340,20 +263,23 @@ class ContentNoSniff(MiddlewareMixin):
def __init__(self, get_response=None):
super().__init__(get_response)
- warnings.warn((
- 'DEPRECATED: The middleware "{name}" will no longer be '
- 'supported in future releases of this library. Refer to {url} for '
- 'an alternative approach with regards to the settings: {settings}'
- ).format(
- name=self.__class__.__name__,
- url=DJANGO_SECURITY_MIDDLEWARE_URL,
- settings="SECURE_CONTENT_TYPE_NOSNIFF"))
+ warnings.warn(
+ (
+ 'DEPRECATED: The middleware "{name}" will no longer be '
+ "supported in future releases of this library. Refer to {url} for "
+ "an alternative approach with regards to the settings: {settings}"
+ ).format(
+ name=self.__class__.__name__,
+ url=DJANGO_SECURITY_MIDDLEWARE_URL,
+ settings="SECURE_CONTENT_TYPE_NOSNIFF",
+ )
+ )
def process_response(self, request, response):
"""
Add ``X-Content-Options: nosniff`` to the response header.
"""
- response['X-Content-Options'] = 'nosniff'
+ response["X-Content-Options"] = "nosniff"
return response
@@ -402,7 +328,7 @@ def process_view(self, request, view, *args, **kwargs):
# because the reason the URL is exempt may be because a special URL
# config is in use (i.e. during a test) that doesn't have URL_NAME.
- path = request.path_info.lstrip('/')
+ path = request.path_info.lstrip("/")
if any(m.match(path) for m in self.exempt_urls):
return
@@ -418,6 +344,7 @@ def process_view(self, request, view, *args, **kwargs):
return
from .password_expiry import password_is_expired
+
if password_is_expired(request.user):
return HttpResponseRedirect(password_change_url)
@@ -460,12 +387,14 @@ def load_setting(self, setting, value):
value = value or {}
self.whitelist = value.get("WHITELIST_ON", False)
if self.whitelist:
- self.whitelist_url_regexes = \
- [compile(x) for x in value['WHITELIST_REGEXES']]
+ self.whitelist_url_regexes = [
+ compile(x) for x in value["WHITELIST_REGEXES"]
+ ]
self.blacklist = value.get("BLACKLIST_ON", False)
if self.blacklist:
- self.blacklist_url_regexes = \
- [compile(x) for x in value['BLACKLIST_REGEXES']]
+ self.blacklist_url_regexes = [
+ compile(x) for x in value["BLACKLIST_REGEXES"]
+ ]
def process_response(self, request, response):
"""
@@ -473,7 +402,7 @@ def process_response(self, request, response):
whitelist non-confidential pages and treat all others as non-
confidential, or specifically blacklist pages as confidential
"""
- path = request.path.lstrip('/')
+ path = request.path.lstrip("/")
if self.whitelist:
if not any(re.match(path) for re in self.whitelist_url_regexes):
self._remove_response_caching(response)
@@ -487,10 +416,9 @@ def _remove_response_caching(self, response):
"""
Overwrites specific headers to make the HTTP response confidential.
"""
- response['Cache-control'] = \
- 'no-cache, no-store, max-age=0, must-revalidate'
- response['Pragma'] = "no-cache"
- response['Expires'] = -1
+ response["Cache-control"] = "no-cache, no-store, max-age=0, must-revalidate"
+ response["Pragma"] = "no-cache"
+ response["Expires"] = -1
# http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-01
@@ -528,29 +456,31 @@ class XFrameOptionsMiddleware(BaseMiddleware):
`_
"""
- OPTIONAL_SETTINGS = ('X_FRAME_OPTIONS', 'X_FRAME_OPTIONS_EXCLUDE_URLS')
+ OPTIONAL_SETTINGS = ("X_FRAME_OPTIONS", "X_FRAME_OPTIONS_EXCLUDE_URLS")
- DEFAULT = 'deny'
+ DEFAULT = "deny"
def __init__(self, get_response=None):
super().__init__(get_response)
- warnings.warn((
- 'An official middleware "{name}" is supported by Django. '
- 'Refer to {url} to see if its approach fits the use case.'
- ).format(
- name="XFrameOptionsMiddleware",
- url=DJANGO_CLICKJACKING_MIDDLEWARE_URL))
+ warnings.warn(
+ (
+ 'An official middleware "{name}" is supported by Django. '
+ "Refer to {url} to see if its approach fits the use case."
+ ).format(
+ name="XFrameOptionsMiddleware", url=DJANGO_CLICKJACKING_MIDDLEWARE_URL
+ )
+ )
def load_setting(self, setting, value):
- if setting == 'X_FRAME_OPTIONS':
+ if setting == "X_FRAME_OPTIONS":
if not value:
self.option = XFrameOptionsMiddleware.DEFAULT
return
value = value.lower()
- options = ['sameorigin', 'deny']
+ options = ["sameorigin", "deny"]
- if value in options or value.startswith('allow-from:'):
+ if value in options or value.startswith("allow-from:"):
self.option = value
return
@@ -558,7 +488,7 @@ def load_setting(self, setting, value):
self.__class__.__name__ + " invalid option for X_FRAME_OPTIONS"
)
- elif setting == 'X_FRAME_OPTIONS_EXCLUDE_URLS':
+ elif setting == "X_FRAME_OPTIONS_EXCLUDE_URLS":
if not value:
self.exclude_urls = []
return
@@ -567,8 +497,10 @@ def load_setting(self, setting, value):
self.exclude_urls = [compile(url) for url in value]
except TypeError:
raise ImproperlyConfigured(
- "{0} invalid option for X_FRAME_OPTIONS_EXCLUDE_URLS"
- .format(self.__class__.__name__))
+ "{0} invalid option for X_FRAME_OPTIONS_EXCLUDE_URLS".format(
+ self.__class__.__name__
+ )
+ )
def process_response(self, request, response):
"""
@@ -578,7 +510,7 @@ def process_response(self, request, response):
if url.match(request.path):
break
else:
- response['X-Frame-Options'] = self.option
+ response["X-Frame-Options"] = self.option
return response
@@ -680,43 +612,44 @@ class ContentSecurityPolicyMiddleware(MiddlewareMixin):
- `HTML5.1 - Sandboxing `_
"""
+
# these types accept CSP locations as arguments
_CSP_LOC_TYPES = [
- 'default-src',
- 'connect-src',
- 'child-src',
- 'font-src',
- 'form-action',
- 'frame-ancestors',
- 'frame-src',
- 'img-src',
- 'media-src',
- 'object-src',
- 'script-src',
- 'style-src',
- 'plugin-types',
- 'worker-src'
+ "default-src",
+ "connect-src",
+ "child-src",
+ "font-src",
+ "form-action",
+ "frame-ancestors",
+ "frame-src",
+ "img-src",
+ "media-src",
+ "object-src",
+ "script-src",
+ "style-src",
+ "plugin-types",
+ "worker-src",
]
# arguments to location types
- _CSP_LOCATIONS = ['self', 'none', 'unsafe-eval', 'unsafe-inline']
+ _CSP_LOCATIONS = ["self", "none", "unsafe-eval", "unsafe-inline"]
# sandbox allowed arguments
# http://www.w3.org/html/wg/drafts/html/master/single-page.html#sandboxing
# https://www.w3.org/TR/CSP2/
_CSP_SANDBOX_ARGS = [
- '',
- 'allow-forms',
- 'allow-pointer-lock',
- 'allow-popups',
- 'allow-same-origin',
- 'allow-scripts',
- 'allow-top-navigation',
+ "",
+ "allow-forms",
+ "allow-pointer-lock",
+ "allow-popups",
+ "allow-same-origin",
+ "allow-scripts",
+ "allow-top-navigation",
]
# reflected-xss allowed arguments
# http://www.w3.org/TR/CSP11/#directive-reflected-xss
- _CSP_XSS_ARGS = ['allow', 'block', 'filter']
+ _CSP_XSS_ARGS = ["allow", "block", "filter"]
# referrer allowed arguments
# http://www.w3.org/TR/CSP11/#directive-referrer
@@ -734,24 +667,24 @@ class ContentSecurityPolicyMiddleware(MiddlewareMixin):
def _csp_loc_builder(self, key, value):
if not isinstance(value, (list, tuple)):
- logger.warn('Arguments to %s must be given as list or tuple', key)
+ logger.warn("Arguments to %s must be given as list or tuple", key)
raise django.core.exceptions.MiddlewareNotUsed
csp_loc_string = "{0}".format(key)
for loc in value:
if loc in self._CSP_LOCATIONS:
csp_loc_string += " '{0}'".format(loc) # quoted
- elif loc == '*':
- csp_loc_string += ' *' # not quoted
+ elif loc == "*":
+ csp_loc_string += " *" # not quoted
else:
# XXX: check for valid hostname or URL
- csp_loc_string += " {0}".format(loc) # not quoted
+ csp_loc_string += " {0}".format(loc) # not quoted
return csp_loc_string
def _csp_sandbox_builder(self, key, value):
if not isinstance(value, (list, tuple)):
- logger.warn('Arguments to %s must be given as list or tuple', key)
+ logger.warn("Arguments to %s must be given as list or tuple", key)
raise django.core.exceptions.MiddlewareNotUsed
csp_sandbox_string = "{0}".format(key)
@@ -759,25 +692,25 @@ def _csp_sandbox_builder(self, key, value):
if opt in self._CSP_SANDBOX_ARGS:
csp_sandbox_string += " {0}".format(opt)
else:
- logger.warn('Invalid CSP sandbox argument %s', opt)
+ logger.warn("Invalid CSP sandbox argument %s", opt)
raise django.core.exceptions.MiddlewareNotUsed
return csp_sandbox_string
def _csp_report_uri_builder(self, key, value):
# XXX: add valid URL check
- return '{0} {1}'.format(key, value)
+ return "{0} {1}".format(key, value)
def _csp_referrer_builder(self, key, value):
if value not in self._CSP_REF_ARGS:
- logger.warning('Invalid CSP %s value %s', key, value)
+ logger.warning("Invalid CSP %s value %s", key, value)
raise django.core.exceptions.MiddlewareNotUsed
return "{0} {1}".format(key, value)
def _csp_reflected_xss_builder(self, key, value):
if value not in self._CSP_XSS_ARGS:
- logger.warning('Invalid CSP %s value %s', key, value)
+ logger.warning("Invalid CSP %s value %s", key, value)
raise django.core.exceptions.MiddlewareNotUsed
return "{0} {1}".format(key, value)
@@ -786,52 +719,51 @@ def _csp_builder(self, csp_dict):
csp_components = []
for key, value in csp_dict.items():
-
if key in self._CSP_LOC_TYPES:
csp_components.append(self._csp_loc_builder(key, value))
- elif key == 'sandbox':
+ elif key == "sandbox":
csp_components.append(self._csp_sandbox_builder(key, value))
- elif key == 'report-uri':
+ elif key == "report-uri":
csp_components.append(self._csp_report_uri_builder(key, value))
- elif key == 'referrer':
+ elif key == "referrer":
csp_components.append(self._csp_referrer_builder(key, value))
- elif key == 'reflected-xss':
+ elif key == "reflected-xss":
csp_components.append(
self._csp_reflected_xss_builder(key, value),
)
else:
- logger.warning('Invalid CSP type %s', key)
+ logger.warning("Invalid CSP type %s", key)
raise django.core.exceptions.MiddlewareNotUsed
- return '; '.join(csp_components)
+ return "; ".join(csp_components)
def __init__(self, get_response=None):
# sanity checks
self.get_response = get_response
- conf_csp_mode = getattr(django.conf.settings, 'CSP_MODE', None)
- self._csp_mode = conf_csp_mode or 'enforce'
- csp_string = getattr(django.conf.settings, 'CSP_STRING', None)
- csp_dict = getattr(django.conf.settings, 'CSP_DICT', None)
- csp_report_string = getattr(django.conf.settings, 'CSP_REPORT_STRING',
- None)
- csp_report_dict = getattr(django.conf.settings, 'CSP_REPORT_DICT',
- None)
-
- set_csp_str = self._csp_mode in ['enforce', 'enforce-and-report-only']
- set_csp_report_str = self._csp_mode in ['report-only',
- 'enforce-and-report-only']
+ conf_csp_mode = getattr(django.conf.settings, "CSP_MODE", None)
+ self._csp_mode = conf_csp_mode or "enforce"
+ csp_string = getattr(django.conf.settings, "CSP_STRING", None)
+ csp_dict = getattr(django.conf.settings, "CSP_DICT", None)
+ csp_report_string = getattr(django.conf.settings, "CSP_REPORT_STRING", None)
+ csp_report_dict = getattr(django.conf.settings, "CSP_REPORT_DICT", None)
+
+ set_csp_str = self._csp_mode in ["enforce", "enforce-and-report-only"]
+ set_csp_report_str = self._csp_mode in [
+ "report-only",
+ "enforce-and-report-only",
+ ]
if not (set_csp_str or set_csp_report_str):
logger.error(
'Invalid CSP_MODE %s, "enforce", "report-only" '
'or "enforce-and-report-only" allowed',
- self._csp_mode
+ self._csp_mode,
)
raise django.core.exceptions.MiddlewareNotUsed
@@ -842,20 +774,21 @@ def __init__(self, get_response=None):
self._set_csp_report_str(csp_report_dict, csp_report_string)
def _set_csp_str(self, csp_dict, csp_string):
- err_msg = 'Middleware requires either CSP_STRING or CSP_DICT setting'
+ err_msg = "Middleware requires either CSP_STRING or CSP_DICT setting"
if not (csp_dict or csp_string):
- logger.error('%s, none found', err_msg)
+ logger.error("%s, none found", err_msg)
raise django.core.exceptions.MiddlewareNotUsed
- self._csp_string = self._choose_csp_str(csp_dict, csp_string,
- err_msg + ', not both')
+ self._csp_string = self._choose_csp_str(
+ csp_dict, csp_string, err_msg + ", not both"
+ )
def _set_csp_report_str(self, csp_report_dict, csp_report_string):
report_err_msg = (
- 'Middleware requires either CSP_REPORT_STRING, '
- 'CSP_REPORT_DICT setting, or neither. If neither, '
- 'middleware requires CSP_STRING or CSP_DICT, '
- 'but not both.'
+ "Middleware requires either CSP_REPORT_STRING, "
+ "CSP_REPORT_DICT setting, or neither. If neither, "
+ "middleware requires CSP_STRING or CSP_DICT, "
+ "but not both."
)
# Default to the regular CSP string if report string not configured
@@ -863,9 +796,7 @@ def _set_csp_report_str(self, csp_report_dict, csp_report_string):
self._csp_report_string = self._csp_string
else:
self._csp_report_string = self._choose_csp_str(
- csp_report_dict,
- csp_report_string,
- report_err_msg
+ csp_report_dict, csp_report_string, report_err_msg
)
def _choose_csp_str(self, csp_dict, csp_str, err_msg):
@@ -883,7 +814,7 @@ def _choose_csp_str(self, csp_dict, csp_str, err_msg):
Log an error message if both are provided.
"""
if csp_dict and csp_str:
- logger.error('%s', err_msg)
+ logger.error("%s", err_msg)
raise django.core.exceptions.MiddlewareNotUsed
if csp_dict:
@@ -891,7 +822,7 @@ def _choose_csp_str(self, csp_dict, csp_str, err_msg):
elif csp_str:
return csp_str
else:
- return ''
+ return ""
def process_response(self, request, response):
"""
@@ -900,21 +831,23 @@ def process_response(self, request, response):
"""
# choose headers based enforcement mode
is_ie = False
- if 'HTTP_USER_AGENT' in request.META:
- parsed_ua = user_agent_parser.ParseUserAgent(request.META['HTTP_USER_AGENT'])
- is_ie = parsed_ua['family'] == 'IE'
+ if "HTTP_USER_AGENT" in request.META:
+ parsed_ua = user_agent_parser.ParseUserAgent(
+ request.META["HTTP_USER_AGENT"]
+ )
+ is_ie = parsed_ua["family"] == "IE"
- csp_header = 'Content-Security-Policy'
+ csp_header = "Content-Security-Policy"
if is_ie:
- csp_header = 'X-Content-Security-Policy'
- report_only_header = 'Content-Security-Policy-Report-Only'
+ csp_header = "X-Content-Security-Policy"
+ report_only_header = "Content-Security-Policy-Report-Only"
# actually add appropriate headers
- if self._csp_mode == 'enforce':
+ if self._csp_mode == "enforce":
response[csp_header] = self._csp_string
- elif self._csp_mode == 'report-only':
+ elif self._csp_mode == "report-only":
response[report_only_header] = self._csp_report_string
- elif self._csp_mode == 'enforce-and-report-only':
+ elif self._csp_mode == "enforce-and-report-only":
response[csp_header] = self._csp_string
response[report_only_header] = self._csp_report_string
@@ -950,19 +883,25 @@ class StrictTransportSecurityMiddleware(MiddlewareMixin):
`_
- `Preloaded HSTS sites `_
"""
+
def __init__(self, get_response=None):
- warnings.warn((
- 'DEPRECATED: The middleware "{name}" will no longer be '
- 'supported in future releases of this library. Refer to {url} for '
- 'an alternative approach with regards to the settings: {settings}'
- ).format(
- name=self.__class__.__name__,
- url=DJANGO_SECURITY_MIDDLEWARE_URL,
- settings=", ".join([
- "SECURE_HSTS_SECONDS",
- "SECURE_HSTS_INCLUDE_SUBDOMAINS",
- "SECURE_HSTS_PRELOAD",
- ])))
+ warnings.warn(
+ (
+ 'DEPRECATED: The middleware "{name}" will no longer be '
+ "supported in future releases of this library. Refer to {url} for "
+ "an alternative approach with regards to the settings: {settings}"
+ ).format(
+ name=self.__class__.__name__,
+ url=DJANGO_SECURITY_MIDDLEWARE_URL,
+ settings=", ".join(
+ [
+ "SECURE_HSTS_SECONDS",
+ "SECURE_HSTS_INCLUDE_SUBDOMAINS",
+ "SECURE_HSTS_PRELOAD",
+ ]
+ ),
+ )
+ )
self.get_response = get_response
@@ -981,19 +920,19 @@ def __init__(self, get_response=None):
except AttributeError:
self.preload = True
- self.value = 'max-age={0}'.format(self.max_age)
+ self.value = "max-age={0}".format(self.max_age)
if self.subdomains:
- self.value += ' ; includeSubDomains'
+ self.value += " ; includeSubDomains"
if self.preload:
- self.value += ' ; preload'
+ self.value += " ; preload"
def process_response(self, request, response):
"""
Add Strict-Transport-Security header.
"""
- response['Strict-Transport-Security'] = self.value
+ response["Strict-Transport-Security"] = self.value
return response
@@ -1021,22 +960,24 @@ class P3PPolicyMiddleware(BaseMiddleware):
def __init__(self, get_response=None):
super().__init__(get_response)
- warnings.warn((
- 'DEPRECATED: The middleware "{name}" will no longer be '
- 'supported in future releases of this library.'
- ).format(name=self.__class__.__name__))
+ warnings.warn(
+ (
+ 'DEPRECATED: The middleware "{name}" will no longer be '
+ "supported in future releases of this library."
+ ).format(name=self.__class__.__name__)
+ )
def load_setting(self, setting, value):
- if setting == 'P3P_COMPACT_POLICY':
+ if setting == "P3P_COMPACT_POLICY":
self.policy = value
- elif setting == 'P3P_POLICY_URL':
- self.policy_url = value or '/w3c/p3p.xml'
+ elif setting == "P3P_POLICY_URL":
+ self.policy_url = value or "/w3c/p3p.xml"
def process_response(self, request, response):
"""
Add P3P policy to the response header.
"""
- response['P3P'] = 'policyref="{0}" CP="{1}"'.format(
+ response["P3P"] = 'policyref="{0}" CP="{1}"'.format(
self.policy_url,
self.policy,
)
@@ -1067,15 +1008,19 @@ class SessionExpiryPolicyMiddleware(CustomLogoutMixin, BaseMiddleware):
e.g. 'django.contrib.auth.logout'.
"""
- OPTIONAL_SETTINGS = ('SESSION_COOKIE_AGE', 'SESSION_INACTIVITY_TIMEOUT',
- 'SESSION_EXPIRY_EXEMPT_URLS', 'CUSTOM_LOGOUT_MODULE')
+ OPTIONAL_SETTINGS = (
+ "SESSION_COOKIE_AGE",
+ "SESSION_INACTIVITY_TIMEOUT",
+ "SESSION_EXPIRY_EXEMPT_URLS",
+ "CUSTOM_LOGOUT_MODULE",
+ )
SECONDS_PER_DAY = 86400
SECONDS_PER_30MINS = 1800
# Session keys
- START_TIME_KEY = 'starttime'
- LAST_ACTIVITY_KEY = 'lastactivity'
+ START_TIME_KEY = "starttime"
+ LAST_ACTIVITY_KEY = "lastactivity"
@classmethod
def _get_datetime_in_session(cls, key, session):
@@ -1087,47 +1032,34 @@ def _set_datetime_in_session(cls, key, value, session):
@classmethod
def get_start_time(cls, request):
- return cls._get_datetime_in_session(
- cls.START_TIME_KEY,
- request.session
- )
+ return cls._get_datetime_in_session(cls.START_TIME_KEY, request.session)
@classmethod
def set_start_time(cls, request, date):
- cls._set_datetime_in_session(
- cls.START_TIME_KEY,
- date,
- request.session
- )
+ cls._set_datetime_in_session(cls.START_TIME_KEY, date, request.session)
@classmethod
def get_last_activity(cls, request):
- return cls._get_datetime_in_session(
- cls.LAST_ACTIVITY_KEY,
- request.session
- )
+ return cls._get_datetime_in_session(cls.LAST_ACTIVITY_KEY, request.session)
@classmethod
def set_last_activity(cls, request, date):
- cls._set_datetime_in_session(
- cls.LAST_ACTIVITY_KEY,
- date,
- request.session
- )
+ cls._set_datetime_in_session(cls.LAST_ACTIVITY_KEY, date, request.session)
def load_setting(self, setting, value):
- if setting == 'SESSION_COOKIE_AGE':
+ if setting == "SESSION_COOKIE_AGE":
self.SESSION_COOKIE_AGE = value or self.SECONDS_PER_DAY
- logger.debug("Max Session Cookie Age is %d seconds",
- self.SESSION_COOKIE_AGE
- )
- elif setting == 'SESSION_INACTIVITY_TIMEOUT':
+ logger.debug(
+ "Max Session Cookie Age is %d seconds", self.SESSION_COOKIE_AGE
+ )
+ elif setting == "SESSION_INACTIVITY_TIMEOUT":
# half an hour in seconds
self.SESSION_INACTIVITY_TIMEOUT = value or self.SECONDS_PER_30MINS
- logger.debug("Session Inactivity Timeout is %d seconds",
- self.SESSION_INACTIVITY_TIMEOUT
- )
- elif setting == 'SESSION_EXPIRY_EXEMPT_URLS':
+ logger.debug(
+ "Session Inactivity Timeout is %d seconds",
+ self.SESSION_INACTIVITY_TIMEOUT,
+ )
+ elif setting == "SESSION_EXPIRY_EXEMPT_URLS":
self.exempt_urls = [compile(expr) for expr in (value or ())]
else:
setattr(self, setting, value)
@@ -1139,13 +1071,13 @@ def process_request(self, request):
is the case. We set the last activity time to now() if the session
is still active.
"""
- if not hasattr(request, 'user'):
+ if not hasattr(request, "user"):
raise ImproperlyConfigured(
"The Login Required middleware "
"requires authentication middleware to be installed."
)
- path = request.path_info.lstrip('/')
+ path = request.path_info.lstrip("/")
if any(m.match(path) for m in self.exempt_urls):
return
@@ -1177,14 +1109,10 @@ def process_existing_session(self, request):
start_time = self.get_start_time(request)
last_activity_time = self.get_last_activity(request)
- logger.debug("Session %s started: %s",
- session.session_key,
- start_time
- )
- logger.debug("Session %s last active: %s",
- session.session_key,
- last_activity_time
- )
+ logger.debug("Session %s started: %s", session.session_key, start_time)
+ logger.debug(
+ "Session %s last active: %s", session.session_key, last_activity_time
+ )
session_age = self.get_diff_in_seconds(now, start_time)
session_too_old = session_age > self.SESSION_COOKIE_AGE
@@ -1213,6 +1141,7 @@ def get_diff_in_seconds(self, now, time):
age = diff.days * self.SECONDS_PER_DAY + diff.seconds
return age
+
# Modified a little bit by us.
# Copyright (c) 2008, Ryan Witt
@@ -1262,19 +1191,19 @@ class LoginRequiredMiddleware(BaseMiddleware, CustomLogoutMixin):
e.g. 'django.contrib.auth.logout'.
"""
- REQUIRED_SETTINGS = ('LOGIN_URL',)
- OPTIONAL_SETTINGS = ('LOGIN_EXEMPT_URLS', 'CUSTOM_LOGOUT_MODULE')
+ REQUIRED_SETTINGS = ("LOGIN_URL",)
+ OPTIONAL_SETTINGS = ("LOGIN_EXEMPT_URLS", "CUSTOM_LOGOUT_MODULE")
def load_setting(self, setting, value):
- if setting == 'LOGIN_URL':
+ if setting == "LOGIN_URL":
self.login_url = value
- elif setting == 'LOGIN_EXEMPT_URLS':
+ elif setting == "LOGIN_EXEMPT_URLS":
self.exempt_urls = [compile(expr) for expr in (value or ())]
else:
setattr(self, setting, value)
def assert_authentication_middleware_installed(self, request):
- if not hasattr(request, 'user'):
+ if not hasattr(request, "user"):
raise ImproperlyConfigured(
"The Login Required middleware "
"requires authentication middleware to be installed."
@@ -1292,12 +1221,12 @@ def process_request(self, request):
if request.user.is_authenticated:
return
- path = request.path_info.lstrip('/')
+ path = request.path_info.lstrip("/")
if any(m.match(path) for m in self.exempt_urls):
return
- if hasattr(request, 'login_url'):
+ if hasattr(request, "login_url"):
login_url = request.login_url
next_url = None
else:
@@ -1312,10 +1241,11 @@ def process_request(self, request):
)
if next_url:
- login_url = login_url + '?next=' + next_url
+ login_url = login_url + "?next=" + next_url
return HttpResponseRedirect(login_url)
+
class ReferrerPolicyMiddleware(BaseMiddleware):
"""
Sends Referrer-Policy HTTP header that controls when the browser will set
@@ -1339,11 +1269,19 @@ class ReferrerPolicyMiddleware(BaseMiddleware):
OPTIONAL_SETTINGS = ("REFERRER_POLICY",)
- OPTIONS = [ 'no-referrer', 'no-referrer-when-downgrade', 'origin',
- 'origin-when-cross-origin', 'same-origin', 'strict-origin',
- 'strict-origin-when-cross-origin', 'unsafe-url', 'off' ]
+ OPTIONS = [
+ "no-referrer",
+ "no-referrer-when-downgrade",
+ "origin",
+ "origin-when-cross-origin",
+ "same-origin",
+ "strict-origin",
+ "strict-origin-when-cross-origin",
+ "unsafe-url",
+ "off",
+ ]
- DEFAULT = 'same-origin'
+ DEFAULT = "same-origin"
def load_setting(self, setting, value):
if not value:
@@ -1364,7 +1302,7 @@ def process_response(self, request, response):
"""
Add Referrer-Policy to the response header.
"""
- if self.option != 'off':
+ if self.option != "off":
header = self.option
- response['Referrer-Policy'] = header
+ response["Referrer-Policy"] = header
return response
diff --git a/testing/settings.py b/testing/settings.py
index a81213b..ce0a287 100644
--- a/testing/settings.py
+++ b/testing/settings.py
@@ -7,93 +7,101 @@
ADMINS = ()
MANAGERS = ADMINS
DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.sqlite3',
- 'NAME': 'testing.db',
- 'USER': '',
- 'PASSWORD': '',
- 'HOST': '',
- 'PORT': '',
+ "default": {
+ "ENGINE": "django.db.backends.sqlite3",
+ "NAME": "testing.db",
+ "USER": "",
+ "PASSWORD": "",
+ "HOST": "",
+ "PORT": "",
}
}
-TIME_ZONE = 'America/Chicago'
+TIME_ZONE = "America/Chicago"
USE_TZ = True
-LANGUAGE_CODE = 'en-us'
+LANGUAGE_CODE = "en-us"
SITE_ID = 1
USE_I18N = True
USE_L10N = True
-MEDIA_ROOT = ''
-MEDIA_URL = ''
-STATIC_ROOT = ''
-STATIC_URL = '/static/'
+MEDIA_ROOT = ""
+MEDIA_URL = ""
+STATIC_ROOT = ""
+STATIC_URL = "/static/"
STATICFILES_DIRS = ()
STATICFILES_FINDERS = (
- 'django.contrib.staticfiles.finders.FileSystemFinder',
- 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+ "django.contrib.staticfiles.finders.FileSystemFinder",
+ "django.contrib.staticfiles.finders.AppDirectoriesFinder",
)
-SECRET_KEY = 'p_2zsf+@4uw$kcdl$!tkf0lrh%w^!#@2@iwo4plef2n$(@uj4_'
+SECRET_KEY = "p_2zsf+@4uw$kcdl$!tkf0lrh%w^!#@2@iwo4plef2n$(@uj4_"
MIDDLEWARE = (
- 'django.middleware.common.CommonMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
- 'security.middleware.SessionExpiryPolicyMiddleware',
- 'security.middleware.LoginRequiredMiddleware',
- 'security.middleware.XFrameOptionsMiddleware',
- 'security.middleware.ContentNoSniff',
- 'security.middleware.ContentSecurityPolicyMiddleware',
- 'security.middleware.StrictTransportSecurityMiddleware',
- 'security.middleware.P3PPolicyMiddleware',
- 'security.middleware.XssProtectMiddleware',
- 'security.middleware.MandatoryPasswordChangeMiddleware',
- 'security.middleware.NoConfidentialCachingMiddleware',
- 'security.auth_throttling.Middleware',
- 'security.middleware.ReferrerPolicyMiddleware',
+ "django.middleware.common.CommonMiddleware",
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "django.middleware.csrf.CsrfViewMiddleware",
+ "django.contrib.messages.middleware.MessageMiddleware",
+ "security.middleware.SessionExpiryPolicyMiddleware",
+ "security.middleware.LoginRequiredMiddleware",
+ "security.middleware.XFrameOptionsMiddleware",
+ "security.middleware.ContentNoSniff",
+ "security.middleware.ContentSecurityPolicyMiddleware",
+ "security.middleware.StrictTransportSecurityMiddleware",
+ "security.middleware.P3PPolicyMiddleware",
+ "security.middleware.MandatoryPasswordChangeMiddleware",
+ "security.middleware.NoConfidentialCachingMiddleware",
+ "security.auth_throttling.Middleware",
+ "security.middleware.ReferrerPolicyMiddleware",
)
-ROOT_URLCONF = 'testing.urls'
+ROOT_URLCONF = "testing.urls"
TEMPLATES = [
{
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [_os.path.join(_PROJECT_PATH, "templates")],
- 'OPTIONS': {
- 'context_processors': [
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
+ "DIRS": [_os.path.join(_PROJECT_PATH, "templates")],
+ "OPTIONS": {
+ "context_processors": [
+ "django.contrib.auth.context_processors.auth",
+ "django.contrib.messages.context_processors.messages",
]
- }
+ },
}
]
INSTALLED_APPS = (
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.sites',
- 'django.contrib.staticfiles',
- 'django.contrib.messages',
- 'django.contrib.admin',
- 'security',
- 'tests'
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.sites",
+ "django.contrib.staticfiles",
+ "django.contrib.messages",
+ "django.contrib.admin",
+ "security",
+ "tests",
)
-TEST_RUNNER = 'django.test.runner.DiscoverRunner'
+TEST_RUNNER = "django.test.runner.DiscoverRunner"
LOGIN_REDIRECT_URL = "/home/"
# The tests for django.contrib.auth use certain URLs, and they'll fail if we
# interfere with these.
_DJANGO_TESTING_URLS = [
- 'login/', 'login_required/', 'login_required_login_url/',
- 'admin_password_reset/', 'logout/', 'password_reset/',
- 'password_reset_from_email/', 'reset/', 'password_change/', 'remote_user/',
- 'auth_processor_messages/', 'auth_processor_perms/',
- 'auth_processor_user/', 'auth_processor_perm_in_perms/',
- 'admin/auth/user/',
+ "login/",
+ "login_required/",
+ "login_required_login_url/",
+ "admin_password_reset/",
+ "logout/",
+ "password_reset/",
+ "password_reset_from_email/",
+ "reset/",
+ "password_change/",
+ "remote_user/",
+ "auth_processor_messages/",
+ "auth_processor_perms/",
+ "auth_processor_user/",
+ "auth_processor_perm_in_perms/",
+ "admin/auth/user/",
]
LOGIN_EXEMPT_URLS = [
@@ -104,7 +112,7 @@
SESSION_EXPIRY_EXEMPT_URLS = LOGIN_EXEMPT_URLS
-CUSTOM_LOGOUT_MODULE = 'tests.tests.mocked_custom_logout'
+CUSTOM_LOGOUT_MODULE = "tests.tests.mocked_custom_logout"
MANDATORY_PASSWORD_CHANGE = {
"URL_NAME": "change_password",
@@ -114,37 +122,33 @@
AUTHENTICATION_THROTTLING = {
"DELAY_FUNCTION": lambda x, y: (0, 0),
- "LOGIN_URLS_WITH_TEMPLATES": [
- ("accounts/login/", "login.html")
- ]
+ "LOGIN_URLS_WITH_TEMPLATES": [("accounts/login/", "login.html")],
}
-XSS_PROTECT = 'on'
-X_FRAME_OPTIONS = 'allow-from: http://example.com'
-X_FRAME_OPTIONS_EXCLUDE_URLS = (
- r'^/test\d/$',
-)
+XSS_PROTECT = "on"
+X_FRAME_OPTIONS = "allow-from: http://example.com"
+X_FRAME_OPTIONS_EXCLUDE_URLS = (r"^/test\d/$",)
CSP_STRING = "allow 'self'; script-src *.google.com"
-CSP_MODE = 'enforce'
-P3P_POLICY_URL = '/w3c/p3p.xml'
-P3P_COMPACT_POLICY = 'PRIVATE'
+CSP_MODE = "enforce"
+P3P_POLICY_URL = "/w3c/p3p.xml"
+P3P_COMPACT_POLICY = "PRIVATE"
LOGGING = {
- 'version': 1,
- 'disable_existing_loggers': False,
- 'handlers': {
- 'console': {
- 'level': 'DEBUG',
- 'class': 'logging.StreamHandler',
+ "version": 1,
+ "disable_existing_loggers": False,
+ "handlers": {
+ "console": {
+ "level": "DEBUG",
+ "class": "logging.StreamHandler",
},
},
- 'loggers': {
- '': {
- 'handlers': ['console'],
- 'level': 'WARNING',
- 'propagate': True,
+ "loggers": {
+ "": {
+ "handlers": ["console"],
+ "level": "WARNING",
+ "propagate": True,
},
},
}
-CLEAR_SITE_DATA_URL_WHITELIST = ('/home/')
+CLEAR_SITE_DATA_URL_WHITELIST = "/home/"
diff --git a/testing/tests/tests.py b/testing/tests/tests.py
index 3ce346d..f72f370 100644
--- a/testing/tests/tests.py
+++ b/testing/tests/tests.py
@@ -18,13 +18,21 @@
from security.auth import min_length
from security.auth_throttling import (
- attempt_count, default_delay_function, delay_message, increment_counters,
- reset_counters, Middleware as AuthThrottlingMiddleware
+ attempt_count,
+ default_delay_function,
+ delay_message,
+ increment_counters,
+ reset_counters,
+ Middleware as AuthThrottlingMiddleware,
)
from security.middleware import (
- BaseMiddleware, ContentSecurityPolicyMiddleware, DoNotTrackMiddleware,
- SessionExpiryPolicyMiddleware, MandatoryPasswordChangeMiddleware,
- XssProtectMiddleware, XFrameOptionsMiddleware, ReferrerPolicyMiddleware
+ BaseMiddleware,
+ ContentSecurityPolicyMiddleware,
+ DoNotTrackMiddleware,
+ SessionExpiryPolicyMiddleware,
+ MandatoryPasswordChangeMiddleware,
+ XFrameOptionsMiddleware,
+ ReferrerPolicyMiddleware,
)
from security.models import PasswordExpiry
from security.password_expiry import never_expire_password
@@ -46,10 +54,11 @@ def login_user(func):
then log that user in. We expect self to be a DjangoTestCase,
or some object with a similar interface.
"""
+
def wrapper(self, *args, **kwargs):
- username_local = 'a2fcf54f63993b7'
- password_local = 'd8327deb882cf90'
- email_local = 'testuser@example.com'
+ username_local = "a2fcf54f63993b7"
+ password_local = "d8327deb882cf90"
+ email_local = "testuser@example.com"
user = User.objects.create_user(
username=username_local,
email=email_local,
@@ -62,21 +71,23 @@ def wrapper(self, *args, **kwargs):
func(self, *args, **kwargs)
self.client.logout()
user.delete()
+
return wrapper
class CustomLoginURLMiddleware(BaseMiddleware):
"""Used to test the custom url support in the login required middleware."""
+
def process_request(self, request):
- request.login_url = '/custom-login/'
+ request.login_url = "/custom-login/"
class BaseMiddlewareTestMiddleware(BaseMiddleware):
- REQUIRED_SETTINGS = ('R1', 'R2')
- OPTIONAL_SETTINGS = ('O1', 'O2')
+ REQUIRED_SETTINGS = ("R1", "R2")
+ OPTIONAL_SETTINGS = ("O1", "O2")
def load_setting(self, setting, value):
- if not hasattr(self, 'loaded_settings'):
+ if not hasattr(self, "loaded_settings"):
self.loaded_settings = {}
self.loaded_settings[setting] = value
@@ -92,96 +103,90 @@ class BaseMiddlewareTests(TestCase):
def __init__(self, *args, **kwargs):
super(BaseMiddlewareTests, self).__init__(*args, **kwargs)
module_name = BaseMiddlewareTests.__module__
- self.MIDDLEWARE_NAME = module_name + '.BaseMiddlewareTestMiddleware'
+ self.MIDDLEWARE_NAME = module_name + ".BaseMiddlewareTestMiddleware"
def test_settings_initially_loaded(self):
- expected_settings = {'R1': 1, 'R2': 2, 'O1': 3, 'O2': 4}
- with self.settings(
- MIDDLEWARE=(self.MIDDLEWARE_NAME,), **expected_settings
- ):
- response = self.client.get('/home/')
+ expected_settings = {"R1": 1, "R2": 2, "O1": 3, "O2": 4}
+ with self.settings(MIDDLEWARE=(self.MIDDLEWARE_NAME,), **expected_settings):
+ response = self.client.get("/home/")
self.assertEqual(expected_settings, response.loaded_settings)
def test_required_settings(self):
with self.settings(MIDDLEWARE=(self.MIDDLEWARE_NAME,)):
- self.assertRaises(ImproperlyConfigured, self.client.get, '/home/')
+ self.assertRaises(ImproperlyConfigured, self.client.get, "/home/")
def test_optional_settings(self):
- with self.settings(
- MIDDLEWARE=(self.MIDDLEWARE_NAME,), R1=True, R2=True
- ):
- response = self.client.get('/home/')
- self.assertEqual(None, response.loaded_settings['O1'])
- self.assertEqual(None, response.loaded_settings['O2'])
+ with self.settings(MIDDLEWARE=(self.MIDDLEWARE_NAME,), R1=True, R2=True):
+ response = self.client.get("/home/")
+ self.assertEqual(None, response.loaded_settings["O1"])
+ self.assertEqual(None, response.loaded_settings["O2"])
def test_setting_change(self):
- with self.settings(
- MIDDLEWARE=(self.MIDDLEWARE_NAME,), R1=123, R2=True
- ):
- response = self.client.get('/home/')
- self.assertEqual(123, response.loaded_settings['R1'])
+ with self.settings(MIDDLEWARE=(self.MIDDLEWARE_NAME,), R1=123, R2=True):
+ response = self.client.get("/home/")
+ self.assertEqual(123, response.loaded_settings["R1"])
with override_settings(R1=456):
- response = self.client.get('/home/')
- self.assertEqual(456, response.loaded_settings['R1'])
+ response = self.client.get("/home/")
+ self.assertEqual(456, response.loaded_settings["R1"])
- response = self.client.get('/home/')
- self.assertEqual(123, response.loaded_settings['R1'])
+ response = self.client.get("/home/")
+ self.assertEqual(123, response.loaded_settings["R1"])
def test_load_setting_abstract_method(self):
base = BaseMiddleware()
self.assertRaises(NotImplementedError, base.load_setting, None, None)
-@override_settings(MIDDLEWARE=(
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'security.middleware.LoginRequiredMiddleware',
-))
+@override_settings(
+ MIDDLEWARE=(
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "security.middleware.LoginRequiredMiddleware",
+ )
+)
class LoginRequiredMiddlewareTests(TestCase):
def setUp(self):
self.login_url = reverse("login")
def test_aborts_if_auth_middleware_missing(self):
middleware_classes = settings.MIDDLEWARE
- auth_mw = 'django.contrib.auth.middleware.AuthenticationMiddleware'
- middleware_classes = [
- m for m in middleware_classes if m != auth_mw
- ]
+ auth_mw = "django.contrib.auth.middleware.AuthenticationMiddleware"
+ middleware_classes = [m for m in middleware_classes if m != auth_mw]
with self.settings(MIDDLEWARE=middleware_classes):
- self.assertRaises(ImproperlyConfigured, self.client.get, '/home/')
+ self.assertRaises(ImproperlyConfigured, self.client.get, "/home/")
def test_redirects_unauthenticated_request(self):
- response = self.client.get('/home/')
+ response = self.client.get("/home/")
self.assertRedirects(response, self.login_url + "?next=/home/")
def test_redirects_unauthenticated_ajax_request(self):
response = self.client.get(
- '/home/',
- HTTP_X_REQUESTED_WITH='XMLHttpRequest',
+ "/home/",
+ HTTP_X_REQUESTED_WITH="XMLHttpRequest",
)
self.assertEqual(response.status_code, 401)
self.assertEqual(
- json.loads(response.content.decode('utf-8')),
+ json.loads(response.content.decode("utf-8")),
{"login_url": self.login_url},
)
def test_redirects_to_custom_login_url(self):
middlware_classes = list(settings.MIDDLEWARE)
- custom_login_middleware = 'tests.tests.CustomLoginURLMiddleware'
+ custom_login_middleware = "tests.tests.CustomLoginURLMiddleware"
with self.settings(
MIDDLEWARE=[custom_login_middleware] + middlware_classes,
):
- response = self.client.get('/home/')
- self.assertRedirects(response, '/custom-login/')
+ response = self.client.get("/home/")
+ self.assertRedirects(response, "/custom-login/")
response = self.client.get(
- '/home/',
- HTTP_X_REQUESTED_WITH='XMLHttpRequest',
+ "/home/",
+ HTTP_X_REQUESTED_WITH="XMLHttpRequest",
)
self.assertEqual(response.status_code, 401)
self.assertEqual(
- json.loads(response.content.decode('utf-8')),
- {"login_url": '/custom-login/'},
+ json.loads(response.content.decode("utf-8")),
+ {"login_url": "/custom-login/"},
)
def test_logs_out_inactive_users(self):
@@ -192,28 +197,30 @@ def test_logs_out_inactive_users(self):
)
never_expire_password(user)
self.client.login(username="foo", password="foo")
- resp = self.client.get('/home/')
+ resp = self.client.get("/home/")
self.assertEqual(resp.status_code, 200) # check we are logged in
user.is_active = False
user.save()
- resp = self.client.get('/home/')
+ resp = self.client.get("/home/")
self.assertRedirects(resp, self.login_url + "?next=/home/")
-@override_settings(MIDDLEWARE=(
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'security.middleware.MandatoryPasswordChangeMiddleware',
-))
+@override_settings(
+ MIDDLEWARE=(
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "security.middleware.MandatoryPasswordChangeMiddleware",
+ )
+)
class RequirePasswordChangeTests(TestCase):
def test_require_password_change(self):
"""
A brand-new user should have an already-expired password, and therefore
be redirected to the password change form on any request.
"""
- user = User.objects.create_user(username="foo",
- password="foo",
- email="foo@foo.com")
+ user = User.objects.create_user(
+ username="foo", password="foo", email="foo@foo.com"
+ )
self.client.login(username="foo", password="foo")
try:
with self.settings(
@@ -233,19 +240,20 @@ def test_superuser_password_change(self):
"""
A superuser can be forced to change their password via settings.
"""
- user = User.objects.create_superuser(username="foo",
- password="foo",
- email="foo@foo.com")
+ user = User.objects.create_superuser(
+ username="foo", password="foo", email="foo@foo.com"
+ )
self.client.login(username="foo", password="foo")
- with self.settings(MANDATORY_PASSWORD_CHANGE={
- "URL_NAME": "change_password"}):
+ with self.settings(MANDATORY_PASSWORD_CHANGE={"URL_NAME": "change_password"}):
self.assertEqual(self.client.get("/home/").status_code, 200)
try:
- with self.settings(MANDATORY_PASSWORD_CHANGE={
- "URL_NAME": "change_password",
- "INCLUDE_SUPERUSERS": True
- }):
+ with self.settings(
+ MANDATORY_PASSWORD_CHANGE={
+ "URL_NAME": "change_password",
+ "INCLUDE_SUPERUSERS": True,
+ }
+ ):
self.assertRedirects(
self.client.get("/home/"),
reverse("change_password"),
@@ -256,18 +264,18 @@ def test_superuser_password_change(self):
def test_dont_redirect_exempt_urls(self):
user = User.objects.create_user(
- username="foo",
- password="foo",
- email="foo@foo.com"
+ username="foo", password="foo", email="foo@foo.com"
)
self.client.login(username="foo", password="foo")
try:
- with self.settings(MANDATORY_PASSWORD_CHANGE={
- "URL_NAME": "change_password",
- "EXEMPT_URLS": (r'^test1/$', r'^test2/$'),
- "EXEMPT_URL_NAMES": ("test3", "test4"),
- }):
+ with self.settings(
+ MANDATORY_PASSWORD_CHANGE={
+ "URL_NAME": "change_password",
+ "EXEMPT_URLS": (r"^test1/$", r"^test2/$"),
+ "EXEMPT_URL_NAMES": ("test3", "test4"),
+ }
+ ):
# Redirect pages in general
self.assertRedirects(
self.client.get("/home/"),
@@ -290,16 +298,18 @@ def test_dont_redirect_exempt_urls(self):
user.delete()
def test_dont_choke_on_exempt_urls_that_dont_resolve(self):
- user = User.objects.create_user(username="foo",
- password="foo",
- email="foo@foo.com")
+ user = User.objects.create_user(
+ username="foo", password="foo", email="foo@foo.com"
+ )
self.client.login(username="foo", password="foo")
try:
- with self.settings(MANDATORY_PASSWORD_CHANGE={
- "URL_NAME": "change_password",
- "EXEMPT_URL_NAMES": ("fake1", "fake2"),
- }):
+ with self.settings(
+ MANDATORY_PASSWORD_CHANGE={
+ "URL_NAME": "change_password",
+ "EXEMPT_URL_NAMES": ("fake1", "fake2"),
+ }
+ ):
# Redirect pages in general
self.assertRedirects(
self.client.get("/home/"),
@@ -314,8 +324,8 @@ def test_raises_improperly_configured(self):
self.assertRaises(
ImproperlyConfigured,
change.load_setting,
- 'MANDATORY_PASSWORD_CHANGE',
- {'EXEMPT_URLS': []},
+ "MANDATORY_PASSWORD_CHANGE",
+ {"EXEMPT_URLS": []},
)
@@ -332,33 +342,32 @@ def ajax_only_view(request):
request = HttpRequest()
response = ajax_only_view(request)
self.assertTrue(isinstance(response, HttpResponseForbidden))
- request.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
+ request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
response = ajax_only_view(request)
self.assertFalse(isinstance(response, HttpResponseForbidden))
-@override_settings(MIDDLEWARE=(
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'security.middleware.SessionExpiryPolicyMiddleware',
- 'security.middleware.LoginRequiredMiddleware',
-))
+@override_settings(
+ MIDDLEWARE=(
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "security.middleware.SessionExpiryPolicyMiddleware",
+ "security.middleware.LoginRequiredMiddleware",
+ )
+)
class SessionExpiryTests(TestCase):
-
def test_session_variables_are_set(self):
"""
Verify the session cookie stores the start time and last active time.
"""
- self.client.get('/home/')
+ self.client.get("/home/")
now = timezone.now()
start_time = SessionExpiryPolicyMiddleware._get_datetime_in_session(
- SessionExpiryPolicyMiddleware.START_TIME_KEY,
- self.client.session
+ SessionExpiryPolicyMiddleware.START_TIME_KEY, self.client.session
)
last_activity = SessionExpiryPolicyMiddleware._get_datetime_in_session(
- SessionExpiryPolicyMiddleware.LAST_ACTIVITY_KEY,
- self.client.session
+ SessionExpiryPolicyMiddleware.LAST_ACTIVITY_KEY, self.client.session
)
self.assertTrue(now - start_time < datetime.timedelta(seconds=10))
@@ -369,17 +378,12 @@ def session_expiry_test(self, key, expired):
Verify that expired sessions are cleared from the system. (And that we
redirect to the login page.)
"""
- self.assertTrue(self.client.get('/home/').status_code, 200)
+ self.assertTrue(self.client.get("/home/").status_code, 200)
session = self.client.session
- SessionExpiryPolicyMiddleware._set_datetime_in_session(
- key,
- expired,
- session
- )
+ SessionExpiryPolicyMiddleware._set_datetime_in_session(key, expired, session)
session.save()
- response = self.client.get('/home/')
- self.assertRedirects(response,
- reverse("login") + '?next=/home/')
+ response = self.client.get("/home/")
+ self.assertRedirects(response, reverse("login") + "?next=/home/")
@login_user
def test_session_too_old(self):
@@ -389,8 +393,7 @@ def test_session_too_old(self):
"""
delta = SessionExpiryPolicyMiddleware().SESSION_COOKIE_AGE + 1
expired = timezone.now() - datetime.timedelta(seconds=delta)
- self.session_expiry_test(SessionExpiryPolicyMiddleware.START_TIME_KEY,
- expired)
+ self.session_expiry_test(SessionExpiryPolicyMiddleware.START_TIME_KEY, expired)
@login_user
def test_session_inactive_too_long(self):
@@ -410,22 +413,19 @@ def test_exempted_session_expiry_urls(self):
delta = SessionExpiryPolicyMiddleware().SESSION_INACTIVITY_TIMEOUT + 1
expired = timezone.now() - datetime.timedelta(seconds=delta)
- self.assertTrue(self.client.get('/home/').status_code, 200)
+ self.assertTrue(self.client.get("/home/").status_code, 200)
session = self.client.session
SessionExpiryPolicyMiddleware._set_datetime_in_session(
- SessionExpiryPolicyMiddleware.LAST_ACTIVITY_KEY,
- expired,
- session
+ SessionExpiryPolicyMiddleware.LAST_ACTIVITY_KEY, expired, session
)
session.save()
- exempted_response = self.client.get('/accounts/login/')
- not_exempted_response = self.client.get('/home/')
+ exempted_response = self.client.get("/accounts/login/")
+ not_exempted_response = self.client.get("/home/")
self.assertTrue(exempted_response.status_code, 200)
- self.assertRedirects(not_exempted_response,
- reverse("login") + '?next=/home/')
+ self.assertRedirects(not_exempted_response, reverse("login") + "?next=/home/")
@login_user
def test_custom_logout(self):
@@ -438,26 +438,26 @@ def test_custom_logout(self):
assert mocked_custom_logout.called
-@override_settings(MIDDLEWARE=(
- 'security.middleware.NoConfidentialCachingMiddleware',
-))
+@override_settings(MIDDLEWARE=("security.middleware.NoConfidentialCachingMiddleware",))
class ConfidentialCachingTests(TestCase):
def setUp(self):
self.header_values = {
- "Cache-Control": 'no-cache, no-store, max-age=0, must-revalidate',
+ "Cache-Control": "no-cache, no-store, max-age=0, must-revalidate",
"Pragma": "no-cache",
- "Expires": '-1'
+ "Expires": "-1",
}
- @override_settings(NO_CONFIDENTIAL_CACHING={
- "WHITELIST_ON": True,
- "BLACKLIST_ON": False,
- "WHITELIST_REGEXES": ["accounts/login/$"],
- "BLACKLIST_REGEXES": ["accounts/logout/$"]
- })
+ @override_settings(
+ NO_CONFIDENTIAL_CACHING={
+ "WHITELIST_ON": True,
+ "BLACKLIST_ON": False,
+ "WHITELIST_REGEXES": ["accounts/login/$"],
+ "BLACKLIST_REGEXES": ["accounts/logout/$"],
+ }
+ )
def test_whitelisting(self):
# Get Non Confidential Page
- response = self.client.get('/accounts/login/')
+ response = self.client.get("/accounts/login/")
for header, value in self.header_values.items():
self.assertNotEqual(response.get(header, None), value)
# Get Confidential Page
@@ -465,15 +465,17 @@ def test_whitelisting(self):
for header, value in self.header_values.items():
self.assertEqual(response.get(header, None), value)
- @override_settings(NO_CONFIDENTIAL_CACHING={
- "WHITELIST_ON": False,
- "BLACKLIST_ON": True,
- "WHITELIST_REGEXES": ["accounts/login/$"],
- "BLACKLIST_REGEXES": ["accounts/logout/$"]
- })
+ @override_settings(
+ NO_CONFIDENTIAL_CACHING={
+ "WHITELIST_ON": False,
+ "BLACKLIST_ON": True,
+ "WHITELIST_REGEXES": ["accounts/login/$"],
+ "BLACKLIST_REGEXES": ["accounts/logout/$"],
+ }
+ )
def test_blacklisting(self):
# Get Non Confidential Page
- response = self.client.get('/accounts/login/')
+ response = self.client.get("/accounts/login/")
for header, value in self.header_values.items():
self.assertNotEqual(response.get(header, None), value)
# Get Confidential Page
@@ -482,124 +484,90 @@ def test_blacklisting(self):
self.assertEqual(response.get(header, None), value)
-@override_settings(MIDDLEWARE=('security.middleware.XFrameOptionsMiddleware',))
+@override_settings(MIDDLEWARE=("security.middleware.XFrameOptionsMiddleware",))
class XFrameOptionsDenyTests(TestCase):
-
def test_option_set(self):
"""
Verify the HTTP Response Header is set.
"""
- response = self.client.get('/accounts/login/')
- self.assertEqual(response['X-Frame-Options'], settings.X_FRAME_OPTIONS)
+ response = self.client.get("/accounts/login/")
+ self.assertEqual(response["X-Frame-Options"], settings.X_FRAME_OPTIONS)
def test_exclude_urls(self):
"""
Verify that pages can be excluded from the X-Frame-Options header.
"""
- response = self.client.get('/home/')
- self.assertEqual(response['X-Frame-Options'], settings.X_FRAME_OPTIONS)
- response = self.client.get('/test1/')
- self.assertNotIn('X-Frame-Options', response)
+ response = self.client.get("/home/")
+ self.assertEqual(response["X-Frame-Options"], settings.X_FRAME_OPTIONS)
+ response = self.client.get("/test1/")
+ self.assertNotIn("X-Frame-Options", response)
def test_improperly_configured(self):
xframe = XFrameOptionsMiddleware()
self.assertRaises(
ImproperlyConfigured,
xframe.load_setting,
- 'X_FRAME_OPTIONS',
- 'invalid',
+ "X_FRAME_OPTIONS",
+ "invalid",
)
self.assertRaises(
ImproperlyConfigured,
xframe.load_setting,
- 'X_FRAME_OPTIONS_EXCLUDE_URLS',
+ "X_FRAME_OPTIONS_EXCLUDE_URLS",
1,
)
@override_settings(X_FRAME_OPTIONS_EXCLUDE_URLS=None)
def test_default_exclude_urls(self):
# This URL is excluded in other tests, see settings.py
- response = self.client.get('/test1/')
+ response = self.client.get("/test1/")
self.assertEqual(
- response['X-Frame-Options'],
+ response["X-Frame-Options"],
settings.X_FRAME_OPTIONS,
)
@override_settings(X_FRAME_OPTIONS=None)
def test_default_xframe_option(self):
- response = self.client.get('/home/')
+ response = self.client.get("/home/")
self.assertEqual(
- response['X-Frame-Options'],
- 'deny',
- )
-
-
-@override_settings(MIDDLEWARE=('security.middleware.XssProtectMiddleware',))
-class XXssProtectTests(TestCase):
-
- def test_option_set(self):
- """
- Verify the HTTP Response Header is set.
- """
- response = self.client.get('/accounts/login/')
- self.assertNotEqual(response['X-XSS-Protection'], None)
-
- def test_default_setting(self):
- with self.settings(XSS_PROTECT=None):
- response = self.client.get('/accounts/login/')
- self.assertEqual(response['X-XSS-Protection'], '1') # sanitize
-
- def test_option_off(self):
- with self.settings(XSS_PROTECT='off'):
- response = self.client.get('/accounts/login/')
- self.assertEqual(response['X-XSS-Protection'], '0') # off
-
- def test_improper_configuration_raises(self):
- xss = XssProtectMiddleware()
- self.assertRaises(
- ImproperlyConfigured,
- xss.load_setting,
- 'XSS_PROTECT',
- 'invalid',
+ response["X-Frame-Options"],
+ "deny",
)
-@override_settings(MIDDLEWARE=('security.middleware.ContentNoSniff',))
+@override_settings(MIDDLEWARE=("security.middleware.ContentNoSniff",))
class ContentNoSniffTests(TestCase):
-
def test_option_set(self):
"""
Verify the HTTP Response Header is set.
"""
- response = self.client.get('/accounts/login/')
- self.assertEqual(response['X-Content-Options'], 'nosniff')
+ response = self.client.get("/accounts/login/")
+ self.assertEqual(response["X-Content-Options"], "nosniff")
-@override_settings(MIDDLEWARE=(
- 'security.middleware.StrictTransportSecurityMiddleware',
-))
+@override_settings(
+ MIDDLEWARE=("security.middleware.StrictTransportSecurityMiddleware",)
+)
class StrictTransportSecurityTests(TestCase):
-
def test_option_set(self):
"""
Verify the HTTP Response Header is set.
"""
- response = self.client.get('/accounts/login/')
- self.assertNotEqual(response['Strict-Transport-Security'], None)
+ response = self.client.get("/accounts/login/")
+ self.assertNotEqual(response["Strict-Transport-Security"], None)
@override_settings(
AUTHENTICATION_THROTTLING={
"DELAY_FUNCTION": lambda x, _: (2 ** (x - 1) if x else 0, 0),
- "LOGIN_URLS_WITH_TEMPLATES": [
- ("accounts/login/", "registration/login.html")
- ]
+ "LOGIN_URLS_WITH_TEMPLATES": [("accounts/login/", "registration/login.html")],
},
MIDDLEWARE=(
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'security.auth_throttling.Middleware',)
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "security.auth_throttling.Middleware",
+ ),
)
class AuthenticationThrottlingTests(TestCase):
def setUp(self):
@@ -607,17 +575,17 @@ def setUp(self):
self.old_time = time.time
self.time = 0
time.time = lambda: self.time
- self.user = User.objects.create_user(username="foo", password="foo",
- email="a@foo.org")
+ self.user = User.objects.create_user(
+ username="foo", password="foo", email="a@foo.org"
+ )
def tearDown(self):
time.time = self.old_time
def attempt(self, password):
- return self.client.post("/accounts/login/",
- {"username": "foo",
- "password": password},
- follow=True)
+ return self.client.post(
+ "/accounts/login/", {"username": "foo", "password": password}, follow=True
+ )
def reset(self):
self.client.logout()
@@ -627,8 +595,7 @@ def typo(self):
self.assertTemplateUsed(self.attempt("bar"), "registration/login.html")
def _succeed(self):
- self.assertTemplateNotUsed(self.attempt("foo"),
- "registration/login.html")
+ self.assertTemplateNotUsed(self.attempt("foo"), "registration/login.html")
self.reset()
def _fail(self):
@@ -712,12 +679,12 @@ def test_per_account_throttling(self):
self.set_time(3)
self._succeed()
- @override_settings(AUTHENTICATION_THROTTLING={
- "DELAY_FUNCTION": lambda x, y: (x, y),
- "LOGIN_URLS_WITH_TEMPLATES": [
- ("accounts/login/", None)
- ]
- })
+ @override_settings(
+ AUTHENTICATION_THROTTLING={
+ "DELAY_FUNCTION": lambda x, y: (x, y),
+ "LOGIN_URLS_WITH_TEMPLATES": [("accounts/login/", None)],
+ }
+ )
def test_too_many_requests_error_when_no_template_provided(self):
"""
Verify we simply return a 429 error when there is no login template
@@ -745,8 +712,9 @@ def test_reset_button(self):
"""
self.set_time(0)
self.typo()
- admin = User.objects.create_user(username="bar", password="bar",
- email="a@bar.org")
+ admin = User.objects.create_user(
+ username="bar", password="bar", email="a@bar.org"
+ )
admin.is_superuser = True
admin.save()
self.client.login(username="bar", password="bar")
@@ -756,9 +724,11 @@ def test_reset_button(self):
self.client.logout()
self._succeed()
- @override_settings(AUTHENTICATION_THROTTLING={
- "DELAY_FUNCTION": lambda x, y: (x, y),
- })
+ @override_settings(
+ AUTHENTICATION_THROTTLING={
+ "DELAY_FUNCTION": lambda x, y: (x, y),
+ }
+ )
def test_improperly_configured_middleware(self):
self.assertRaises(ImproperlyConfigured, AuthThrottlingMiddleware)
@@ -785,16 +755,15 @@ def test_throttle_reset_404_on_not_found(self):
self.assertEqual(resp.status_code, 404)
-@override_settings(MIDDLEWARE=('security.middleware.P3PPolicyMiddleware',))
+@override_settings(MIDDLEWARE=("security.middleware.P3PPolicyMiddleware",))
class P3PPolicyTests(TestCase):
-
def setUp(self):
self.policy = "NN AD BLAH"
settings.P3P_COMPACT_POLICY = self.policy
def test_p3p_header(self):
expected_header = 'policyref="/w3c/p3p.xml" CP="%s"' % self.policy
- response = self.client.get('/accounts/login/')
+ response = self.client.get("/accounts/login/")
self.assertEqual(response["P3P"], expected_header)
@@ -804,13 +773,12 @@ def test_min_length(self):
min_length(6)("abcdef")
-@override_settings(MIDDLEWARE=(
- 'security.middleware.ContentSecurityPolicyMiddleware',
-))
+@override_settings(MIDDLEWARE=("security.middleware.ContentSecurityPolicyMiddleware",))
class ContentSecurityPolicyTests(TestCase):
class FakeHttpRequest(object):
- method = 'POST'
- body = """{
+ method = "POST"
+ body = (
+ """{
"csp-report": {
"document-uri": "http://example.org/page.html",
"referrer": "http://evil.example.com/haxor.html",
@@ -819,25 +787,26 @@ class FakeHttpRequest(object):
"original-policy": "%s"
}
}
- """ % settings.CSP_STRING
+ """
+ % settings.CSP_STRING
+ )
META = {
- 'CONTENT_TYPE': 'application/json',
- 'REMOTE_ADDR': '127.0.0.1',
- 'HTTP_USER_AGENT': 'FakeHTTPRequest'
+ "CONTENT_TYPE": "application/json",
+ "REMOTE_ADDR": "127.0.0.1",
+ "HTTP_USER_AGENT": "FakeHTTPRequest",
}
def test_option_set(self):
"""
Verify the HTTP Response Header is set.
"""
- response = self.client.get('/accounts/login/')
+ response = self.client.get("/accounts/login/")
self.assertEqual(
- response['Content-Security-Policy'],
+ response["Content-Security-Policy"],
settings.CSP_STRING,
)
def test_json(self):
-
req = ContentSecurityPolicyTests.FakeHttpRequest()
parsed = json.loads(req.body)
@@ -846,7 +815,6 @@ def test_json(self):
# http://www.w3.org/TR/CSP/#sample-violation-report
def test_csp_view(self):
-
req = ContentSecurityPolicyTests.FakeHttpRequest()
# call the view
@@ -855,21 +823,30 @@ def test_csp_view(self):
self.assertEqual(resp.status_code, 204)
def test_csp_gen_1(self):
-
csp_dict = {
- 'default-src': ['self', 'cdn.example.com'],
- 'script-src': ['self', 'js.example.com'],
- 'style-src': ['self', 'css.example.com'],
- 'img-src': ['self', 'img.example.com'],
- 'connect-src': ['self', ],
- 'font-src': ['fonts.example.com', ],
- 'object-src': ['self'],
- 'media-src': ['media.example.com', ],
- 'frame-src': ['*', ],
- 'sandbox': ['', ],
- 'reflected-xss': 'filter',
- 'referrer': 'origin',
- 'report-uri': 'http://example.com/csp-report',
+ "default-src": ["self", "cdn.example.com"],
+ "script-src": ["self", "js.example.com"],
+ "style-src": ["self", "css.example.com"],
+ "img-src": ["self", "img.example.com"],
+ "connect-src": [
+ "self",
+ ],
+ "font-src": [
+ "fonts.example.com",
+ ],
+ "object-src": ["self"],
+ "media-src": [
+ "media.example.com",
+ ],
+ "frame-src": [
+ "*",
+ ],
+ "sandbox": [
+ "",
+ ],
+ "reflected-xss": "filter",
+ "referrer": "origin",
+ "report-uri": "http://example.com/csp-report",
}
expected = (
@@ -894,36 +871,33 @@ def test_csp_gen_1(self):
# We can't assume the iteration order on the csp_dict, so we split the
# output, sort, and ensure we got all the results back, regardless of
# the order.
- expected_list = sorted(x.strip() for x in expected.split(';'))
- generated_list = sorted(x.strip() for x in generated.split(';'))
+ expected_list = sorted(x.strip() for x in expected.split(";"))
+ generated_list = sorted(x.strip() for x in generated.split(";"))
self.assertEqual(generated_list, expected_list)
def test_csp_gen_2(self):
- csp_dict = {'default-src': ('none',), 'script-src': ['none']}
+ csp_dict = {"default-src": ("none",), "script-src": ["none"]}
expected = "default-src 'none'; script-src 'none'"
csp = ContentSecurityPolicyMiddleware()
generated = csp._csp_builder(csp_dict)
- expected_list = sorted(x.strip() for x in expected.split(';'))
- generated_list = sorted(x.strip() for x in generated.split(';'))
+ expected_list = sorted(x.strip() for x in expected.split(";"))
+ generated_list = sorted(x.strip() for x in generated.split(";"))
self.assertEqual(generated_list, expected_list)
def test_csp_gen_3(self):
csp_dict = {
- 'script-src': [
- 'self',
- 'www.google-analytics.com',
- 'ajax.googleapis.com',
+ "script-src": [
+ "self",
+ "www.google-analytics.com",
+ "ajax.googleapis.com",
],
}
- expected = (
- "script-src "
- "'self' www.google-analytics.com ajax.googleapis.com"
- )
+ expected = "script-src " "'self' www.google-analytics.com ajax.googleapis.com"
csp = ContentSecurityPolicyMiddleware()
generated = csp._csp_builder(csp_dict)
@@ -932,40 +906,40 @@ def test_csp_gen_3(self):
def test_csp_gen_err(self):
# argument not passed as array, expect failure
- csp_dict = {'default-src': 'self'}
+ csp_dict = {"default-src": "self"}
csp = ContentSecurityPolicyMiddleware()
self.assertRaises(MiddlewareNotUsed, csp._csp_builder, csp_dict)
def test_csp_gen_err2(self):
- csp_dict = {'invalid': 'self'} # invalid directive
+ csp_dict = {"invalid": "self"} # invalid directive
csp = ContentSecurityPolicyMiddleware()
self.assertRaises(MiddlewareNotUsed, csp._csp_builder, csp_dict)
def test_csp_gen_err3(self):
- csp_dict = {'sandbox': 'none'} # not a list or tuple, expect failure
+ csp_dict = {"sandbox": "none"} # not a list or tuple, expect failure
csp = ContentSecurityPolicyMiddleware()
self.assertRaises(MiddlewareNotUsed, csp._csp_builder, csp_dict)
def test_csp_gen_err4(self):
# Not an allowed directive, expect failure
- csp_dict = {'sandbox': ('invalid', )}
+ csp_dict = {"sandbox": ("invalid",)}
csp = ContentSecurityPolicyMiddleware()
self.assertRaises(MiddlewareNotUsed, csp._csp_builder, csp_dict)
def test_csp_gen_err5(self):
# Not an allowed directive, expect failure
- csp_dict = {'referrer': 'invalid'}
+ csp_dict = {"referrer": "invalid"}
csp = ContentSecurityPolicyMiddleware()
self.assertRaises(MiddlewareNotUsed, csp._csp_builder, csp_dict)
def test_csp_gen_err6(self):
# Not an allowed directive, expect failure
- csp_dict = {'reflected-xss': 'invalid'}
+ csp_dict = {"reflected-xss": "invalid"}
csp = ContentSecurityPolicyMiddleware()
self.assertRaises(MiddlewareNotUsed, csp._csp_builder, csp_dict)
@@ -973,29 +947,29 @@ def test_csp_gen_err6(self):
def test_enforced_by_default(self):
with self.settings(CSP_MODE=None):
response = self.client.get(settings.LOGIN_URL)
- self.assertIn('Content-Security-Policy', response)
- self.assertNotIn('Content-Security-Policy-Report-Only', response)
+ self.assertIn("Content-Security-Policy", response)
+ self.assertNotIn("Content-Security-Policy-Report-Only", response)
def test_enforced_when_on(self):
- with self.settings(CSP_MODE='enforce'):
+ with self.settings(CSP_MODE="enforce"):
response = self.client.get(settings.LOGIN_URL)
- self.assertIn('Content-Security-Policy', response)
- self.assertNotIn('Content-Security-Policy-Report-Only', response)
+ self.assertIn("Content-Security-Policy", response)
+ self.assertNotIn("Content-Security-Policy-Report-Only", response)
def test_report_only_set(self):
- with self.settings(CSP_MODE='report-only'):
+ with self.settings(CSP_MODE="report-only"):
response = self.client.get(settings.LOGIN_URL)
- self.assertNotIn('Content-Security-Policy', response)
- self.assertIn('Content-Security-Policy-Report-Only', response)
+ self.assertNotIn("Content-Security-Policy", response)
+ self.assertIn("Content-Security-Policy-Report-Only", response)
def test_both_enforce_and_report_only(self):
- with self.settings(CSP_MODE='enforce-and-report-only'):
+ with self.settings(CSP_MODE="enforce-and-report-only"):
response = self.client.get(settings.LOGIN_URL)
- self.assertIn('Content-Security-Policy', response)
- self.assertIn('Content-Security-Policy-Report-Only', response)
+ self.assertIn("Content-Security-Policy", response)
+ self.assertIn("Content-Security-Policy-Report-Only", response)
def test_invalid_csp_mode(self):
- with self.settings(CSP_MODE='invalid'):
+ with self.settings(CSP_MODE="invalid"):
self.assertRaises(
MiddlewareNotUsed,
ContentSecurityPolicyMiddleware,
@@ -1009,7 +983,7 @@ def test_no_csp_options_set(self):
)
def test_both_csp_options_set(self):
- with self.settings(CSP_DICT={'x': 'y'}, CSP_STRING='x y;'):
+ with self.settings(CSP_DICT={"x": "y"}, CSP_STRING="x y;"):
self.assertRaises(
MiddlewareNotUsed,
ContentSecurityPolicyMiddleware,
@@ -1017,31 +991,30 @@ def test_both_csp_options_set(self):
def test_sets_from_csp_dict(self):
with self.settings(
- CSP_DICT={'default-src': ('self',)},
+ CSP_DICT={"default-src": ("self",)},
CSP_STRING=None,
):
- response = self.client.get('/accounts/login/')
+ response = self.client.get("/accounts/login/")
self.assertEqual(
- response['Content-Security-Policy'],
+ response["Content-Security-Policy"],
"default-src 'self'",
)
-@override_settings(MIDDLEWARE=('security.middleware.DoNotTrackMiddleware',))
+@override_settings(MIDDLEWARE=("security.middleware.DoNotTrackMiddleware",))
class DoNotTrackTests(TestCase):
-
def setUp(self):
self.dnt = DoNotTrackMiddleware()
self.request = HttpRequest()
self.response = HttpResponse()
def test_set_DNT_on(self):
- self.request.META['HTTP_DNT'] = '1'
+ self.request.META["HTTP_DNT"] = "1"
self.dnt.process_request(self.request)
self.assertTrue(self.request.dnt)
def test_set_DNT_off(self):
- self.request.META['HTTP_DNT'] = 'off'
+ self.request.META["HTTP_DNT"] = "off"
self.dnt.process_request(self.request)
self.assertFalse(self.request.dnt)
@@ -1050,84 +1023,86 @@ def test_default_DNT(self):
self.assertFalse(self.request.dnt)
def test_DNT_echo_on(self):
- self.request.META['HTTP_DNT'] = '1'
+ self.request.META["HTTP_DNT"] = "1"
self.dnt.process_response(self.request, self.response)
- self.assertIn('DNT', self.response)
- self.assertEqual(self.response['DNT'], '1')
+ self.assertIn("DNT", self.response)
+ self.assertEqual(self.response["DNT"], "1")
def test_DNT_echo_off(self):
- self.request.META['HTTP_DNT'] = 'off'
+ self.request.META["HTTP_DNT"] = "off"
self.dnt.process_response(self.request, self.response)
- self.assertEqual(self.response['DNT'], 'off')
+ self.assertEqual(self.response["DNT"], "off")
def test_DNT_echo_default(self):
self.dnt.process_response(self.request, self.response)
- self.assertNotIn('DNT', self.response)
+ self.assertNotIn("DNT", self.response)
-class ReferrerPolicyTests(TestCase):
+class ReferrerPolicyTests(TestCase):
def test_option_set(self):
"""
Verify the HTTP Referrer-Policy Header is set.
"""
- response = self.client.get('/accounts/login/')
- self.assertNotEqual(response['Referrer-Policy'], None)
+ response = self.client.get("/accounts/login/")
+ self.assertNotEqual(response["Referrer-Policy"], None)
def test_default_setting(self):
with self.settings(REFERRER_POLICY=None):
- response = self.client.get('/accounts/login/')
- self.assertEqual(response['Referrer-Policy'], 'same-origin')
+ response = self.client.get("/accounts/login/")
+ self.assertEqual(response["Referrer-Policy"], "same-origin")
def test_no_referrer_setting(self):
- with self.settings(REFERRER_POLICY='no-referrer'):
- response = self.client.get('/accounts/login/')
- self.assertEqual(response['Referrer-Policy'], 'no-referrer')
+ with self.settings(REFERRER_POLICY="no-referrer"):
+ response = self.client.get("/accounts/login/")
+ self.assertEqual(response["Referrer-Policy"], "no-referrer")
def test_no_referrer_when_downgrade_setting(self):
- with self.settings(REFERRER_POLICY='no-referrer-when-downgrade'):
- response = self.client.get('/accounts/login/')
- self.assertEqual(response['Referrer-Policy'], 'no-referrer-when-downgrade')
+ with self.settings(REFERRER_POLICY="no-referrer-when-downgrade"):
+ response = self.client.get("/accounts/login/")
+ self.assertEqual(response["Referrer-Policy"], "no-referrer-when-downgrade")
def test_origin_setting(self):
- with self.settings(REFERRER_POLICY='origin'):
- response = self.client.get('/accounts/login/')
- self.assertEqual(response['Referrer-Policy'], 'origin')
+ with self.settings(REFERRER_POLICY="origin"):
+ response = self.client.get("/accounts/login/")
+ self.assertEqual(response["Referrer-Policy"], "origin")
def test_origin_when_cross_origin_setting(self):
- with self.settings(REFERRER_POLICY='origin-when-cross-origin'):
- response = self.client.get('/accounts/login/')
- self.assertEqual(response['Referrer-Policy'], 'origin-when-cross-origin')
+ with self.settings(REFERRER_POLICY="origin-when-cross-origin"):
+ response = self.client.get("/accounts/login/")
+ self.assertEqual(response["Referrer-Policy"], "origin-when-cross-origin")
def test_same_origin_setting(self):
- with self.settings(REFERRER_POLICY='same-origin'):
- response = self.client.get('/accounts/login/')
- self.assertEqual(response['Referrer-Policy'], 'same-origin')
+ with self.settings(REFERRER_POLICY="same-origin"):
+ response = self.client.get("/accounts/login/")
+ self.assertEqual(response["Referrer-Policy"], "same-origin")
def test_strict_origin_setting(self):
- with self.settings(REFERRER_POLICY='strict-origin'):
- response = self.client.get('/accounts/login/')
- self.assertEqual(response['Referrer-Policy'], 'strict-origin')
+ with self.settings(REFERRER_POLICY="strict-origin"):
+ response = self.client.get("/accounts/login/")
+ self.assertEqual(response["Referrer-Policy"], "strict-origin")
def test_strict_origin_when_cross_origin_setting(self):
- with self.settings(REFERRER_POLICY='strict-origin-when-cross-origin'):
- response = self.client.get('/accounts/login/')
- self.assertEqual(response['Referrer-Policy'], 'strict-origin-when-cross-origin')
+ with self.settings(REFERRER_POLICY="strict-origin-when-cross-origin"):
+ response = self.client.get("/accounts/login/")
+ self.assertEqual(
+ response["Referrer-Policy"], "strict-origin-when-cross-origin"
+ )
def test_unsafe_url_setting(self):
- with self.settings(REFERRER_POLICY='unsafe-url'):
- response = self.client.get('/accounts/login/')
- self.assertEqual(response['Referrer-Policy'], 'unsafe-url')
+ with self.settings(REFERRER_POLICY="unsafe-url"):
+ response = self.client.get("/accounts/login/")
+ self.assertEqual(response["Referrer-Policy"], "unsafe-url")
def test_off_setting(self):
- with self.settings(REFERRER_POLICY='off'):
- response = self.client.get('/accounts/login/')
- self.assertEqual('Referrer-Policy' in response, False)
+ with self.settings(REFERRER_POLICY="off"):
+ response = self.client.get("/accounts/login/")
+ self.assertEqual("Referrer-Policy" in response, False)
def test_improper_configuration_raises(self):
referer_policy_middleware = ReferrerPolicyMiddleware()
self.assertRaises(
ImproperlyConfigured,
referer_policy_middleware.load_setting,
- 'REFERRER_POLICY',
- 'invalid',
+ "REFERRER_POLICY",
+ "invalid",
)
|