Fix #014: sanitize themed text kinds + script-blocking CSP (stored XSS)
markdown/deck/html_fragment rendered author-influenced content verbatim,
so <script>/<img onerror>/etc. executed on the Daybook origin — self-XSS
against the owner's session, and (post-sharing) unauthenticated via /s/.
Two layers, both in artifacts/views.py:
- Create-time allowlist sanitization via nh3 (maintained ammonia/Rust
sanitizer, not deprecated bleach). Keeps formatting/tables/links/images
(incl. relative /assets URLs) and codehilite markup; drops script, on*
handlers, style, unknown tags, and javascript:/data: URLs. html_full is
deliberately NOT sanitized (documented verbatim escape hatch).
- Serve-time CSP (script-src 'none') for the sanitized kinds, on both
/a/{id} and the unauthenticated /s/{token} route. Being a response
header, it also neutralizes scripts in artifacts stored before this fix.
Not applied to html_full or json/csv (json/csv data is already escaped;
their viewers need their own JS).
Verified live across all kinds; 7 regression tests added. 102 tests pass.
Not addressed (noted in the issue): CSV formula injection,
CSRF_COOKIE_HTTPONLY (moot with XSS closed), nonce CSP for json/csv/full.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
6d2fe2147c
commit
e3c5773e8f
6 changed files with 196 additions and 19 deletions
24
docs/api.md
24
docs/api.md
|
|
@ -28,10 +28,10 @@ Currently supported kinds:
|
|||
|
||||
| Kind | Upload type | Rendered behavior | Raw support |
|
||||
|---|---|---|---|
|
||||
| `html_fragment` | JSON text | Wrapped with a Daybook theme; tables styled automatically | No |
|
||||
| `html_full` | JSON text | Stored as complete HTML, verbatim — no theme or styling injected | Yes |
|
||||
| `markdown` | JSON text | Rendered to HTML (code blocks syntax-highlighted, tables styled) and original `.md` preserved | Yes |
|
||||
| `deck` | JSON text | Wrapped as a simple HTML slide/deck page | No |
|
||||
| `html_fragment` | JSON text | Wrapped with a Daybook theme; tables styled automatically; **HTML sanitized** | No |
|
||||
| `html_full` | JSON text | Stored as complete HTML, verbatim — no theme, styling, or sanitization | Yes |
|
||||
| `markdown` | JSON text | Rendered to HTML (code blocks syntax-highlighted, tables styled), **sanitized**, original `.md` preserved | Yes |
|
||||
| `deck` | JSON text | Wrapped as a simple HTML slide/deck page; **HTML sanitized** | No |
|
||||
| `image` | Multipart file | Browser image preview | No |
|
||||
| `pdf` | Multipart file | Browser PDF preview | No |
|
||||
| `binary` | Multipart file | Stored and downloadable | No |
|
||||
|
|
@ -42,6 +42,22 @@ Currently supported kinds:
|
|||
though the value itself is JSON/CSV text — i.e. `{"kind": "json", "content":
|
||||
"{\"a\": 1}"}`, not a nested JSON object in the request body.
|
||||
|
||||
### HTML sanitization
|
||||
|
||||
The "themed" kinds — `markdown`, `html_fragment`, `deck` — have their
|
||||
rendered HTML run through an allowlist sanitizer (nh3) before storage.
|
||||
Ordinary formatting, tables, links, images (including relative
|
||||
`/assets/<id>` URLs), and code-highlight markup are preserved; `<script>`,
|
||||
event handlers (`on*`), `<style>`, `javascript:`/`data:` URLs, and unknown
|
||||
tags are dropped. These kinds are also served with a `script-src 'none'`
|
||||
Content-Security-Policy. As a side effect, sanitized output is always
|
||||
well-formed HTML.
|
||||
|
||||
`html_full` is the deliberate exception: it is stored and served **exactly**
|
||||
as posted, with no sanitization and no restrictive CSP. It is the
|
||||
"poster owns all presentation, including any scripts and the security
|
||||
implications" escape hatch — only post trusted content as `html_full`.
|
||||
|
||||
## Source Fields
|
||||
|
||||
Every artifact can identify who or what produced it. `source` is a free-text label —
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue