Skip to content

Harden imported markup sanitization - #9

Merged
NouraldinFarge merged 1 commit into
mainfrom
agent/harden-imported-markup
Aug 17, 2026
Merged

Harden imported markup sanitization#9
NouraldinFarge merged 1 commit into
mainfrom
agent/harden-imported-markup

Conversation

@NouraldinFarge

Copy link
Copy Markdown
Owner

What changed

  • Added pinned, locally vendored DOMPurify 3.4.13 as the first-stage sanitizer for imported HTML, Markdown, and EPUB markup.
  • Retained Reader's narrower namespace, element, attribute, ID, link, and embedded-image policies after DOMPurify.
  • Replaced recursive entity decoding and script-specific tag regular expressions in plain-text extraction with one-pass entity decoding.
  • Tightened the unused frame capability from frame-src 'self' blob: to frame-src 'none'.
  • Recorded exact upstream provenance, integrity, runtime and license hashes, notices, and the updated SPDX SBOM.
  • Expanded hostile-markup tests through sanitization, serialization, reparsing, live insertion, and persisted IndexedDB reload.

Why

CodeQL reported three high-severity findings on main:

  1. js/xss-through-dom at the raw imported-markup parse boundary.
  2. js/double-escaping in stripMarkup.
  3. js/bad-tag-filter in stripMarkup.

The existing detached parser and strict Reader allowlist already prevented execution in the hostile browser journey, but the raw parse remained a fragile custom security boundary. The new first pass uses an independently maintained sanitizer without weakening Reader's product-specific policy. The text helper now decodes entities exactly once and no longer treats a script-specific regular expression as a sanitizer.

User and developer impact

Authorized HTML, Markdown, and EPUB reading remains offline and preserves headings, document titles, safe internal or external handoff metadata, and byte-verified embedded raster images. Hostile active markup, foreign namespaces, event attributes, remote resources, popup affordances, and clobbering IDs remain blocked. DOMPurify is shipped locally, so the app introduces no runtime network dependency.

Validation

  • Formatting, JavaScript/CSS/Markdown lint, facts, links, vendor hashes, and public-tree scan passed.
  • 40/40 Node tests passed.
  • 21/21 coverage-gate tests passed: 99.19% statements/lines, 89.67% branches, and 100% functions.
  • 7/7 real Chromium journey groups passed with zero serious/critical axe findings.
  • Adversarial mXSS, malformed tag, nested entity, SVG/MathML/template/noscript/comment, live-insertion, dialog/popup/request, and persisted-reload assertions passed.
  • Production and development JavaScript audits reported no known vulnerabilities.
  • Rust format, Clippy with warnings denied, check, and tests passed.
  • Windows-resolved dependency verification and cargo-audit passed with the documented cross-platform warnings only.
  • A local unsigned x64 Reader and NSIS package build passed GUI-subsystem, path-remapping, and private-data checks.

No tag, GitHub Release, supported binary, signing claim, or public artifact is introduced.

@NouraldinFarge
NouraldinFarge marked this pull request as ready for review August 17, 2026 22:55
@NouraldinFarge
NouraldinFarge merged commit 4b607a4 into main Aug 17, 2026
5 checks passed
@NouraldinFarge
NouraldinFarge deleted the agent/harden-imported-markup branch August 17, 2026 22:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant