Skip to content

Commit d0e891b

Browse files
author
Rob Hudson
committed
Prep for 4.0 release
1 parent 9d06e25 commit d0e891b

File tree

2 files changed

+161
-1
lines changed

2 files changed

+161
-1
lines changed

CHANGES.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,166 @@
11
CHANGES
22
=======
33

4+
4.0
5+
===
6+
7+
This release contains several breaking changes. For a complete migration guide, see:
8+
https://django-csp.readthedocs.io/en/latest/migration-guide.html
9+
10+
## Breaking Changes
11+
12+
- **Configuration Format**: Moved to dict-based configuration which allows for setting policies for
13+
both enforced and report-only. Instead of using individual settings with `CSP_` prefixes, you now use
14+
dictionaries called `CONTENT_SECURITY_POLICY` and/or `CONTENT_SECURITY_POLICY_REPORT_ONLY`.
15+
16+
You can use Django's check command to automatically identify existing CSP settings and generate a
17+
template for the new configuration format:
18+
19+
```
20+
python manage.py check
21+
```
22+
23+
This will detect your old `CSP_` prefixed settings and output a draft of the new dict-based
24+
configuration, giving you a starting point for migration.
25+
26+
**Example:**
27+
28+
Change from:
29+
```python
30+
CSP_DEFAULT_SRC = ["'self'", "*.example.com"]
31+
CSP_SCRIPT_SRC = ["'self'", "js.cdn.com/example/"]
32+
CSP_IMG_SRC = ["'self'", "data:", "example.com"]
33+
CSP_EXCLUDE_URL_PREFIXES = ["/admin"]
34+
```
35+
36+
to:
37+
```python
38+
from csp.constants import SELF
39+
40+
CONTENT_SECURITY_POLICY = {
41+
"EXCLUDE_URL_PREFIXES": ["/admin"],
42+
"DIRECTIVES": {
43+
"default-src": [SELF, "*.example.com"],
44+
"script-src": [SELF, "js.cdn.com/example/"],
45+
"img-src": [SELF, "data:", "example.com"],
46+
},
47+
}
48+
```
49+
50+
- **Nonce Configuration**: Switched from specifying directives that should contain nonces as a
51+
separate list to using a sentinel `NONCE` value in the directive itself.
52+
53+
**Example:**
54+
55+
Change from:
56+
```python
57+
CSP_INCLUDE_NONCE_IN = ['script-src', 'style-src']
58+
```
59+
60+
to:
61+
```python
62+
from csp.constants import NONCE, SELF
63+
64+
CONTENT_SECURITY_POLICY = {
65+
"DIRECTIVES": {
66+
"script-src": [SELF, NONCE],
67+
"style-src": [SELF, NONCE],
68+
}
69+
}
70+
```
71+
72+
- **Nonce Behavior**: Changed how `request.csp_nonce` works - it is now Falsy
73+
(`bool(request.csp_nonce)`) until it is read as a string (e.g., used in a template or with
74+
`str(request.csp_nonce)`). Previously, it always tested as `True`, and testing generated the nonce.
75+
76+
**Before:**
77+
```python
78+
# The nonce was generated when this was evaluated
79+
if request.csp_nonce:
80+
# Do something with nonce
81+
```
82+
83+
**After:**
84+
```python
85+
# This won't generate the nonce, and will evaluate to False until nonce is read as a string
86+
if request.csp_nonce:
87+
# This code won't run until nonce is used as a string
88+
89+
# To generate and use the nonce
90+
nonce_value = str(request.csp_nonce)
91+
```
92+
93+
- Dropped support for Django ≤3.2.
94+
- Dropped support for Python 3.8.
95+
96+
## New Features and Improvements
97+
98+
- **Dual Policy Support**: Added support for enforced and report-only policies simultaneously using
99+
the separate `CONTENT_SECURITY_POLICY` and `CONTENT_SECURITY_POLICY_REPORT_ONLY` settings.
100+
101+
**Example:**
102+
```python
103+
from csp.constants import NONE, SELF
104+
105+
# Enforced policy
106+
CONTENT_SECURITY_POLICY = {
107+
"DIRECTIVES": {
108+
"default-src": [SELF, "cdn.example.net"],
109+
"frame-ancestors": [SELF],
110+
},
111+
}
112+
113+
# Report-only policy (stricter for testing)
114+
CONTENT_SECURITY_POLICY_REPORT_ONLY = {
115+
"DIRECTIVES": {
116+
"default-src": [NONE],
117+
"script-src": [SELF],
118+
"style-src": [SELF],
119+
"report-uri": "/csp-report/",
120+
},
121+
}
122+
```
123+
124+
- **CSP Constants**: Added CSP keyword constants in `csp.constants` (e.g., `SELF` instead of
125+
`"'self'"`) to minimize quoting mistakes and typos.
126+
127+
**Example:**
128+
129+
Change from:
130+
```python
131+
CSP_DEFAULT_SRC = ["'self'", "'none'"]
132+
```
133+
134+
to:
135+
```python
136+
from csp.constants import SELF, NONE
137+
138+
CONTENT_SECURITY_POLICY = {
139+
"DIRECTIVES": {
140+
"default-src": [SELF, NONE], # No need to worry about quoting
141+
}
142+
}
143+
```
144+
145+
- Added comprehensive type hints.
146+
- Added `EXCLUDE_URL_PREFIXES` check.
147+
- Added support for CSP configuration as sets.
148+
- Changed `REPORT_PERCENTAGE` to allow floats (e.g., for values < 1%) and improved behavior for 100%
149+
report percentage to always send CSP reports.
150+
- Added ability to read the nonce after response if it was included in the header. This will raise
151+
an error when nonce is accessed after response if not already generated.
152+
- Made changes to simplify middleware logic and make `CSPMiddleware` easier to subclass. The updated
153+
middleware returns a PolicyParts dataclass that can be modified before the policy is built.
154+
155+
## Other Changes
156+
157+
- Added Python 3.13 support.
158+
- Added support for Django 5.1 and 5.2.
159+
- Documentation improvements including fixed trusted_types links and clarification on NONE vs Python's None.
160+
- Documentation note that reporting percentage needs rate limiting middleware.
161+
- Expanded ruff configuration and moved into pyproject.toml.
162+
163+
4164
4.0b7
5165
=====
6166
- Removed ``CSPMiddlewareAlwaysGenerateNonce`` middleware that forced nonce headers when not used in

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ requires = [ "setuptools>=61.2" ]
44

55
[project]
66
name = "django-csp"
7-
version = "4.0b7"
7+
version = "4.0"
88
description = "Django Content Security Policy support."
99
readme = "README.rst"
1010
license = { text = "BSD" }

0 commit comments

Comments
 (0)