fix(editor): harden inline markdown renderer against XSS#55
Conversation
`parseInline` previously interpolated user-supplied text directly into HTML, so a markdown source like `<img onerror=alert(1)>` or `[x](javascript:alert(1))` would reach `innerHTML` as live markup. The `<details>` NodeView also fell back to assigning the raw decoded HTML to `innerHTML` when DOMParser couldn't find a `<details>` element. This change: - Escapes HTML before applying markdown patterns. - Allowlists `http(s)` and `mailto` schemes for link URLs, and rejects any URL containing C0 control characters or DEL — browsers strip tab/newline/CR from `href` values, which would otherwise let `java<TAB>script:` normalize back to `javascript:` after a naive scheme check. - Replaces the `dom.innerHTML = rawHtml` fallback in the DetailsBlock NodeView with a plain text "[Invalid details block]" message. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughMarkdown link parsing and HTML output handling are hardened to prevent XSS and injection attacks. The ChangesEditor Security Hardening
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
parseInlinewas interpolating user text directly into HTML, so markdown like<img onerror=alert(1)>or[x](javascript:alert(1))reachedinnerHTMLas live markup.DetailsBlockNodeView also fell back todom.innerHTML = rawHtmlwhen DOMParser couldn't find a<details>element — same XSS vector.Changes
isSafeUrlallowlistshttp(s)andmailtoschemes for linkhref.isSafeUrlalso rejects any URL containing C0 control chars or DEL. Browsers strip tab/newline/CR fromhrefvalues, so a naive scheme check onjava<TAB>script:alert(1)would mis-classify it as a relative URL; once rendered the browser normalizes it back tojavascript:and clicking executes. The control-char filter closes that bypass.innerHTML = rawHtmlfallback in the DetailsBlock NodeView with a plain[Invalid details block]text marker.Notes for reviewer
parseInline/renderBasicMarkdownare only used inside anatom: trueNodeView withcontentEditable = 'false'(the<details>preview at editor.ts:202-253). The source markdown the user actually edits is unaffected.\x00-\x1F\x7Fwith aneslint-disable-next-line no-control-regexso the source stays readable.markdown-it(escapes by default, has avalidateLinkhook) — left as a follow-up so this PR stays scoped to the security fix.Test plan
pnpm exec vue-tsc --noEmitpasses.javascript:,java<TAB>script:,java<LF>script:,data:, plainhttps://…,mailto:, relative paths, and fragments all classify correctly.<details>previews still render normally and that malformed<details>blocks render the[Invalid details block]placeholder.http(s)URL still renders as a clickable anchor in the details preview, and ajavascript:link renders as escaped text.🤖 Generated with Claude Code
Summary by CodeRabbit
target="_blank"andrel="noopener"attributes to all rendered links