Skip to content

fix(export): harden HTML export against XSS via adversarial identifiers#48

Merged
gianlucaciocci merged 2 commits into
mainfrom
fix/html-export-xss-42
May 13, 2026
Merged

fix(export): harden HTML export against XSS via adversarial identifiers#48
gianlucaciocci merged 2 commits into
mainfrom
fix/html-export-xss-42

Conversation

@gianlucaciocci

Copy link
Copy Markdown
Owner

Summary

  • Escape <, >, & (and U+2028/U+2029) to \uXXXX in the JSON payload before embedding in HTML — these never legally appear outside string contents in serde_json output, so the escaped output is still valid JSON that round-trips through JSON.parse.
  • Move the payload into a <script type="application/json" id="grafly-data">…</script> data tag, parsed via JSON.parse(document.getElementById('grafly-data').textContent). Defense in depth: the data tag never executes as JS, and the escape would neutralise a </script> breakout even if it did.
  • Applied to all three HTML emitters: artifact, module, and package views.
  • Updated SECURITY.md: removed the Known-limitation entry; added a Threat-surface row describing the mitigation.

Closes #42

Test plan

  • cargo test -p grafly-export — 4 new unit tests cover: </script> breakout neutralisation, ampersand escape, serde JSON round-trip, and end-to-end rendered HTML containing no raw < in the data payload.
  • cargo test --workspace — full suite green.
  • cargo build --release — clean.
  • grafly analyze . smoke test — generated grafly_artifacts.html, grafly_modules.html, grafly_packages.html all contain the new grafly-data data tag.

gianlucaciocci and others added 2 commits May 13, 2026 20:44
…rs (#42)

The JSON payload was embedded inline as `const DATA = {payload}`, which
let a `</script>` substring in any artifact label break out of the
script context. Escape `<`, `>`, `&` (and U+2028/U+2029) to `\uXXXX` in
the JSON, and move the payload into a `<script type="application/json">`
data tag parsed via `JSON.parse(textContent)`.

Closes #42

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves CI lint failures: rustfmt wanted multi-line asserts for the
long error messages, and clippy's items-after-test-module triggered
because the test block sat above `build_package_html`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gianlucaciocci gianlucaciocci merged commit 84c026b into main May 13, 2026
4 checks passed
@gianlucaciocci gianlucaciocci deleted the fix/html-export-xss-42 branch May 13, 2026 19:54
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.

Harden HTML export against XSS via adversarial identifier names

1 participant