HTML サニタイザー API

コミュニティグループ報告(ドラフト),

このバージョン:
https://wicg.github.io/sanitizer-api/
課題追跡:
GitHub
編集者:
Frederik Braun (Mozilla)
Mario Heiderich (Cure53)
Daniel Vogelheim (Google LLC)
Tom Schuster (Mozilla)

概要

本書は、信頼できない HTML 入力を受け取り、文書の DOM に安全に挿入できるようにサニタイズするための API 群を規定します。

この文書の位置付け

この仕様は Web Platform Incubator Community Group によって公開されました。 これは W3C 標準ではなく、W3C 標準化トラック上にもありません。 なお、 W3C Community Contributor License Agreement (CLA) に基づき、限定的なオプトアウトおよびその他の条件が適用されます。 詳細は W3C の Community および Business Group をご覧ください。

1. 導入

この節は規範ではありません。

Web アプリケーションはクライアント側で HTML 文字列を扱う必要がしばしばあります。 これはクライアントサイドのテンプレート ソリューションの一部であったり、ユーザー生成コンテンツのレンダリングの一部であったりします。これを安全に行うのは難しいです。 文字列を安易に連結して ElementinnerHTML に流し込む素朴な手法は、多くの予期しない形で JavaScript の実行を引き起こし得るため、危険が伴います。

[DOMPURIFY] のようなライブラリは、 挿入前に文字列を注意深く解析・サニタイズし、DOM を構築して許可リストを通してその構成要素をフィルタリングすることで、この問題に対処しようとします。 しかしこれは脆いアプローチであることが分かっています。というのも、Web に公開されている解析 API は、実際に 文字列を「実際の」DOM としてレンダリングする際のブラウザーの挙動と、必ずしも合理的に一致しないからです。 さらに、ライブラリは時間とともに変化するブラウザーの挙動を追い続ける必要があります。かつて安全だったものが、 プラットフォームレベルの新機能によって時限爆弾になってしまうこともあります。

ブラウザーは、自身がいつコードを実行するかについてかなり把握しています。任意の文字列から HTML を安全にレンダリングする方法を ブラウザー自身に教えることで、ユーザー空間のライブラリよりも改善できます。そうすれば、ブラウザーのパーサー実装の変更に合わせて 保守・更新される可能性もはるかに高くなります。本書は、そのための API を概説します。

1.1. 目標

1.2. API 概要

Sanitizer API は、HTML を含む文字列を DOM ツリーに解析し、結果のツリーをユーザー指定の構成に従って フィルタリングする機能を提供します。メソッドは 2 つの観点で用意されています:

2. フレームワーク

2.1. サニタイザー API

Element インターフェイスは、setHTML()setHTMLUnsafe() の 2 つのメソッドを定義します。いずれも HTML マークアップを含む DOMString と、任意の構成を受け取ります。

partial interface Element {
  [CEReactions] undefined setHTMLUnsafe((TrustedHTML or DOMString) html, optional SetHTMLUnsafeOptions options = {});
};
partial interface Element {
  [CEReactions] undefined setHTML(DOMString html, optional SetHTMLOptions options = {});
};
ElementsetHTMLUnsafe(html, options) メソッドの手順は次のとおりです:
  1. compliantHTML を、get trusted type compliant string アルゴリズムに TrustedHTMLthis関連グローバルオブジェクトhtml、"Element setHTMLUnsafe"、および "script" を与えて呼び出した結果とする。

  2. target を、thistemplate 要素である場合はその template contents、そうでない場合は this とする。

  3. Set and filter HTML を、targetthiscompliantHTMLoptions、false を与えて実行する。

ElementsetHTML(html, options) メソッドの手順は次のとおりです:
  1. target を、thistemplate の場合はその template contents、 そうでない場合は this とする。

  2. Set and filter HTML を、targetthishtmloptions、true を与えて実行する。

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

これらのメソッドは ShadowRoot にも反映されています:

ShadowRootsetHTMLUnsafe(html, options) メソッドの手順は次のとおりです:
  1. compliantHTML を、get trusted type compliant string アルゴリズムに、 TrustedHTMLthis関連グローバルオブジェクトhtml、"ShadowRoot setHTMLUnsafe"、および "script" を与えて呼び出した結果とする。

  2. Set and filter HTMLthisthisshadow host(コンテキスト要素として)、 compliantHTMLoptions、false を与えて実行する。

ShadowRootsetHTML(html, options) メソッドの手順は次のとおりです:
  1. Set and filter HTML を、this(target として)、this(コンテキスト 要素として)、 htmloptions、true を与えて実行する。

Document インターフェイスは、文書全体を解析する 2 つの新しいメソッドを得ます:

partial interface Document {
  static Document parseHTMLUnsafe((TrustedHTML or DOMString) html, optional SetHTMLUnsafeOptions options = {});
};
partial interface Document {
  static Document parseHTML(DOMString html, optional SetHTMLOptions options = {});
};
parseHTMLUnsafe(html, options) メソッドの手順は次のとおりです:
  1. compliantHTML を、get trusted type compliant string アルゴリズムに、 TrustedHTML現在のグローバルオブジェクトhtml、"Document parseHTMLUnsafe"、および "script" を与えて呼び出した結果とする。

  2. document を新しい Document とし、 その content type は "text/html" とする。

    注: document には browsing context がないため、 スクリプティングは無効です。

  3. documentallow declarative shadow roots を true に設定する。

  4. Parse HTML from a stringdocumentcompliantHTML を与えて実行する。

  5. sanitizer を、get a sanitizer instance from optionsoptions と false を与えて呼び出した結果とする。

  6. sanitizedocument に対して sanitizer と false を与えて呼び出す。

  7. document を返す。

parseHTML(html, options) メソッドの手順は次のとおりです:
  1. document を新しい Document とし、 その content type は "text/html" とする。

    注: document には browsing context がないため、 スクリプティングは無効です。

  2. documentallow declarative shadow roots を true に設定する。

  3. Parse HTML from a stringdocumenthtml を与えて実行する。

  4. sanitizer を、get a sanitizer instance from optionsoptions と true を与えて呼び出した結果とする。

  5. sanitizedocument に対して sanitizer と true を与えて呼び出す。

  6. document を返す。

2.2. SetHTML のオプションと構成オブジェクト。

setHTML() 系の メソッドはすべて options ディクショナリを受け取ります。現時点では、このディクショナリのメンバーは 1 つだけ定義されています:

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

Sanitizer 構成オブジェクトは、フィルター構成をカプセル化します。 同じ構成は "safe" または "unsafe" の両方のメソッドで使用でき、「safe」メソッドは渡された構成に対して暗黙に removeUnsafe を行い、構成が渡されない場合は既定の構成を用います。既定は「safe」と「unsafe」で異なります: 「safe」メソッドは既定で安全であることを目指し制限的で、「unsafe」メソッドは既定で無制限です。 構成の利用意図としては、ページのライフタイムの早い段階で 1 つ(または少数)の構成を作成し、必要に応じて再利用することです。これにより実装は構成を前処理できます。

構成オブジェクトは、構成ディクショナリを返すようにクエリできます。また、直接変更することもできます。

[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 allowProcessingInstruction(SanitizerPI pi);
  boolean removeProcessingInstruction(SanitizerPI pi);
  boolean allowAttribute(SanitizerAttribute attribute);
  boolean removeAttribute(SanitizerAttribute attribute);
  boolean setComments(boolean allow);
  boolean setDataAttributes(boolean allow);

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

Sanitizer には対応する SanitizerConfigconfiguration が関連付けられています。

constructor(configuration) メソッドの手順は次のとおりです:
  1. configurationSanitizerPresetsstring である場合:

    1. Assert: configurationis default である。

    2. configurationbuilt-in safe default configuration に設定する。

  2. valid を、set a configurationconfiguration と true を与えて this に対して実行した戻り値とする。

  3. valid が false の場合、TypeError を投げる。

get() メソッドの手順は以下のとおりです:
注記: get() メソッドの外側では、Sanitizerの要素や属性の順序は観測できません。 明示的にこのメソッドの結果をソートすることにより、たとえば内部的に順不同のセットを利用するなど、 実装が最適化できる機会を与えます。
  1. configthis設定とする。

  2. 断言: config有効である。

  3. もし config["elements"] が存在する場合:

    1. すべての config["elements"] の element に対して:

      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["processingInstructions"] が存在する場合:

    1. config["processingInstructions"] を、昇順でソートする config["processingInstructions"] の結果で設定し、piA["target"] が コード単位比較でより小さい piB["target"] である。

  7. その他の場合:

    1. config["removeProcessingInstructions"] を、昇順でソートする config["removeProcessingInstructions"] の結果で設定し、piA["target"] が コード単位比較でより小さい piB["target"] である。

  8. もし config["attributes"] が存在する場合:

    1. config["attributes"] を、昇順でソートする config["attributes"] の結果で設定し、attrA小さい項目として attrB と比較する。

  9. その他の場合:

    1. config["removeAttributes"] を、昇順でソートする config["removeAttributes"] の結果で設定し、attrA小さい項目として attrB と比較する。

  10. config を返す。

allowElement(element) メソッドの手順は以下のとおりです:
注記: このアルゴリズムは比較的複雑です。なぜなら、要素許可リストでは 個々の要素に対して属性の許可・除外リストを指定できるからです。これにより、以下の4つの場合を区別する必要があります:
  • グローバルな許可・除外リストがあるかどうか、

  • これらのリストに element がすでに含まれているかどうか、

  1. configurationthis設定とする。

  2. 断言: configuration有効である。

  3. elementサニタイザー要素と属性を正規化する の 結果にする。

  4. もし configuration["elements"] が存在する場合:

    1. modifiedremove elementconfiguration["replaceWithChildrenElements"] から実行した結果とする。

    2. コメント: 個別要素の属性がグローバル属性と重複しないようにする必要があります。

    3. もし configuration["attributes"] が存在する場合:

      1. もし element["attributes"] が存在する場合:

        1. element["attributes"] を重複削除した element["attributes"] に設定する。

        2. element["attributes"] を 差集合 element["attributes"] と configuration["attributes"] の結果に設定する。

        3. もし configuration["dataAttributes"] が true の場合:

          1. すべての itemelement["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"] が 含まれていない場合:

      1. コメント: これは、グローバル許可リストに element がまだ存在しない場合です。

      2. 追加 elementconfiguration["elements"] に。

      3. trueを返す。

    6. コメント: これは、グローバル許可リストにすでに element が存在する場合です。

    7. current elementconfiguration["elements"] 内で item["name"] が element["name"] と等しく、かつ item["namespace"] が element["namespace"] と等しい item とする。

    8. もし element等しい current element なら modified を返す。

    9. 削除 elementconfiguration["elements"] から。

    10. 追加 elementconfiguration["elements"] に。

    11. trueを返す。

  5. その他の場合:

    1. もし element["attributes"] が存在するまたは element["removeAttributes"] (デフォルト« »付き)空でない場合:

      1. ユーザーエージェントは、この操作がサポートされていない旨を コンソールに警告出力してもよい。

      2. falseを返す。

    2. modifiedremove elementconfiguration["replaceWithChildrenElements"] から実行した結果とする。

    3. もし configuration["removeElements"] が 含まれていない element の場合:

      1. コメント: これはグローバル除外リストに element が含まれていない場合です。

      2. modified を返す。

    4. コメント: これは、グローバル除外リストに element が含まれている場合です。

    5. 削除 elementconfiguration["removeElements"] から。

    6. trueを返す。

removeElement(element) メソッドの手順は、 要素を除去するelementthisconfiguration で実行することである。
replaceElementWithChildren(element) メソッドの手順は次のとおりです:
  1. configurationthisconfigurationとする。

  2. アサートconfiguration有効である。

  3. elementに、サニタイザー要素を正規化するの結果(elementを用いて)を設定する。

  4. もし組み込みの置換不可要素リスト含むなら、elementを:

    1. falseを返す。

  5. もしconfiguration["replaceWithChildrenElements"] が含むなら、elementを:

    1. falseを返す。

  6. 削除elementconfiguration["removeElements"] から。

  7. 削除elementconfiguration["elements"] リストから。

  8. 追加elementconfiguration["replaceWithChildrenElements"] へ。

  9. trueを返す。

allowProcessingInstruction(pi) メソッドの手順は以下の通りです:
  1. configurationthis設定とする。

  2. 断言: configuration有効である。

  3. piサニタイザー処理命令を正規化するの結果に設定する。

  4. もし configuration["processingInstructions"] が存在する場合:

    1. もし configuration["processingInstructions"] pi を含む場合:

      1. false を返す。

    2. 追加 piconfiguration["processingInstructions"] に。

    3. true を返す。

  5. その他の場合:

    1. もし configuration["removeProcessingInstructions"] pi を含む場合:

      1. 削除 "target" が pi["target"] に等しい項目を configuration["removeProcessingInstructions"] から。

      2. true を返す。

    2. false を返す。

removeProcessingInstruction(pi) メソッドの手順は以下の通りです:
  1. configurationthis設定とする。

  2. 断言: configuration有効である。

  3. piサニタイザー処理命令を正規化するの結果に設定する。

  4. もし configuration["processingInstructions"] が存在する場合:

    1. もし configuration["processingInstructions"] pi を含む場合:

      1. 削除 "target" が pi["target"] に等しい項目を configuration["processingInstructions"] から。

      2. true を返す。

    2. false を返す。

  5. その他の場合:

    1. もし configuration["removeProcessingInstructions"] pi を含む場合:

      1. false を返す。

    2. 追加 piconfiguration["removeProcessingInstructions"] に。

    3. true を返す。

allowAttribute(attribute) メソッドの手順は以下の通りです:
注記: このメソッドは、グローバルな許可リストとグローバルな除外リストを区別します。グローバル許可リストに attribute を追加する場合、個別の要素ごとの許可リストや除外リストの整合性を維持するため追加の処理が必要になるかもしれません。
  1. configurationthis設定とする。

  2. 断言: configuration有効である。

  3. attributeサニタイザー属性を正規化する の結果に設定する。

  4. もし configuration["attributes"] が存在する場合:

    1. コメント: グローバル許可リストがある場合、attribute を追加する必要がある。

    2. もし configuration["dataAttributes"] が true かつ attributeカスタムデータ属性 である場合、false を返す。

    3. もし configuration["attributes"] attribute を含む場合 false を返す。

    4. コメント: 個別要素ごとに許可・除外リストの調整。

    5. もし configuration["elements"] が存在する場合:

      1. configuration["elements"] の element について:

        1. もし element["attributes"] (デフォルト « » 付きで) attribute を含む場合:

          1. 削除 attributeelement["attributes"] から。

        2. 断言: element["removeAttributes"] (デフォルト « » 付きで) attribute を含まない

    6. 追加 attributeconfiguration["attributes"] に。

    7. true を返す。

  5. その他の場合:

    1. コメント: グローバル除外リストがある場合は attribute を除外する。

    2. もし configuration["removeAttributes"] attribute を含まない場合:

      1. false を返す。

    3. 削除 attributeconfiguration["removeAttributes"] から。

    4. true を返す。

removeAttribute(attribute) メソッドの手順は 属性を除去するattributethis設定 で呼び出すことである。
setComments(allow) メソッドの手順は以下の通りです:
  1. configurationthis設定とする。

  2. 断言: configuration有効である。

  3. もし configuration["comments"] が存在するかつ configuration["comments"] が allow と等しい場合、false を返す;

  4. configuration["comments"] を allow に設定する。

  5. true を返す。

setDataAttributes(allow) メソッドの手順は以下の通りです:
  1. configurationthis設定とする。

  2. 断言: configuration有効である。

  3. もし configuration["attributes"] が存在しない場合、false を返す。

  4. もし configuration["dataAttributes"] が allow と等しい場合、false を返す。

  5. もし allow が true の場合:

    1. 削除 configuration["attributes"] から attrカスタムデータ属性 であるすべての項目を。

    2. もし configuration["elements"] が存在する場合:

      1. configuration["elements"] の element について:

        1. もし element["attributes"] が存在する場合:

          1. 削除 element["attributes"] から attrカスタムデータ属性 であるすべての項目を。

  6. configuration["dataAttributes"] を allow に設定する。

  7. true を返す。

removeUnsafe() メソッドの手順は、 this設定安全でないものを除去するthis設定 で呼び出した結果で更新することである。

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 SanitizerProcessingInstruction {
  required DOMString target;
};

typedef (DOMString or SanitizerProcessingInstruction) SanitizerPI;

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<SanitizerPI> processingInstructions;
  sequence<SanitizerPI> removeProcessingInstructions;

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

  boolean comments;
  boolean dataAttributes;
};

2.4. 構成の不変条件

構成(configuration)は、開発者が自分の目的に合わせて修正することができ、また修正すべきものです。選択肢としては、新しく構成ディクショナリを一から書く、既存のSanitizerの構成を 修正用メソッドで変更する、あるいは既存のget()Sanitizer構成を辞書として取得し、その辞書を編集して新しいSanitizerを作る、などがあります。

空の構成は(setHTMLUnsafe のような "unsafe" メソッドで呼び出した場合に)すべてを許可します。 "default" 構成は組み込みの安全な既定構成を含みます。なお、「safe」と「unsafe」のサニタイザーメソッドは既定値が異なります。

すべての構成ディクショナリが有効とは限りません。有効な構成は、冗長(同じ要素を二重に許可するなど)や矛盾(同じ要素を削除と許可の両方に指定するなど)を避けます。

構成が有効であるためには、いくつかの条件を満たす必要があります:

elements の要素許可リストは、特定の要素に対して、属性を許可または除去することも指定できる。これは [HTML] の構造(特定の要素に適用されるローカルな attributes と、 global attributes の両方を持つ)を反映することを意図している。グローバル属性とローカル属性は混在可能だが、 ある属性が一方のリストで許可され、他方のリストで禁止されるような曖昧な構成は、一般に無効であることに注意。

グローバル attributes グローバル removeAttributes
ローカル attributes 属性は、いずれかのリストに一致すれば許可される。重複は許可されない。 属性はローカル許可リストにある場合にのみ許可される。 グローバル除去リストとローカル許可リストの間で重複エントリは許可されない。 この特定の要素に関してはグローバル除去リストは機能を持たないが、ローカル許可リストを持たない他の要素には適用されうる点に注意。
ローカル removeAttributes 属性は、グローバル許可リストに含まれ、かつローカル除去リストに含まれない場合に許可される。ローカル除去はグローバル許可リストの部分集合でなければならない。 属性はどちらのリストにもない場合に許可される。 グローバル除去リストとローカル除去リストの間で重複エントリは許可されない。

多くの場合、グローバルと要素ごとのリスト間で重複が許されない非対称性に注意されたい。ただし、グローバル許可リストと要素ごとの除去リストの組み合わせの場合は、後者は前者の部分集合でなければならない。上記表の重複にのみ焦点を当てた抜粋は次のとおり:

グローバル attributes グローバル removeAttributes
ローカル attributes 重複は許可されない。 重複は許可されない。
ローカル removeAttributes ローカル除去はグローバル許可リストの部分集合でなければならない。 重複は許可されない。

dataAttributes 設定は、custom data attributes を許可する。上記の規則は、dataAttributes を許可リストと見なせば、custom data attributes にも容易に拡張できる:

グローバル attributesdataAttributes が設定されている
ローカル attributes すべての custom data attributes が許可される。重複となるため、いかなる custom data attributes もいかなる許可リストにも列挙してはならない。
ローカル removeAttributes custom data attribute は、ローカル除去リストに列挙されていない限り許可される。 重複となるため、いかなる custom data attribute もグローバル許可リストに列挙してはならない。

これらの規則を言葉でまとめると:

正規化された SanitizerConfig config有効 かどうかを判定するには:

注記: 渡される設定は事前に 設定の正規化手順を実行されていることが想定されています。 ここではそのアルゴリズムで保証された条件を単に断言するだけです。

  1. 断言: config["elements"] が存在する、または config["removeElements"] が存在する。

  2. もし config["elements"] が存在し、かつ config["removeElements"] が存在する場合、falseを返す。

  3. 断言: config["processingInstructions"] が存在する、または config["removeProcessingInstructions"] が存在する。

  4. もし config["processingInstructions"] が存在し、かつ config["removeProcessingInstructions"] が存在する場合、falseを返す。

  5. 断言: config["attributes"] が存在する、または config["removeAttributes"] が存在する。

  6. もし config["attributes"] が存在し、かつ config["removeAttributes"] が存在する場合、falseを返す。

  7. 断言: すべての SanitizerElementNamespaceWithAttributesSanitizerElementNamespaceSanitizerProcessingInstruction、 および SanitizerAttributeNamespace 項目は正規化済みでなければならない(該当する 要素の正規化処理命令の正規化 または 属性の正規化 を経ている)。

  8. もし config["elements"] が存在する場合:

    1. もし config["elements"] 重複がある場合、falseを返す。

  9. その他の場合:

    1. もし config["removeElements"] 重複がある場合、falseを返す。

  10. もし config["replaceWithChildrenElements"] が存在かつ 重複がある場合、falseを返す。

  11. もし config["processingInstructions"] が存在する場合:

    1. もし config["processingInstructions"] targetが重複している場合、falseを返す。

  12. その他の場合:

    1. もし config["removeProcessingInstructions"] targetが重複している場合、falseを返す。

  13. もし config["attributes"] が存在する場合:

    1. もし config["attributes"] 重複がある場合、falseを返す。

  14. その他の場合:

    1. もし config["removeAttributes"] 重複がある場合、falseを返す。

  15. もし config["replaceWithChildrenElements"] が存在する場合:

    1. config["replaceWithChildrenElements"] の element について:

      1. 組み込み非置換要素リスト element を含む場合、falseを返す。

    2. もし config["elements"] が存在する場合:

      1. config["elements"] と config["replaceWithChildrenElements"] の 共通部分(intersection)空でない場合、falseを返す。

    3. その他の場合:

      1. config["removeElements"] と config["replaceWithChildrenElements"] の 共通部分(intersection)空でない場合、falseを返す。

  16. もし config["attributes"] が存在する場合:

    1. 断言: config["dataAttributes"] が存在する。

    2. もし config["elements"] が存在する場合:

      1. config["elements"] の element について:

        1. もし element["attributes"] が存在 かつ element["attributes"] 重複がある 場合、falseを返す。

        2. もし element["removeAttributes"] が存在かつ element["removeAttributes"] 重複がある 場合、falseを返す。

        3. もし config["attributes"] と element["attributes"] (デフォルト« »付き)共通部分が空でない場合、falseを返す。

        4. もし element["removeAttributes"] (デフォルト« »付き)config["attributes"]の部分集合でない場合、 falseを返す。

        5. もし config["dataAttributes"] が true かつ element["attributes"] が カスタムデータ属性 を含む場合、falseを返す。

    3. config["dataAttributes"] が true かつ config["attributes"] が カスタムデータ属性 を含む場合、 falseを返す。

  17. その他の場合:

    1. もし config["elements"] が存在する場合:

      1. config["elements"] の element について:

        1. element["attributes"] が存在 かつ element["removeAttributes"] が存在する場合、falseを返す。

        2. もし element["attributes"] が存在 かつ element["attributes"] 重複がある場合、falseを返す。

        3. もし element["removeAttributes"] が存在 かつ element["removeAttributes"] 重複がある場合、falseを返す。

        4. もし config["removeAttributes"] と element["attributes"] (デフォルト« »付き)共通部分が空でない場合、falseを返す。

        5. もし config["removeAttributes"] と element["removeAttributes"] (デフォルト« »付き)共通部分が空でない場合、falseを返す。

    2. config["dataAttributes"] が存在する場合、falseを返す。

  18. trueを返す。

注記: 設定をセットする際、 ディクショナリからの場合は 正規化されます。特に、allow-listとremove-listが両方とも省略されている場合は、 空のremove-listとして扱われます。したがって {} 自体は 有効 な設定ではありませんが、 正規化により {removeElements:[],removeAttributes:[]} となり有効となります。この正規化処理は、 ディクショナリが省略された場合と空の場合を同じとするために選択されました。 すなわち setHTMLUnsafe(txt)setHTMLUnsafe(txt, {sanitizer: {}}) の挙動を一致させるためです。

3. アルゴリズム

HTML をセットしフィルターするためには、Element または DocumentFragmenttargetElementcontextElement文字列 html辞書 optionsboolean safe を受け取り、次を行う:
  1. もし safe かつ contextElementローカル名 が "script" であり、 contextElement名前空間HTML 名前空間 または SVG 名前空間 の場合、何もせずリターンする。

  2. sanitizerget a sanitizer instance from optionsoptionssafe を与えて呼び出した結果とする。

  3. newChildrenHTML フラグメントパースアルゴリズムcontextElementhtml、true を指定して実行した結果とする。

  4. fragment を新しい DocumentFragment とし、 その ノードドキュメントcontextElementノードドキュメント とする。

  5. newChildren の各 node について、fragment に append する。

  6. sanitizefragmentsanitizersafe で呼び出す。

  7. target の全てを fragment で置き換える

オプションからサニタイザーインスタンスを取得するには、 ディクショナリ options真偽値 safe を用いる:

注記: このアルゴリズムは SetHTMLOptionsSetHTMLUnsafeOptions の両方に対応しており、デフォルト値のみが異なります。

  1. sanitizerSpec を "default" にする。

  2. もし options["sanitizer"] が存在する場合:

    1. sanitizerSpecoptions["sanitizer"] の値をセットする。

  3. 断言: sanitizerSpecSanitizer インスタンス・ SanitizerPresetsメンバーの文字列 または ディクショナリ のいずれかである。

  4. もし sanitizerSpec文字列 である場合:

    1. 断言: sanitizerSpec "default" である。

    2. sanitizerSpec組み込みの安全なデフォルト設定 をセットする。

  5. 断言: sanitizerSpecSanitizer インスタンスまたは ディクショナリ である。

  6. もし sanitizerSpecディクショナリ である場合:

    1. sanitizer を新しい Sanitizer インスタンスとする。

    2. setConfigurationResultset a configurationsanitizerSpecnot safesanitizer に対して呼び出した結果とする。

    3. もし setConfigurationResult が false の場合、例外を投げる TypeError

    4. sanitizerSpecsanitizer をセットする。

  7. 断言: sanitizerSpecSanitizer インスタンスである。

  8. sanitizerSpec を返す。

3.1. サニタイズ

メインの sanitize 操作は、ParentNode nodeSanitizer sanitizer、および boolean safe を用いて、次のステップを実行する:
  1. configurationsanitizer設定の値とする。

  2. 断言configuration有効である。

  3. もし safe が true ならば、configurationremove unsafeconfiguration に 実行した結果に設定する。

  4. sanitize corenodeconfiguration、および handleJavascriptNavigationUrlssafe に設定して呼び出す。

サニタイズコア 操作は、 ParentNode nodeSanitizerConfig configuration、および boolean handleJavascriptNavigationUrls を用いて、 node から始まる DOM ツリーを再帰的に処理する。 これは次の手順からなる:
  1. nodechildren の各 child について:

    1. AssertchildTextCommentElementProcessingInstruction または DocumentType実装する。

      注: 現在、このアルゴリズムは HTML パーサーの出力に対してのみ呼び出され、この表明は成り立つはずである。DocumentTypeparseHTML および parseHTMLUnsafe に対してのみ出現するべきである。 将来このアルゴリズムが別の文脈で使用される場合、 この仮定は再検討する必要がある。

    2. childDocumentType実装するなら、 continue する。

    3. childText実装するなら、 continue する。

    4. childComment実装するなら:

      1. configuration["comments"] が true でないなら、 child除去する。

    5. childProcessingInstruction実装するなら:

      1. piTargetchildtarget とする。

      2. configuration["processingInstructions"] が存在するなら:

        1. configuration["processingInstructions"] が piTarget含まない なら:

          1. child除去する。

      3. そうでなければ:

        1. configuration["removeProcessingInstructions"] が piTarget含む なら:

          1. child除去する。

    6. そうでなければ:

      1. elementName を、childlocal name および namespace を持つ SanitizerElementNamespace とする。

      2. configuration["replaceWithChildrenElements"] が存在し、 かつ configuration["replaceWithChildrenElements"] が elementName含むなら:

        1. AssertnodeDocument実装しない。

        2. configuration および handleJavascriptNavigationUrls を用いて、 child に対して sanitize core を呼び出す。

        3. fragment を、その node documentnodenode document である新しい DocumentFragment とする。

        4. childchildren の各 innerChild について、 innerChildfragment追加する。

        5. node 内で childfragment置換する。

          注: ここで Replace は例外を投げないはずである。 アルゴリズムを成功裏に実行するための構造上の前提条件は 満たされているはずだからである。

        6. Continue する。

      3. configuration["elements"] が存在するなら:

        1. configuration["elements"] が elementName含まない なら:

          1. child除去する。

          2. Continue する。

      4. そうでなければ:

        1. configuration["removeElements"] が elementName含む なら:

          1. child除去する。

          2. Continue する。

      5. elementName が «[ "name" → "template", "namespace" → HTML namespace ]» と 等しいなら、 configuration および handleJavascriptNavigationUrls を用いて、 childtemplate contents に対して sanitize core を呼び出す。

      6. childshadow host なら、 configuration および handleJavascriptNavigationUrls を用いて、 childshadow root に対して sanitize core を呼び出す。

      7. childattribute list 内の各 attribute について:

        1. attrName を、attributelocal name および namespace を持つ SanitizerAttributeNamespace とする。

        2. configuration を与えたもとで、 attrName について is attribute allowedblocked であるなら、 attribute除去する。

        3. handleJavascriptNavigationUrls なら:

          1. «[elementName, attrName]» が 組み込み navigating URL attributes list 内のエントリーに一致し、かつ attributejavascript: URL を含むなら、 attribute除去する。

          2. childnamespaceMathML Namespace であり、 かつ attrlocal name が "href" でありattrnamespacenull または XLink namespace であり、かつ attrjavascript: URL を含むなら、 attribute除去する。

          3. 組み込み animating URL attributes list が «[elementName, attrName]» を 含み、 かつ attrvalue が "href" または "xlink:href" であるなら、 attribute除去する。

      8. configuration および handleJavascriptNavigationUrls を用いて、 child に対して sanitize core を呼び出す。

SanitizerAttributeNamespace attrName について、 SanitizerConfig configuration、 および SanitizerElementNamespace elementName を与えたもとで、 属性が許可されるか を決定するには:
  1. elementWithLocalAttributes を空の ordered map とする。

  2. configuration["elements"] が存在し、かつ configuration["elements"] が elementName含む なら:

    1. elementWithLocalAttributesconfiguration["elements"] 内の item のうち、 elementName["name"] が item["name"] であり、 かつ elementName["namespace"] が item["namespace"] であるものに設定する。

  3. elementWithLocalAttributes["removeAttributes"] が « » を既定値として 用いたときに attrName含むなら:

    1. blocked を返す。

  4. configuration["attributes"] が存在するなら:

    1. boolean globallyAllowed を、 configuration["attributes"] が attrName含むかどうかとする。

    2. boolean locallyAllowed を、 elementWithLocalAttributes["attributes"] が « » を既定値として 用いたときに attrName含むかどうかとする。

    3. boolean isDataAttributeAllowed を、次の両方が成り立つかどうかとする: "data-" が attrName[name] のcode unit prefix であり、 かつ attrName[namespace] が null であり、かつ configuration["dataAttributes"] が true である。

    4. globallyAllowedlocallyAllowed、および isDataAttributeAllowed のいずれも true でないなら、 blocked を返す。

  5. そうでなければ:

    1. elementWithLocalAttributes["attributes"] が存在し、かつ elementWithLocalAttributes["attributes"] が attrName含まないなら:

      1. blocked を返す。

    2. configuration["removeAttributes"] が attrName含むなら:

      1. blocked を返す。

  6. allowed を返す。

注記: 現在のブラウザは javascript: URL を ナビゲーション時のみサポートしている。ナビゲーション自体は XSS 脅威でないため javascript: へのナビゲーションのみ処理し、その他のナビゲーションは一般化しない。

宣言的ナビゲーションには主に次のカテゴリがある:

  1. アンカー要素(HTMLやSVG名前空間の <a>

  2. フォーム送信時のアクションによりナビゲーションされるフォーム要素

  3. [MathML] では 任意の要素をアンカーとして扱える

  4. [SVG11] のアニメーション。

最初の2つは 組み込みナビゲーションURL属性リスト でカバーされる。

MathML の場合はこの仕様で正式に「名前空間ごとのグローバル」ルールがないため、個別ルールでカバーされる。

SVGアニメーションの場合は 組み込みアニメーションURL属性リストでカバーされる。 ただしSVGアニメーション要素の意味合いはアニメーション対象に依存し、サニタイズ実行時に最終対象が分からないため、 sanitize アルゴリズムは href 属性のアニメーションを常にブロックする。

attributejavascript: URL を含むかを判定するには:
  1. url基本 URL パーサattribute で実行した結果とする。

  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>` は削除される。
要素を削除するには SanitizerElement elementSanitizerConfig configuration から削除する:
注記: このメソッドは4つの場合を区別する必要があります:
  • グローバル許可リストかグローバル除外リストか、

  • それらにすでにelementが含まれているかどうか。

  1. 断言: configuration有効である。

  2. elementサニタイザー要素の正規化の結果に設定する。

  3. modifiedremoveconfiguration["replaceWithChildrenElements"] から element を削除した結果に設定する。

  4. もし configuration["elements"] が存在する場合:

    1. もし configuration["elements"] element が含まれる場合:

      1. コメント: グローバル許可リストがあり、そこに element が含まれている。

      2. removeconfiguration["elements"] から element を削除する。

      3. true を返す。

    2. コメント: グローバル許可リストだがelementは含まれていない。

    3. modified を返す。

  5. その他の場合:

    1. もし configuration["removeElements"] element が含まれる場合:

      1. コメント: グローバル除外リストで、すでに element を含んでいる。

      2. modified を返す。

    2. コメント: グローバル除外リストだがelementは含まれていない。

    3. addconfiguration["removeElements"] に element を追加する。

    4. true を返す。

属性を削除するには SanitizerAttributeattributeSanitizerConfig configuration から削除する:

注記: このメソッドは2つの場合、つまりグローバル許可リストか、グローバル除外リストか、を区別します。 グローバル除外リストに attribute を追加するなら、妥当性基準を維持するため追加の処理が必要かもしれません。 グローバル許可リストから attribute を削除する場合も、ローカル除外リストからも削除が必要なことがあります。

  1. 断言: configuration有効である。

  2. attributeサニタイザー属性の正規化の結果に設定する。

  3. もし configuration["attributes"] が存在する場合:

    1. コメント: グローバル許可リストの場合、attributeを除去する必要がある。

    2. modifiedremoveconfiguration["attributes"] から attribute を削除した結果に設定する。

    3. コメント: 各要素ごとの許可・除外リストを修正する。

    4. もし configuration["elements"] が存在する場合:

      1. configuration["elements"] の element について:

        1. もし element["attributes"] (デフォルト« »付き) attributeが含まれる場合:

          1. modified を true に設定する。

          2. removeelement["attributes"] から attribute を削除。

        2. もし element["removeAttributes"] (デフォルト« »付き) attributeが含まれる場合:

          1. 断言: modified は true。

          2. removeelement["removeAttributes"] から attribute を削除。

    5. modified を返す。

  4. その他の場合:

    1. コメント: グローバル除外リストの場合、attributeを追加する。

    2. もし configuration["removeAttributes"] attribute を含む場合、 falseを返す。

    3. コメント: 各要素ごとの許可・除外リストを修正する。

    4. もし configuration["elements"] が存在する場合:

      1. configuration["elements"] の elementについて:

        1. もし element["attributes"] (デフォルト« »付き) attributeが含まれる場合:

          1. removeelement["attributes"] から attribute を削除。

        2. もし element["removeAttributes"] (デフォルト« »付き) attributeが含まれる場合:

          1. removeelement["removeAttributes"] から attribute を削除。

    5. appendattributeconfiguration["removeAttributes"] に追加する。

    6. true を返す。

unsafeを除去するには、 SanitizerConfig configuration に対して、次を実行する:

注記: このアルゴリズムは remove unsafe と呼ばれているが、 この仕様での「unsafe」は厳密には、 ドキュメント挿入時に JavaScript が実行される内容のみを指します。 つまり、このメソッドはXSSの機会を除去します。

  1. アサートbuilt-in safe baseline configurationキーセット次と等しい: « [ "removeElements", "removeAttributes" ] ».

  2. 断言: configuration有効である。

  3. result を false にする。

  4. element組み込み安全ベースライン構成["removeElements"] の 要素について:

    1. 要素を削除するelementconfiguration から削除する。

    2. 呼び出し結果が true なら result を true にする。

  5. attribute組み込み安全ベースライン構成["removeAttributes"] の属性について:

    1. 属性を削除するattributeconfiguration から削除する。

    2. 呼び出し結果が true なら result を true にする。

  6. attributeイベントハンドラーコンテンツ属性で:

    1. 属性を削除するattributeconfiguration から削除する。

    2. 呼び出し結果が true なら result を true にする。

  7. result を返す。

3.3. 構成を設定する

設定をセットするには、ディクショナリ configuration真偽値 allowCommentsPIsAndDataAttributesSanitizer sanitizer が与えられたとき:
  1. 正規化 configurationallowCommentsPIsAndDataAttributes とともに行う。

  2. もし configuration有効 でなければ、false を返す。

  3. sanitizer設定configuration にセットする。

  4. trueを返す。

3.4. 構成の正規化

Sanitizerは、設定を正規化された形式で格納します。これは、複数の処理手順を簡略化するためです。

elements リスト {elements: ["div"]}{elements: [{name: "div", namespace: "http://www.w3.org/1999/xhtml"}] のように格納されます。
SanitizerConfig configuration を、 boolean allowCommentsPIsAndDataAttributes とともに 設定を正準化するには:

注: configuration は、 JavaScript 値を SanitizerConfig に変換した [WebIDL] の結果であると仮定する。

  1. configuration["elements"] も configuration["removeElements"] も存在しない場合、configuration["removeElements"] を « » に設定する

  2. configuration["processingInstructions"] も configuration["removeProcessingInstructions"] も存在しない場合:

    1. allowCommentsPIsAndDataAttributes が true の場合、configuration["removeProcessingInstructions"] を « » に設定する

    2. そうでない場合、configuration["processingInstructions"] を « » に設定する

  3. configuration["attributes"] も configuration["removeAttributes"] も存在しない場合、configuration["removeAttributes"] を « » に設定する

  4. configuration["elements"] が存在する場合:

    1. elements を « » とする。

    2. configuration["elements"] の各 element について反復し、次を行う:

      1. element について属性付き sanitizer 要素を正規化する結果を elements追加する

    3. configuration["elements"] を elements に設定する。

  5. configuration["removeElements"] が存在する場合:

    1. elements を « » とする。

    2. configuration["removeElements"] の各 element について反復し、次を行う:

      1. element についてsanitizer 要素を正規化する結果を elements追加する

    3. configuration["removeElements"] を elements に設定する。

  6. configuration["replaceWithChildrenElements"] が存在する場合:

    1. elements を « » とする。

    2. configuration["replaceWithChildrenElements"] の各 element について反復し、次を行う:

      1. element についてsanitizer 要素を正規化する結果を elements追加する

    3. configuration["replaceWithChildrenElements"] を elements に設定する。

  7. configuration["processingInstructions"] が存在する場合:

    1. processingInstructions を « » とする。

    2. configuration["processingInstructions"] の各 pi について反復する

      1. pi についてsanitizer 処理命令を正規化する結果を processingInstructions追加する

    3. configuration["processingInstructions"] を processingInstructions に設定する。

  8. configuration["removeProcessingInstructions"] が存在する場合:

    1. processingInstructions を « » とする。

    2. configuration["removeProcessingInstructions"] の各 pi について反復する

      1. pi についてsanitizer 処理命令を正規化する結果を processingInstructions追加する

    3. configuration["removeProcessingInstructions"] を processingInstructions に設定する。

  9. configuration["attributes"] が存在する場合:

    1. attributes を « » とする。

    2. configuration["attributes"] の各 attribute について反復し、次を行う:

      1. attribute についてsanitizer 属性を正規化する結果を attributes追加する

    3. configuration["attributes"] を attributes に設定する。

  10. configuration["removeAttributes"] が存在する場合:

    1. attributes を « » とする。

    2. configuration["removeAttributes"] の各 attribute について反復し、次を行う:

      1. attribute についてsanitizer 属性を正規化する結果を attributes追加する

    3. configuration["removeAttributes"] を attributes に設定する。

  11. configuration["comments"] が存在しない場合、configuration["comments"] を allowCommentsPIsAndDataAttributes設定する

  12. configuration["attributes"] が存在し、かつ configuration["dataAttributes"] が存在しない場合、 configuration["dataAttributes"] を allowCommentsPIsAndDataAttributes設定する

SanitizerElementWithAttributes element属性付き sanitizer 要素を 正規化するには:
  1. result を、elementsanitizer 要素を正規化する結果とする。

  2. element辞書である場合:

    1. element["attributes"] が存在する場合:

      1. attributes を « » とする。

      2. element["attributes"] の各 attribute について反復する

        1. attributesanitizer 属性を正規化する結果を attributes追加する

      3. result["attributes"] を attributes設定する

    2. element["removeAttributes"] が存在する場合:

      1. attributes を « » とする。

      2. element["removeAttributes"] の各 attribute について反復する

        1. attributesanitizer 属性を正規化する結果を attributes追加する

      3. result["removeAttributes"] を attributes設定する

  3. result["attributes"] も result["removeAttributes"] も存在しない場合:

    1. result["removeAttributes"] を « » に設定する

  4. result を返す。

サニタイザー要素を正規化するには、 SanitizerElement elementサニタイザー名を正規化する を使い、 elementとデフォルト名前空間「HTML namespace」を渡し、その結果を返す。
サニタイザー処理命令を正規化するには pi について次を実行する:
  1. 断言: piDOMString または ディクショナリである。

  2. もし piDOMString なら «[ "target" → pi ]» を返す。

  3. 断言: piディクショナリ であり pi["target"] が存在する

  4. «[ "target" → pi["target"] ]» を返す。

サニタイザー属性を正規化するには、 SanitizerAttribute attributeについて、サニタイザー名を正規化する を使い、 attributeとデフォルト名前空間nullを渡して、その結果を返す。
サニタイザー名を正規化するには name とデフォルト名前空間 defaultNamespace を使い、次の手順を実行する:
  1. 断言: nameDOMString または ディクショナリである。

  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. 補助アルゴリズム

正準化された element および attribute name リストであって この仕様で使用されるものについては、リストのメンバーシップは "name" と "namespace" の両方のエントリーが一致することに基づく:

Sanitizer 名 list は、listentry であって ordered map であるものが存在し、かつ item["name"] が entry["name"] と 等しく、かつ item["namespace"] が entry["namespace"] と 等しい場合、item含む
Sanitizer ターゲット list は、listentry であって ordered map であるものが存在し、かつ targetentry["target"] と 等しい場合、targetターゲットとして含む
list list から item除去するには:
  1. removed を false に設定する。

  2. list の各 entry について反復する

    1. item["name"] が entry["name"] と等しく、 かつ item["namespace"] が entry["namespace"] と等しいなら:

      1. list から item entry を除去する。

      2. removed を true に設定する。

  3. removed を返す。

name正準化されており、かつ listordered map である場合に、namelist追加するには:
  1. listname含むなら、返す。

  2. namelist追加する。

item itemA は、次の場合に itemB より 小さい item である:
  1. itemA["namespace"] が null なら:

    1. itemB["namespace"] が null でないなら、true を返す。

  2. そうでなければ:

    1. itemB["namespace"] が null なら、false を返す。

    2. itemA["namespace"] が itemB["namespace"] よりcode unit less than なら、true を返す。

    3. itemA["namespace"] が itemB["namespace"] でない なら、false を返す。

  3. itemA["name"] が itemB["name"] より code unit less than であるかどうかを返す。

ordered sets における等価性は、そのメンバーの等価性であるが、 順序は考慮しない: Ordered sets A および B は、ABsuperset であり、かつ BAsuperset である場合、 等しい
ordered map は、keyvaluetuples の列である。 ordered maps の等価性は、ordered set として扱ったときの、このタプル列の等価性である。 Ordered maps A および B は、Aentries からなる ordered set と、 Bentriesordered set とが 等しい場合、 等しい
list list は、 list任意の item について、 item["name"] が entry["name"] であり、かつ item["namespace"] が entry["namespace"] であるような entrylist 内に複数存在する場合、 重複を持つ
list list は、 list任意の item について、 item["target"] が entry["target"] であるような entrylist 内に複数存在する場合、 重複するターゲットを持つ
list list から 重複を除去するには、
  1. result を « » とする。

  2. list の各 entry について反復しentryresult追加する。

  3. result を返す。

SanitizerElement を含む 2 つの lists A および Bintersection は、set intersection と同じだが、 set エントリーは事前に 正準化される
  1. set A を « [] » とする。

  2. set B を « [] » とする。

  3. A の各 entry について反復しentrycanonicalize a sanitizer name した結果を set A追加する。

  4. B の各 entry について反復しentrycanonicalize a sanitizer name した結果を set B追加する。

  5. set Aset Bintersection を返す。

boolean boolnot を決定するには、 bool が true なら false を返し、そうでなければ true を返す。
Comment は、 アルゴリズム内の特定の点に適用される説明的なテキストを含む。

3.6. 組み込み

組み込みは5つあります:

組み込みの安全な既定構成は次の通りです:

{
  "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": "a",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "href",
          "namespace": null
        },
        {
          "name": "hreflang",
          "namespace": null
        },
        {
          "name": "type",
          "namespace": null
        }
      ]
    },
    {
      "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
        }
      ]
    }
  ],
  "processingInstructions": [],
  "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" → "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" → "animateTransform", "namespace" → SVG名前空間 }, { "name" → "attributeName", "namespace" → null } ],
[ { "name" → "set", "namespace" → SVG名前空間 }, { "name" → "attributeName", "namespace" → null } ],

組み込みの置換不可要素リストには、 子要素への置換を許可すると再パース問題や無効なノードツリーになる可能性があるため絶対に置換してはいけない要素が含まれます。

«[
{ "name" → "html", "namespace" → HTML名前空間 },
{ "name" → "svg", "namespace" → SVG名前空間 },
{ "name" → "math", "namespace" → MathML名前空間 },

注:

これらの制限だけでは、例えば次のようなすべての再パース攻撃を防ぐには不十分です:

// BAD EXAMPLE(悪い例)
let sanitizer = { replaceWithChildrenElements: ["tr"] };
div.setHTML("<table><tbody><tr><td><div>", { sanitizer });

div.innerHTML = div.innerHTML;
// div.innerHTML は現在 `<div></div><table><tbody><tr></tr></tbody></table>`

しかし、この例の <iframe> については再パースされて通常のHTML iframeとして有効になってしまうことは防げます:

let sanitizer = { replaceWithChildrenElements: [
    { name: "svg", namespace: "http://www.w3.org/2000/svg"}
  ]
};
div.setHTML(`<svg><iframe src="https://example.com"></iframe></svg>`, { sanitizer }); // 例外発生

4. セキュリティ考慮事項

Sanitizer API は、与えられた HTML コンテンツを走査し、構成に従って要素や属性を除去することで DOM ベースのクロスサイトスクリプティングを防ぐことを目的としています。指定された API は、スクリプト実行可能なマークアップを残すような Sanitizer オブジェクトの構築を許してはならず、もしそうなった場合は脅威モデル上のバグです。

とはいえ、Sanitizer API の正しい利用だけでは防げないセキュリティ上の問題も存在し、それについては以下のセクションで説明します。

4.1. サーバーサイド反射型および保存型XSS

このセクションは規範的ではありません。

Sanitizer API は DOM 内だけで動作し、既存の DocumentFragment を走査・フィルターする機能を提供します。Sanitizer はサーバーサイドの反射型XSSや保存型XSSには対応しません。

4.2. DOMクラッタリング

このセクションは規範的ではありません。

DOMクラッタリングとは、攻撃者が idname 属性を使って要素に名前をつけ、HTML要素の children などのプロパティを悪意のある内容で上書きしてアプリケーションを混乱させる攻撃です。

Sanitizer API はデフォルトでは DOMクラッタリング攻撃を防ぎませんが、構成で idname 属性を除去することができます。

4.3. スクリプトガジェットによるXSS

このセクションは規範的ではありません。

スクリプトガジェットとは、攻撃者が有名なJavaScriptライブラリなどの既存アプリケーションコードを利用して、攻撃者自身のコードを実行させる手法です。これは無害に見えるコードや一見無害なDOMノードを注入し、フレームワークがそれを解析・解釈することで JavaScript の実行につなげます。

Sanitizer API ではこの種の攻撃を防ぐことはできません。ページ作成者は未知の要素を許可する場合は特に注意し、data-slot 属性、<slot><template> など広く使われるテンプレート・フレームワーク固有のマークアップも明示的に設定する必要があります。これらの制限は網羅的ではないので、ページ作成者はサードパーティライブラリの挙動もよく確認してください。

4.4. 変異型XSS

このセクションは規範的ではありません。

変異型XSS(mXSS)は、HTMLスニペットを誤った文脈でパースした際にパーサの文脈不一致を利用する攻撃です。特に、一度パースしたHTMLフラグメントを文字列としてシリアライズし、別の親要素に再挿入した場合、同じように解釈される保証はありません。例えば外部コンテンツやタグの入れ子ミスによるパース挙動の変化を利用します。

Sanitizer API は文字列をノードツリー化する機能のみを提供します。文脈は各サニタイザー関数によって暗黙的に与えられ、Element.setHTML() は現在の要素、Document.parseHTML() は新しいドキュメントを作成します。そのためSanitizer API自体は変異型XSSの直接的な影響は受けません。

もし開発者がサニタイズ済みノードツリーを .innerHTML などで文字列にし、再度パースする場合はmXSSが発生しうるため、この運用は推奨されません。どうしても文字列で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 Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; 他. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. 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 Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WebIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. 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. Mathematical Markup Language (MathML™) 1.01 Specification. 2023年3月7日. REC. URL: https://www.w3.org/TR/REC-MathML/
[MXSS]
mXSS Attacks: Attacking well-secured Web-Applications by using innerHTML Mutations. URL: https://cure53.de/fp170.pdf
[SafeMathML]
MathML Safe List. URL: https://w3c.github.io/mathml-docs/mathml-safe-list
[SVG11]
Erik Dahlström; 他. Scalable Vector Graphics (SVG) 1.1 (Second Edition). 2011年8月16日. REC. URL: https://www.w3.org/TR/SVG11/

IDL 索引

partial interface Element {
  [CEReactions] undefined setHTML(DOMString html, optional SetHTMLOptions options = {});
};

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

partial interface Document {
  static Document parseHTML(DOMString html, optional SetHTMLOptions options = {});
};

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 allowProcessingInstruction(SanitizerPI pi);
  boolean removeProcessingInstruction(SanitizerPI pi);
  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 SanitizerProcessingInstruction {
  required DOMString target;
};

typedef (DOMString or SanitizerProcessingInstruction) SanitizerPI;

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<SanitizerPI> processingInstructions;
  sequence<SanitizerPI> removeProcessingInstructions;

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

  boolean comments;
  boolean dataAttributes;
};