Skip to content

Commit a346647

Browse files
author
Rob Hudson
committed
Add constants for CSP keywords
This helps avoid potential errors introduced by incorrectly quoting CSP keywords.
1 parent 3413de3 commit a346647

File tree

6 files changed

+57
-21
lines changed

6 files changed

+57
-21
lines changed

csp/constants.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,12 @@
11
HEADER = "Content-Security-Policy"
22
HEADER_REPORT_ONLY = "Content-Security-Policy-Report-Only"
3+
4+
NONE = "'none'"
5+
REPORT_SAMPLE = "'report-sample'"
6+
SELF = "'self'"
7+
STRICT_DYNAMIC = "'strict-dynamic'"
8+
UNSAFE_ALLOW_REDIRECTS = "'unsafe-allow-redirects'"
9+
UNSAFE_EVAL = "'unsafe-eval'"
10+
UNSAFE_HASHES = "'unsafe-hashes'"
11+
UNSAFE_INLINE = "'unsafe-inline'"
12+
WASM_UNSAFE_EVAL = "'wasm-unsafe-eval'"

csp/tests/test_middleware.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from django.test import RequestFactory
77
from django.test.utils import override_settings
88

9-
from csp.constants import HEADER, HEADER_REPORT_ONLY
9+
from csp.constants import HEADER, HEADER_REPORT_ONLY, SELF
1010
from csp.middleware import CSPMiddleware
1111
from csp.tests.utils import response
1212

@@ -23,7 +23,7 @@ def test_add_header():
2323

2424
@override_settings(
2525
CONTENT_SECURITY_POLICY={"DIRECTIVES": {"default-src": ["example.com"]}},
26-
CONTENT_SECURITY_POLICY_REPORT_ONLY={"DIRECTIVES": {"default-src": ["'self'"]}},
26+
CONTENT_SECURITY_POLICY_REPORT_ONLY={"DIRECTIVES": {"default-src": [SELF]}},
2727
)
2828
def test_both_headers():
2929
request = rf.get("/")
@@ -51,7 +51,7 @@ def text_exclude():
5151

5252
@override_settings(
5353
CONTENT_SECURITY_POLICY=None,
54-
CONTENT_SECURITY_POLICY_REPORT_ONLY={"DIRECTIVES": {"default-src": ["'self'"]}},
54+
CONTENT_SECURITY_POLICY_REPORT_ONLY={"DIRECTIVES": {"default-src": [SELF]}},
5555
)
5656
def test_report_only():
5757
request = rf.get("/")

csp/tests/test_utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from django.test.utils import override_settings
22
from django.utils.functional import lazy
33

4+
from csp.constants import NONE, SELF
45
from csp.utils import build_policy, default_config, DEFAULT_DIRECTIVES
56

67

@@ -182,7 +183,7 @@ def test_replace_missing_setting():
182183

183184

184185
def test_config():
185-
policy = build_policy(config={"default-src": ["'none'"], "img-src": ["'self'"]})
186+
policy = build_policy(config={"default-src": [NONE], "img-src": [SELF]})
186187
policy_eq("default-src 'none'; img-src 'self'", policy)
187188

188189

csp/utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
from django.conf import settings
77
from django.utils.encoding import force_str
88

9+
from csp.constants import SELF
910

1011
DEFAULT_DIRECTIVES = {
1112
# Fetch Directives
1213
"child-src": None,
1314
"connect-src": None,
14-
"default-src": ["'self'"],
15+
"default-src": [SELF],
1516
"script-src": None,
1617
"script-src-attr": None,
1718
"script-src-elem": None,

docs/configuration.rst

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,31 +43,51 @@ a more slightly strict policy and is used to test the policy without breaking th
4343

4444
.. code-block:: python
4545
46+
from csp.constants import NONE, SELF
47+
4648
CONTENT_SECURITY_POLICY = {
4749
"EXCLUDE_URL_PREFIXES": ["/excluded-path/"],
4850
"DIRECTIVES": {
49-
"default-src": ["'self'", "cdn.example.net"],
50-
"frame-ancestors": ["'self'"],
51-
"form-action": ["'self'"],
51+
"default-src": [SELF, "cdn.example.net"],
52+
"frame-ancestors": [SELF],
53+
"form-action": [SELF],
5254
"report-uri": "/csp-report/",
5355
},
5456
}
5557
5658
CONTENT_SECURITY_POLICY_REPORT_ONLY = {
5759
"EXCLUDE_URL_PREFIXES": ["/excluded-path/"],
5860
"DIRECTIVES": {
59-
"default-src": ["'none'"],
60-
"connect-src": ["'self'"],
61-
"img-src": ["'self'"],
62-
"form-action": ["'self'"],
63-
"frame-ancestors": ["'self'"],
64-
"script-src": ["'self'"],
65-
"style-src": ["'self'"],
61+
"default-src": [NONE],
62+
"connect-src": [SELF],
63+
"img-src": [SELF],
64+
"form-action": [SELF],
65+
"frame-ancestors": [SELF],
66+
"script-src": [SELF],
67+
"style-src": [SELF],
6668
"upgrade-insecure-requests": True,
6769
"report-uri": "/csp-report/",
6870
},
6971
}
7072
73+
.. note::
74+
75+
In the above example, the constant ``NONE`` is converted to the CSP keyword ``"'none'"`` and
76+
is distinct from Python's ``None`` value. The CSP keyword ``'none'`` is a special value that
77+
signifies that you do not want any sources for this directive. The ``None`` value is a
78+
Python keyword that represents the absence of a value and when used as the value of a directive,
79+
it will remove the directive from the policy, e.g. the following will remove the
80+
``frame-ancestors`` directive from the policy:
81+
82+
.. code-block:: python
83+
84+
CONTENT_SECURITY_POLICY = {
85+
"DIRECTIVES": {
86+
# ...
87+
"frame-ancestors": None,
88+
}
89+
}
90+
7191
7292
Policy Settings
7393
===============
@@ -101,8 +121,11 @@ policy.
101121

102122
.. note::
103123
The "special" source values of ``'self'``, ``'unsafe-inline'``, ``'unsafe-eval'``,
104-
``'none'`` and hash-source (``'sha256-...'``) must be quoted!
105-
e.g.: ``"default-src": ["'self'"]``. Without quotes they will not work as intended.
124+
``'strict-dynamic'``, ``'none'``, etc. must be quoted! e.g.: ``"default-src": ["'self'"]``.
125+
Without quotes they will not work as intended.
126+
127+
Consider using the ``csp.constants`` module to get these values to help avoiding quoting
128+
errors or typos, e.g., ``from csp.constants import SELF, STRICT_DYNAMIC``.
106129

107130
.. note::
108131
Deprecated features of CSP in general have been moved to the bottom of this list.

docs/decorators.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,18 +99,19 @@ If you need to set the entire policy on a view, ignoring all the settings, you c
9999
decorator. This, and the other decorators, can be stacked to update both policies if both are in
100100
use, as shown below. The arguments and values are as above::
101101

102+
from csp.constants import SELF, UNSAFE_INLINE
102103
from csp.decorators import csp
103104

104105
@csp({
105-
"default_src": ["'self'"],
106+
"default_src": [SELF],
106107
"img-src": ["imgsrv.com"],
107-
"script-src": ["scriptsrv.com", "googleanalytics.com", "'unsafe-inline'"]}
108+
"script-src": ["scriptsrv.com", "googleanalytics.com", UNSAFE_INLINE]}
108109
})
109110
@csp({
110-
"default_src": ["'self'"],
111+
"default_src": [SELF],
111112
"img-src": ["imgsrv.com"],
112113
"script-src": ["scriptsrv.com", "googleanalytics.com"]},
113-
"frame-src": ["'self'"],
114+
"frame-src": [SELF],
114115
REPORT_ONLY=True
115116
})
116117
def myview(request):

0 commit comments

Comments
 (0)