XSS from raw HTML in react-markdown

XSS from rendering raw HTML in react-markdown

Description

Allowing raw HTML in untrusted Markdown rendered by react-markdown can expose a React application to cross-site scripting (XSS). Current versions escape or skip HTML by default. Enabling HTML parsing with rehype-raw without a sanitization step such as rehype-sanitize can allow malicious tags or attributes into the output. Older options such as allowDangerousHtml or escapeHtml={false} can create the same risk.

Potential impact

  • An attacker may obtain cookies, session information, or other sensitive data available to the page.
  • Malicious code may run in the user's browser and support further attacks.
  • Malicious content may undermine users' trust in the site.

Remediation

  • Keep the default rendering for untrusted Markdown and avoid rehype-raw.
  • If raw HTML is necessary, apply rehype-sanitize after rehype-raw and keep the schema of permitted tags and attributes minimal.
  • Validate and sanitize user-supplied Markdown and other rendered data.
  • If overriding URL transformation options, block dangerous schemes such as javascript: and data:.

Examples

Before

tsx
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";

function UnsafeComponent({ markdown }) {
  // Unsafe: parse raw HTML from untrusted Markdown
  return <ReactMarkdown rehypePlugins={[rehypeRaw]}>{markdown}</ReactMarkdown>;
}

After

tsx
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";
import rehypeSanitize from "rehype-sanitize";

function SafeComponent({ markdown }) {
  // Safer default: do not parse raw HTML
  return <ReactMarkdown>{markdown}</ReactMarkdown>;
}

function SafeHtmlComponent({ trustedButNeedsHtml }) {
  // Sanitize HTML when it must be allowed
  return (
    <ReactMarkdown rehypePlugins={[rehypeRaw, rehypeSanitize]}>
      {trustedButNeedsHtml}
    </ReactMarkdown>
  );
}

Explanation:

  • Before: Using rehype-raw alone turns raw Markdown HTML into React elements. Unsanitized HTML may enter the DOM with executable behavior.
  • After: Default rendering does not execute raw HTML. When HTML rendering is needed, rehype-sanitize retains only permitted tags and attributes to reduce XSS risk.

References