资讯动态

Editor.js Sanitizer 模块完全指南:HTML 清洗 API、配置规则与源码实现

发布时间:2026/9/20 21:24:46 来源:尧图企业网站定制
Editor.js Sanitizer 模块完全指南HTML 清洗 API、配置规则与源码实现【免费下载链接】editor.jsA block-style editor with clean JSON output项目地址: https://gitcode.com/gh_mirrors/ed/editor.jsEditor.js 采用块级编辑器 干净 JSON 输出的设计理念其中 Sanitizer 模块负责把用户输入、粘贴内容或 Tool 产生的 HTML 字符串清洗为只保留白名单标签的安全内容。本文以 docs/sanitizer.md 为核心骨架结合 src/components/utils/sanitizer.ts 的源码实现、types/configs/sanitizer-config.d.ts 的类型定义以及 test/cypress/tests/sanitisation.cy.ts 的测试用例系统讲解 Sanitizer 模块的clean方法、配置规则语法、Tool 级 sanitize 配置的聚合机制以及它在保存、粘贴、合并等流程中的实际作用帮助你写出安全且格式完整的编辑器集成方案。Sanitizer 模块是什么Sanitizer 模块是 Editor.js 内建的一组用于清理污染字符串clear taint strings的方法集合。所谓污染字符串指包含未知、不可信或未在白名单内 HTML 标签与属性的字符串例如用户从网页复制的内容、外部系统导入的 HTML 片段或 Tool 内部渲染出的富文本。从源码结构看Sanitizer 的实现位于 src/components/utils/sanitizer.ts版本注释为2.0.0它底层基于轻量级 npm 包 html-janitor。Sanitizer 模块在项目中的典型应用场景包括保存输出前save()时对每个 Block 的 data 做递归清洗见 src/components/modules/saver.ts保证最终 JSON 输出是干净的粘贴内容时对粘贴进来的 HTML 片段按 Tool 的 sanitize 规则过滤插件开发时Tool 与 Tune 开发者通过this.api.sanitizer.clean()主动清洗自己处理的字符串。核心方法cleanSanitizer 模块对外暴露的唯一方法是clean(taintString, customConfig)其签名与说明如下clean(taintString, customConfig)清理传入的污染字符串Cleans up the passed taint string参数说明参数类型说明taintStringString需要被清洗的字符串HTML 片段customConfigObject每次调用可传入的新配置默认使用默认配置customConfig是可选的。从 src/components/utils/sanitizer.ts 的实现可以看到当未传配置时使用空对象作为默认值内部会把用户配置包装为{ tags: customConfig }后交给HTMLJanitor实例export function clean(taintString: string, customConfig: SanitizerConfig {} as SanitizerConfig): string { const sanitizerConfig { tags: customConfig, }; // API client can use custom config to manage sanitize process const sanitizerInstance new HTMLJanitor(sanitizerConfig); return sanitizerInstance.clean(taintString); }也就是说customConfig直接对应 html-janitor 的tags白名单只有出现在配置中的标签会被保留未声明的标签及其属性一律被剥离。通过 API 调用Sanitizer 以模块形式挂载到 Editor.js API 上见 src/components/modules/api/sanitizer.ts并在 API 对象中暴露为sanitizer字段docs/api.md 中有对应说明。API 类型定义见 types/api/sanitizer.d.tsexport interface Sanitizer { clean(taintString: string, config: SanitizerConfig): string; }在 Tool 或 Tune 内部典型的调用方式是this.api.sanitizer.clean(taintString, customConfig);实际示例结合 docs/api.md 的示例假设有一段不可信 HTMLlet taintString divp stylefont-size: 5em;b/bBlockWithTexta onclickvoid(0)/div let customConfig { b: true, p: { style: true, }, } this.api.sanitizer.clean(taintString, customConfig);在这个配置下清洗结果会保留b标签以及p的style属性而div、a、onclick等不在白名单中的内容都会被清除。这正是 Editor.js 保证输出 JSON 里只包含声明过的安全标记的机制。SanitizerConfig 配置语法配置规则由 types/configs/sanitizer-config.d.ts 定义。每一个标签的规则取值类型为export type TagConfig boolean | { [attr: string]: boolean | string }; export type SanitizerRule TagConfig | ((el: Element) TagConfig)规则形式一布尔值直接声明标签是否保留p: true // 保留 p 标签剥离其所有属性true表示保留标签本身但不保留任何属性false表示连标签一并清除。从 src/components/utils/sanitizer.ts 的cleanOneItem实现看当规则为false时等价于使用空配置清洗即清除全部标签function cleanOneItem(taintString: string, rule: SanitizerConfig | boolean): string { if (_.isObject(rule)) { return clean(taintString, rule); } else if (rule false) { return clean(taintString, {} as SanitizerConfig); } else { return taintString; } }规则形式二属性对象以对象形式声明要保留的属性属性值可以是true保留属性或固定字符串把属性强制改写为该值a: { href: true, // 保留 href 属性 rel: nofollow, // 强制 relnofollow target: _blank // 强制 target_blank }这正是 src/components/utils/sanitizer.ts 文件头部注释中的官方示例链接被强制加上relnofollow和target_blank这在防止外链 SEO 权重流失和防钓鱼场景中非常实用。规则形式三函数按元素动态返回规则当规则无法静态描述时可以传入一个接收元素、返回TagConfig的函数。以下是 types/configs/sanitizer-config.d.ts 中给出的几个典型场景// 只保留 target_blank 的 a 标签 a: function (aTag) { return aTag.target _blank; } // 只保留非空 u 标签 u: function (el) { return el.textContent ! ; } // 只对带 indent 类的 blockquote 保留 class 和 style 属性其余全部剥离 blockquote: function (el) { if (el.classList.contains(indent)) { return { class: true, style: true }; } else { return {}; } }这种函数式规则赋予配置极大的灵活性可以依据元素的实际内容、class、属性等运行时状态决定保留或剥离适合处理复杂富文本场景。配置文件示例把上述形式组合成一个完整的SanitizerConfigconst config { p: true, a: { href: true, rel: nofollow, target: _blank }, blockquote: function (el) { return el.classList.contains(indent) ? { class: true, style: true } : {}; }, u: function (el) { return el.textContent ! ; } };递归深清洗deepSanitize 的实现原理clean只负责清洗单个字符串而一个 Block 的data往往是对象套数组、数组套对象的嵌套结构。为此 src/components/utils/sanitizer.ts 提供了deepSanitize做递归清洗它按数据结构分为三种情况处理function deepSanitize(dataToSanitize: object | string, rules: SanitizerConfig): object | string { if (Array.isArray(dataToSanitize)) { // 数组对每个元素递归调用 return cleanArray(dataToSanitize, rules); } else if (_.isObject(dataToSanitize)) { // 对象继续深入清洗对象的每个字段 return cleanObject(dataToSanitize, rules); } else { // 原始值number | string | boolean仅对字符串执行清洗 if (_.isString(dataToSanitize)) { return cleanOneItem(dataToSanitize, rules); } return dataToSanitize; } }关键点有三数组cleanArray对每个元素再次调用deepSanitizesrc/components/utils/sanitizer.ts#L124-L126对象cleanObject遍历对象自有属性跳过原型链上的属性对每个字段按字段名在规则中查找专属规则找到则用字段级规则否则沿用父级规则src/components/utils/sanitizer.ts#L135-L156const ruleForItem isRule(rules[fieldName] as SanitizerConfig) ? rules[fieldName] : rules; cleanData[fieldName] deepSanitize(currentIterationItem, ruleForItem as SanitizerConfig);原始值只有字符串会被清洗数字、布尔值原样返回清洗时按规则类型分别处理对象规则直接清洗、false规则按空配置清洗、其余情况原样保留。isRule判定一个值是否属于合法的 HTML Janitor 规则src/components/utils/sanitizer.ts#L182-L184function isRule(config: SanitizerConfig): boolean { return _.isObject(config) || _.isBoolean(config) || _.isFunction(config); }从注释可以明确规则边界{ a: true }、{}、false、true、function(){}都是合法规则而undefined、null、0、1、2不是规则遇到这些值时会回退到父级配置。Tool 级 sanitize 配置的聚合机制除了手动调用cleanSanitizer 模块更核心的用途是在保存时按 Tool 的 sanitize 配置自动清洗每个 Block。这涉及三层配置来源的合并。第一层save 时按 Tool 取配置src/components/modules/saver.ts 在保存流程中调用sanitizeBlocks并通过一个回调函数按 Block 所属的 Tool 动态获取其 sanitize 配置const sanitizedData await sanitizeBlocks(extractedData, (name) { return Tools.blockTools.get(name).sanitizeConfig; });对应的sanitizeBlocks实现src/components/utils/sanitizer.ts#L44-L59逐块取出数据若配置为空则跳过该 Block否则用deepSanitize递归清洗export function sanitizeBlocks(blocksData, sanitizeConfig) { return blocksData.map((block) { const toolConfig _.isFunction(sanitizeConfig) ? sanitizeConfig(block.tool) : sanitizeConfig; if (_.isEmpty(toolConfig)) { return block; } block.data deepSanitize(block.data, toolConfig) as BlockToolData; return block; }); }第二层Block Tool 合并 Inline Tools 与 Tunes 的规则Tool 自身的 sanitize 配置由静态属性sanitize声明src/components/tools/base.ts 中将其定义为CommonInternalSettings.SanitizeConfig读取逻辑见 src/components/tools/base.ts#L232-L234。而 src/components/tools/block.ts 的sanitizeConfiggetter 会把 Tool 自己的规则与该 Tool 启用的所有 Inline Tools、所有 Block Tunes 的 sanitize 配置合并若 Tool 规则为空则直接返回合并基础配置若某字段规则是对象则与基础配置做Object.assign合并baseConfig优先、Tool 自己的规则覆盖_.cacheable public get sanitizeConfig(): SanitizerConfig { const toolRules super.sanitizeConfig; const baseConfig this.baseSanitizeConfig; if (_.isEmpty(toolRules)) { return baseConfig; } const toolConfig {} as SanitizerConfig; for (const fieldName in toolRules) { if (Object.prototype.hasOwnProperty.call(toolRules, fieldName)) { const rule toolRules[fieldName]; if (_.isObject(rule)) { toolConfig[fieldName] Object.assign({}, baseConfig, rule); } else { toolConfig[fieldName] rule; } } } return toolConfig; }baseSanitizeConfigsrc/components/tools/block.ts#L203-L216把全部内联工具与 Tunes 的 sanitize 配置聚合为一个基础配置_.cacheable public get baseSanitizeConfig(): SanitizerConfig { const baseConfig {}; Array.from(this.inlineTools.values()) .forEach(tool Object.assign(baseConfig, tool.sanitizeConfig)); Array.from(this.tunes.values()) .forEach(tune Object.assign(baseConfig, tune.sanitizeConfig)); return baseConfig; }这也印证了 docs/api.md 中的说明如果 Tool 启用了内联工具其 sanitize 规则会与你自己传入的自定义规则合并。例如段落 Tool 开启了加粗内联工具那么在保存段落时b标签会自动被保留无需在段落 Tool 的 sanitize 配置中重复声明。第三层API 的 clean 合并行为通过this.api.sanitizer.clean(taintString, config)手动清洗时Editor.js 提供的默认配置不含属性basic config without attributes你传入的config会与之合并后再执行清洗。这也是插件开发时保持输出一致性的关键行为。测试验证sanitize 在真实流程中的表现仓库的 Cypress 测试 test/cypress/tests/sanitisation.cy.ts 从多个角度验证了 Sanitizer 的实际行为可作为理解模块语义的最佳参照。场景一保存时保留白名单内的格式化段落内容bBold text/b通过editor.save()后输出仍为bBold text/b——因为段落 Tool 的内联工具配置声明了b标签test/cypress/tests/sanitisation.cy.ts#L8-L25。同样用户通过 UI 加粗后保存输出匹配/^bThis text should be bold\.(br)?\/b$/。场景二粘贴内容自动清洗粘贴 HTMLpText/ppbBold text/b/p后保存第二段的输出仍为bBold text/b说明粘贴流程同样经过 sanitizetest/cypress/tests/sanitisation.cy.ts#L55-L75。场景三Block 合并时剥离未声明标签这是最直观的 XSS 防护验证test/cypress/tests/sanitisation.cy.ts#L78-L116当两个段落 Block 合并时第二段中的span idtaint-htmlXSSspan因段落 Tool 的 sanitize 配置不支持span而被剥离最终合并文本为First blockSecond XSS block注释明确写道 Tool does not support spans in its sanitization config。与相关文档的衔接Sanitizer 配置贯穿多个模块本文只聚焦其本身相关的延伸阅读包括docs/api.mdAPI 层面的sanitizer.clean用法与合并规则说明docs/tools.mdBlock Tool、Inline Tool 的sanitize静态属性声明方式docs/usage.md 与 docs/installation.md编辑器初始化与整体使用流程docs/sanitizer.md本文的原始骨架文档。小结Editor.js Sanitizer 模块以白名单 递归深清洗为设计核心对外提供clean(taintString, customConfig)一个入口底层由 html-janitor 执行标签过滤对内通过deepSanitize对 Block data 的嵌套结构逐层清洗并在保存时按 Tool 聚合其自身、Inline Tools 与 Block Tunes 的 sanitize 规则。开发者既可以在 Tool 内用this.api.sanitizer.clean()主动清洗也可以依赖保存/粘贴流程中的自动清洗两者共同保证 Editor.js 输出的 JSON 始终只包含声明过的安全标记从源头降低 XSS 风险。【免费下载链接】editor.jsA block-style editor with clean JSON output项目地址: https://gitcode.com/gh_mirrors/ed/editor.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价