📑 fix: Sanitize Markdown Artifacts (#12249)

* 🛡️ fix: Sanitize markdown artifact rendering to prevent stored XSS

Replace marked-react with react-markdown + remark-gfm for artifact
markdown preview. react-markdown's skipHtml strips raw HTML tags,
and a urlTransform guard blocks javascript: and data: protocol links.

* fix: Update useArtifactProps test to expect react-markdown dependencies

* fix: Harden markdown artifact sanitization

- Convert isSafeUrl from denylist to allowlist (http, https, mailto, tel
  plus relative/anchor URLs); unknown protocols are now fail-closed
- Add remark-breaks to restore single-newline-to-<br> behavior that was
  silently dropped when replacing marked-react
- Export isSafeUrl from the host module and add 16 direct unit tests
  covering allowed protocols, blocked schemes (javascript, data, blob,
  vbscript, file, custom), edge cases (empty, whitespace, mixed case)
- Hoist remarkPlugins to a module-level constant to avoid per-render
  array allocation in the generated Sandpack component
- Fix import order in generated template (shortest to longest per
  AGENTS.md) and remove pre-existing trailing whitespace

* fix: Return null for blocked URLs, add sync-guard comments and test

- urlTransform returns null (not '') for blocked URLs so react-markdown
  omits the href/src attribute entirely instead of producing <a href="">
- Hoist urlTransform to module-level constant alongside remarkPlugins
- Add JSDoc sync-guard comments tying the exported isSafeUrl to its
  template-string mirror, so future maintainers know to update both
- Add synchronization test asserting the embedded isSafeUrl contains the
  same allowlist set, URL parsing, and relative-path checks as the export
This commit is contained in:
Danny Avila 2026-03-15 18:40:42 -04:00 committed by GitHub
parent bcf45519bd
commit f9927f0168
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 148 additions and 13 deletions

View file

@ -108,7 +108,9 @@ const mermaidDependencies = {
};
const markdownDependencies = {
'marked-react': '^2.0.0',
'remark-gfm': '^4.0.0',
'remark-breaks': '^4.0.0',
'react-markdown': '^9.0.1',
};
const dependenciesMap: Record<