 如何拦截恶意指令对安全配置键的覆盖)
Mermaid 配置净化机制解析sanitize() 如何拦截恶意指令对安全配置键的覆盖【免费下载链接】mermaidGeneration of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown项目地址: https://gitcode.com/GitHub_Trending/me/mermaidMermaid 支持通过%%{init}%%指令在图内直接修改渲染配置这给渲染用户生成内容的应用程序带来了风险恶意构造的图可能试图覆盖安全策略或注入脚本。本文以官方 API 文档 sanitize.md 为主体结合 packages/mermaid/src/config.ts 的真实实现、packages/mermaid/src/utils/sanitizeDirective.ts 的配套防线与单元测试讲清楚sanitize()函数的职责、四条拦截规则、它在配置链路中的触发位置以及secure配置项如何配合mermaid.initialize实现安全键的只读保护。读完本文你可以理解 Mermaid 如何防御配置层面的原型污染与 XSS并知道在集成 Mermaid 时应把哪些关键配置锁定为 secure。1. 官方 API 文档对 sanitize() 的定义自动生成的 API 文档 docs/config/setup/config/functions/sanitize.md 给出的函数签名为sanitize(options): void定义位置packages/mermaid/src/config.ts功能确保 options 参数不会试图覆盖siteConfig中的 secure安全键。参数options类型为any即潜在的setConfig参数。返回值void。重要备注Remarks会原地in-place修改 options 对象——被判定为违规的键会被直接从传入的对象上delete掉而不是返回一份过滤后的副本。这个备注对集成方很关键任何把原始配置对象缓存起来、期望其内容不变的调用方都会在sanitize()执行后发现部分键消失了。2. sanitize() 的实现逐条解析sanitize()的完整实现位于 packages/mermaid/src/config.tsexport const sanitize (options: any) { if (!options) { return; } // Checking that options are not in the list of excluded options [secure, ...(siteConfig.secure ?? [])].forEach((key) { if (Object.hasOwn(options, key)) { // DO NOT attempt to print options[key] within ${} as a malicious script // can exploit the loggers attempt to stringify the value and execute arbitrary code log.debug(Denied attempt to modify a secure key ${key}, options[key]); delete options[key]; } }); // Check that there no attempts of prototype pollution Object.keys(options).forEach((key) { if (key.startsWith(__)) { delete options[key]; } }); // Check that there no attempts of xss, there should be no tags at all in the directive // blocking data urls as base64 urls can contain svgs with inline script tags Object.keys(options).forEach((key) { if ( typeof options[key] string (options[key].includes() || options[key].includes() || options[key].includes(url(data:)) ) { delete options[key]; } if (typeof options[key] object) { sanitize(options[key]); } }); };可以拆解为四条规则空值守卫options为null/undefined时直接返回不做任何处理。secure 键保护遍历[secure, ...(siteConfig.secure ?? [])]只要传入对象拥有Object.hasOwn精确匹配自有属性其中任何一个键就删除该键并打一条 debug 日志。注意两点secure键本身永远在保护名单中——图内指令永远不能修改哪些键是受保护的形成自我防御闭环源码中的注释特别说明日志中不要用模板字符串插值options[key]因为恶意脚本可以构造特殊值利用 logger 对值做字符串化时触发任意代码执行。这是典型的日志也是攻击面的防御细节。原型污染防御删除所有以__开头的键即__proto__一类阻止通过配置合并污染原型链。XSS 字符串过滤 递归任何字符串值若包含、或url(data:即被整体删除——指令中不允许出现任何标签而data:URL 被拦截是因为 base64 编码的 data URL 里可以藏带内联脚本的 SVG。最后对值为对象属性的键递归调用自身使上述规则对嵌套配置如flowchart: {...}同样生效。3. secure 配置项哪些键受保护secure是一个字符串数组类型的顶层配置项其类型定义见 packages/mermaid/src/config.type.ts/** * This option controls which currentConfig keys are considered secure and * can only be changed via call to mermaid.initialize. * This prevents malicious graph directives from overriding a sites default security. */ secure?: string[];注释阐明了设计意图secure 键只能通过mermaid.initialize修改防止恶意的图内指令directive覆盖站点默认的 security 策略。单元测试 packages/mermaid/src/config.spec.ts 中 should respect secure keys when applying directives 用例完整验证了这条链路const config_0: MermaidConfig { fontFamily: foo-font, securityLevel: strict, // cant be changed fontSize: 12345, // cant be changed secure: [...configApi.defaultConfig.secure!, fontSize], }; configApi.setSiteConfig(config_0); const directive: MermaidConfig { fontFamily: baf, fontSize: 54321, securityLevel: loose, }; configApi.addDirective(directive); const cfg: MermaidConfig configApi.getConfig(); expect(cfg.fontFamily).toEqual(directive.fontFamily); expect(cfg.fontSize).toBe(config_0.fontSize); expect(cfg.securityLevel).toBe(config_0.securityLevel);该用例说明defaultConfig自带一组默认 secure 键测试中通过defaultConfig.secure!展开站点可以再追加fontSize等自定义安全键随后图内指令虽然同时携带了fontFamily可改、fontSizesecure与securityLevelsecure但最终只有fontFamily生效。secure 机制在渲染逻辑中也有实际体现。例如流程图数据库 packages/mermaid/src/diagrams/flowchart/flowDb.ts 在边数超过maxEdges上限时抛出的错误信息明确写道Initialize mermaid with maxEdges set to a higher number to allow more edges. You cannot set this config via configuration inside the diagram as it is a secure config. You have to call mermaid.initialize.这正是secure名单把maxEdges类防御性阈值锁死的直接结果——防止恶意图通过指令调高限制来放大资源消耗。4. 调用链路sanitize() 在何处被触发从源码结构看sanitize()是配置合并流水线updateCurrentConfig的前置过滤器packages/mermaid/src/config.tsconst updateCurrentConfig (siteCfg: MermaidConfig, _directives: MermaidConfig[]) { let cfg: MermaidConfig assignWithDepth({}, siteCfg); let sumOfDirectives: MermaidConfig {}; for (const d of _directives) { sanitize(d); // ← 每条指令先净化再深度合并 sumOfDirectives assignWithDepth(sumOfDirectives, d); } cfg assignWithDepth(cfg, sumOfDirectives); // ...theme 处理... currentConfig cfg; checkConfig(currentConfig); return currentConfig; };由此得到三条主要触发路径入口路径说明图内指令addDirective()→updateCurrentConfig(siteConfig, directives)→sanitize(d)每条%%{init}%%指令在参与合并前逐条净化addDirective()内部还会先调用姊妹函数sanitizeDirective()做白名单校验见第 5 节编程式配置setConfig(conf)→updateCurrentConfig(currentConfig, [conf])→sanitize(conf)见 config.tssetConfig被 packages/mermaid/src/mermaidAPI.ts 以setConfig: configApi.setConfig的形式暴露到 mermaid API 上重置reset(config?)→updateCurrentConfig(config, [])指令清空后以 siteConfig 重建 currentConfig需要区分的是setSiteConfig()/updateSiteConfig()不走sanitize()。这是有意的设计——sanitize()保护的是从指令/setConfig进入 currentConfig 的数据而 siteConfig 属于站点侧受信任的输入只能通过应用自身的mermaid.initialize调用建立。5. 第二道防线sanitizeDirective() 的白名单校验sanitize()之外packages/mermaid/src/utils/sanitizeDirective.ts 中的sanitizeDirective()对指令 JSON 做更严格的清洗由addDirective()在sanitize之前调用。两者分工互补键级白名单删除__前缀键、键名中含proto/constr的键、不在configKeys源自 packages/mermaid/src/defaultConfig.js 派生的合法键集合中的键以及值为null的键。相比sanitize()只拦__前缀这里进一步把任何拼上未知键的尝试都丢弃。字典型配置的取值校验像 sankey 的nodeColors、treeView 的filenameIcons/extensionIcons这类键由用户任意定义的配置不查键名而是对取值做模式匹配如nodeColors必须是#hex/rgb(...)/hsl(...)/ 命名色可疑条目直接删除sanitizeDirective.ts。CSS 相关键的括号平衡检查themeCSS、fontFamily、altFontFamily等键经sanitizeCss()校验大括号不平衡时整体替换为{ /* ERROR: Unbalanced CSS */ }占位防止截断的 CSS 逃逸出预期作用域。themeVariables 字符白名单值不匹配/^[\d #%(),.;A-Za-z]$/时被置空杜绝样式变量里夹带特殊字符。仓库的 e2e 目录还包含xss与ghsa系列测试页面如 e2e/other/xss.spec.js从测试组织上可见团队对指令注入 → 渲染输出这条攻击路径的持续回归验证配合 docs/community/security.md 中关于 DOMPurify 默认基线配置与漏洞上报流程的说明sanitize()/sanitizeDirective()属于配置层防御DOMPurify 属于输出层防御二者共同构成 XSS 缓解体系。6. 集成实践建议把安全策略写入mermaid.initializesecurityLevelstrict/loose/antiscript/sandbox见 config.type.ts以及maxEdges等防御性阈值应通过initialize设定并列入secure数组而不是依赖图内指令。利用默认 secure 名单再追加defaultConfig已内置一组 secure 键测试用例中以defaultConfig.secure!展开追加即为官方推荐写法站点侧只需补充自己关心的键。注意原地修改语义sanitize()会直接delete调用方传入对象的键若在业务代码中复用同一配置对象净化后再使用不要假设对象内容不变。不要绕过setSiteConfig不受sanitize约束意味着应用代码传入initialize的内容是受信任的若这部分内容来自用户输入应用需自行先做校验。7. 延伸阅读本仓库的相关文档与源码API 文档本文主体docs/config/setup/config/functions/sanitize.md同目录下还有 setConfig.md、addDirective.md、reset.md 等配套函数文档实现源码packages/mermaid/src/config.tssanitize/updateCurrentConfig、packages/mermaid/src/utils/sanitizeDirective.ts类型定义packages/mermaid/src/config.type.tssecure键行为验证packages/mermaid/src/config.spec.ts、packages/mermaid/src/config.usecase.spec.ts配置层使用约束示例packages/mermaid/src/diagrams/flowchart/flowDb.ts安全总览docs/community/security.md【免费下载链接】mermaidGeneration of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown项目地址: https://gitcode.com/GitHub_Trending/me/mermaid创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考