~ubuntuone-hackers/django-secure/trunk

« back to all changes in this revision

Viewing changes to djangosecure/tests/tests.py

  • Committer: Carl Meyer
  • Date: 2014-10-23 18:00:11 UTC
  • Revision ID: git-v1:2605d682acc99dbf9fb9c69dea60671638d4323c
Update tox.ini; hide tests from old Django test runners.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from django.conf import settings
 
2
from django.core.exceptions import ImproperlyConfigured
 
3
from django.core.management import call_command
 
4
from django.http import HttpResponse
 
5
from django.test import TestCase, RequestFactory
 
6
from django.test.utils import override_settings
 
7
from django.utils.six import StringIO
 
8
 
 
9
 
 
10
 
 
11
class SecurityMiddlewareTest(TestCase):
 
12
    @property
 
13
    def middleware(self):
 
14
        from djangosecure.middleware import SecurityMiddleware
 
15
        return SecurityMiddleware()
 
16
 
 
17
 
 
18
    @property
 
19
    def secure_request_kwargs(self):
 
20
        return {"wsgi.url_scheme": "https"}
 
21
 
 
22
 
 
23
    def response(self, *args, **kwargs):
 
24
        headers = kwargs.pop("headers", {})
 
25
        response = HttpResponse(*args, **kwargs)
 
26
        for k, v in headers.items():
 
27
            response[k] = v
 
28
        return response
 
29
 
 
30
 
 
31
    def process_response(self, *args, **kwargs):
 
32
        request_kwargs = {}
 
33
        if kwargs.pop("secure", False):
 
34
            request_kwargs.update(self.secure_request_kwargs)
 
35
        request = (kwargs.pop("request", None) or
 
36
                   self.request.get("/some/url", **request_kwargs))
 
37
        ret = self.middleware.process_request(request)
 
38
        if ret:
 
39
            return ret
 
40
        return self.middleware.process_response(
 
41
            request, self.response(*args, **kwargs))
 
42
 
 
43
 
 
44
    request = RequestFactory()
 
45
 
 
46
 
 
47
    def process_request(self, method, *args, **kwargs):
 
48
        if kwargs.pop("secure", False):
 
49
            kwargs.update(self.secure_request_kwargs)
 
50
        req = getattr(self.request, method.lower())(*args, **kwargs)
 
51
        return self.middleware.process_request(req)
 
52
 
 
53
 
 
54
    @override_settings(SECURE_FRAME_DENY=True)
 
55
    def test_frame_deny_on(self):
 
56
        """
 
57
        With SECURE_FRAME_DENY True, the middleware adds "x-frame-options:
 
58
        DENY" to the response.
 
59
 
 
60
        """
 
61
        self.assertEqual(self.process_response()["x-frame-options"], "DENY")
 
62
 
 
63
 
 
64
    @override_settings(SECURE_FRAME_DENY=True)
 
65
    def test_frame_deny_already_present(self):
 
66
        """
 
67
        The middleware will not override an "x-frame-options" header already
 
68
        present in the response.
 
69
 
 
70
        """
 
71
        response = self.process_response(
 
72
            headers={"x-frame-options": "SAMEORIGIN"})
 
73
        self.assertEqual(response["x-frame-options"], "SAMEORIGIN")
 
74
 
 
75
 
 
76
    @override_settings(SECURE_FRAME_DENY=True)
 
77
    def test_frame_deny_exempt(self):
 
78
        """
 
79
        If the response has the _frame_deny_exempt attribute set to True, the
 
80
        middleware does not add an "x-frame-options" header to the response.
 
81
 
 
82
        """
 
83
        response = HttpResponse()
 
84
        response._frame_deny_exempt = True
 
85
        response = self.middleware.process_response("not used", response)
 
86
        self.assertFalse("x-frame-options" in response)
 
87
 
 
88
 
 
89
    @override_settings(SECURE_FRAME_DENY=False)
 
90
    def test_frame_deny_off(self):
 
91
        """
 
92
        With SECURE_FRAME_DENY False, the middleware does not add an
 
93
        "x-frame-options" header to the response.
 
94
 
 
95
        """
 
96
        self.assertFalse("x-frame-options" in self.process_response())
 
97
 
 
98
 
 
99
    @override_settings(SECURE_HSTS_SECONDS=3600)
 
100
    def test_sts_on(self):
 
101
        """
 
102
        With SECURE_HSTS_SECONDS=3600, the middleware adds
 
103
        "strict-transport-security: max-age=3600" to the response.
 
104
 
 
105
        """
 
106
        self.assertEqual(
 
107
            self.process_response(secure=True)["strict-transport-security"],
 
108
            "max-age=3600")
 
109
 
 
110
 
 
111
    @override_settings(SECURE_HSTS_SECONDS=3600)
 
112
    def test_sts_already_present(self):
 
113
        """
 
114
        The middleware will not override a "strict-transport-security" header
 
115
        already present in the response.
 
116
 
 
117
        """
 
118
        response = self.process_response(
 
119
            secure=True,
 
120
            headers={"strict-transport-security": "max-age=7200"})
 
121
        self.assertEqual(response["strict-transport-security"], "max-age=7200")
 
122
 
 
123
 
 
124
    @override_settings(SECURE_HSTS_SECONDS=3600)
 
125
    def test_sts_only_if_secure(self):
 
126
        """
 
127
        The "strict-transport-security" header is not added to responses going
 
128
        over an insecure connection.
 
129
 
 
130
        """
 
131
        self.assertFalse(
 
132
            "strict-transport-security" in self.process_response(secure=False))
 
133
 
 
134
 
 
135
    @override_settings(SECURE_HSTS_SECONDS=0)
 
136
    def test_sts_off(self):
 
137
        """
 
138
        With SECURE_HSTS_SECONDS of 0, the middleware does not add a
 
139
        "strict-transport-security" header to the response.
 
140
 
 
141
        """
 
142
        self.assertFalse(
 
143
            "strict-transport-security" in self.process_response(secure=True))
 
144
 
 
145
 
 
146
    @override_settings(
 
147
        SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=True)
 
148
    def test_sts_include_subdomains(self):
 
149
        """
 
150
        With SECURE_HSTS_SECONDS non-zero and SECURE_HSTS_INCLUDE_SUBDOMAINS
 
151
        True, the middleware adds a "strict-transport-security" header with the
 
152
        "includeSubDomains" tag to the response.
 
153
 
 
154
        """
 
155
        response = self.process_response(secure=True)
 
156
        self.assertEqual(
 
157
            response["strict-transport-security"],
 
158
            "max-age=600; includeSubDomains",
 
159
            )
 
160
 
 
161
 
 
162
    @override_settings(
 
163
        SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=False)
 
164
    def test_sts_no_include_subdomains(self):
 
165
        """
 
166
        With SECURE_HSTS_SECONDS non-zero and SECURE_HSTS_INCLUDE_SUBDOMAINS
 
167
        False, the middleware adds a "strict-transport-security" header without
 
168
        the "includeSubDomains" tag to the response.
 
169
 
 
170
        """
 
171
        response = self.process_response(secure=True)
 
172
        self.assertEqual(response["strict-transport-security"], "max-age=600")
 
173
 
 
174
 
 
175
    @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True)
 
176
    def test_content_type_on(self):
 
177
        """
 
178
        With SECURE_CONTENT_TYPE_NOSNIFF set to True, the middleware adds
 
179
        "x-content-type-options: nosniff" header to the response.
 
180
 
 
181
        """
 
182
        self.assertEqual(
 
183
            self.process_response()["x-content-type-options"],
 
184
            "nosniff")
 
185
 
 
186
 
 
187
    @override_settings(SECURE_CONTENT_TYPE_NO_SNIFF=True)
 
188
    def test_content_type_already_present(self):
 
189
        """
 
190
        The middleware will not override an "x-content-type-options" header
 
191
        already present in the response.
 
192
 
 
193
        """
 
194
        response = self.process_response(
 
195
            secure=True,
 
196
            headers={"x-content-type-options": "foo"})
 
197
        self.assertEqual(response["x-content-type-options"], "foo")
 
198
 
 
199
 
 
200
    @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=False)
 
201
    def test_content_type_off(self):
 
202
        """
 
203
        With SECURE_CONTENT_TYPE_NOSNIFF False, the middleware does not add an
 
204
        "x-content-type-options" header to the response.
 
205
 
 
206
        """
 
207
        self.assertFalse("x-content-type-options" in self.process_response())
 
208
 
 
209
 
 
210
    @override_settings(SECURE_BROWSER_XSS_FILTER=True)
 
211
    def test_xss_filter_on(self):
 
212
        """
 
213
        With SECURE_BROWSER_XSS_FILTER set to True, the middleware adds
 
214
        "s-xss-protection: 1; mode=block" header to the response.
 
215
 
 
216
        """
 
217
        self.assertEqual(
 
218
            self.process_response()["x-xss-protection"],
 
219
            "1; mode=block")
 
220
 
 
221
 
 
222
    @override_settings(SECURE_BROWSER_XSS_FILTER=True)
 
223
    def test_xss_filter_already_present(self):
 
224
        """
 
225
        The middleware will not override an "x-xss-protection" header
 
226
        already present in the response.
 
227
 
 
228
        """
 
229
        response = self.process_response(
 
230
            secure=True,
 
231
            headers={"x-xss-protection": "foo"})
 
232
        self.assertEqual(response["x-xss-protection"], "foo")
 
233
 
 
234
 
 
235
    @override_settings(SECURE_BROWSER_XSS_FILTER=False)
 
236
    def test_xss_filter_off(self):
 
237
        """
 
238
        With SECURE_BROWSER_XSS_FILTER set to False, the middleware does not add an
 
239
        "x-xss-protection" header to the response.
 
240
 
 
241
        """
 
242
        self.assertFalse("x-xss-protection" in self.process_response())
 
243
 
 
244
 
 
245
    @override_settings(SECURE_SSL_REDIRECT=True)
 
246
    def test_ssl_redirect_on(self):
 
247
        """
 
248
        With SECURE_SSL_REDIRECT True, the middleware redirects any non-secure
 
249
        requests to the https:// version of the same URL.
 
250
 
 
251
        """
 
252
        ret = self.process_request("get", "/some/url?query=string")
 
253
        self.assertEqual(ret.status_code, 301)
 
254
        self.assertEqual(
 
255
            ret["Location"], "https://testserver/some/url?query=string")
 
256
 
 
257
 
 
258
    @override_settings(SECURE_SSL_REDIRECT=True)
 
259
    def test_no_redirect_ssl(self):
 
260
        """
 
261
        The middleware does not redirect secure requests.
 
262
 
 
263
        """
 
264
        ret = self.process_request("get", "/some/url", secure=True)
 
265
        self.assertEqual(ret, None)
 
266
 
 
267
 
 
268
    @override_settings(
 
269
        SECURE_SSL_REDIRECT=True, SECURE_REDIRECT_EXEMPT=["^insecure/"])
 
270
    def test_redirect_exempt(self):
 
271
        """
 
272
        The middleware does not redirect requests with URL path matching an
 
273
        exempt pattern.
 
274
 
 
275
        """
 
276
        ret = self.process_request("get", "/insecure/page")
 
277
        self.assertEqual(ret, None)
 
278
 
 
279
 
 
280
    @override_settings(
 
281
        SECURE_SSL_REDIRECT=True, SECURE_SSL_HOST="secure.example.com")
 
282
    def test_redirect_ssl_host(self):
 
283
        """
 
284
        The middleware redirects to SECURE_SSL_HOST if given.
 
285
 
 
286
        """
 
287
        ret = self.process_request("get", "/some/url")
 
288
        self.assertEqual(ret.status_code, 301)
 
289
        self.assertEqual(ret["Location"], "https://secure.example.com/some/url")
 
290
 
 
291
 
 
292
    @override_settings(SECURE_SSL_REDIRECT=False)
 
293
    def test_ssl_redirect_off(self):
 
294
        """
 
295
        With SECURE_SSL_REDIRECT False, the middleware does no redirect.
 
296
 
 
297
        """
 
298
        ret = self.process_request("get", "/some/url")
 
299
        self.assertEqual(ret, None)
 
300
 
 
301
 
 
302
 
 
303
class ProxySecurityMiddlewareTest(SecurityMiddlewareTest):
 
304
    """
 
305
    Test that SecurityMiddleware behaves the same even if our "secure request"
 
306
    indicator is a proxy header.
 
307
 
 
308
    """
 
309
    def setUp(self):
 
310
        self.override = override_settings(
 
311
            SECURE_PROXY_SSL_HEADER=("HTTP_X_FORWARDED_PROTOCOL", "https"))
 
312
 
 
313
        self.override.enable()
 
314
 
 
315
 
 
316
    def tearDown(self):
 
317
        self.override.disable()
 
318
 
 
319
 
 
320
    @property
 
321
    def secure_request_kwargs(self):
 
322
        return {"HTTP_X_FORWARDED_PROTOCOL": "https"}
 
323
 
 
324
 
 
325
    def test_is_secure(self):
 
326
        """
 
327
        SecurityMiddleware patches request.is_secure() to report ``True`` even
 
328
        with a proxy-header secure request.
 
329
 
 
330
        """
 
331
        request = self.request.get("/some/url", **self.secure_request_kwargs)
 
332
        self.middleware.process_request(request)
 
333
 
 
334
        self.assertEqual(request.is_secure(), True)
 
335
 
 
336
 
 
337
 
 
338
 
 
339
class FrameDenyExemptTest(TestCase):
 
340
    def test_adds_exempt_attr(self):
 
341
        """
 
342
        Test that the decorator adds a _frame_deny_exempt attribute to the
 
343
        response. (We test above in the middleware tests that this attribute
 
344
        causes the X-Frame-Options header to not be added.)
 
345
 
 
346
        """
 
347
        from djangosecure.decorators import frame_deny_exempt
 
348
 
 
349
        @frame_deny_exempt
 
350
        def myview(request):
 
351
            return HttpResponse()
 
352
 
 
353
        self.assertEqual(myview("not used")._frame_deny_exempt, True)
 
354
 
 
355
 
 
356
 
 
357
def fake_test():
 
358
    return set(["SOME_WARNING"])
 
359
 
 
360
fake_test.messages = {
 
361
    "SOME_WARNING": "This is the warning message."
 
362
    }
 
363
 
 
364
def nomsg_test():
 
365
    return set(["OTHER WARNING"])
 
366
 
 
367
def passing_test():
 
368
    return []
 
369
 
 
370
 
 
371
class RunChecksTest(TestCase):
 
372
    @property
 
373
    def func(self):
 
374
        from djangosecure.check import run_checks
 
375
        return run_checks
 
376
 
 
377
 
 
378
    @override_settings(
 
379
        SECURE_CHECKS=[
 
380
            "djangosecure.tests.tests.fake_test",
 
381
            "djangosecure.tests.tests.nomsg_test"])
 
382
    def test_returns_warnings(self):
 
383
        self.assertEqual(self.func(), set(["SOME_WARNING", "OTHER WARNING"]))
 
384
 
 
385
 
 
386
 
 
387
class CheckSettingsCommandTest(TestCase):
 
388
    def call(self, **options):
 
389
        stdout = options.setdefault("stdout", StringIO())
 
390
        stderr = options.setdefault("stderr", StringIO())
 
391
 
 
392
        call_command("checksecure", **options)
 
393
 
 
394
        stderr.seek(0)
 
395
        stdout.seek(0)
 
396
 
 
397
        return stdout.read(), stderr.read()
 
398
 
 
399
 
 
400
    @override_settings(SECURE_CHECKS=["djangosecure.tests.tests.fake_test"])
 
401
    def test_prints_messages(self):
 
402
        stdout, stderr = self.call()
 
403
        self.assertTrue("This is the warning message." in stderr)
 
404
 
 
405
 
 
406
    @override_settings(SECURE_CHECKS=["djangosecure.tests.tests.nomsg_test"])
 
407
    def test_prints_code_if_no_message(self):
 
408
        stdout, stderr = self.call()
 
409
        self.assertTrue("OTHER WARNING" in stderr)
 
410
 
 
411
 
 
412
    @override_settings(SECURE_CHECKS=["djangosecure.tests.tests.fake_test"])
 
413
    def test_prints_code_if_verbosity_0(self):
 
414
        stdout, stderr = self.call(verbosity=0)
 
415
        self.assertTrue("SOME_WARNING" in stderr)
 
416
 
 
417
 
 
418
    @override_settings(SECURE_CHECKS=["djangosecure.tests.tests.fake_test"])
 
419
    def test_prints_check_names(self):
 
420
        stdout, stderr = self.call()
 
421
        self.assertTrue("djangosecure.tests.tests.fake_test" in stdout)
 
422
 
 
423
 
 
424
    @override_settings(SECURE_CHECKS=["djangosecure.tests.tests.fake_test"])
 
425
    def test_no_verbosity(self):
 
426
        stdout, stderr = self.call(verbosity=0)
 
427
        self.assertEqual(stdout, "")
 
428
 
 
429
 
 
430
    @override_settings(SECURE_CHECKS=["djangosecure.tests.tests.passing_test"])
 
431
    def test_all_clear(self):
 
432
        stdout, stderr = self.call()
 
433
        self.assertTrue("All clear!" in stdout)
 
434
 
 
435
 
 
436
 
 
437
class CheckSessionCookieSecureTest(TestCase):
 
438
    @property
 
439
    def func(self):
 
440
        from djangosecure.check.sessions import check_session_cookie_secure
 
441
        return check_session_cookie_secure
 
442
 
 
443
 
 
444
    @override_settings(
 
445
        SESSION_COOKIE_SECURE=False,
 
446
        INSTALLED_APPS=["django.contrib.sessions"],
 
447
        MIDDLEWARE_CLASSES=[])
 
448
    def test_session_cookie_secure_with_installed_app(self):
 
449
        """
 
450
        Warns if SESSION_COOKIE_SECURE is off and "django.contrib.sessions" is
 
451
        in INSTALLED_APPS.
 
452
 
 
453
        """
 
454
        self.assertEqual(
 
455
            self.func(), set(["SESSION_COOKIE_NOT_SECURE_APP_INSTALLED"]))
 
456
 
 
457
 
 
458
    @override_settings(
 
459
        SESSION_COOKIE_SECURE=False,
 
460
        INSTALLED_APPS=[],
 
461
        MIDDLEWARE_CLASSES=[
 
462
            "django.contrib.sessions.middleware.SessionMiddleware"])
 
463
    def test_session_cookie_secure_with_middleware(self):
 
464
        """
 
465
        Warns if SESSION_COOKIE_SECURE is off and
 
466
        "django.contrib.sessions.middleware.SessionMiddleware" is in
 
467
        MIDDLEWARE_CLASSES.
 
468
 
 
469
        """
 
470
        self.assertEqual(
 
471
            self.func(), set(["SESSION_COOKIE_NOT_SECURE_MIDDLEWARE"]))
 
472
 
 
473
 
 
474
    @override_settings(
 
475
        SESSION_COOKIE_SECURE=False,
 
476
        INSTALLED_APPS=["django.contrib.sessions"],
 
477
        MIDDLEWARE_CLASSES=[
 
478
            "django.contrib.sessions.middleware.SessionMiddleware"])
 
479
    def test_session_cookie_secure_both(self):
 
480
        """
 
481
        If SESSION_COOKIE_SECURE is off and we find both the session app and
 
482
        the middleware, we just provide one common warning.
 
483
 
 
484
        """
 
485
        self.assertEqual(
 
486
            self.func(), set(["SESSION_COOKIE_NOT_SECURE"]))
 
487
 
 
488
 
 
489
    @override_settings(
 
490
        SESSION_COOKIE_SECURE=True,
 
491
        INSTALLED_APPS=["django.contrib.sessions"],
 
492
        MIDDLEWARE_CLASSES=[
 
493
            "django.contrib.sessions.middleware.SessionMiddleware"])
 
494
    def test_session_cookie_secure_true(self):
 
495
        """
 
496
        If SESSION_COOKIE_SECURE is on, there's no warning about it.
 
497
 
 
498
        """
 
499
        self.assertEqual(self.func(), set())
 
500
 
 
501
 
 
502
 
 
503
class CheckSessionCookieHttpOnlyTest(TestCase):
 
504
    @property
 
505
    def func(self):
 
506
        from djangosecure.check.sessions import check_session_cookie_httponly
 
507
        return check_session_cookie_httponly
 
508
 
 
509
 
 
510
    @override_settings(
 
511
        SESSION_COOKIE_HTTPONLY=False,
 
512
        INSTALLED_APPS=["django.contrib.sessions"],
 
513
        MIDDLEWARE_CLASSES=[])
 
514
    def test_session_cookie_httponly_with_installed_app(self):
 
515
        """
 
516
        Warns if SESSION_COOKIE_HTTPONLY is off and "django.contrib.sessions"
 
517
        is in INSTALLED_APPS.
 
518
 
 
519
        """
 
520
        self.assertEqual(
 
521
            self.func(), set(["SESSION_COOKIE_NOT_HTTPONLY_APP_INSTALLED"]))
 
522
 
 
523
 
 
524
    @override_settings(
 
525
        SESSION_COOKIE_HTTPONLY=False,
 
526
        INSTALLED_APPS=[],
 
527
        MIDDLEWARE_CLASSES=[
 
528
            "django.contrib.sessions.middleware.SessionMiddleware"])
 
529
    def test_session_cookie_httponly_with_middleware(self):
 
530
        """
 
531
        Warns if SESSION_COOKIE_HTTPONLY is off and
 
532
        "django.contrib.sessions.middleware.SessionMiddleware" is in
 
533
        MIDDLEWARE_CLASSES.
 
534
 
 
535
        """
 
536
        self.assertEqual(
 
537
            self.func(), set(["SESSION_COOKIE_NOT_HTTPONLY_MIDDLEWARE"]))
 
538
 
 
539
 
 
540
    @override_settings(
 
541
        SESSION_COOKIE_HTTPONLY=False,
 
542
        INSTALLED_APPS=["django.contrib.sessions"],
 
543
        MIDDLEWARE_CLASSES=[
 
544
            "django.contrib.sessions.middleware.SessionMiddleware"])
 
545
    def test_session_cookie_httponly_both(self):
 
546
        """
 
547
        If SESSION_COOKIE_HTTPONLY is off and we find both the session app and
 
548
        the middleware, we just provide one common warning.
 
549
 
 
550
        """
 
551
        self.assertTrue(
 
552
            self.func(), set(["SESSION_COOKIE_NOT_HTTPONLY"]))
 
553
 
 
554
 
 
555
    @override_settings(
 
556
        SESSION_COOKIE_HTTPONLY=True,
 
557
        INSTALLED_APPS=["django.contrib.sessions"],
 
558
        MIDDLEWARE_CLASSES=[
 
559
            "django.contrib.sessions.middleware.SessionMiddleware"])
 
560
    def test_session_cookie_httponly_true(self):
 
561
        """
 
562
        If SESSION_COOKIE_HTTPONLY is on, there's no warning about it.
 
563
 
 
564
        """
 
565
        self.assertEqual(self.func(), set())
 
566
 
 
567
 
 
568
 
 
569
class CheckCSRFMiddlewareTest(TestCase):
 
570
    @property
 
571
    def func(self):
 
572
        from djangosecure.check.csrf import check_csrf_middleware
 
573
        return check_csrf_middleware
 
574
 
 
575
 
 
576
    @override_settings(MIDDLEWARE_CLASSES=[])
 
577
    def test_no_csrf_middleware(self):
 
578
        self.assertEqual(
 
579
            self.func(), set(["CSRF_VIEW_MIDDLEWARE_NOT_INSTALLED"]))
 
580
 
 
581
 
 
582
    @override_settings(
 
583
        MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"])
 
584
    def test_with_csrf_middleware(self):
 
585
        self.assertEqual(self.func(), set())
 
586
 
 
587
 
 
588
 
 
589
class CheckSecurityMiddlewareTest(TestCase):
 
590
    @property
 
591
    def func(self):
 
592
        from djangosecure.check.djangosecure import check_security_middleware
 
593
        return check_security_middleware
 
594
 
 
595
 
 
596
    @override_settings(MIDDLEWARE_CLASSES=[])
 
597
    def test_no_security_middleware(self):
 
598
        self.assertEqual(
 
599
            self.func(), set(["SECURITY_MIDDLEWARE_NOT_INSTALLED"]))
 
600
 
 
601
 
 
602
    @override_settings(
 
603
        MIDDLEWARE_CLASSES=["djangosecure.middleware.SecurityMiddleware"])
 
604
    def test_with_security_middleware(self):
 
605
        self.assertEqual(self.func(), set())
 
606
 
 
607
 
 
608
 
 
609
class CheckStrictTransportSecurityTest(TestCase):
 
610
    @property
 
611
    def func(self):
 
612
        from djangosecure.check.djangosecure import check_sts
 
613
        return check_sts
 
614
 
 
615
 
 
616
    @override_settings(SECURE_HSTS_SECONDS=0)
 
617
    def test_no_sts(self):
 
618
        self.assertEqual(
 
619
            self.func(), set(["STRICT_TRANSPORT_SECURITY_NOT_ENABLED"]))
 
620
 
 
621
 
 
622
    @override_settings(SECURE_HSTS_SECONDS=3600)
 
623
    def test_with_sts(self):
 
624
        self.assertEqual(self.func(), set())
 
625
 
 
626
 
 
627
 
 
628
class CheckStrictTransportSecuritySubdomainsTest(TestCase):
 
629
    @property
 
630
    def func(self):
 
631
        from djangosecure.check.djangosecure import check_sts_include_subdomains
 
632
        return check_sts_include_subdomains
 
633
 
 
634
 
 
635
    @override_settings(SECURE_HSTS_INCLUDE_SUBDOMAINS=False)
 
636
    def test_no_sts_subdomains(self):
 
637
        self.assertEqual(
 
638
            self.func(), set(["STRICT_TRANSPORT_SECURITY_NO_SUBDOMAINS"]))
 
639
 
 
640
 
 
641
    @override_settings(SECURE_HSTS_INCLUDE_SUBDOMAINS=True)
 
642
    def test_with_sts_subdomains(self):
 
643
        self.assertEqual(self.func(), set())
 
644
 
 
645
 
 
646
 
 
647
class CheckFrameDenyTest(TestCase):
 
648
    @property
 
649
    def func(self):
 
650
        from djangosecure.check.djangosecure import check_frame_deny
 
651
        return check_frame_deny
 
652
 
 
653
 
 
654
    @override_settings(SECURE_FRAME_DENY=False)
 
655
    def test_no_frame_deny(self):
 
656
        self.assertEqual(
 
657
            self.func(), set(["FRAME_DENY_NOT_ENABLED"]))
 
658
 
 
659
 
 
660
    @override_settings(SECURE_FRAME_DENY=True)
 
661
    def test_with_frame_deny(self):
 
662
        self.assertEqual(self.func(), set())
 
663
 
 
664
 
 
665
 
 
666
class CheckContentTypeNosniffTest(TestCase):
 
667
    @property
 
668
    def func(self):
 
669
        from djangosecure.check.djangosecure import check_content_type_nosniff
 
670
        return check_content_type_nosniff
 
671
 
 
672
 
 
673
    @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=False)
 
674
    def test_no_content_type_nosniff(self):
 
675
        self.assertEqual(
 
676
            self.func(), set(["CONTENT_TYPE_NOSNIFF_NOT_ENABLED"]))
 
677
 
 
678
 
 
679
    @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True)
 
680
    def test_with_content_type_nosniff(self):
 
681
        self.assertEqual(self.func(), set())
 
682
 
 
683
 
 
684
 
 
685
class CheckXssFilterTest(TestCase):
 
686
    @property
 
687
    def func(self):
 
688
        from djangosecure.check.djangosecure import check_xss_filter
 
689
        return check_xss_filter
 
690
 
 
691
 
 
692
    @override_settings(SECURE_BROWSER_XSS_FILTER=False)
 
693
    def test_no_xss_filter(self):
 
694
        self.assertEqual(
 
695
            self.func(), set(["BROWSER_XSS_FILTER_NOT_ENABLED"]))
 
696
 
 
697
 
 
698
    @override_settings(SECURE_BROWSER_XSS_FILTER=True)
 
699
    def test_with_xss_filter(self):
 
700
        self.assertEqual(self.func(), set())
 
701
 
 
702
 
 
703
 
 
704
class CheckSSLRedirectTest(TestCase):
 
705
    @property
 
706
    def func(self):
 
707
        from djangosecure.check.djangosecure import check_ssl_redirect
 
708
        return check_ssl_redirect
 
709
 
 
710
 
 
711
    @override_settings(SECURE_SSL_REDIRECT=False)
 
712
    def test_no_sts(self):
 
713
        self.assertEqual(
 
714
            self.func(), set(["SSL_REDIRECT_NOT_ENABLED"]))
 
715
 
 
716
 
 
717
    @override_settings(SECURE_SSL_REDIRECT=True)
 
718
    def test_with_sts(self):
 
719
        self.assertEqual(self.func(), set())
 
720
 
 
721
 
 
722
class CheckSecretKeyTest(TestCase):
 
723
    @property
 
724
    def func(self):
 
725
        from djangosecure.check.djangosecure import check_secret_key
 
726
        return check_secret_key
 
727
 
 
728
 
 
729
    @override_settings(SECRET_KEY='awcetupav$#!^h9wTUAPCJWE&!T#``Ho;ta9w4tva')
 
730
    def test_okay_secret_key(self):
 
731
        self.assertEqual(self.func(), set())
 
732
 
 
733
 
 
734
    @override_settings(SECRET_KEY='')
 
735
    def test_empty_secret_key(self):
 
736
        self.assertEqual(self.func(), set(['BAD_SECRET_KEY']))
 
737
 
 
738
 
 
739
    @override_settings(SECRET_KEY=None)
 
740
    def test_missing_secret_key(self):
 
741
        del settings.SECRET_KEY
 
742
        self.assertEqual(self.func(), set(['BAD_SECRET_KEY']))
 
743
 
 
744
 
 
745
    @override_settings(SECRET_KEY=None)
 
746
    def test_none_secret_key(self):
 
747
        self.assertEqual(self.func(), set(['BAD_SECRET_KEY']))
 
748
 
 
749
 
 
750
    @override_settings(SECRET_KEY='bla bla')
 
751
    def test_low_entropy_secret_key(self):
 
752
        self.assertEqual(self.func(), set(['BAD_SECRET_KEY']))
 
753
 
 
754
 
 
755
 
 
756
class ConfTest(TestCase):
 
757
    def test_no_fallback(self):
 
758
        """
 
759
        Accessing a setting without a default value raises in
 
760
        ImproperlyConfigured.
 
761
 
 
762
        """
 
763
        from djangosecure.conf import conf
 
764
 
 
765
        self.assertRaises(ImproperlyConfigured, getattr, conf, "HAS_NO_DEFAULT")
 
766
 
 
767
 
 
768
    def test_defaults(self):
 
769
        from djangosecure.conf import conf
 
770
 
 
771
        self.assertEqual(
 
772
            conf.defaults,
 
773
            {
 
774
                "SECURE_CHECKS":[
 
775
                    "djangosecure.check.csrf.check_csrf_middleware",
 
776
                    "djangosecure.check.sessions.check_session_cookie_secure",
 
777
                    "djangosecure.check.sessions.check_session_cookie_httponly",
 
778
                    "djangosecure.check.djangosecure.check_security_middleware",
 
779
                    "djangosecure.check.djangosecure.check_sts",
 
780
                    "djangosecure.check.djangosecure.check_sts_include_subdomains",
 
781
                    "djangosecure.check.djangosecure.check_frame_deny",
 
782
                    "djangosecure.check.djangosecure.check_content_type_nosniff",
 
783
                    "djangosecure.check.djangosecure.check_xss_filter",
 
784
                    "djangosecure.check.djangosecure.check_ssl_redirect",
 
785
                    "djangosecure.check.djangosecure.check_secret_key",
 
786
                    ],
 
787
                "SECURE_HSTS_SECONDS": 0,
 
788
                "SECURE_HSTS_INCLUDE_SUBDOMAINS": False,
 
789
                "SECURE_FRAME_DENY": False,
 
790
                "SECURE_CONTENT_TYPE_NOSNIFF": False,
 
791
                "SECURE_BROWSER_XSS_FILTER": False,
 
792
                "SECURE_SSL_REDIRECT": False,
 
793
                "SECURE_SSL_HOST": None,
 
794
                "SECURE_REDIRECT_EXEMPT": [],
 
795
                "SECURE_PROXY_SSL_HEADER": None,
 
796
                }
 
797
            )