# 014 — Stored XSS: markdown (and deck/html_fragment) render unsanitized script; no CSP; now reachable unauthenticated via public share links **Status:** Fixed **Severity:** High — stored XSS executing on the Daybook origin, with a concrete escalation chain against the owner's session, and (since the sharing feature shipped in `91ea19d`) reachable by unauthenticated third parties. Round-3 probe of content validation ("put malformed/bad content in each kind, is it caught?"). Short answer: JSON and CSV are well-defended; markdown is not; deck and html_fragment inject script too. ## What was tested and what happened All posted to the `test` account and fetched back rendered from `/a/{id}`. | Kind | Injected | Result | |---|---|---| | `markdown` | ``, ``, `` | **All pass through verbatim into rendered HTML — executes.** | | `deck` | `
…` | **Script passes through into the themed deck page — executes.** | | `html_fragment` | `

hi

` | **Script/`svg onload` pass through into the themed page — executes.** (The #004 content-shape check blocks `//` but never `"}` | **Defended.** `escape_script_body` (`re.compile(r"` data block; renderjson shows it as inert text. Case-variation bypass (``) correctly caught. | | `csv` | `` in a cell | **Defended.** Same escaping via `json.dumps` + `escape_script_body`. | | `csv` | `=cmd|/c calc`, `+SUM(...)` as cell values | Passes through into the `?raw=1` sidecar verbatim (classic CSV/formula injection). Low severity / debatable — see below. | ## Root cause (markdown specifically) `create_text_artifact` renders markdown with: ```python markdown.markdown(content, extensions=["extra", "sane_lists", "codehilite"], ...) ``` Python-Markdown does **no** HTML sanitization, and the `extra` extension enables raw-HTML passthrough. Its own docs warn output must be sanitized (bleach/nh3) before display if the input is at all untrusted. The rendered HTML is then written to the artifact's `.html` file and served as-is. Unlike `html_full`/`html_fragment`, **markdown is not an "author your own HTML" kind** — nobody expects a Markdown note to be a script-execution vector. In an agent-driven system this is exactly the kind most likely to carry *indirectly* untrusted content: an agent that summarizes a scraped page, an email, or an ingested document can emit a summary containing an attacker-controlled `