2026/10/12 7:48:54

Streamdown 安全指南:为 AI 流式 Markdown 渲染构建可靠的安全边界

Streamdown 安全指南:为 AI 流式 Markdown 渲染构建可靠的安全边界 前端AI 应用UI组件【免费下载链接】streamdownA drop-in replacement for react-markdown, designed for AI-powered streaming.项目地址https://gitcode.com/gh_mirrors/stre/streamdown点击查看免费下载Streamdown 是一款面向 AI 流式输出的 react-markdown 替代方案。本文以其安全参考文档为主体结合仓库源码packages/streamdown/index.tsx、packages/streamdown/lib/link-modal.tsx、packages/streamdown/lib/components.tsx等展开系统讲解 Streamdown 的默认安全姿态、协议与链接/图片白名单、外链安全确认弹窗、自定义 HTML 标签白名单、URL 改写与生产级严格配置帮助你针对不可信的 AI 生成内容搭建层层防御。默认安全姿态宽松渲染但三层防线护航Streamdown 的默认行为是宽松的permissive所有 URL 前缀、所有协议、data: 图片均被放行。这符合 AI 流式场景「先出效果、再谈管控」的需求但并不意味着裸奔——默认安全能力由三层机制提供rehype-sanitize—— XSS 预防负责剔除危险标签与属性、危险协议是安全的第一道闸门rehype-harden—— URL/协议限制默认全部放行allowedProtocols: [*]、allowedLinkPrefixes: [*]、allowedImagePrefixes: [*]、allowDataImages: true但允许你随时收紧外链安全确认弹窗Link Safety Modal—— 打开外部链接前弹出确认框默认启用。这些默认值可以在源码中直接验证defaultRehypePlugins定义于packages/streamdown/index.tsx其中 harden 插件的默认配置为export const defaultRehypePlugins: Recordstring, Pluggable { raw: rehypeRaw, sanitize: [rehypeSanitize, defaultSanitizeSchema], harden: [ harden, { allowedImagePrefixes: [*], allowedLinkPrefixes: [*], allowedProtocols: [*], defaultOrigin: undefined, allowDataImages: true, }, ], };同时默认的 sanitize schema 也做了针对流式场景的定制defaultSanitizeSchema见index.tsxclobberPrefix: —— 避免 remark-rehype 与 hast-util-sanitize 双重加user-content-前缀导致脚注锚点 id 错位在默认协议基础上追加tel与streamdown—— 其中streamdown:是 remend 在流式渲染过程中为未完成链接/图片注入的哨兵协议streamdown:incomplete-link/incomplete-imagecode元素额外放行metastring属性支撑代码块元信息如startLine的传递。结论默认配置足够安全地渲染静态或流式 Markdown一旦你的内容来自 AI 或第三方用户输入就应当按下文逐步收紧配置。限制协议从「全放行」到「白名单」rehype-harden 的allowedProtocols控制链接与图片可用的 URL 协议。默认是[*]即全部放行。收紧到仅允许https与mailtoStreamdown rehypePlugins{[ rehypeRaw, [rehypeSanitize, {}], [harden, { allowedProtocols: [https, mailto], allowedLinkPrefixes: [*], allowedImagePrefixes: [*], allowDataImages: true, }], ]} 配置要点参数默认值作用allowedProtocols[*]允许的 URL 协议白名单如https、mailto、telallowedLinkPrefixes[*]链接 href 允许的域名前缀白名单allowedImagePrefixes[*]图片 src 允许的域名前缀白名单allowDataImagestrue是否允许data:协议的图片base64 内嵌图片rehype-sanitize同样在协议层做兜底校验。需要注意的是自定义rehypePlugins会替换默认插件数组而不是追加因此一旦你传入自定义数组就应把rehypeRaw、rehypeSanitize、harden三层完整补齐否则会出现开了 raw 却没消毒的安全缺口。限制链接只允许可信域名当 AI 生成的内容可能夹带钓鱼或追踪链接时把allowedLinkPrefixes收窄到企业自有域名即可[harden, { allowedLinkPrefixes: [ https://example.com, https://docs.example.com, ], allowedProtocols: [https], }]前缀匹配是逐段包含的https://example.com可以放行https://example.com/foo及子路径但不会放行https://example.com.evil.com这类变体域名因为前缀https://example.com与https://example.com.evil.com并不构成连续前缀。如需更精确的域名级判断可结合下文linkSafety.onLinkCheck做二次校验。限制图片域名白名单与禁用 data: 图片图片是 XSS 与隐私追踪的常见载体例如通过src触发远程请求、通过data:内嵌恶意 HTML 渲染上下文。收紧方式[harden, { allowedImagePrefixes: [ https://images.example.com, https://cdn.example.com, ], allowDataImages: false, // 禁用 data: URL }]allowDataImages: false会拒绝 base64 内嵌图片。这对流式场景的影响是若你的 AI 输出依赖data:图片如部分 LLM 直接生成 SVG 或 base64 位图需权衡是否放行生产环境建议默认关闭。Link Safety Modal打开外链前的确认闸门默认行为Link Safety Modal默认启用defaultLinkSafetyConfig { enabled: true }见index.tsx。当渲染的链接开启该功能时a会被渲染成button带data-streamdownlink标记点击后拦截默认跳转先弹出确认框用户点击继续才通过window.open(href, _blank, noreferrer)打开见LinkComponent。默认弹窗LinkSafetyModal具备以下行为展示目标 URL超长 URL 自动限高滚动break-all防溢出提供「复制链接」与「打开链接」按钮点击遮罩或按Esc关闭打开时锁定页面滚动lockBodyScroll关闭时释放通过createPortal渲染到document.body或portalprop 指定的容器避免被父级样式/层级干扰。测试用例验证了该流程link-safety.test.tsx中「点击链接 → 弹窗出现 → 展示 URL」的断言覆盖了默认拦截路径。完全禁用Streamdown linkSafety{{ enabled: false }}{markdown}/Streamdown禁用后链接恢复为原生a行为点击直接跳转。白名单可信域名onLinkCheckonLinkCheck是异步或同步返回布尔值的校验函数返回true时跳过弹窗、直接window.open返回false时弹出确认框。基于 hostname 后缀匹配的常见实现Streamdown linkSafety{{ enabled: true, onLinkCheck: async (url) { const trusted [example.com, docs.example.com]; const hostname new URL(url).hostname; return trusted.some((d) hostname.endsWith(d)); }, }} 测试覆盖了onLinkCheck返回true时「不弹窗、直接window.open(..., _blank, noreferrer)」的行为。这里有一个值得注意的安全细节endsWith(d)会匹配example.com与sub.example.com这是期望的层级域名匹配若想杜绝notexample.com这类子串欺诈可先取 hostname 再比对完整域名或使用hostname d || hostname.endsWith(. d)。自定义弹窗renderModal弹窗 UI 完全可替换。renderModal接收{ url, isOpen, onClose, onConfirm }四个参数你可以接任意 UI 库Streamdown linkSafety{{ enabled: true, renderModal: ({ url, isOpen, onClose, onConfirm }) ( Dialog open{isOpen} onOpenChange{onClose} DialogContent pOpen {url}?/p Button onClick{onConfirm}Continue/Button Button onClick{onClose}Cancel/Button /DialogContent /Dialog ), }} LinkSafetyModalProps的类型定义{ isOpen; onClose; onConfirm; url }见index.tsx与自定义组件签名完全对齐。图标、文案还可用icons与translationsprop 覆盖。自定义 HTML 标签白名单安全承载 AI 生成的富组件AI 输出里常出现mention、widget、source这类语义化自定义标签。默认 sanitize schema 会将其剥离需要通过allowedTags显式白名单并在components中提供对应渲染组件Streamdown allowedTags{{ source: [id], mention: [user_id, type], widget: [data*], // 通配符放行所有>import { Streamdown, defaultUrlTransform } from streamdown; // 通过你的 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签名(url, key, node)中url是原始 URLkey是属性名如href/srcnode是当前的 hast 元素。实现层面transformUrls遍历html-url-attributes定义的 URL 属性表只对匹配当前标签的属性调用 transform。注意urlTransform返回undefined时该属性会被置空不要误返回空值导致链接丢失。跳过与禁用 HTMLskipHtml完全忽略原始 HTMLskipHtml会丢弃 Markdown 中的所有原始 HTML 标签以纯文本或直接删除的方式处理适用于只要 Markdown 语义、绝不渲染任何 HTML的严格场景Streamdown skipHtml{markdown}/Streamdown禁用 HTML移除 rehype-rawrehype-raw负责把 Markdown 中的原始 HTML 解析为真实 DOM 节点。去掉它所有原始 HTML 都会被当作纯文本输出等价于看不见 HTML 语法。由于默认插件是打包导出的可以这样移除import { defaultRehypePlugins } from streamdown; const { raw, ...rest } defaultRehypePlugins; Streamdown rehypePlugins{Object.values(rest)}{markdown}/StreamdowndefaultRehypePlugins包含raw、sanitize、harden三个键见index.tsx去掉raw后剩下的sanitizeharden仍会执行因此这条路比skipHtml更温和HTML 变成字面文本展示给用户而不是被静默丢弃。生产级严格配置AI 生成内容的完整安全示例综合以上全部手段针对 AI 生成内容aiContent的严格生产配置如下Streamdown rehypePlugins{[ rehypeRaw, [rehypeSanitize, {}], [harden, { allowedProtocols: [https, mailto], allowedLinkPrefixes: [https://your-domain.com], allowedImagePrefixes: [https://your-cdn.com], allowDataImages: false, }], ]} linkSafety{{ enabled: true, onLinkCheck: async (url) { const trusted [your-domain.com]; return trusted.some((d) new URL(url).hostname.endsWith(d)); }, }} {aiContent} /Streamdown该配置对应三层防线防线配置效果sanitize 层rehypeSanitize剥离危险标签/属性/协议兜底 XSSharden 层allowedProtocolsallowedLinkPrefixesallowedImagePrefixesallowDataImages: false链接只指向自家域名、图片只来自自家 CDN、禁用 data: 图片交互层linkSafety.onLinkCheck非白名单域名一律弹窗确认杜绝无感知跳转补充建议基于源码与仓库实践代码块与表格元数据如需放行代码块metastring、表格等增强能力请保留默认 sanitize schema 的扩展项packages/streamdown/index.tsx的defaultSanitizeSchema已内置自定义 schema 时勿删减streamdown:哨兵协议否则流式渲染中未完成的链接/图片会因协议被拒而闪烁。尾部 URL 探测如需彻底收紧自动链接如裸邮箱被 GFM 自动转为mailto:可使用disableAutolinkProtocols{[mailto]}prop它会在 remarkGfm 之后运行remarkDisableAutolinkProtocols剔除指定协议的自动链接节点index.tsx。不要绕过 sanitizerehypeRaw只负责解析、不负责安全任何自定义插件数组都必须显式携带 sanitize/harden切勿只传rehypeRaw。结语Streamdown 的安全体系遵循「默认宽松、三层兜底、按需收紧」的设计rehype-sanitize负责 XSS 预防rehype-harden负责 URL/协议白名单linkSafety负责交互层的外链确认allowedTags为 AI 自定义标签提供受控的渲染空间。结合packages/streamdown/__tests__下的测试用例link-safety.test.tsx、allowed-tags.test.tsx等可以验证上述每个安全行为。在生产环境中面向 AI 输出时请始终以内容不可信为前提将协议、域名、data: 图片逐项收紧并保留外链确认这道人机交互的最后防线。赞分享前端AI 应用UI组件【免费下载链接】streamdownA drop-in replacement for react-markdown, designed for AI-powered streaming.项目地址https://gitcode.com/gh_mirrors/stre/streamdown点击查看免费下载相关推荐Streamdown 完全指南面向 AI 流式渲染的 React Markdown 引擎Streamdown 完全指南面向 AI 流式渲染的 React Markdown 引擎 Streamdown 是一款专为 AI 流式输出设计的 React前端AI 应用UI组件ClawX Markdown 渲染安全与性能规范Streamdown 流式渲染的架构约束与验证基线ClawX Markdown 渲染安全与性能规范Streamdown 流式渲染的架构约束与验证基线 ClawX 将 OpenClaw CLI 的 AI 编排能人工智能AI 应用桌面应用交互助手为什么选择Streamdown5大核心优势让AI流式Markdown渲染更高效为什么选择Streamdown5大核心优势让AI流式Markdown渲染更高效 Streamdown是一款专为AI驱动的流式渲染设计的Markdown处理工具前端AI 应用UI组件上一篇5分钟掌握Spek免费开源音频频谱分析器的专业指南下一篇免费开源的音频频谱分析神器Spek终极使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考