Render, Markdown, and preview-boundary batch¶
Signal: The 2026-05-09 00:15 UTC scan added render-path advisories where escaping, preview dispatch, or redirect assumptions failed after the main route looked safe.
Advisory cluster¶
- Mistune math plugin escape bypass — GHSA-8g87-j6q8-g93x / CVE-2026-44708: math plugin render functions emitted
$...$and$$...$$content without honoringescape=True;mistune <=3.2.0is affected. - Mistune figure directive attribute injection — GHSA-58cw-g322-p94v / CVE-2026-44896:
figclassandfigwidthoptions were concatenated into HTML attributes without escaping;mistune <=3.2.0is affected. - Mistune heading ID attribute injection — GHSA-v87v-83h2-53w7 / CVE-2026-44897: caller-supplied
heading_idcallbacks could return raw heading text that breaks out of theid=attribute. Patch to 3.2.1+ where available. - ViewComponent inherited preview helper dispatch — GHSA-7f3r-gwc9-2995 / CVE-2026-44836: preview routes could call inherited
render_with_templateand render internal templates. Patchview_componentto 4.9.0+. - ViewComponent system-test path containment — GHSA-hg3h-g7xc-f7vp / CVE-2026-44837:
realpathplus rawstart_with?allowed sibling-directory escapes in test entrypoint rendering. Patch to 4.9.0+. - Snipe-IT Referer-backed open redirect — GHSA-mghp-5cq4-v6mg / CVE-2026-44833: unvalidated
Refererstored in session could driveredirect()->to($backUrl). Patch to 8.4.1+.
Why this matters¶
Render bugs often hide in “secondary” paths: Markdown plugins, figure options, generated anchor IDs, preview helpers, test routes, and redirect conveniences. The application may escape normal body text correctly while plugin attributes, helper method dispatch, or session-backed return URLs bypass the same contract.
Triage¶
- Patch
mistune,view_component, and Snipe-IT where untrusted users can submit Markdown, documentation content, preview route paths, or redirect-affecting headers. - Search for
mistunemath/figure/TOC hooks, especially customheading_idcallbacks that derive IDs from raw heading text. - Treat Rails preview and system-test routes as internal-only; verify they are disabled in production, staging, review apps, and shared CI unless protected by strong auth.
- Reject redirect targets unless they are relative paths or match an explicit same-origin allowlist; never trust
Refereras an authorization boundary. - Add regression fixtures with quote characters in attributes, raw HTML inside plugin delimiters, sibling path prefixes, and inherited helper names.
Durable controls¶
- Define one escaping contract for every renderer extension point; plugin renderers should call the same escape helper as the core renderer.
- Use path-aware containment (
relative_path_from, resolved prefix plus separator, or equivalent), not string-prefix containment. - Dispatch only methods explicitly declared for a route surface; inherited framework helpers should not be route-callable by default.
- Make preview/test routes fail closed outside local development and require a visible startup warning when enabled.
August 7 follow-up: Showdown metadata and generated-attribute contexts¶
Two reviewed Showdown records reinforce that Markdown body escaping does not cover generated document or attribute contexts:
- GHSA-cr32-g25g-vxjj / CVE-2026-59711 reports that frontmatter metadata entered the HTML
<title>without context-appropriate escaping whencompleteHTMLDocumentwas enabled. - GHSA-22g5-r2x5-97cx / CVE-2026-59710 reports that table-header text entered a generated
idattribute without quote escaping under the GitHub-flavor table path.
The reviewed records list Showdown through 2.1.0 as affected but do not identify a patched package release. The linked upstream commits are useful fixed-code controls; verify package provenance rather than assuming a later version exists.
October 1 follow-up: serialize-javascript function-body script termination¶
GHSA-gfhx-hw2g-v5hg / CVE-2026-97711 shows the same escape-regex boundary-condition failure in a different generated context: serialize-javascript 7.1.1 exists specifically to make output safe inside <script>, but its SCRIPT_CLOSE_REGEXP first alternative <\/script[^>]*> excluded only > from the character class — so one match could run from the first `