fix(markdown): drop a script-bearing href from Markdown links
Summary
DocsUI::Markdown#node_link rendered whatever URL Commonmarker handed it. Phlex refuses a javascript: href on its own, but data:, vbscript: and the rest went straight into the page. The link handler now uses the same scheme guard the image handler got in #99: an href is kept for http, https, mailto, tel or no scheme at all (relative, root-relative, protocol-relative, #anchor); any other scheme renders the link text with no <a>. The scheme is read after stripping ASCII whitespace and control characters, as browsers do, so java<TAB>script: can't slip through.
Found while implementing #94 (noted in #99's deviation log).
Test plan
-
spec/docs_ui/markdown_spec.rb"link schemes": ajavascript:href keeps its text and loses the<a>;data:, a tab-obfuscated scheme and an upper-case scheme are dropped; http(s),mailto:,tel:and every scheme-less form are kept verbatim -
bundle exec rspec1119 examples, 0 failures (93.0% line coverage) -
bundle exec rubocopclean
Deviations & judgment calls
LINK_SCHEMESandlink_hreflive inDocsUI::MarkdownImagesnext toURL_SCHEMEandimage_src, not inmarkdown.rb: that class sits at RuboCop's 100-line limit, and the two guards share the whitespace-stripping scheme read.- Blocked links keep their text (an image with a blocked src is dropped entirely): losing prose mid-sentence would be worse than losing a link.
mailto:andtel:are allowed for links; images stay http(s)-only.
Summary by cubic
Fixes a Markdown injection vector where script-bearing link hrefs could make it into the page.
[text](javascript:…) previously rendered a live <a>; data:, vbscript:, and obfuscated schemes slipped through while only javascript: was blocked by Phlex. Links now keep their href only for http, https, mailto, tel, or no scheme at all. Blocked links render their text without the anchor; scheme-less hrefs (relative, root-relative, protocol-relative, #anchor) are unchanged.
- Applies the same scheme guard the image handler already had.
- Reads the scheme after stripping ASCII whitespace and control characters, matching browser behavior.
- Adds tests covering allowed, blocked, and obfuscated schemes.