From 015a1077867e36d72d9566902ef16d7b55727b05 Mon Sep 17 00:00:00 2001 From: khangnghiem <56039341+khangnghiem@users.noreply.github.com> Date: Mon, 6 Jul 2026 20:02:02 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]=20Fi?= =?UTF-8?q?x=20XSS=20in=20markdown=20rendering?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .jules/sentinel.md | 5 +++++ site/app.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 5e0d967e..a3b40c02 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -14,3 +14,8 @@ **Vulnerability:** Several `escapeHtml` implementations used DOM manipulation (`document.createElement('div').innerHTML`) or incomplete string replacement, failing to escape single and double quotes. **Learning:** Incomplete escaping allows XSS payloads to break out of HTML attributes (e.g., ``). **Prevention:** Always use `String(text)` cast combined with a comprehensive replace chain for `&`, `<`, `>`, `"`, and `'` (e.g., `'`) in custom string escaping functions. + +## 2026-07-06 - XSS vulnerability in Markdown rendering fallback +**Vulnerability:** The application was vulnerable to Cross-Site Scripting (XSS) when rendering user-provided Markdown notes due to missing HTML sanitization on the `marked.parse` output. A 'fail open' state could exist if fallback mechanisms returned unsanitized raw HTML. +**Learning:** When using external parsers like `marked.parse`, their raw output is inherently unsafe and requires sanitization. Furthermore, conditional sanitization (e.g. `window.DOMPurify ? sanitize(...) : fallback(...)`) MUST use a secure fallback mechanism such as regex-based HTML escaping rather than falling back to the raw unsanitized HTML. +**Prevention:** Always wrap dynamically rendered content with `DOMPurify.sanitize()` using required attribute allowlists. Ensure any fallback logic provides escaped text rather than unsanitized raw data to prevent 'fail open' scenarios. diff --git a/site/app.js b/site/app.js index 788bd7e7..03e6993e 100644 --- a/site/app.js +++ b/site/app.js @@ -2724,7 +2724,7 @@ function renderSpecsPanel() { if (typeof marked !== 'undefined') { const rendered = marked.parse(processedNote); - html += rendered; + html += window.DOMPurify ? window.DOMPurify.sanitize(rendered, { ADD_ATTR: ['data-note-node', 'data-node'] }) : `
${processedNote.replace(//g, '>')}
`; } else { html += `
${processedNote.replace(//g, '>')}
`; }