惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
G
GRAHAM CLULEY
www.infosecurity-magazine.com
www.infosecurity-magazine.com
V2EX - 技术
V2EX - 技术
The Last Watchdog
The Last Watchdog
S
Secure Thoughts
Webroot Blog
Webroot Blog
PCI Perspectives
PCI Perspectives
L
LINUX DO - 最新话题
Hacker News: Ask HN
Hacker News: Ask HN
N
News and Events Feed by Topic
H
Heimdal Security Blog
H
Help Net Security
T
The Blog of Author Tim Ferriss
P
Proofpoint News Feed
The GitHub Blog
The GitHub Blog
Jina AI
Jina AI
Recent Commits to openclaw:main
Recent Commits to openclaw:main
F
Full Disclosure
小众软件
小众软件
S
Securelist
罗磊的独立博客
NISL@THU
NISL@THU
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cisco Blogs
云风的 BLOG
云风的 BLOG
C
CERT Recently Published Vulnerability Notes
Cisco Talos Blog
Cisco Talos Blog
Know Your Adversary
Know Your Adversary
S
Schneier on Security
D
DataBreaches.Net
M
MIT News - Artificial intelligence
V
Vulnerabilities – Threatpost
N
News and Events Feed by Topic
有赞技术团队
有赞技术团队
F
Fortinet All Blogs
T
Tenable Blog
The Register - Security
The Register - Security
C
Check Point Blog
AWS News Blog
AWS News Blog
Cloudbric
Cloudbric
C
CXSECURITY Database RSS Feed - CXSecurity.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cyber Attacks, Cyber Crime and Cyber Security
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Google Online Security Blog
Google Online Security Blog
博客园 - 叶小钗
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - 司徒正美

Streamdown Documentation

Configuration FAQ Getting Started Introduction Animation Carets Code Blocks Components GitHub Flavored Markdown Interactivity Internationalization Link Safety Memoization Migrate from react-markdown Styling Unterminated Block Parsing Typography Usage @streamdown/cjk @streamdown/code Built-in Plugins @streamdown/math @streamdown/mermaid Custom renderers
Security
Vercel · 2026-06-02 · via Streamdown Documentation

Built-in content hardening and security features to protect against malicious Markdown.

Streamdown is built with security as a top priority. When rendering user-generated or AI-generated Markdown content, it's crucial to protect against malicious content, especially when dealing with content that might have been subject to prompt injection attacks.

Markdown can contain:

  • Links to malicious sites - Phishing or malware distribution
  • External images - Privacy tracking or CSRF attacks
  • HTML content - XSS vulnerabilities
  • JavaScript execution - Code injection
  • Prompt injection - AI models manipulated to include harmful content

Streamdown uses two layers of protection:

  1. rehype-sanitize — strips dangerous HTML elements and attributes using GitHub's sanitization schema, extended with tel: protocol support
  2. rehype-harden — restricts URL protocols, link domains, and image sources

By default, Streamdown is configured with permissive security to allow maximum functionality:

// Default rehype-harden configuration
{
  allowedImagePrefixes: ["*"],  // All images allowed
  allowedLinkPrefixes: ["*"],   // All links allowed
  allowedProtocols: ["*"],      // All protocols allowed
  defaultOrigin: undefined,     // No origin restriction
  allowDataImages: true,        // Base64 images allowed
}

The default rehype-sanitize schema allows http, https, irc, ircs, mailto, xmpp, and tel protocols for links.

This works well for trusted content but should be tightened for untrusted sources.

By default, all protocols are allowed. You can restrict which URL protocols are permitted:

import { Streamdown, defaultRehypePlugins } from 'streamdown';
import { harden } from 'rehype-harden';

export default function Page() {
  return (
    <Streamdown
      rehypePlugins={[
        defaultRehypePlugins.raw,
        defaultRehypePlugins.sanitize,
        [
          harden,
          {
            allowedProtocols: [
              'http',
              'https',
              'mailto',
            ],
          },
        ],
      ]}
    >
      {markdown}
    </Streamdown>
  );
}

When overriding rehypePlugins, always include defaultRehypePlugins.sanitize to preserve XSS protection. The rehypePlugins prop replaces the entire default array — it does not merge.

This is useful for security-sensitive applications where you want to prevent custom protocol schemes like javascript:, data:, or desktop app protocols.

Custom Protocol Schemes

To enable custom protocol schemes like postman://, vscode://, or slack://, include them in the allowedProtocols array:

{
  allowedProtocols: [
    'http',
    'https',
    'postman',
    'vscode',
    'slack',
  ],
}

Limit which domains users can link to:

import { Streamdown, defaultRehypePlugins } from 'streamdown';
import { harden } from 'rehype-harden';

export default function Page() {
  return (
    <Streamdown
      rehypePlugins={[
        defaultRehypePlugins.raw,
        defaultRehypePlugins.sanitize,
        [
          harden,
          {
            defaultOrigin: 'https://streamdown.ai',
            allowedLinkPrefixes: [
              'https://streamdown.ai',
              'https://github.com',
              'https://vercel.com',
            ],
          },
        ],
      ]}
    >
      {markdown}
    </Streamdown>
  );
}

Any links not matching the allowed prefixes will be rewritten to point to the defaultOrigin.

Example

With the above configuration:

[Safe link](https://github.com/vercel/streamdown)
[Unsafe link](https://malicious-site.com)

Results in:

  • Safe link: Works normally
  • Unsafe link: Renders as [blocked]

Similarly, restrict which domains can serve images:

<Streamdown
  rehypePlugins={[
    defaultRehypePlugins.raw,
    defaultRehypePlugins.sanitize,
    [
      harden,
      {
        allowedImagePrefixes: [
          'https://your-cdn.com',
          'https://trusted-images.com',
        ],
        allowDataImages: false,  // Disable base64 images
      },
    ],
  ]}
>
  {markdown}
</Streamdown>

Data Images

Base64-encoded images (data:image/...) can be disabled:

This prevents embedding arbitrary image data in Markdown, which could be used for:

  • Tracking pixels
  • Large embedded files
  • Malicious payloads

When using AI-generated content, models can be manipulated to include malicious links or content. Here's a production-ready configuration:

import { Streamdown, defaultRehypePlugins } from 'streamdown';
import { harden } from 'rehype-harden';

export default function ChatMessage({ content, isAIGenerated }) {
  const securityConfig = isAIGenerated ? {
    defaultOrigin: 'https://your-app.com',
    allowedLinkPrefixes: [
      'https://your-app.com',
      'https://docs.your-app.com',
      'https://github.com',
    ],
    allowedImagePrefixes: [
      'https://your-cdn.com',
    ],
    allowedProtocols: [
      'http',
      'https',
      'mailto',
    ],
    allowDataImages: false,
  } : {
    // More permissive for user content
    allowedLinkPrefixes: ['*'],
    allowedImagePrefixes: ['*'],
    allowedProtocols: ['*'],
  };

  return (
    <Streamdown
      rehypePlugins={[
        defaultRehypePlugins.raw,
        defaultRehypePlugins.sanitize,
        [harden, securityConfig],
      ]}
    >
      {content}
    </Streamdown>
  );
}

By default, Streamdown's sanitizer strips unknown HTML tags (while preserving their content). If you need to render custom tags like <ref> or <mention>, use the allowedTags prop:

<Streamdown
  allowedTags={{
    ref: ["note_id"],      // Allow <ref> with note_id attribute
    mention: ["user_id"],  // Allow <mention> with user_id attribute
  }}
  components={{
    ref: (props) => <NoteBadge noteId={props.note_id} />,
    mention: (props) => <UserMention userId={props.user_id} />,
  }}
>
  {markdown}
</Streamdown>

Only attributes explicitly listed in allowedTags are preserved—all other attributes are stripped for security. See the Styling documentation for more details.

The allowedTags prop only works with the default rehype plugins. If you provide custom rehypePlugins, you must configure sanitization yourself.

HTML Content

Streamdown supports raw HTML through rehype-raw. To disable HTML entirely:

import { Streamdown, defaultRehypePlugins } from 'streamdown';

export default function Page() {
  return (
    <Streamdown
      rehypePlugins={[
        // Omit defaultRehypePlugins.raw to disable HTML
        defaultRehypePlugins.sanitize,
        defaultRehypePlugins.harden,
      ]}
    >
      {markdown}
    </Streamdown>
  );
}

Without rehype-raw, HTML tags will be escaped and displayed as text.

Control how relative URLs are handled:

{
  defaultOrigin: 'https://your-app.com'
}

Relative URLs will be resolved against this origin:

[Relative link](/docs/guide)

Becomes: https://your-app.com/docs/guide

For URL-level control without writing a rehype plugin, use the urlTransform prop. This runs on every URL in the rendered Markdown (links, images, etc.) and matches the react-markdown API.

By default, defaultUrlTransform is a passthrough — URL security is already handled by rehype-sanitize and rehype-harden. Use urlTransform when you need custom URL rewriting beyond what those plugins provide.

import { Streamdown, defaultUrlTransform } from 'streamdown';

// Proxy images through your CDN
<Streamdown
  urlTransform={(url, key, node) => {
    if (key === 'src') {
      return `https://your-cdn.com/proxy?url=${encodeURIComponent(url)}`;
    }
    return defaultUrlTransform(url, key, node);
  }}
>
  {markdown}
</Streamdown>

To completely ignore raw HTML in Markdown (rather than escaping it), use the skipHtml prop:

<Streamdown skipHtml>
  {markdown}
</Streamdown>

For advanced URL handling beyond what urlTransform and rehype-harden provide, you can create a custom rehype plugin. This gives you full control over URL transformation and validation in your markdown content.