# Security
## Reporting a vulnerability
Please report security issues privately via
**GitHub Security Advisories** (Security tab → "Report a vulnerability") on
this repository. If that is not possible, open an issue asking for a private
contact channel — do not include exploit details in a public issue.
You can expect an acknowledgement within a week. Fixes ship as a patch release
with credit unless you prefer otherwise.
## Threat model
mdreader renders markdown files that arrive from repositories, downloads, LLM
output, and email attachments. **Every document is treated as untrusted
input.** The interesting attacker is a document author trying to execute code,
exfiltrate data, or mislead the reader — not a local attacker with the user's
privileges.
### Defenses, in layers
1. **Raw HTML is escaped by default.** The default Markdig pipeline renders
raw HTML in documents as visible text. Rendering raw HTML is a per-document
opt-in — and the sanitizer below still runs even then.
2. **Allowlist sanitization.** All rendered HTML passes through
[HtmlSanitizer](https://github.com/mganss/HtmlSanitizer) configured with an
explicit allowlist of tags and attributes
([MarkdownSanitizer.cs](src/MdReader.Core/MarkdownSanitizer.cs)). `script`,
`iframe`, `object`, `embed`, `form`, `style`, `link`, `meta`, `base`, event
handlers (`on*`), and `javascript:`/`vbscript:`/`data:` URIs are stripped.
The only `` allowed to survive is a disabled task-list checkbox.
3. **Content Security Policy.** The reader page runs with
`default-src 'none'; script-src 'self'; connect-src 'none'` (see
[reader.html](src/MdReader.Web/reader.html) for the documented, minimal
relaxations). Document content cannot run script or phone home even if
something got past the sanitizer.
4. **Remote resources blocked by default.** Remote images are a read-receipt/
tracking vector; they render as placeholders until the user opts in (per
document or globally). The opt-in swaps to a page whose CSP admits
`img-src http: https:` — scripts stay impossible.
5. **Navigation interception.** The WebView never navigates away from the
app's own pages. Link clicks are routed to the host: `http`/`https`/
`mailto` open in the default browser, in-document anchors scroll, and every
other scheme (`file:`, `ms-*`, custom protocols) is refused with a visible
notice.
6. **Path containment.** Relative image paths resolve against the document's
directory and are refused if they climb more than 3 parent levels
(configurable). Parent-traversal counting is explicit — `Path.GetFullPath`'s
silent clamping at the drive root is not trusted. Local content is served
through read-only WebView2 virtual host mappings scoped to the app's asset
folder and the document's root — never the whole filesystem.
7. **WebView2 hardening.** DevTools disabled in release builds, autofill and
password saving disabled, browser accelerator keys disabled, new-window
requests suppressed, host objects disallowed.
8. **Mermaid** runs with `securityLevel: 'strict'` and `htmlLabels: false`.
9. **No elevation.** The app never requests administrator rights; the
installer defaults to per-user. File-type registration is additive
(`OpenWithProgids`) and never writes the hash-protected `UserChoice` key.
### Known accepted risks
- The sanitizer and CSP defend the renderer; they do not stop a document from
*displaying* misleading text. Phishing-style content is out of scope.
- `mdreader-convert` output opens in the user's browser without mdreader's
navigation interception; it carries a strict CSP instead.
## Verification
The test suite includes an XSS corpus ([fixtures/xss/](fixtures/xss/)) covering
the standard markdown vectors — `javascript:` links, `onerror` images, raw
`