Conversation
…unce fetched from GitHub
App.js rendered the announce — Markdown fetched from the upstream
repository's ANNOUNCE.md through raw.githubusercontent.com — with
`dangerouslySetInnerHTML={{__html: marked(...)}}`, through marked 1.x
(end of life, known XSS issues before 2.0) and with no sanitization.
Whoever could change that file, or sit between the user and GitHub,
could run script in every STUdio user's browser. Four more sinks
injected HTML from the translation bundles the same way.
All raw HTML now goes through one helper, utils/html.js:
renderMarkdown() for the announce, sanitizeHtml() for the translated
dialogs. Both run DOMPurify, which keeps the markup the dialogs rely on
(paragraphs, lists, emphasis, links, the glyphicon spans) and drops
scripts, event handlers and javascript: URLs; form controls are
forbidden on top, since a form in a dialog fetched from the network is
a phishing prompt rather than content.
marked goes to 4.3.0, the last line that still supports the Node 12 CI
uses. It is imported by its UMD build: its `main` is a .cjs file that
the jest of react-scripts 3 hands to its catch-all file transform, and
that config cannot be overridden without ejecting.
html.test.js pins both directions: XSS payloads are removed, and the
markup the announce and the translations actually use survives.
Closes #12
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
App.jsrendered the announce — Markdown fetched from the upstream repository'sANNOUNCE.mdthrough raw.githubusercontent.com — withdangerouslySetInnerHTML={{__html: marked(...)}}, throughmarked@1.x(EOL, known XSS issues before 2.0) and with no sanitization. Whoever could change that file, or sit between the user and GitHub, could run script in every STUdio user's browser. Four more sinks (PackLibrary.js×2,PackDiagramWidget.js×2) injected HTML from the translation bundles the same way.All raw HTML now goes through one helper,
src/utils/html.js:renderMarkdown(md)— the announcesanitizeHtml(html)— the translated dialogsBoth run DOMPurify. It keeps the markup the dialogs rely on (
<p>,<ul>/<li>,<strong>,<em>, links, the<span class="glyphicon …">of the help pages — inventoried from the locale files) and drops scripts, event handlers andjavascript:URLs. Form controls are forbidden on top: a form in a dialog fetched from the network is a phishing prompt, not content.Dependencies
marked^1.0.0→^4.3.0, the last line that still supports Node 12 (what CI andweb-ui/pom.xmlpin).dompurify^3.4.15added (no engine constraint; browser code).yarn.lockdiff is exactly those entries. Lockfile regenerated with yarn 1.22 — no collateral churn.One wart, documented in the code: marked is imported as
marked/lib/marked.umd.jsrather thanmarked. marked 4'smainis a.cjsfile, and the jest of react-scripts 3 hands anything that isn't.js/.jsx/.ts/.tsx/.css/.jsonto its catch-all file transform, which turns it into a filename string; the fix (moduleNameMapper/transform) is one of the options CRA 3.0.1 refuses to let you override without ejecting. The UMD file is the same source, plain.js, and loads under both webpack 4 and jest 24. It goes away with a react-scripts upgrade, which is out of scope here.Tests
html.test.js(9 tests) pins both directions — a sanitizer that also stripped the dialogs' markup would pass every "no script" assertion while breaking the help pages:<script>,onerror/onclick,javascript:(Markdown link and HTML),<iframe>,<form>/<input>are removednull/undefinedrender as empty rather than throwingLocally:
yarn test→ 66 passed (57 + 9);yarn buildcompiles; Java suite untouched and green.git diff --exit-codeclean.Tracked in lgnap#12.
🤖 Generated with Claude Code