Sanitize HTML in our VS Code panels - #3675
Conversation
...and add CSP for added protection.
This is overbroad, but the trusted content appearing in places other than the doc panel should already be sanitized, so this should be a no-op.
|
Vibe-coded but reviewed and tested by me. |
|
No idea what I clicked to remove non-copilot reviewers. 🤷 |
There was a problem hiding this comment.
🟡 Changes recommended
The new markdown-it { html: false } setting will break existing kata/doc markdown that relies on inline HTML, and there are remaining http:// links that will be stripped by the new URI allowlist.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR hardens HTML rendering in the QDK VS Code extension webviews (documentation, estimates, histogram, circuit, help) and the playground, aiming to reduce XSS risk by tightening CSP and sanitizing rendered markdown.
Changes:
- Add a Content Security Policy to the VS Code panel host HTML and remove reliance on inline scripts.
- Sanitize markdown rendering in the VS Code webview bundle and the playground via DOMPurify, with a protocol allowlist.
- Update a kata link from
http://tohttps://to comply with the new link sanitization policy.
File summaries
| File | Description |
|---|---|
| source/vscode/src/webviewPanel.ts | Adds CSP to the shared panel HTML and passes resource URI via a DOM attribute instead of an inline script. |
| source/vscode/src/webview/webview.tsx | Introduces DOMPurify-based sanitization for rendered markdown and adjusts markdown-it configuration. |
| source/vscode/src/webview/help.tsx | Switches help panel resource URI lookup from a global injected variable to data-resources-uri. |
| source/playground/src/main.tsx | Mirrors the DOMPurify + markdown-it sanitization approach used by the VS Code webview. |
| katas/content/linear_algebra/index.md | Updates an external link to HTTPS to remain valid under stricter URI sanitization. |
Review details
Suppressed comments (2)
source/vscode/src/webview/webview.tsx:37
- The URI allowlist regex does not include
http:. There is still at least onehttp://link in kata content (katas/content/teleportation/index.md:11), so DOMPurify will strip or neutralize that link when rendering markdown. Either migrate remaininghttp://links tohttps://or temporarily allowhttp:here to avoid breaking content.
// Allow only the protocols used in doc/kata/estimator content
// Borrowed from DOMPurify and filtered to our protocols
const ALLOWED_URI = /^(?:(?:https|xref):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i;
setRenderer((input: string) =>
DOMPurify.sanitize(md.render(input), { ALLOWED_URI_REGEXP: ALLOWED_URI }),
source/playground/src/main.tsx:71
- The URI allowlist regex does not include
http:. Since there is still at least onehttp://link in kata content (katas/content/teleportation/index.md:11), it will be stripped/neutralized when rendered in the playground. Either migrate remaininghttp://links tohttps://or temporarily allowhttp:in the allowlist to avoid breaking content.
// Allow only the protocols used in doc/kata/estimator content
// Borrowed from DOMPurify and filtered to our protocols
const ALLOWED_URI = /^(?:(?:https|xref):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i;
setRenderer((input: string) =>
DOMPurify.sanitize(md.render(input), { ALLOWED_URI_REGEXP: ALLOWED_URI }),
- Files reviewed: 5/5 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
|
||
| const md = markdownIt("commonmark"); | ||
| import DOMPurify from "dompurify"; | ||
| const md = markdownIt("commonmark", { html: false }); |
| // Ensure that the fetch is kicked off once for the module | ||
| if (!svgPromise) { | ||
| const resourcesUri = document.body.dataset.resourcesUri ?? ""; | ||
| svgPromise = fetch(`${resourcesUri}/DebugDropDown.svg`); | ||
| } |
| import mk from "@vscode/markdown-it-katex"; | ||
| import markdownIt from "markdown-it"; | ||
| const md = markdownIt("commonmark"); | ||
| const md = markdownIt("commonmark", { html: false }); |
We don't expect our extension to be activated unless the user has explicitly trusted the workspace, but it's good defence in depth to sanitize the HTML anyway. The affected panels are:
Bonus: sanitize in the playground too. It only loads content we've authored, but it seemed preferable to be consistent (and may protect us from automated security scans in the future).