Repository navigation
fix(app): escape mermaid content for the script element it lands in - #1836
Open
ethicnology-agent wants to merge 1 commit into
Open
ethicnology-agent wants to merge 1 commit into
ethicnology-agent wants to merge 1 commit into
Conversation
MermaidRenderer encodes the diagram body with JSON.stringify and then interpolates the result into an inline <script> in the WebView document. JSON.stringify is a JavaScript-string encoder; the HTML tokenizer ends a script element at the first case-insensitive `</script`, whatever the JavaScript string state, so a fence containing that sequence closes the element and the remainder is parsed as markup and executed. The comment above the call claimed this encoding prevented exactly that. The content is not the user's own writing: it reaches this component from agent and tool output, from artifacts, and from workstation files rendered in the app. Escape `<` so the closing sequence can never be produced. The value mermaid receives is unchanged, because `<` is `<` inside a JavaScript string literal. U+2028 and U+2029 are escaped alongside it, since JSON permits them raw where JavaScript historically does not. The encoder lives in its own module because the app's vitest project only collects .ts files, and this way the property is asserted directly rather than inferred from the rendered template. The web and desktop path is unaffected: it renders through mermaid's non-overridable strict security level, which runs DOMPurify before returning.
ethicnology-agent
force-pushed
the
fix/app-mermaid-script-element-escaping
branch
from
October 1, 2026 19:02
19550e2 to
0a408c7
Compare
Author
|
Rebased onto current |
This branch has not been deployed
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.
Fixes the first defect reported in #1832.
Problem
MermaidRenderer.tsx:112encodes the diagram body withJSON.stringifyand line 157 interpolates the result into an inline<script>in the WebView document:JSON.stringifyis a JavaScript-string encoder, not an HTML one. The HTML tokenizer ends a script element at the first case-insensitive</script, whatever JavaScript string state it appears in. A fence containing</script>therefore closes the element, and everything after it is parsed as markup and executed inside the WebView. The comment above the call states that this encoding prevents exactly that.This content is not the user's own writing. It reaches the component from agent and tool output (
MessageView.tsx:276), from artifacts, and from workstation files rendered in the app (FileViewPanel.tsx:419), viaparseMarkdownBlock.ts:103. No CSP is set on the document.What this changes
scriptEncoding.ts(new) —jsonForScriptElement, which escapes<so the closing sequence can never be produced. Mermaid receives an identical string, because<is<inside a JavaScript string literal. U+2028 and U+2029 are escaped alongside it, since JSON permits them raw where JavaScript historically does not.MermaidRenderer.tsx— two lines: the import and the call.scriptEncoding.test.ts— the property, asserted directly.The encoder is its own module because the app's vitest project only collects
sources/**/*.{spec,test}.ts, so a.tsxcomponent cannot be covered.linkUtils.ts+linkUtils.test.tsin the same folder already follow this shape.One of the cases asserts the defect itself rather than only the fix, so the reason this module exists stays visible:
tsc --noEmitis clean andsources/components/markdown/passes 15/15.What this deliberately does not change
dangerouslySetInnerHTML, but mermaid 11.12.2 defaults tosecurityLevel: "strict", which is in the non-overridable "secure" list, andrender()runs DOMPurify before returning. I checked this before touching anything, and it is not affected.mermaid@11from jsDelivr on a mutable major-version tag with no integrity attribute and no CSP. That is a separate defect, reported in App: a mermaid code fence escapes its inline <script> in the mobile WebView; session file RPCs follow symlinks out of the workspace #1832, and it needs a decision about bundling rather than a patch here.