Skip to content

Commit

Permalink
Add constants for CSP keywords
Browse files Browse the repository at this point in the history
This helps avoid potential errors introduced by incorrectly quoting CSP keywords.
  • Loading branch information
robhudson committed Jun 6, 2024
1 parent 3413de3 commit a346647
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 21 deletions.
10 changes: 10 additions & 0 deletions csp/constants.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
HEADER = "Content-Security-Policy"
HEADER_REPORT_ONLY = "Content-Security-Policy-Report-Only"

NONE = "'none'"
REPORT_SAMPLE = "'report-sample'"
SELF = "'self'"
STRICT_DYNAMIC = "'strict-dynamic'"
UNSAFE_ALLOW_REDIRECTS = "'unsafe-allow-redirects'"
UNSAFE_EVAL = "'unsafe-eval'"
UNSAFE_HASHES = "'unsafe-hashes'"
UNSAFE_INLINE = "'unsafe-inline'"
WASM_UNSAFE_EVAL = "'wasm-unsafe-eval'"
6 changes: 3 additions & 3 deletions csp/tests/test_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.test import RequestFactory
from django.test.utils import override_settings

from csp.constants import HEADER, HEADER_REPORT_ONLY
from csp.constants import HEADER, HEADER_REPORT_ONLY, SELF
from csp.middleware import CSPMiddleware
from csp.tests.utils import response

Expand All @@ -23,7 +23,7 @@ def test_add_header():

@override_settings(
CONTENT_SECURITY_POLICY={"DIRECTIVES": {"default-src": ["example.com"]}},
CONTENT_SECURITY_POLICY_REPORT_ONLY={"DIRECTIVES": {"default-src": ["'self'"]}},
CONTENT_SECURITY_POLICY_REPORT_ONLY={"DIRECTIVES": {"default-src": [SELF]}},
)
def test_both_headers():
request = rf.get("/")
Expand Down Expand Up @@ -51,7 +51,7 @@ def text_exclude():

@override_settings(
CONTENT_SECURITY_POLICY=None,
CONTENT_SECURITY_POLICY_REPORT_ONLY={"DIRECTIVES": {"default-src": ["'self'"]}},
CONTENT_SECURITY_POLICY_REPORT_ONLY={"DIRECTIVES": {"default-src": [SELF]}},
)
def test_report_only():
request = rf.get("/")
Expand Down
3 changes: 2 additions & 1 deletion csp/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.test.utils import override_settings
from django.utils.functional import lazy

from csp.constants import NONE, SELF
from csp.utils import build_policy, default_config, DEFAULT_DIRECTIVES


Expand Down Expand Up @@ -182,7 +183,7 @@ def test_replace_missing_setting():


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


Expand Down
3 changes: 2 additions & 1 deletion csp/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
from django.conf import settings
from django.utils.encoding import force_str

from csp.constants import SELF

DEFAULT_DIRECTIVES = {
# Fetch Directives
"child-src": None,
"connect-src": None,
"default-src": ["'self'"],
"default-src": [SELF],
"script-src": None,
"script-src-attr": None,
"script-src-elem": None,
Expand Down
47 changes: 35 additions & 12 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,31 +43,51 @@ a more slightly strict policy and is used to test the policy without breaking th

.. code-block:: python
from csp.constants import NONE, SELF
CONTENT_SECURITY_POLICY = {
"EXCLUDE_URL_PREFIXES": ["/excluded-path/"],
"DIRECTIVES": {
"default-src": ["'self'", "cdn.example.net"],
"frame-ancestors": ["'self'"],
"form-action": ["'self'"],
"default-src": [SELF, "cdn.example.net"],
"frame-ancestors": [SELF],
"form-action": [SELF],
"report-uri": "/csp-report/",
},
}
CONTENT_SECURITY_POLICY_REPORT_ONLY = {
"EXCLUDE_URL_PREFIXES": ["/excluded-path/"],
"DIRECTIVES": {
"default-src": ["'none'"],
"connect-src": ["'self'"],
"img-src": ["'self'"],
"form-action": ["'self'"],
"frame-ancestors": ["'self'"],
"script-src": ["'self'"],
"style-src": ["'self'"],
"default-src": [NONE],
"connect-src": [SELF],
"img-src": [SELF],
"form-action": [SELF],
"frame-ancestors": [SELF],
"script-src": [SELF],
"style-src": [SELF],
"upgrade-insecure-requests": True,
"report-uri": "/csp-report/",
},
}
.. note::

In the above example, the constant ``NONE`` is converted to the CSP keyword ``"'none'"`` and
is distinct from Python's ``None`` value. The CSP keyword ``'none'`` is a special value that
signifies that you do not want any sources for this directive. The ``None`` value is a
Python keyword that represents the absence of a value and when used as the value of a directive,
it will remove the directive from the policy, e.g. the following will remove the
``frame-ancestors`` directive from the policy:

.. code-block:: python
CONTENT_SECURITY_POLICY = {
"DIRECTIVES": {
# ...
"frame-ancestors": None,
}
}
Policy Settings
===============
Expand Down Expand Up @@ -101,8 +121,11 @@ policy.

.. note::
The "special" source values of ``'self'``, ``'unsafe-inline'``, ``'unsafe-eval'``,
``'none'`` and hash-source (``'sha256-...'``) must be quoted!
e.g.: ``"default-src": ["'self'"]``. Without quotes they will not work as intended.
``'strict-dynamic'``, ``'none'``, etc. must be quoted! e.g.: ``"default-src": ["'self'"]``.
Without quotes they will not work as intended.

Consider using the ``csp.constants`` module to get these values to help avoiding quoting
errors or typos, e.g., ``from csp.constants import SELF, STRICT_DYNAMIC``.

.. note::
Deprecated features of CSP in general have been moved to the bottom of this list.
Expand Down
9 changes: 5 additions & 4 deletions docs/decorators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,18 +99,19 @@ If you need to set the entire policy on a view, ignoring all the settings, you c
decorator. This, and the other decorators, can be stacked to update both policies if both are in
use, as shown below. The arguments and values are as above::

from csp.constants import SELF, UNSAFE_INLINE
from csp.decorators import csp

@csp({
"default_src": ["'self'"],
"default_src": [SELF],
"img-src": ["imgsrv.com"],
"script-src": ["scriptsrv.com", "googleanalytics.com", "'unsafe-inline'"]}
"script-src": ["scriptsrv.com", "googleanalytics.com", UNSAFE_INLINE]}
})
@csp({
"default_src": ["'self'"],
"default_src": [SELF],
"img-src": ["imgsrv.com"],
"script-src": ["scriptsrv.com", "googleanalytics.com"]},
"frame-src": ["'self'"],
"frame-src": [SELF],
REPORT_ONLY=True
})
def myview(request):
Expand Down

0 comments on commit a346647

Please sign in to comment.