Sprint Item
#2 of 8 — Medium severity — after #1 (Monday PR 2 of 3)
Block Relations
|
|
| Blocked by |
#1 — SRI must pin cdnjs before CSP allow-lists it |
| Blocks |
#3 (soft) — XSS test should assert the full SRI + CSP + DOMPurify stack |
| Repo/day |
Independent of #4–#8 |
Calendar Day
Monday, July 20, 2026 (PR 2 of 3)
Planned Effort
3 story points (Medium) — sprint item #2
Problem
The app serves rendered HTML with no Content-Security-Policy header. app.py has no after_request hook, so there is no defense-in-depth layer behind DOMPurify: if a sanitizer bypass ever landed, nothing at the browser policy layer would contain it.
Goal
One PR adding a single @app.after_request hook that sets a restrictive CSP on served HTML pages, permitting the app's inline bootstrap via nonce/hash (not blanket unsafe-inline) and allowing the SRI-pinned cdnjs origin.
Scope
Part A — CSP header
Part B — inline handling (no blanket unsafe-inline)
- Permit the inline theme-bootstrap script and handler via a per-response nonce (or hash):
- inline
<script> at base.html:9-16
- inline
onclick at base.html:43
- inline SVG
- Prefer nonce injection into the template; refactor inline handlers out where cheaper than nonce-ing them.
Part C — test
- A test asserts the
Content-Security-Policy header is present on a page route (and, ideally, absent/irrelevant on a JSON API route).
Out of scope
Acceptance Criteria
Verification
cd C:\Users\Jasen\CppAliance\cppa-cursor-browser
.\.venv\Scripts\Activate.ps1
pytest -q -k "csp or header"
python app.py
curl -sI http://127.0.0.1:<port>/ → Content-Security-Policy present.
- Browser DevTools: no CSP violation reports on normal use; highlight.js/Marked/DOMPurify load (allowed origin); inline bootstrap runs (nonce).
- A JSON API response does not carry the page CSP.
Sprint Item
#2 of 8 — Medium severity — after #1 (Monday PR 2 of 3)
Block Relations
Calendar Day
Monday, July 20, 2026 (PR 2 of 3)
Planned Effort
3 story points (Medium) — sprint item #2
Problem
The app serves rendered HTML with no
Content-Security-Policyheader.app.pyhas noafter_requesthook, so there is no defense-in-depth layer behind DOMPurify: if a sanitizer bypass ever landed, nothing at the browser policy layer would contain it.Goal
One PR adding a single
@app.after_requesthook that sets a restrictive CSP on served HTML pages, permitting the app's inline bootstrap via nonce/hash (not blanketunsafe-inline) and allowing the SRI-pinned cdnjs origin.Scope
Part A — CSP header
@app.after_requestincreate_app(app.py:39-108).default-srcrestrictive;script-srcandstyle-srcallow'self'andhttps://cdnjs.cloudflare.com(the SRI-pinned origin from item Add an optional parameter to disable rendering/exporting sensitive projects/chats #1).Part B — inline handling (no blanket
unsafe-inline)<script>atbase.html:9-16onclickatbase.html:43Part C — test
Content-Security-Policyheader is present on a page route (and, ideally, absent/irrelevant on a JSON API route).Out of scope
fix/highlightjs-sri-pin) — depends on it for the cdnjs allow-listing.test/browser-xss-execution) — it may additionally assert the CSP header exists, but owns no CSP logic.Acceptance Criteria
Content-Security-Policyheader.default-srcis restrictive;script-src/style-srcallow'self'+https://cdnjs.cloudflare.com.unsafe-inline, or refactored out.Verification
curl -sI http://127.0.0.1:<port>/→Content-Security-Policypresent.