1from django.http import HttpResponse, HttpResponseServerError
2from django.test import RequestFactory
3from django.test.utils import override_settings
4
5from csp.middleware import CSPMiddleware
6
7
8HEADER = 'Content-Security-Policy'
9mw = CSPMiddleware()
10rf = RequestFactory()
11
12
13def test_add_header():
14    request = rf.get('/')
15    response = HttpResponse()
16    mw.process_response(request, response)
17    assert HEADER in response
18
19
20def test_exempt():
21    request = rf.get('/')
22    response = HttpResponse()
23    response._csp_exempt = True
24    mw.process_response(request, response)
25    assert HEADER not in response
26
27
28@override_settings(CSP_EXCLUDE_URL_PREFIXES=('/inlines-r-us'))
29def text_exclude():
30    request = rf.get('/inlines-r-us/foo')
31    response = HttpResponse()
32    mw.process_response(request, response)
33    assert HEADER not in response
34
35
36@override_settings(CSP_REPORT_ONLY=True)
37def test_report_only():
38    request = rf.get('/')
39    response = HttpResponse()
40    mw.process_response(request, response)
41    assert HEADER not in response
42    assert HEADER + '-Report-Only' in response
43
44
45def test_dont_replace():
46    request = rf.get('/')
47    response = HttpResponse()
48    response[HEADER] = 'default-src example.com'
49    mw.process_response(request, response)
50    assert response[HEADER] == 'default-src example.com'
51
52
53def test_use_config():
54    request = rf.get('/')
55    response = HttpResponse()
56    response._csp_config = {'default-src': ['example.com']}
57    mw.process_response(request, response)
58    assert response[HEADER] == 'default-src example.com'
59
60
61def test_use_update():
62    request = rf.get('/')
63    response = HttpResponse()
64    response._csp_update = {'default-src': ['example.com']}
65    mw.process_response(request, response)
66    assert response[HEADER] == "default-src 'self' example.com"
67
68
69@override_settings(CSP_IMG_SRC=['foo.com'])
70def test_use_replace():
71    request = rf.get('/')
72    response = HttpResponse()
73    response._csp_replace = {'img-src': ['bar.com']}
74    mw.process_response(request, response)
75    policy_list = sorted(response[HEADER].split('; '))
76    assert policy_list == ["default-src 'self'", "img-src bar.com"]
77
78
79@override_settings(DEBUG=True)
80def test_debug_exempt():
81    request = rf.get('/')
82    response = HttpResponseServerError()
83    mw.process_response(request, response)
84    assert HEADER not in response
85
86
87def test_nonce_created_when_accessed():
88    request = rf.get('/')
89    mw.process_request(request)
90    nonce = str(request.csp_nonce)
91    response = HttpResponse()
92    mw.process_response(request, response)
93    assert nonce in response[HEADER]
94
95
96def test_no_nonce_when_not_accessed():
97    request = rf.get('/')
98    mw.process_request(request)
99    response = HttpResponse()
100    mw.process_response(request, response)
101    assert 'nonce-' not in response[HEADER]
102
103
104def test_nonce_regenerated_on_new_request():
105    request1 = rf.get('/')
106    request2 = rf.get('/')
107    mw.process_request(request1)
108    mw.process_request(request2)
109    nonce1 = str(request1.csp_nonce)
110    nonce2 = str(request2.csp_nonce)
111    assert request1.csp_nonce != request2.csp_nonce
112
113    response1 = HttpResponse()
114    response2 = HttpResponse()
115    mw.process_response(request1, response1)
116    mw.process_response(request2, response2)
117    assert nonce1 not in response2[HEADER]
118    assert nonce2 not in response1[HEADER]
119
120
121@override_settings(CSP_INCLUDE_NONCE_IN=[])
122def test_no_nonce_when_disabled_by_settings():
123    request = rf.get('/')
124    mw.process_request(request)
125    nonce = str(request.csp_nonce)
126    response = HttpResponse()
127    mw.process_response(request, response)
128    assert nonce not in response[HEADER]
129