with securehstsseconds3600 the middleware adds stricttransportsecurity maxage3600 to the response the middleware will not override a stricttransportsecurity header already present in the response the stricttransportsecurity header is not added to responses going over an insecure connection with securehstsseconds0 the middleware does not add a stricttransportsecurity header to the response with securehstsseconds nonzero and securehstsincludesubdomains true the middleware adds a stricttransportsecurity header with the includesubdomains directive to the response with securehstsseconds nonzero and securehstsincludesubdomains false the middleware adds a stricttransportsecurity header without the includesubdomains directive to the response with securehstsseconds nonzero and securehstspreload true the middleware adds a stricttransportsecurity header with the preload directive to the response with securehstsseconds nonzero securehstsincludesubdomains and securehstspreload true the middleware adds a stricttransportsecurity header containing both the includesubdomains and preload directives to the response with securehstsseconds nonzero and securehstspreload false the middleware adds a stricttransportsecurity header without the preload directive to the response with securecontenttypenosniff set to true the middleware adds xcontenttypeoptions nosniff header to the response the middleware will not override an xcontenttypeoptions header already present in the response with securecontenttypenosniff false the middleware does not add an xcontenttypeoptions header to the response with securesslredirect true the middleware redirects any nonsecure requests to the https version of the same url the middleware does not redirect secure requests the middleware does not redirect requests with url path matching an exempt pattern the middleware redirects to securesslhost if given with securesslredirect false the middleware does not redirect with securereferrerpolicy set to none the middleware does not add a referrerpolicy header to the response with securereferrerpolicy set to a valid value the middleware adds a referrerpolicy header to the response the middleware will not override a referrerpolicy header already present in the response with securecrossoriginopenerpolicy set to none the middleware does not add a crossoriginopenerpolicy header to the response securecrossoriginopenerpolicy defaults to sameorigin self assertequal self processresponse headerscrossoriginopenerpolicy sameorigin def testcooponself tests sameorigin sameoriginallowpopups unsafenone for value in tests with self subtestvaluevalue overridesettings securecrossoriginopenerpolicyvalue self assertequal self processresponse headerscrossoriginopenerpolicy value overridesettingssecurecrossoriginopenerpolicyunsafenone def testcoopalreadypresentself response self processresponse headerscrossoriginopenerpolicy sameorigin self assertequalresponse headerscrossoriginopenerpolicy sameorigin with secure_hsts_seconds 3600 the middleware adds strict transport security max age 3600 to the response the middleware will not override a strict transport security header already present in the response the strict transport security header is not added to responses going over an insecure connection with secure_hsts_seconds 0 the middleware does not add a strict transport security header to the response with secure_hsts_seconds non zero and secure_hsts_include_subdomains true the middleware adds a strict transport security header with the includesubdomains directive to the response with secure_hsts_seconds non zero and secure_hsts_include_subdomains false the middleware adds a strict transport security header without the includesubdomains directive to the response with secure_hsts_seconds non zero and secure_hsts_preload true the middleware adds a strict transport security header with the preload directive to the response with secure_hsts_seconds non zero secure_hsts_include_subdomains and secure_hsts_preload true the middleware adds a strict transport security header containing both the includesubdomains and preload directives to the response with secure_hsts_seconds non zero and secure_hsts_preload false the middleware adds a strict transport security header without the preload directive to the response with secure_content_type_nosniff set to true the middleware adds x content type options nosniff header to the response the middleware will not override an x content type options header already present in the response with secure_content_type_nosniff false the middleware does not add an x content type options header to the response with secure_ssl_redirect true the middleware redirects any non secure requests to the https version of the same url the middleware does not redirect secure requests the middleware does not redirect requests with url path matching an exempt pattern the middleware redirects to secure_ssl_host if given with secure_ssl_redirect false the middleware does not redirect with secure_referrer_policy set to none the middleware does not add a referrer policy header to the response with secure_referrer_policy set to a valid value the middleware adds a referrer policy header to the response the middleware will not override a referrer policy header already present in the response with secure_cross_origin_opener_policy set to none the middleware does not add a cross origin opener policy header to the response secure_cross_origin_opener_policy defaults to same origin with secure_cross_origin_opener_policy set to a valid value the middleware adds a cross origin_opener policy header to the response the middleware doesn t override a cross origin opener policy header already present in the response
from django.http import HttpResponse
from django.test import RequestFactory, SimpleTestCase
from django.test.utils import override_settings


class SecurityMiddlewareTest(SimpleTestCase):
    def middleware(self, *args, **kwargs):
        from django.middleware.security import SecurityMiddleware

        return SecurityMiddleware(self.response(*args, **kwargs))

    @property
    def secure_request_kwargs(self):
        return {"wsgi.url_scheme": "https"}

    def response(self, *args, headers=None, **kwargs):
        def get_response(req):
            response = HttpResponse(*args, **kwargs)
            if headers:
                for k, v in headers.items():
                    response.headers[k] = v
            return response

        return get_response

    def process_response(self, *args, secure=False, request=None, **kwargs):
        request_kwargs = {}
        if secure:
            request_kwargs.update(self.secure_request_kwargs)
        if request is None:
            request = self.request.get("/some/url", **request_kwargs)
        ret = self.middleware(*args, **kwargs).process_request(request)
        if ret:
            return ret
        return self.middleware(*args, **kwargs)(request)

    request = RequestFactory()

    def process_request(self, method, *args, secure=False, **kwargs):
        if secure:
            kwargs.update(self.secure_request_kwargs)
        req = getattr(self.request, method.lower())(*args, **kwargs)
        return self.middleware().process_request(req)

    @override_settings(SECURE_HSTS_SECONDS=3600)
    def test_sts_on(self):
        
        self.assertEqual(
            self.process_response(secure=True).headers["Strict-Transport-Security"],
            "max-age=3600",
        )

    @override_settings(SECURE_HSTS_SECONDS=3600)
    def test_sts_already_present(self):
        
        response = self.process_response(
            secure=True, headers={"Strict-Transport-Security": "max-age=7200"}
        )
        self.assertEqual(response.headers["Strict-Transport-Security"], "max-age=7200")

    @override_settings(SECURE_HSTS_SECONDS=3600)
    def test_sts_only_if_secure(self):
        
        self.assertNotIn(
            "Strict-Transport-Security",
            self.process_response(secure=False).headers,
        )

    @override_settings(SECURE_HSTS_SECONDS=0)
    def test_sts_off(self):
        
        self.assertNotIn(
            "Strict-Transport-Security",
            self.process_response(secure=True).headers,
        )

    @override_settings(SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=True)
    def test_sts_include_subdomains(self):
        
        response = self.process_response(secure=True)
        self.assertEqual(
            response.headers["Strict-Transport-Security"],
            "max-age=600; includeSubDomains",
        )

    @override_settings(SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=False)
    def test_sts_no_include_subdomains(self):
        
        response = self.process_response(secure=True)
        self.assertEqual(response.headers["Strict-Transport-Security"], "max-age=600")

    @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_PRELOAD=True)
    def test_sts_preload(self):
        
        response = self.process_response(secure=True)
        self.assertEqual(
            response.headers["Strict-Transport-Security"],
            "max-age=10886400; preload",
        )

    @override_settings(
        SECURE_HSTS_SECONDS=10886400,
        SECURE_HSTS_INCLUDE_SUBDOMAINS=True,
        SECURE_HSTS_PRELOAD=True,
    )
    def test_sts_subdomains_and_preload(self):
        
        response = self.process_response(secure=True)
        self.assertEqual(
            response.headers["Strict-Transport-Security"],
            "max-age=10886400; includeSubDomains; preload",
        )

    @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_PRELOAD=False)
    def test_sts_no_preload(self):
        
        response = self.process_response(secure=True)
        self.assertEqual(
            response.headers["Strict-Transport-Security"],
            "max-age=10886400",
        )

    @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True)
    def test_content_type_on(self):
        
        self.assertEqual(
            self.process_response().headers["X-Content-Type-Options"],
            "nosniff",
        )

    @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True)
    def test_content_type_already_present(self):
        
        response = self.process_response(
            secure=True, headers={"X-Content-Type-Options": "foo"}
        )
        self.assertEqual(response.headers["X-Content-Type-Options"], "foo")

    @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=False)
    def test_content_type_off(self):
        
        self.assertNotIn("X-Content-Type-Options", self.process_response().headers)

    @override_settings(SECURE_SSL_REDIRECT=True)
    def test_ssl_redirect_on(self):
        
        ret = self.process_request("get", "/some/url?query=string")
        self.assertEqual(ret.status_code, 301)
        self.assertEqual(ret["Location"], "https://testserver/some/url?query=string")

    @override_settings(SECURE_SSL_REDIRECT=True)
    def test_no_redirect_ssl(self):
        
        ret = self.process_request("get", "/some/url", secure=True)
        self.assertIsNone(ret)

    @override_settings(SECURE_SSL_REDIRECT=True, SECURE_REDIRECT_EXEMPT=["^insecure/"])
    def test_redirect_exempt(self):
        
        ret = self.process_request("get", "/insecure/page")
        self.assertIsNone(ret)

    @override_settings(SECURE_SSL_REDIRECT=True, SECURE_SSL_HOST="secure.example.com")
    def test_redirect_ssl_host(self):
        
        ret = self.process_request("get", "/some/url")
        self.assertEqual(ret.status_code, 301)
        self.assertEqual(ret["Location"], "https://secure.example.com/some/url")

    @override_settings(SECURE_SSL_REDIRECT=False)
    def test_ssl_redirect_off(self):
        
        ret = self.process_request("get", "/some/url")
        self.assertIsNone(ret)

    @override_settings(SECURE_REFERRER_POLICY=None)
    def test_referrer_policy_off(self):
        
        self.assertNotIn("Referrer-Policy", self.process_response().headers)

    def test_referrer_policy_on(self):
        
        tests = (
            ("strict-origin", "strict-origin"),
            ("strict-origin,origin", "strict-origin,origin"),
            ("strict-origin, origin", "strict-origin,origin"),
            (["strict-origin", "origin"], "strict-origin,origin"),
            (("strict-origin", "origin"), "strict-origin,origin"),
        )
        for value, expected in tests:
            with self.subTest(value=value), override_settings(
                SECURE_REFERRER_POLICY=value
            ):
                self.assertEqual(
                    self.process_response().headers["Referrer-Policy"],
                    expected,
                )

    @override_settings(SECURE_REFERRER_POLICY="strict-origin")
    def test_referrer_policy_already_present(self):
        
        response = self.process_response(headers={"Referrer-Policy": "unsafe-url"})
        self.assertEqual(response.headers["Referrer-Policy"], "unsafe-url")

    @override_settings(SECURE_CROSS_ORIGIN_OPENER_POLICY=None)
    def test_coop_off(self):
        
        self.assertNotIn("Cross-Origin-Opener-Policy", self.process_response())

    def test_coop_default(self):
        
        self.assertEqual(
            self.process_response().headers["Cross-Origin-Opener-Policy"],
            "same-origin",
        )

    def test_coop_on(self):
        
        tests = ["same-origin", "same-origin-allow-popups", "unsafe-none"]
        for value in tests:
            with self.subTest(value=value), override_settings(
                SECURE_CROSS_ORIGIN_OPENER_POLICY=value,
            ):
                self.assertEqual(
                    self.process_response().headers["Cross-Origin-Opener-Policy"],
                    value,
                )

    @override_settings(SECURE_CROSS_ORIGIN_OPENER_POLICY="unsafe-none")
    def test_coop_already_present(self):
        
        response = self.process_response(
            headers={"Cross-Origin-Opener-Policy": "same-origin"}
        )
        self.assertEqual(response.headers["Cross-Origin-Opener-Policy"], "same-origin")