HTML 清理器 API

社区工作组草案报告,

此版本:
https://wicg.github.io/sanitizer-api/
问题跟踪:
GitHub
编辑:
Frederik Braun (Mozilla)
Mario Heiderich (Cure53)
Daniel Vogelheim (Google LLC)
Tom Schuster (Mozilla)

摘要

本文档规定了一组 API,使开发者可以接收不受信任的 HTML 输入并将其净化,以便安全地插入到文档的 DOM 中。

本文档状态

本规范由 Web Platform Incubator Community Group 发布。 它不是 W3C 标准,也不在 W3C 标准轨道上。 请注意,依据 W3C Community Contributor License Agreement (CLA), 存在有限的退出选项,且适用其他条件。 了解更多: W3C 社区与商业工作组

1. 引言

本节为非规范性内容。

Web 应用经常需要在客户端处理 HTML 字符串, 可能作为客户端模板解决方案的一部分,也可能作为渲染用户生成内容的一部分等。要以安全方式做到这一点很困难。 将字符串简单拼接后塞入 ElementinnerHTML 的朴素做法充满风险,因为它可能以多种出人意料的方式触发 JavaScript 执行。

诸如 [DOMPURIFY] 之类的库尝试 通过在插入前仔细解析并净化字符串、构建 DOM 并通过允许列表过滤其成员来管理这一问题。 但事实证明这是一种脆弱的方法,因为暴露给 Web 的解析 API 并不总能以合理方式映射到浏览器在“真实” DOM 中实际渲染字符串时的行为。 此外,这些库需要随时间跟进浏览器行为的变化;曾经安全的做法可能会因新的平台级特性而变成定时炸弹。

浏览器相当清楚自己何时会执行代码。我们可以通过教会浏览器如何以安全方式从任意字符串渲染 HTML 来改进用户空间库, 并以更有可能随着浏览器自身解析器实现变化而维护与更新的方式来实现。本文档概述了一个旨在完成此目标的 API。

1.1. 目标

1.2. API 概览

Sanitizer API 提供了将包含 HTML 的字符串解析为 DOM 树的功能,并按用户提供的配置过滤生成的树。方法以两种风格成对提供:

2. 框架

2.1. 清理器 API

Element 接口定义了两个方法,setHTML()setHTMLUnsafe()。 二者均接收包含 HTML 标记的 DOMString 与可选配置。

partial interface Element {
  [CEReactions] undefined setHTMLUnsafe((TrustedHTML or DOMString) html, optional SetHTMLUnsafeOptions options = {});
  [CEReactions] undefined setHTML(DOMString html, optional SetHTMLOptions options = {});
};
ElementsetHTMLUnsafe(html, options) 方法步骤为:
  1. compliantHTML 为调用 Get Trusted Type compliant string 算法,传入 TrustedHTMLthisrelevant global objecthtml、"Element setHTMLUnsafe" 与 "script" 的结果。

  2. thistemplate 元素,则令 target 为其 template contents;否则为 this 本身。

  3. 设置并 过滤 HTML,给定 targetthiscompliantHTMLoptions 和 false。

ElementsetHTML(html, options) 方法步骤为:
  1. thistemplate, 则令 target 为其 template contents;否则为 this

  2. 设置并 过滤 HTML,给定 targetthishtmloptions 和 true。

partial interface ShadowRoot {
  [CEReactions] undefined setHTMLUnsafe((TrustedHTML or DOMString) html, optional SetHTMLUnsafeOptions options = {});
  [CEReactions] undefined setHTML(DOMString html, optional SetHTMLOptions options = {});
};

这些方法也在 ShadowRoot 上提供:

ShadowRootsetHTMLUnsafe(html, options) 方法步骤为:
  1. compliantHTML 为调用 Get Trusted Type compliant string 算法,传入 TrustedHTMLthisrelevant global objecthtml、 "ShadowRoot setHTMLUnsafe" 与 "script" 的结果。

  2. 设置并 过滤 HTML,使用 thisthisshadow host(作为上下文元素)、 compliantHTMLoptions 和 false。

ShadowRootsetHTML(html, options) 方法步骤为:
  1. 设置并 过滤 HTML,使用 this(作为目标)、this(作为上下文 元素)、 htmloptions 和 true。

Document 接口新增两个用于解析整个 Document 的方法:

partial interface Document {
  static Document parseHTMLUnsafe((TrustedHTML or DOMString) html, optional SetHTMLUnsafeOptions options = {});
  static Document parseHTML(DOMString html, optional SetHTMLOptions options = {});
};
parseHTMLUnsafe(html, options) 方法步骤为:
  1. compliantHTML 为调用 Get Trusted Type compliant string 算法,传入 TrustedHTMLcurrent global objecthtml、"Document parseHTMLUnsafe" 与 "script" 的结果。

  2. document 为一个新的 Document, 其 content type 为 "text/html"。

    注意:由于 document 没有浏览上下文,脚本被禁用。

  3. documentallow declarative shadow roots 设为 true。

  4. 给定 documentcompliantHTML从字符串解析 HTML

  5. sanitizer 为使用 options 和 false 调用 根据选项获取一个 sanitizer 实例 的结果。

  6. document 上以 sanitizer 和 false 调用 sanitize

  7. 返回 document

parseHTML(html, options) 方法步骤为:
  1. document 为一个新的 Document, 其 content type 为 "text/html"。

    注意:由于 document 没有浏览上下文,脚本被禁用。

  2. documentallow declarative shadow roots 设为 true。

  3. 给定 documenthtml从字符串解析 HTML

  4. sanitizer 为使用 options 和 true 调用 根据选项获取一个 sanitizer 实例 的结果。

  5. document 上以 sanitizer 和 true 调用 sanitize

  6. 返回 document

2.2. SetHTML 选项与配置对象。

一系列类似 setHTML() 的 方法都接收一个选项字典。当前仅定义了该字典的一个成员:

enum SanitizerPresets { "default" };
dictionary SetHTMLOptions {
  (Sanitizer or SanitizerConfig or SanitizerPresets) sanitizer = "default";
};
dictionary SetHTMLUnsafeOptions {
  (Sanitizer or SanitizerConfig or SanitizerPresets) sanitizer = {};
};

Sanitizer 配置对象封装了过滤配置。同一份配置可用于 “安全” 与“不安全” 方法,其中“安全”方法会对传入配置隐式执行 removeUnsafe 操作,并在未传入配置时使用默认配置。默认配置在“安全”与“不安全”方法间不同:“安全”方法以默认安全为目标,使用更严格的默认;而“不安全”方法默认不受限制。配置的使用意图是:在页面生命周期早期构建一份(或少数几份)配置,此后按需复用。这允许实现对配置进行预处理。

配置对象可被查询以返回配置字典,也可直接修改。

[Exposed=Window]
interface Sanitizer {
  constructor(optional (SanitizerConfig or SanitizerPresets) configuration = "default");

  // Query configuration:
  SanitizerConfig get();

  // Modify a Sanitizer’s lists and fields:
  boolean allowElement(SanitizerElementWithAttributes element);
  boolean removeElement(SanitizerElement element);
  boolean replaceElementWithChildren(SanitizerElement element);
  boolean allowAttribute(SanitizerAttribute attribute);
  boolean removeAttribute(SanitizerAttribute attribute);
  boolean setComments(boolean allow);
  boolean setDataAttributes(boolean allow);

  // Remove markup that executes script.
  boolean removeUnsafe();
};

Sanitizer 具有一个关联的 SanitizerConfig configuration

constructor(configuration) 方法步骤为:
  1. 如果 configurationSanitizerPresetsstring,则:

    1. 断言configuration default

    2. configuration 设为 内置安全默认配置

  2. valid 为在 this 上以 configuration 和 true 调用 设置配置 的返回值。

  3. valid 为 false,则抛出 TypeError

get() 方法的步骤为:
注意: 在 get() 方法之外,Sanitizer 的元素和属性的顺序是不可观察的。 通过对该方法的结果进行显式排序,我们让实现有机会进行优化,例如在内部使用无序集合。
  1. configthisconfiguration

  2. 断言config有效的

  3. 如果 config["elements"] 存在

    1. 对于任意 element 属于 config["elements"]:

      1. 如果 element["attributes"] 存在

        1. element["attributes"] 设为对 element["attributes"] 按升序排序 的结果, 其中比较准则为 attrA 小于项 attrB

      2. 如果 element["removeAttributes"] 存在

        1. element["removeAttributes"] 设为对 element["removeAttributes"] 按升序排序 的结果, 其中比较准则为 attrA 小于项 attrB

    2. config["elements"] 设为对 按升序排序 config["elements"] 的结果, 其中比较准则为 elementA 小于项 elementB

  4. 否则:

    1. config["removeElements"] 设为对 按升序排序 config["removeElements"] 的结果, 其中比较准则为 elementA 小于项 elementB

  5. 如果 config["replaceWithChildrenElements"] 存在

    1. config["replaceWithChildrenElements"] 设为对 按升序排序 config["replaceWithChildrenElements"] 的结果, 其中比较准则为 elementA 小于项 elementB

  6. 如果 config["attributes"] 存在

    1. config["attributes"] 设为对 按升序排序 config["attributes"] 的结果, 其中比较准则为 attrA 小于项 attrB

  7. 否则:

    1. config["removeAttributes"] 设为对 按升序排序 config["removeAttributes"] 的结果, 其中比较准则为 attrA 小于项 attrB

  8. 返回 config

allowElement(element) 方法的步骤为:
注意: 该算法相对复杂,因为元素允许列表可能为属性指定按元素的允许或移除列表。 这需要我们区分 4 种情况:
  • 我们是拥有全局的允许列表还是全局的移除列表,以及

  • 这些列表是否已经包含 element

  1. configurationthisconfiguration

  2. 断言configuration有效的

  3. element 设为以 规范化带属性的 sanitizer 元素 处理 element 的结果。

  4. 如果 configuration["elements"] 存在

    1. modified 设为从 configuration["replaceWithChildrenElements"] 中 移除 element 的结果。

    2. 注释:我们需要确保按元素的属性不会与全局属性重叠。

    3. 如果 configuration["attributes"] 存在

      1. 如果 element["attributes"] 存在

        1. element["attributes"] 设为 从 element["attributes"] 中 去重 的结果。

        2. element["attributes"] 设为 差集element["attributes"] 与 configuration["attributes"]。

        3. 如果 configuration["dataAttributes"] 为 true:

          1. 移除 所有 item,这些 item 属于 element["attributes"], 且 item自定义数据属性

      2. 如果 element["removeAttributes"] 存在

        1. element["removeAttributes"] 设为 从 element["removeAttributes"] 中 去重 的结果。

        2. element["removeAttributes"] 设为 交集element["removeAttributes"] 与 configuration["attributes"]。

    4. 否则:

      1. 如果 element["attributes"] 存在

        1. element["attributes"] 设为 从 element["attributes"] 中 去重 的结果。

        2. element["attributes"] 设为 差集element["attributes"] 与 element["removeAttributes"] 带默认值 « »。

        3. 移除 element["removeAttributes"]。

        4. element["attributes"] 设为 差集element["attributes"] 与 configuration["removeAttributes"]。

      2. 如果 element["removeAttributes"] 存在

        1. element["removeAttributes"] 设为 从 element["removeAttributes"] 中 去重 的结果。

        2. element["removeAttributes"] 设为 差集element["removeAttributes"] 与 configuration["removeAttributes"]。

    5. 如果 configuration["elements"] 不 包含 element

      1. 注释:这是拥有全局允许列表但尚未包含 element 的情况。

      2. 追加 elementconfiguration["elements"]。

      3. 返回 true。

    6. 注释:这是拥有全局允许列表且已包含 element 的情况。

    7. current elementconfiguration["elements"] 中的 item,其满足 item[name] 等于 element[name] 且 item[namespace] 等于 element[namespace]。

    8. 如果 element 等于 current element,则返回 modified

    9. configuration["elements"] 中移除 element

    10. 追加 elementconfiguration["elements"]

    11. 返回 true。

  5. 否则:

    1. 如果 element["attributes"] 存在element["removeAttributes"] 带默认值 « » 非

      1. 用户代理可以 向控制台报告一个警告,指出该操作不受支持。

      2. 返回 false。

    2. modified 设为从 configuration["replaceWithChildrenElements"] 中 移除 element 的结果。

    3. 如果 configuration["removeElements"] 不 包含 element

      1. 注释:这是拥有全局移除列表但不包含 element 的情况。

      2. 返回 modified

    4. 注释:这是拥有全局移除列表且包含 element 的情况。

    5. configuration["removeElements"] 中移除 element

    6. 返回 true。

removeElement(element) 方法步骤为 使用 elementthisconfiguration移除一个元素
replaceElementWithChildren(element) 方法的步骤为:
  1. configurationthisconfiguration

  2. 断言configuration有效的

  3. element 设为以 规范化 sanitizer 元素 处理 element 的结果。

  4. 如果 configuration["replaceWithChildrenElements"] 包含 element

    1. 返回 false。

  5. configuration["removeElements"] 中移除 element

  6. configuration["elements"] 列表中移除 element

  7. 添加 elementconfiguration["replaceWithChildrenElements"]。

  8. 返回 true。

allowAttribute(attribute) 方法的步骤为:
注意: 该方法区分两种情况,即我们拥有全局允许列表或全局移除列表。 如果将 attribute 添加到全局允许列表,我们可能需要做额外工作来修正按元素的允许或移除列表,以维持我们的有效性准则。
  1. configurationthisconfiguration

  2. 断言configuration有效的

  3. attribute 设为以 规范化 sanitizer 属性 处理 attribute 的结果。

  4. 如果 configuration["attributes"] 存在

    1. 注释:如果我们有全局允许列表,需要添加 attribute

    2. 如果 configuration["dataAttributes"] 为 true 且 attribute自定义数据属性,则返回 false。

    3. 如果 configuration["attributes"] 包含 attribute, 则返回 false。

    4. 注释:修正按元素的允许和移除列表。

    5. 如果 configuration["elements"] 存在

      1. 对每个 element 属于 configuration["elements"]:

        1. 如果 element["attributes"] 带默认值 « » 包含 attribute

          1. element["attributes"] 中移除 attribute

        2. 断言element["removeAttributes"] 带默认值 « » 不 包含 attribute

    6. 追加 attributeconfiguration["attributes"]

    7. 返回 true。

  5. 否则:

    1. 注释:如果我们有全局移除列表,需要移除 attribute

    2. 如果 configuration["removeAttributes"] 不 包含 attribute

      1. 返回 false。

    3. configuration["removeAttributes"] 中移除 attribute

    4. 返回 true。

removeAttribute(attribute) 方法步骤为 使用 attributethisconfiguration移除一个属性
setComments(allow) 方法的步骤为:
  1. configurationthisconfiguration

  2. 断言configuration有效的

  3. 如果 configuration["comments"] 存在configuration["comments"] 等于 allow,则返回 false;

  4. configuration["comments"] 设为 allow

  5. 返回 true。

setDataAttributes(allow) 方法的步骤为:
  1. configurationthisconfiguration

  2. 断言configuration有效的

  3. 如果 configuration["attributes"] 不 存在,则返回 false。

  4. 如果 configuration["dataAttributes"] 等于 allow,则返回 false。

  5. 如果 allow 为 true:

    1. 移除 任意 attr,这些 attr 属于 configuration["attributes"], 且 attr自定义数据属性

    2. 如果 configuration["elements"] 存在

      1. 对每个 element 属于 configuration["elements"]:

        1. 如果 element[attributes] 存在

          1. 移除 任意 attr,这些 attr 属于 element[attributes], 且 attr自定义数据属性

  6. configuration["dataAttributes"] 设为 allow

  7. 返回 true。

removeUnsafe() 方法步骤为 将 thisconfiguration 更新为对 thisconfiguration 调用 remove unsafe 的结果。

2.3. 配置字典

dictionary SanitizerElementNamespace {
  required DOMString name;
  DOMString? _namespace = "http://www.w3.org/1999/xhtml";
};

// Used by "elements"
dictionary SanitizerElementNamespaceWithAttributes : SanitizerElementNamespace {
  sequence<SanitizerAttribute> attributes;
  sequence<SanitizerAttribute> removeAttributes;
};

typedef (DOMString or SanitizerElementNamespace) SanitizerElement;
typedef (DOMString or SanitizerElementNamespaceWithAttributes) SanitizerElementWithAttributes;

dictionary SanitizerAttributeNamespace {
  required DOMString name;
  DOMString? _namespace = null;
};
typedef (DOMString or SanitizerAttributeNamespace) SanitizerAttribute;

dictionary SanitizerConfig {
  sequence<SanitizerElementWithAttributes> elements;
  sequence<SanitizerElement> removeElements;
  sequence<SanitizerElement> replaceWithChildrenElements;

  sequence<SanitizerAttribute> attributes;
  sequence<SanitizerAttribute> removeAttributes;

  boolean comments;
  boolean dataAttributes;
};

2.4. 配置不变式

配置可以且应该由开发者根据其用途进行修改。可选方式包括从零开始编写新的配置字典,通过修改方法修改已有Sanitizer的配置,或用get()得到已有Sanitizer配置字典,修改后再创建新Sanitizer实例。

空配置(当用“unsafe”方法如setHTMLUnsafe调用时)允许所有内容。 配置"default"包含内置安全默认配置。 注意“safe”和“unsafe”净化方法有不同的默认值。

并非所有配置字典都是有效的。有效配置应避免冗余(如重复允许同一元素)和矛盾(如同时移除和允许同一元素)。

配置要有效,需满足如下条件:

elements 元素允许列表还可以为给定元素指定允许或移除的属性。这意在映射到 [HTML] 的结构,其同时包含 全局属性 和适用于特定元素的局部 属性。全局与局部属性可以混用,但请注意,如果某个配置存在歧义——某属性在一处被允许而在另一处被禁止——通常视为无效。

全局 attributes 全局 removeAttributes
局部 attributes 若属性在任一列表中匹配,则允许该属性。不得有重复条目。 只有当属性在局部允许列表中时才允许。 全局移除与局部允许列表之间不得有重复条目。 注意:全局移除列表对这个特定元素不起作用,但可能适用于没有局部允许列表的其他元素。
局部 removeAttributes 若属性在全局允许列表中且不在局部移除列表中,则允许该属性。局部移除必须是全局允许列表的子集。 若属性既不在全局移除列表也不在局部移除列表中,则允许该属性。 全局移除与局部移除列表之间不得有重复条目。

请注意其中的不对称性:大多数情况下,全局与逐元素列表之间不允许有重复;但当存在全局允许列表与逐元素移除列表时,后者必须是前者的子集。上表中仅聚焦重复条目的摘录如下:

全局 attributes 全局 removeAttributes
局部 attributes 不允许有重复条目。 不允许有重复条目。
局部 removeAttributes 局部移除必须是全局允许列表的子集。 不允许有重复条目。

dataAttributes 设置允许 自定义数据属性。若将 dataAttributes 视为一个允许列表,则上述规则可直接延伸到 自定义数据属性

全局 attributesdataAttributes 已设置
局部 attributes 所有 自定义数据属性 都被允许。任何 自定义数据属性 都不得被列入任何允许列表,否则将造成重复条目。
局部 removeAttributes 除非被列入局部移除列表,否则 自定义数据属性 是被允许的。 任何 自定义数据属性 都不得被列入全局允许列表,否则将造成重复条目。

用文字表述这些规则:

SanitizerConfig config 在以下所有条件均满足时,被认为是有效
  1. config 具有 elementsremoveElements 之一,但不能同时存在两者。

  2. config 具有 attributesremoveAttributes之一,但不能同时存在两者。

  3. 断言config 中所有 SanitizerElementNamespaceWithAttributesSanitizerElementNamespaceSanitizerAttributeNamespace 条目均为规范化的,即分别经过 规范化 sanitizer 元素规范化 sanitizer 属性 的处理(按需)。

  4. config[elements]、 config[removeElements]、 config[replaceWithChildrenElements]、 config[attributes] 或 config[removeAttributes] 中,若其存在, 则均不得存在重复项

  5. 如果同时存在 config[elements] 与 config[replaceWithChildrenElements], 则 config[elements] 与 config[replaceWithChildrenElements] 的交集为空

  6. 如果同时存在 config[removeElements] 与 config[replaceWithChildrenElements], 则 config[removeElements] 与 config[replaceWithChildrenElements] 的交集为空

  7. 如果 config[attributes] 存在

    1. 如果 config[elements] 存在

      1. 对于每个 config[elements] 中的 element

        1. element[attributes] 与 element[removeAttributes] (若存在)均不得存在重复项

        2. config[attributes] 与 element[attributes] (带默认值) « » 的交集为空

        3. element[removeAttributes] (带默认值) « » 是 config[attributes] 的子集

        4. 如果 dataAttributes 存在dataAttributes 为 true:

          1. element[attributes] 不得包含 自定义数据属性

    2. 如果 dataAttributes 为 true:

      1. config[attributes] 不得包含 自定义数据属性

  8. 如果 config[removeAttributes] 存在

    1. 如果 config[elements] 存在, 则对于每个 config[elements] 中的 element

      1. element[attributes] 与 element[removeAttributes] 不得同时存在

      2. element[attributes] 与 element[removeAttributes] (若存在)均不得存在重复项

      3. config[removeAttributes] 与 element[attributes] (带默认值) « » 的交集为空

      4. config[removeAttributes] 与 element[removeAttributes] (带默认值) « » 的交集为空

    2. config[dataAttributes] 不得存在

注意:从字典设置配置将进行一定的规范化处理。特别地,如果允许列表与移除列表都缺失,则将其解释为一个空的移除列表。所以 {} 本身不是一个有效的配置,但它会被规范化为 {removeElements:[],removeAttributes:[]},这是有效的。选择此规范化步骤是为了让缺失的字典与空字典保持一致,即让 setHTMLUnsafe(txt)setHTMLUnsafe(txt, {sanitizer: {}}) 的行为一致。

3. 算法

若要设置并过滤 HTML,给定一个 ElementDocumentFragment target,一个 Element contextElement,一个 字符串 html,一个 字典 options 和一个 布尔值 safe
  1. 如果 safe 为真且 contextElement本地名 为 "script",且 contextElement命名空间HTML 命名空间SVG 命名空间,则返回。

  2. sanitizer 为调用 根据 options 获取 sanitizer 实例 的结果,参数为 optionssafe

  3. newChildren 为根据 HTML 片段解析算法,传入 contextElementhtml 和 true 的结果。

  4. fragment 为一个新的 DocumentFragment, 其 节点文档contextElement节点文档

  5. 遍历 newChildren 中的每一个 node追加 nodefragment

  6. fragment 使用 sanitizersafe 执行 sanitize

  7. targetfragment 替换所有

要从带有 字典 options(且含有 布尔值 safe)中 从选项获取一个 sanitizer 实例

注意: 该算法同时适用于 SetHTMLOptionsSetHTMLUnsafeOptions。 它们仅在默认值上有所不同。

  1. sanitizerSpec 为 "default"。

  2. 如果 options["sanitizer"] 存在,则:

    1. sanitizerSpec 设为 options["sanitizer"]。

  3. 断言sanitizerSpec 要么是一个 Sanitizer 实例, 要么是一个作为 SanitizerPresets 成员的 字符串,或者是一个 字典

  4. 如果 sanitizerSpec字符串

    1. 断言sanitizerSpec 等于 "default"

    2. sanitizerSpec 设为 内置的安全默认配置

  5. 断言sanitizerSpec 要么是一个 Sanitizer 实例, 要么是一个 字典

  6. 如果 sanitizerSpec 是一个 字典

    1. sanitizer 为一个新的 Sanitizer 实例。

    2. setConfigurationResult 为在 sanitizer 上,以 sanitizerSpec safe 作为参数调用 设置配置 的结果。

    3. 如果 setConfigurationResult 为 false,则抛出 TypeError

    4. sanitizerSpec 设为 sanitizer

  7. 断言sanitizerSpec 是一个 Sanitizer 实例。

  8. 返回 sanitizerSpec

3.1. 净化

对于主要的 sanitize 操作,给定一个 ParentNode node、一个 Sanitizer sanitizer,以及一个 boolean safe,执行以下步骤:
  1. configurationsanitizerconfiguration 的值。

  2. 断言configuration有效的

  3. 如果 safe 为 true,则将 configuration 设为在 configuration 上调用 remove unsafe 的结果。

  4. nodeconfiguration 上调用 sanitize core, 并将 handleJavascriptNavigationUrls 设为 safe

sanitize core 操作, 使用一个 ParentNode node、一个 SanitizerConfig configuration,以及一个 boolean handleJavascriptNavigationUrls,从 node 开始递归遍历 DOM 树。其步骤如下:
  1. 对于每个 child 属于 nodechildren

    1. 断言child 实现TextCommentElementDocumentType

      注意: 目前,此算法仅在 HTML 解析器的输出上被调用,在该情形下该断言应当成立。DocumentType 只应出现在 parseHTMLparseHTMLUnsafe 中。若未来该算法用于不同的上下文,则需要重新审视这一假设。

    2. 如果 child 实现DocumentType, 则继续

    3. 如果 child 实现Text, 则继续

    4. 如果 child 实现Comment

      1. 如果 configuration["comments"] 不为 true, 则移除 child

    5. 否则:

      1. elementName 为一个 SanitizerElementNamespace, 其由 child本地名称命名空间 组成。

      2. 如果 configuration["replaceWithChildrenElements"] 存在, 且 configuration["replaceWithChildrenElements"] 包含 elementName

        1. child 上,以 configurationhandleJavascriptNavigationUrls 调用 sanitize core

        2. 调用 replace all,以 childchildren 替换 child 内的所有内容。

        3. 继续

      3. 如果 configuration["elements"] 存在

        1. 如果 configuration["elements"] 不 包含 elementName

          1. 移除 child

          2. 继续

      4. 否则:

        1. 如果 configuration["removeElements"] 包含 elementName

          1. 移除 child

          2. 继续

      5. 如果 elementName 等于 «[ "name" → "template", "namespace" → HTML 命名空间 ]», 则在 childtemplate contents 上,以 configurationhandleJavascriptNavigationUrls 调用 sanitize core

      6. 如果 child 是一个 shadow host, 则在 childshadow root 上,以 configurationhandleJavascriptNavigationUrls 调用 sanitize core

      7. elementWithLocalAttributes 为 « [] »。

      8. 如果 configuration["elements"] 存在configuration["elements"] 包含 elementName

        1. elementWithLocalAttributes 设为 configuration["elements"][elementName]。

      9. 对于每个 attribute 属于 child属性列表

        1. attrName 为一个 SanitizerAttributeNamespace, 其由 attribute本地名称命名空间 组成。

        2. 如果 elementWithLocalAttributes["removeAttributes"] 带默认值 « » 包含 attrName

          1. 移除 attribute

        3. 否则,若 configuration["attributes"] 存在

          1. 如果 configuration["attributes"] 不 包含 attrName,且 elementWithLocalAttributes["attributes"] 带默认值 « » 不 包含 attrName,并且如果 "data-" 不是 代码单元前缀(针对 attribute本地名称), 且 命名空间 不为 null,或者 configuration["dataAttributes"] 不为 true:

            1. 移除 attribute

        4. 否则:

          1. 如果 elementWithLocalAttributes["attributes"] 存在elementWithLocalAttributes["attributes"] 不 包含 attrName

            1. 移除 attribute

          2. 否则,若 configuration["removeAttributes"] 包含 attrName

            1. 移除 attribute

        5. 如果 handleJavascriptNavigationUrls

          1. 如果 «[elementName, attrName]» 匹配 内置“导航型 URL 属性”列表 中的一项,且 attribute 包含一个 javascript: URL,则移除 attribute

          2. 如果 child命名空间 MathML 命名空间attr本地名称 "href",且 attr命名空间nullXLink 命名空间,并且 attr 包含一个 javascript: URL, 则移除 attribute

          3. 如果 内置“动画型 URL 属性”列表 包含 «[elementName, attrName]»,且 attr "href" 或 "xlink:href",则移除 attribute

      10. child 上,以 configurationhandleJavascriptNavigationUrls 调用 sanitize core

注意: 现代浏览器只在导航时支持 javascript: URL。 由于导航本身不是 XSS 威胁,我们只处理导航到 javascript: URL,不处理一般的导航。

声明式导航主要包括以下几类:

  1. 锚点元素。(HTML 与 SVG 命名空间中的 <a>

  2. 作为表单 action 导致导航的表单元素。

  3. [MathML] 允许 任意元素作为锚点

  4. [SVG11] 动画。

前两类由 内置导航 URL 属性列表 覆盖。

MathML 情况单独处理,因为本规范中没有"按命名空间全局"规则的形式化描述。

SVG 动画情况由 内置动画 URL 属性列表覆盖。但由于 SVG 动画元素的解释依赖于动画目标,且净化时无法知道最终目标,sanitize 算法会阻止对 href 属性的任何动画。

要判断 attribute 是否 包含 javascript: URL
  1. url 为对 attribute,执行 基本 URL 解析器 的结果。

  2. 如果 urlfailure,返回 false。

  3. 返回 urlscheme 是否 "javascript"。

3.2. 修改配置

配置修改方法是 Sanitizer 上用于修改其配置的方法。 这些方法会保持有效性准则。 它们返回布尔值,指示调用者配置是否已被修改。

let s = new Sanitizer({elements: ["div"]});
s.allowElement("p"); // 返回 true。
div.setHTML("<div><p>", {sanitizer: s});  // 允许 `<div>` 和 `<p>`。
let s = new Sanitizer({elements: ["div"]});
s.removeElement("p");  // 返回 false,因为 <p> 之前未被允许。
div.setHTML("<div><p>", {sanitizer: s});  // 允许 `<div>`,`<p>` 被移除。
要从 SanitizerConfig configuration移除一个元素 SanitizerElement element
注意: 此方法需要我们区分 4 种情况:
  • 我们是拥有全局允许列表还是全局移除列表,

  • 以及它们是否已经包含 element

  1. 断言configuration有效的

  2. element 设为以 规范化一个 sanitizer 元素 处理 element 的结果。

  3. modified 设为从 移除 elementconfiguration["replaceWithChildrenElements"] 的结果。

  4. 如果 configuration["elements"] 存在

    1. 如果 configuration["elements"] 包含 element

      1. 注释:我们拥有全局允许列表且它包含 element

      2. 从中移除 elementconfiguration["elements"]。

      3. 返回 true。

    2. 注释:我们拥有全局允许列表但它不包含 element

    3. 返回 modified

  5. 否则:

    1. 如果 configuration["removeElements"] 包含 element

      1. 注释:我们拥有全局移除列表且它已经包含 element

      2. 返回 modified

    2. 注释:我们拥有全局移除列表且它不包含 element

    3. 添加 elementconfiguration["removeElements"]。

    4. 返回 true。

要从 SanitizerConfig configuration移除一个属性 SanitizerAttribute attribute

注意: 此方法区分两种情况,即我们拥有全局允许列表或全局移除列表。 如果我们将 attribute 添加到全局移除列表,我们可能需要执行额外工作来修正按元素的允许或移除列表,以维持有效性准则。 如果我们从全局允许列表中移除 attribute,我们也可能需要从局部移除列表中移除它。

  1. 断言configuration有效的

  2. attribute 设为以 规范化一个 sanitizer 属性 处理 attribute 的结果。

  3. 如果 configuration["attributes"] 存在

    1. 注释:若我们有全局允许列表,我们需要添加 attribute

    2. 如果 configuration["attributes"] 不 包含 attribute

      1. 返回 false。

    3. 注释:修正按元素的允许与移除列表。

    4. 如果 configuration["elements"] 存在

      1. 对于每个 element 属于 configuration["elements"]:

        1. 如果 element["removeAttributes"] 带默认值 « » 包含 attribute

          1. 从中移除 attributeelement["removeAttributes"]。

    5. 从中移除 attributeconfiguration["attributes"]。

    6. 返回 true。

  4. 否则:

    1. 注释:若我们有全局移除列表,我们需要添加 attribute

    2. 如果 configuration["removeAttributes"] 包含 attribute 返回 false。

    3. 注释:修正按元素的允许与移除列表。

    4. 如果 configuration["elements"] 存在

      1. 对于每个 element 属于 configuration["elements"]:

        1. 如果 element["attributes"] 带默认值 « » 包含 attribute

          1. 从中移除 attributeelement["attributes"]。

        2. 如果 element["removeAttributes"] 带默认值 « » 包含 attribute

          1. 从中移除 attributeelement["removeAttributes"]。

    5. 追加 attributeconfiguration["removeAttributes"]

    6. 返回 true。

要从 SanitizerConfig configuration移除不安全内容,执行如下操作:

注意: 虽然该算法称为 remove unsafe,我们对“unsafe”一词的使用 严格依据本规范中的含义,用以指代在插入文档时会执行 JavaScript 的内容。 换言之,该方法将消除 XSS 的机会。

  1. 断言键集合内置安全基线配置的)等于 «[ "removeElements", "removeAttributes" ] »。

  2. 断言configuration有效的

  3. result 为 false。

  4. 对于每个 element 属于 内置安全基线配置[removeElements]:

    1. configuration 上调用 移除一个元素 element

    2. 如果调用返回 true,则将 result 设为 true。

  5. 对于每个 attribute 属于 内置安全基线配置[removeAttributes]:

    1. configuration 上调用 移除一个属性 attribute

    2. 如果调用返回 true,则将 result 设为 true。

  6. 对于每个 列于 事件处理器内容属性attribute

    1. configuration 上调用 移除一个属性 attribute

    2. 如果调用返回 true,则将 result 设为 true。

  7. 返回 result

3.3. 设置配置

若要设置配置,给定一个字典 configuration, 一个布尔值 allowCommentsAndDataAttributes,以及一个 Sanitizer sanitizer
  1. allowCommentsAndDataAttributesconfiguration 执行 规范化

  2. 如果 configuration 不是 有效配置,则返回 false。

  3. sanitizerconfiguration 设为 configuration

  4. 返回 true。

3.4. 规范化配置

Sanitizer 以规范化形式存储configuration,这样能简化许多处理流程。

elements 列表 {elements: ["div"]} 会被存储为 {elements: [{name: "div", namespace: "http://www.w3.org/1999/xhtml"}]
要对 canonicalize the configuration SanitizerConfig configuration 且带有一个 布尔值 allowCommentsAndDataAttributes

Note: 我们假设 configuration[WebIDL] 将 JavaScript 值转换为 SanitizerConfig 的结果。

  1. 如果 configuration["elements"] 与 configuration["removeElements"] 均不存在,则设置 configuration["removeElements"] 为 « »。

  2. 如果 configuration["attributes"] 与 configuration["removeAttributes"] 均不存在,则设置 configuration["removeAttributes"] 为 « »。

  3. 如果 configuration["elements"] 存在

    1. elements 为 « »。

    2. 对于每个 element 属于 configuration["elements"], 执行:

      1. 追加 canonicalize a sanitizer element with attributes element 的结果到 elements

    3. configuration["elements"] 设为 elements

  4. 如果 configuration["removeElements"] 存在

    1. elements 为 « »。

    2. 对于每个 element 属于 configuration["removeElements"], 执行:

      1. 追加 canonicalize a sanitizer element element 的结果到 elements

    3. configuration["removeElements"] 设为 elements

  5. 如果 configuration["replaceWithChildrenElements"] 存在

    1. elements 为 « »。

    2. 对于每个 element 属于 configuration["replaceWithChildrenElements"], 执行:

      1. 追加 canonicalize a sanitizer element element 的结果到 elements

    3. configuration["replaceWithChildrenElements"] 设为 elements

  6. 如果 configuration["attributes"] 存在

    1. attributes 为 « »。

    2. 对于每个 attribute 属于 configuration["attributes"], 执行:

      1. 追加 canonicalize a sanitizer attribute attribute 的结果到 attributes

    3. configuration["attributes"] 设为 attributes

  7. 如果 configuration["removeAttributes"] 存在

    1. attributes 为 « »。

    2. 对于每个 attribute 属于 configuration["removeAttributes"], 执行:

      1. 追加 canonicalize a sanitizer attribute attribute 的结果到 attributes

    3. configuration["removeAttributes"] 设为 attributes

  8. 如果 configuration["comments"] 不存在,则设置 configuration["comments"] 为 allowCommentsAndDataAttributes

  9. 如果 configuration["attributes"] 存在configuration["dataAttributes"] 不存在,则 设置 configuration["dataAttributes"] 为 allowCommentsAndDataAttributes

要对 canonicalize a sanitizer element with attributes 一个 SanitizerElementWithAttributes element
  1. result 为以 canonicalize a sanitizer element 处理 element 的结果。

  2. 如果 element 是一个 字典

    1. 如果 element["attributes"] 存在

      1. attributes 为 « »。

      2. 对于每个 attribute 属于 element["attributes"]:

        1. 追加canonicalize a sanitizer attribute 处理 attribute 的结果至 attributes

      3. 设置 result["attributes"] 为 attributes

    2. 如果 element["removeAttributes"] 存在

      1. attributes 为 « »。

      2. 对于每个 attribute 属于 element["removeAttributes"]:

        1. 追加canonicalize a sanitizer attribute 处理 attribute 的结果至 attributes

      3. 设置 result["removeAttributes"] 为 attributes

  3. 如果 result["attributes"] 与 result["removeAttributes"] 均不存在

    1. 设置 result["removeAttributes"] 为 « »。

  4. 返回 result

为了对 canonicalize a sanitizer element 一个 SanitizerElement element, 返回以 canonicalize a sanitizer name 处理 element 且以 HTML 命名空间 作为默认命名空间的结果。
为了对 canonicalize a sanitizer attribute 一个 SanitizerAttribute attribute, 返回以 canonicalize a sanitizer name 处理 attribute 且以 null 作为默认命名空间的结果。
为了对 canonicalize a sanitizer name name,并带有默认 命名空间 defaultNamespace,执行以下步骤:
  1. 断言name 要么是 DOMString, 要么是一个 字典

  2. 如果 nameDOMString, 则返回 «[ "name" → name, "namespace" → defaultNamespace]»。

  3. 断言name 是一个 字典,且 name["name"] 与 name["namespace"] 均存在

  4. 如果 name["namespace"] 是空字符串,则将其设为 null。

  5. 返回 «[
    "name" → name["name"],
    "namespace" → name["namespace"]
    ]»。

3.5. 辅助算法

对于本规范中使用的 规范化 元素属性名 列表,成员资格基于同时匹配 "name" 和 "namespace" 两个条目:

当下列条件成立时,Sanitizer 名称 list 包含某个 item:存在 list 中的 entry,且它是一个 有序映射,并且 item["name"] 等于 entry["name"] 且 item["namespace"] 等于 entry["namespace"]。
若要从列表 list 移除某个 item
  1. removed 为 false。

  2. 遍历 list 中的每个 entry

    1. 如果 item["name"] 等于 entry["name"] 且 item["namespace"] 等于 entry["namespace"]:

      1. entrylist 中移除。

      2. removed 设为 true。

  3. 返回 removed

若要向 list添加一个 name,其中 name 规范化list 是一个 有序映射
  1. 如果 list 包含 name,则返回。

  2. 追加 namelist

itemA 小于项 itemB,则:
  1. 如果 itemA["namespace"] 为 null:

    1. 如果 itemB["namespace"] 不为 null,返回 true。

  2. 否则:

    1. 如果 itemB["namespace"] 为 null,返回 false。

    2. 如果 itemA["namespace"] 码元小于 itemB["namespace"],返回 true。

  3. 返回 itemA["name"] 是否 码元小于itemB["name"]。

有序集合的相等,是成员相等但不考虑顺序: 有序集合 AB 当且仅当 AB 的超集BA 的超集 时,相等
有序映射 元组的序列。 有序映射的相等,是将其元组序列视为有序集合后的集合相等。 有序映射 AB 当且仅当 有序集合 A条目有序集合 B条目 相等 时,相等
列表 list 存在重复,当且仅当 对任一 item 属于 list,存在超过一个 entry 满足 item["name"] 等于 entry["name"] 且 item["namespace"] 等于 entry["namespace"]。
若要去重列表 list list
  1. result 为 « »。

  2. 遍历 list 中的每个 entry添加 entryresult

  3. 返回 result

两个包含 SanitizerElement列表 AB交集, 等价于 集合交集, 但集合条目需先规范化
  1. set A 为 « [] »

  2. set B 为 « [] »

  3. 遍历 A,将 规范化 sanitizer 名称 得到的 entry 添加set A

  4. 遍历 B,将 规范化 sanitizer 名称 得到的 entry 添加set B

  5. 返回 set A 与 set B 的交集

若要判断布尔值 bool, 若 bool 为 true 则返回 false,否则返回 true。
注释 包含对算法中特定步骤的解释性说明。

3.6. 内置项

共有四项内置项:

内置安全默认配置 如下:

{
  "elements": [
    {
      "name": "math",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "merror",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mfrac",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mi",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mmultiscripts",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mn",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mo",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "fence",
          "namespace": null
        },
        {
          "name": "form",
          "namespace": null
        },
        {
          "name": "largeop",
          "namespace": null
        },
        {
          "name": "lspace",
          "namespace": null
        },
        {
          "name": "maxsize",
          "namespace": null
        },
        {
          "name": "minsize",
          "namespace": null
        },
        {
          "name": "movablelimits",
          "namespace": null
        },
        {
          "name": "rspace",
          "namespace": null
        },
        {
          "name": "separator",
          "namespace": null
        },
        {
          "name": "stretchy",
          "namespace": null
        },
        {
          "name": "symmetric",
          "namespace": null
        }
      ]
    },
    {
      "name": "mover",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "accent",
          "namespace": null
        }
      ]
    },
    {
      "name": "mpadded",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "depth",
          "namespace": null
        },
        {
          "name": "height",
          "namespace": null
        },
        {
          "name": "lspace",
          "namespace": null
        },
        {
          "name": "voffset",
          "namespace": null
        },
        {
          "name": "width",
          "namespace": null
        }
      ]
    },
    {
      "name": "mphantom",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mprescripts",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mroot",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mrow",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "ms",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mspace",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "depth",
          "namespace": null
        },
        {
          "name": "height",
          "namespace": null
        },
        {
          "name": "width",
          "namespace": null
        }
      ]
    },
    {
      "name": "msqrt",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mstyle",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "msub",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "msubsup",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "msup",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mtable",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mtd",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "columnspan",
          "namespace": null
        },
        {
          "name": "rowspan",
          "namespace": null
        }
      ]
    },
    {
      "name": "mtext",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mtr",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "munder",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "accentunder",
          "namespace": null
        }
      ]
    },
    {
      "name": "munderover",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "accent",
          "namespace": null
        },
        {
          "name": "accentunder",
          "namespace": null
        }
      ]
    },
    {
      "name": "semantics",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "a",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "href",
          "namespace": null
        },
        {
          "name": "hreflang",
          "namespace": null
        },
        {
          "name": "type",
          "namespace": null
        }
      ]
    },
    {
      "name": "abbr",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "address",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "article",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "aside",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "b",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "bdi",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "bdo",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "blockquote",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "cite",
          "namespace": null
        }
      ]
    },
    {
      "name": "body",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "br",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "caption",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "cite",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "code",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "col",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "span",
          "namespace": null
        }
      ]
    },
    {
      "name": "colgroup",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "span",
          "namespace": null
        }
      ]
    },
    {
      "name": "data",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "value",
          "namespace": null
        }
      ]
    },
    {
      "name": "dd",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "del",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "cite",
          "namespace": null
        },
        {
          "name": "datetime",
          "namespace": null
        }
      ]
    },
    {
      "name": "dfn",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "div",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "dl",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "dt",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "em",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "figcaption",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "figure",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "footer",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "h1",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "h2",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "h3",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "h4",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "h5",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "h6",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "head",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "header",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "hgroup",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "hr",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "html",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "i",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "ins",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "cite",
          "namespace": null
        },
        {
          "name": "datetime",
          "namespace": null
        }
      ]
    },
    {
      "name": "kbd",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "li",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "value",
          "namespace": null
        }
      ]
    },
    {
      "name": "main",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "mark",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "menu",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "nav",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "ol",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "reversed",
          "namespace": null
        },
        {
          "name": "start",
          "namespace": null
        },
        {
          "name": "type",
          "namespace": null
        }
      ]
    },
    {
      "name": "p",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "pre",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "q",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "rp",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "rt",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "ruby",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "s",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "samp",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "search",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "section",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "small",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "span",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "strong",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "sub",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "sup",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "table",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "tbody",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "td",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "colspan",
          "namespace": null
        },
        {
          "name": "headers",
          "namespace": null
        },
        {
          "name": "rowspan",
          "namespace": null
        }
      ]
    },
    {
      "name": "tfoot",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "th",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "abbr",
          "namespace": null
        },
        {
          "name": "colspan",
          "namespace": null
        },
        {
          "name": "headers",
          "namespace": null
        },
        {
          "name": "rowspan",
          "namespace": null
        },
        {
          "name": "scope",
          "namespace": null
        }
      ]
    },
    {
      "name": "thead",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "time",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "datetime",
          "namespace": null
        }
      ]
    },
    {
      "name": "title",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "tr",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "u",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "ul",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "var",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "wbr",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "circle",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "cx",
          "namespace": null
        },
        {
          "name": "cy",
          "namespace": null
        },
        {
          "name": "pathLength",
          "namespace": null
        },
        {
          "name": "r",
          "namespace": null
        }
      ]
    },
    {
      "name": "defs",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": []
    },
    {
      "name": "desc",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": []
    },
    {
      "name": "ellipse",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "cx",
          "namespace": null
        },
        {
          "name": "cy",
          "namespace": null
        },
        {
          "name": "pathLength",
          "namespace": null
        },
        {
          "name": "rx",
          "namespace": null
        },
        {
          "name": "ry",
          "namespace": null
        }
      ]
    },
    {
      "name": "foreignObject",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "height",
          "namespace": null
        },
        {
          "name": "width",
          "namespace": null
        },
        {
          "name": "x",
          "namespace": null
        },
        {
          "name": "y",
          "namespace": null
        }
      ]
    },
    {
      "name": "g",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": []
    },
    {
      "name": "line",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "pathLength",
          "namespace": null
        },
        {
          "name": "x1",
          "namespace": null
        },
        {
          "name": "x2",
          "namespace": null
        },
        {
          "name": "y1",
          "namespace": null
        },
        {
          "name": "y2",
          "namespace": null
        }
      ]
    },
    {
      "name": "marker",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "markerHeight",
          "namespace": null
        },
        {
          "name": "markerUnits",
          "namespace": null
        },
        {
          "name": "markerWidth",
          "namespace": null
        },
        {
          "name": "orient",
          "namespace": null
        },
        {
          "name": "preserveAspectRatio",
          "namespace": null
        },
        {
          "name": "refX",
          "namespace": null
        },
        {
          "name": "refY",
          "namespace": null
        },
        {
          "name": "viewBox",
          "namespace": null
        }
      ]
    },
    {
      "name": "metadata",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": []
    },
    {
      "name": "path",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "d",
          "namespace": null
        },
        {
          "name": "pathLength",
          "namespace": null
        }
      ]
    },
    {
      "name": "polygon",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "pathLength",
          "namespace": null
        },
        {
          "name": "points",
          "namespace": null
        }
      ]
    },
    {
      "name": "polyline",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "pathLength",
          "namespace": null
        },
        {
          "name": "points",
          "namespace": null
        }
      ]
    },
    {
      "name": "rect",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "height",
          "namespace": null
        },
        {
          "name": "pathLength",
          "namespace": null
        },
        {
          "name": "rx",
          "namespace": null
        },
        {
          "name": "ry",
          "namespace": null
        },
        {
          "name": "width",
          "namespace": null
        },
        {
          "name": "x",
          "namespace": null
        },
        {
          "name": "y",
          "namespace": null
        }
      ]
    },
    {
      "name": "svg",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "height",
          "namespace": null
        },
        {
          "name": "preserveAspectRatio",
          "namespace": null
        },
        {
          "name": "viewBox",
          "namespace": null
        },
        {
          "name": "width",
          "namespace": null
        },
        {
          "name": "x",
          "namespace": null
        },
        {
          "name": "y",
          "namespace": null
        }
      ]
    },
    {
      "name": "text",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "dx",
          "namespace": null
        },
        {
          "name": "dy",
          "namespace": null
        },
        {
          "name": "lengthAdjust",
          "namespace": null
        },
        {
          "name": "rotate",
          "namespace": null
        },
        {
          "name": "textLength",
          "namespace": null
        },
        {
          "name": "x",
          "namespace": null
        },
        {
          "name": "y",
          "namespace": null
        }
      ]
    },
    {
      "name": "textPath",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "lengthAdjust",
          "namespace": null
        },
        {
          "name": "method",
          "namespace": null
        },
        {
          "name": "path",
          "namespace": null
        },
        {
          "name": "side",
          "namespace": null
        },
        {
          "name": "spacing",
          "namespace": null
        },
        {
          "name": "startOffset",
          "namespace": null
        },
        {
          "name": "textLength",
          "namespace": null
        }
      ]
    },
    {
      "name": "title",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": []
    },
    {
      "name": "tspan",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "dx",
          "namespace": null
        },
        {
          "name": "dy",
          "namespace": null
        },
        {
          "name": "lengthAdjust",
          "namespace": null
        },
        {
          "name": "rotate",
          "namespace": null
        },
        {
          "name": "textLength",
          "namespace": null
        },
        {
          "name": "x",
          "namespace": null
        },
        {
          "name": "y",
          "namespace": null
        }
      ]
    }
  ],
  "attributes": [
    {
      "name": "alignment-baseline",
      "namespace": null
    },
    {
      "name": "baseline-shift",
      "namespace": null
    },
    {
      "name": "clip-path",
      "namespace": null
    },
    {
      "name": "clip-rule",
      "namespace": null
    },
    {
      "name": "color",
      "namespace": null
    },
    {
      "name": "color-interpolation",
      "namespace": null
    },
    {
      "name": "cursor",
      "namespace": null
    },
    {
      "name": "dir",
      "namespace": null
    },
    {
      "name": "direction",
      "namespace": null
    },
    {
      "name": "display",
      "namespace": null
    },
    {
      "name": "displaystyle",
      "namespace": null
    },
    {
      "name": "dominant-baseline",
      "namespace": null
    },
    {
      "name": "fill",
      "namespace": null
    },
    {
      "name": "fill-opacity",
      "namespace": null
    },
    {
      "name": "fill-rule",
      "namespace": null
    },
    {
      "name": "font-family",
      "namespace": null
    },
    {
      "name": "font-size",
      "namespace": null
    },
    {
      "name": "font-size-adjust",
      "namespace": null
    },
    {
      "name": "font-stretch",
      "namespace": null
    },
    {
      "name": "font-style",
      "namespace": null
    },
    {
      "name": "font-variant",
      "namespace": null
    },
    {
      "name": "font-weight",
      "namespace": null
    },
    {
      "name": "lang",
      "namespace": null
    },
    {
      "name": "letter-spacing",
      "namespace": null
    },
    {
      "name": "marker-end",
      "namespace": null
    },
    {
      "name": "marker-mid",
      "namespace": null
    },
    {
      "name": "marker-start",
      "namespace": null
    },
    {
      "name": "mathbackground",
      "namespace": null
    },
    {
      "name": "mathcolor",
      "namespace": null
    },
    {
      "name": "mathsize",
      "namespace": null
    },
    {
      "name": "opacity",
      "namespace": null
    },
    {
      "name": "paint-order",
      "namespace": null
    },
    {
      "name": "pointer-events",
      "namespace": null
    },
    {
      "name": "scriptlevel",
      "namespace": null
    },
    {
      "name": "shape-rendering",
      "namespace": null
    },
    {
      "name": "stop-color",
      "namespace": null
    },
    {
      "name": "stop-opacity",
      "namespace": null
    },
    {
      "name": "stroke",
      "namespace": null
    },
    {
      "name": "stroke-dasharray",
      "namespace": null
    },
    {
      "name": "stroke-dashoffset",
      "namespace": null
    },
    {
      "name": "stroke-linecap",
      "namespace": null
    },
    {
      "name": "stroke-linejoin",
      "namespace": null
    },
    {
      "name": "stroke-miterlimit",
      "namespace": null
    },
    {
      "name": "stroke-opacity",
      "namespace": null
    },
    {
      "name": "stroke-width",
      "namespace": null
    },
    {
      "name": "text-anchor",
      "namespace": null
    },
    {
      "name": "text-decoration",
      "namespace": null
    },
    {
      "name": "text-overflow",
      "namespace": null
    },
    {
      "name": "text-rendering",
      "namespace": null
    },
    {
      "name": "title",
      "namespace": null
    },
    {
      "name": "transform",
      "namespace": null
    },
    {
      "name": "transform-origin",
      "namespace": null
    },
    {
      "name": "unicode-bidi",
      "namespace": null
    },
    {
      "name": "vector-effect",
      "namespace": null
    },
    {
      "name": "visibility",
      "namespace": null
    },
    {
      "name": "white-space",
      "namespace": null
    },
    {
      "name": "word-spacing",
      "namespace": null
    },
    {
      "name": "writing-mode",
      "namespace": null
    }
  ],
  "comments": false,
  "dataAttributes": false
}

注意:所包含的 [MathML] 标记基于 [SafeMathML]

内置安全基线配置旨在仅阻止脚本内容。具体如下:

{
  "removeElements": [
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "embed"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "frame"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "iframe"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "object"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "script"
    },
    {
      "namespace": "http://www.w3.org/2000/svg",
      "name": "script"
    },
    {
      "namespace": "http://www.w3.org/2000/svg",
      "name": "use"
    }
  ],
  "removeAttributes": []
}

警告: remove unsafe 算法还规定要额外移除所有 事件处理内容属性,详见 [HTML]。 如果某个 用户代理[HTML] 规范基础上扩展了额外的 事件处理内容属性,则如何处理这些属性由其自行决定。基于当前的 事件处理内容属性 列表, 安全基线配置实际上大致如下所示:

{
  "removeElements": [
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "embed"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "frame"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "iframe"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "object"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "script"
    },
    {
      "namespace": "http://www.w3.org/2000/svg",
      "name": "script"
    },
    {
      "namespace": "http://www.w3.org/2000/svg",
      "name": "use"
    }
  ],
  "removeAttributes": [
    "onafterprint",
    "onauxclick",
    "onbeforeinput",
    "onbeforematch",
    "onbeforeprint",
    "onbeforeunload",
    "onbeforetoggle",
    "onblur",
    "oncancel",
    "oncanplay",
    "oncanplaythrough",
    "onchange",
    "onclick",
    "onclose",
    "oncontextlost",
    "oncontextmenu",
    "oncontextrestored",
    "oncopy",
    "oncuechange",
    "oncut",
    "ondblclick",
    "ondrag",
    "ondragend",
    "ondragenter",
    "ondragleave",
    "ondragover",
    "ondragstart",
    "ondrop",
    "ondurationchange",
    "onemptied",
    "onended",
    "onerror",
    "onfocus",
    "onformdata",
    "onhashchange",
    "oninput",
    "oninvalid",
    "onkeydown",
    "onkeypress",
    "onkeyup",
    "onlanguagechange",
    "onload",
    "onloadeddata",
    "onloadedmetadata",
    "onloadstart",
    "onmessage",
    "onmessageerror",
    "onmousedown",
    "onmouseenter",
    "onmouseleave",
    "onmousemove",
    "onmouseout",
    "onmouseover",
    "onmouseup",
    "onoffline",
    "ononline",
    "onpagehide",
    "onpagereveal",
    "onpageshow",
    "onpageswap",
    "onpaste",
    "onpause",
    "onplay",
    "onplaying",
    "onpopstate",
    "onprogress",
    "onratechange",
    "onreset",
    "onresize",
    "onrejectionhandled",
    "onscroll",
    "onscrollend",
    "onsecuritypolicyviolation",
    "onseeked",
    "onseeking",
    "onselect",
    "onslotchange",
    "onstalled",
    "onstorage",
    "onsubmit",
    "onsuspend",
    "ontimeupdate",
    "ontoggle",
    "onunhandledrejection",
    "onunload",
    "onvolumechange",
    "onwaiting",
    "onwheel"
  ]
}
内置导航 URL 属性列表,其中 "javascript:" 导航被视为“不安全”,内容如下:

«[
[ { "name" → "a", "namespace" → HTML 命名空间 }, { "name" → "href", "namespace" → null } ],
[ { "name" → "area", "namespace" → HTML 命名空间 }, { "name" → "href", "namespace" → null } ],
[ { "name" → "base", "namespace" → HTML 命名空间 }, { "name" → "href", "namespace" → null } ],
[ { "name" → "button", "namespace" → HTML 命名空间 }, { "name" → "formaction", "namespace" → null } ],
[ { "name" → "form", "namespace" → HTML 命名空间 }, { "name" → "action", "namespace" → null } ],
[ { "name" → "iframe", "namespace" → HTML 命名空间 }, { "name" → "src", "namespace" → null } ],
[ { "name" → "input", "namespace" → HTML 命名空间 }, { "name" → "formaction", "namespace" → null } ],
[ { "name" → "a", "namespace" → SVG 命名空间 }, { "name" → "href", "namespace" → null } ],
[ { "name" → "a", "namespace" → SVG 命名空间 }, { "name" → "href", "namespace" → XLink 命名空间 } ],

内置动画 URL 属性列表,可用于 [SVG11] 中以声明方式修改导航元素以使用 "javascript:" URL,内容如下:

«[
[ { "name" → "animate", "namespace" → SVG 命名空间 }, { "name" → "attributeName", "namespace" → null] } ],
[ { "name" → "animateMotion", "namespace" → SVG 命名空间 }, { "name" → "attributeName", "namespace" → null } ],
[ { "name" → "animateTransform", "namespace" → SVG 命名空间 }, { "name" → "attributeName", "namespace" → null } ],
[ { "name" → "set", "namespace" → SVG 命名空间 }, { "name" → "attributeName", "namespace" → null } ],

4. 安全注意事项

Sanitizer API 旨在通过遍历所提供的 HTML 内容,并根据配置移除元素和属性,防止基于 DOM 的跨站脚本(XSS)攻击。规范要求,API 不应支持构造会遗留可执行脚本标记的 Sanitizer 对象,否则将会破坏威胁模型。

但即便如此,正确使用 Sanitizer API 也无法防护所有安全问题,相关场景会在下文中说明。

4.1. 服务端反射型和存储型 XSS

本节为非规范性内容。

Sanitizer API 仅在 DOM 中操作,提供遍历和过滤现有 DocumentFragment 的能力。Sanitizer 不涉及服务端反射型或存储型 XSS 问题。

4.2. DOM 覆写攻击(clobbering)

本节为非规范性内容。

DOM 覆写攻击指通过恶意 HTML 使用 idname 属性命名元素,从而让 HTML 元素的某些属性(如 children)被恶意内容覆盖,导致应用混淆。

Sanitizer API 的默认行为无法防护 DOM 覆写攻击,但可以通过配置移除 idname 属性。

4.3. 利用脚本组件的 XSS

本节为非规范性内容。

脚本组件攻击是一种攻击者利用流行 JavaScript 库中的已有应用代码,通过注入看似无害的代码或仅被框架解析的 DOM 节点,最终由框架解析并执行这些输入中的 JavaScript 的技术。

Sanitizer API 无法防止此类攻击,要求页面作者一般要显式允许未知元素,并且还需针对未知属性、元素及如 data-slot 属性和 <slot><template> 等广泛用于模板和框架的标记作出明确配置。 我们认为这些限制并不详尽,鼓励页面作者检查所用第三方库是否有此类行为。

4.4. 变异型 XSS(mXSS)

本节为非规范性内容。

变异型 XSS(mXSS)描述的是由于解析器上下文不一致,在错误上下文下解析 HTML 片段时发生攻击。当某个已解析的 HTML 片段序列化为字符串后,在插入到不同父元素时未必能保证解析效果一致。例如,依赖于外部内容或标签嵌套错误导致解析行为变化,即可发动此类攻击。

Sanitizer API 只提供将字符串转为节点树的功能。上下文由所有 sanitizer 函数隐式提供:Element.setHTML() 使用当前元素;Document.parseHTML() 创建新文档。因此 Sanitizer API 不会直接受到变异型 XSS 影响。

如果开发者将净化后的节点树通过 .innerHTML 取字符串,再重新解析,则有可能发生变异型 XSS。我们不推荐这种做法。如果确实需要处理或传递 HTML 字符串,则应将其视为不可信内容,并在插入 DOM 时重新净化。换言之,净化并序列化后的 HTML 树不应再被视作已净化。

关于 mXSS 的更完整讨论可见 [MXSS]

5. 致谢

本项工作借鉴了 cure53 团队的 [DOMPURIFY], 以及 Internet Explorer 的 window.toStaticHTML() 和 Ben Bucksch 的 [HTMLSanitizer]。感谢 Anne van Kesteren、Krzysztof Kotowicz、 Andrew C. H. Mc Millan、Tom Schuster、Luke Warlow、Guillaume Weghsteen 以及 Mike West 的宝贵反馈。

索引

本规范定义的术语

引用中定义的术语

参考文献

规范性引用

[DOM]
Anne van Kesteren. DOM 标准. 活标准. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; 等. HTML 标准. 活标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 活标准. URL: https://infra.spec.whatwg.org/
[TRUSTED-TYPES]
Krzysztof Kotowicz. Trusted Types. URL: https://w3c.github.io/trusted-types/dist/spec/
[URL]
Anne van Kesteren. URL 标准. 活标准. URL: https://url.spec.whatwg.org/
[WebIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. 活标准. URL: https://webidl.spec.whatwg.org/

参考性引用

[DOMPURIFY]
DOMPurify. URL: https://github.com/cure53/DOMPurify
[HTMLSanitizer]
HTML Sanitizer. URL: https://www.bucksch.org/1/projects/mozilla/108153/
[MathML]
Patrick D F Ion; Robert R Miner. 数学标记语言 (MathML™) 1.01 规范. 2023年3月7日. REC. URL: https://www.w3.org/TR/REC-MathML/
[MXSS]
mXSS 攻击:通过 innerHTML 变异攻击安全性良好的 web 应用. URL: https://cure53.de/fp170.pdf
[SafeMathML]
MathML 安全列表. URL: https://w3c.github.io/mathml-docs/mathml-safe-list
[SVG11]
Erik Dahlström; 等. 可缩放矢量图形 (SVG) 1.1(第二版). 2011年8月16日. REC. URL: https://www.w3.org/TR/SVG11/

IDL 索引

enum SanitizerPresets { "default" };
dictionary SetHTMLOptions {
  (Sanitizer or SanitizerConfig or SanitizerPresets) sanitizer = "default";
};
dictionary SetHTMLUnsafeOptions {
  (Sanitizer or SanitizerConfig or SanitizerPresets) sanitizer = {};
};

[Exposed=Window]
interface Sanitizer {
  constructor(optional (SanitizerConfig or SanitizerPresets) configuration = "default");

  // Query configuration:
  SanitizerConfig get();

  // Modify a Sanitizer’s lists and fields:
  boolean allowElement(SanitizerElementWithAttributes element);
  boolean removeElement(SanitizerElement element);
  boolean replaceElementWithChildren(SanitizerElement element);
  boolean allowAttribute(SanitizerAttribute attribute);
  boolean removeAttribute(SanitizerAttribute attribute);
  boolean setComments(boolean allow);
  boolean setDataAttributes(boolean allow);

  // Remove markup that executes script.
  boolean removeUnsafe();
};

dictionary SanitizerElementNamespace {
  required DOMString name;
  DOMString? _namespace = "http://www.w3.org/1999/xhtml";
};

// Used by "elements"
dictionary SanitizerElementNamespaceWithAttributes : SanitizerElementNamespace {
  sequence<SanitizerAttribute> attributes;
  sequence<SanitizerAttribute> removeAttributes;
};

typedef (DOMString or SanitizerElementNamespace) SanitizerElement;
typedef (DOMString or SanitizerElementNamespaceWithAttributes) SanitizerElementWithAttributes;

dictionary SanitizerAttributeNamespace {
  required DOMString name;
  DOMString? _namespace = null;
};
typedef (DOMString or SanitizerAttributeNamespace) SanitizerAttribute;

dictionary SanitizerConfig {
  sequence<SanitizerElementWithAttributes> elements;
  sequence<SanitizerElement> removeElements;
  sequence<SanitizerElement> replaceWithChildrenElements;

  sequence<SanitizerAttribute> attributes;
  sequence<SanitizerAttribute> removeAttributes;

  boolean comments;
  boolean dataAttributes;
};