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 and Business Groups를 참고하십시오.

1. 소개

이 섹션은 표준이 아닙니다.

웹 애플리케이션은 클라이언트 측에서 HTML 문자열을 다뤄야 하는 경우가 많습니다. 예를 들어 클라이언트 측 템플릿 솔루션의 일부로, 또는 사용자 생성 콘텐츠를 렌더링하는 과정 등에서 필요할 수 있습니다. 이러한 작업을 안전하게 수행하는 것은 어렵습니다. 문자열을 단순히 결합하여 ElementinnerHTML 에 넣는 순진한 방식은 여러 예기치 않은 방식으로 JavaScript 실행을 유발할 위험이 있습니다.

[DOMPURIFY]와 같은 라이브러리는 문자열을 삽입하기 전에 신중하게 파싱하고 정제(필터링)하여, DOM을 구성하고 허용 리스트를 통해 그 멤버를 필터링함으로써 이러한 문제를 관리하려고 시도합니다. 그러나 웹에서 노출된 파싱 API가 실제 브라우저가 문자열을 "진짜" DOM으로 렌더링할 때의 동작과 합리적으로 매핑되지 않는 경우가 있어, 이 접근법은 취약한 것으로 입증되었습니다. 또한 라이브러리는 브라우저의 변화하는 동작을 지속적으로 따라잡아야 하며, 과거에는 안전했던 것이 새로운 플랫폼 기능에 의해 위험요소로 바뀔 수 있습니다.

브라우저는 코드가 언제 실행될지에 대해 상당히 잘 알고 있습니다. 우리는 브라우저에게 임의의 문자열에서 HTML을 안전하게 렌더링하는 방법을 가르침으로써, 사용자 공간 라이브러리보다 더 나은 방식으로 이 문제를 해결할 수 있습니다. 또한 브라우저 자체의 파서 구현이 변경될 때마다 함께 유지보수되고 업데이트될 가능성이 훨씬 높습니다. 이 문서는 바로 그 목적을 위한 API를 제시합니다.

1.1. 목표

1.2. API 요약

Sanitizer API는 HTML을 포함하는 문자열을 DOM 트리로 파싱하고, 사용자 정의 설정에 따라 결과 트리를 필터링하는 기능을 제공합니다. 메서드는 두 가지 유형으로 제공됩니다:

2. 프레임워크

2.1. Sanitizer 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. compliantHTMLGet Trusted Type compliant string 알고리즘을 TrustedHTML, thisrelevant global object, html, "Element setHTMLUnsafe", "script"와 함께 실행한 결과로 얻는다.

  2. targetthistemplate contents로 설정(만약 thistemplate 요소라면), 아니라면 this로 설정한다.

  3. Set and filter HTMLtarget, this, compliantHTML, options, 그리고 false로 실행한다.

ElementsetHTML(html, options) 메서드 단계는 다음과 같다:
  1. targetthistemplate contents로 설정(만약 thistemplate라면), 아니라면 this로 설정한다.

  2. Set and filter HTMLtarget, this, html, options, 그리고 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. compliantHTMLGet Trusted Type compliant string 알고리즘을 TrustedHTML, thisrelevant global object, html, "ShadowRoot setHTMLUnsafe", "script"와 함께 실행한 결과로 얻는다.

  2. Set and filter HTMLthis, thisshadow host(컨텍스트 요소)와, compliantHTML, options, false와 함께 실행한다.

ShadowRootsetHTML(html, options) 메서드 단계는 다음과 같다:
  1. HTML 설정 및 필터링this(대상), this(컨텍스트 요소), html, options, 그리고 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. compliantHTMLGet Trusted Type compliant string 알고리즘을 TrustedHTML, thisrelevant global object, html, "Document parseHTMLUnsafe", 그리고 "script"와 함께 호출한 결과로 설정한다.

  2. document를 새로운 Document로 설정하며, content type은 "text/html"로 한다.

    참고: document에 browsing context가 없으므로, 스크립트 실행이 비활성화된다.

  3. documentallow declarative shadow roots를 true로 설정한다.

  4. HTML 문자열 파싱documentcompliantHTML로 수행한다.

  5. sanitizer옵션에서 sanitizer 인스턴스 가져오기options와 false로 호출한 결과로 설정한다.

  6. sanitizedocumentsanitizer, false로 호출한다.

  7. document를 반환한다.

parseHTML(html, options) 메서드 단계는 다음과 같다:
  1. document를 새로운 Document로 설정하며, content type은 "text/html"로 한다.

    참고: document에 browsing context가 없으므로, 스크립트 실행이 비활성화된다.

  2. documentallow declarative shadow roots를 true로 설정한다.

  3. HTML 문자열 파싱documenthtml로 수행한다.

  4. sanitizer옵션에서 sanitizer 인스턴스 가져오기options와 true로 호출한 결과로 설정한다.

  5. sanitizedocumentsanitizer, true로 호출한다.

  6. document를 반환한다.

2.2. SetHTML 옵션 및 구성 객체

setHTML() 계열의 메서드는 모두 options 딕셔너리를 인자로 받는다. 현재 이 딕셔너리에는 한 가지 멤버만 정의되어 있다:

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" 메서드는 제한 없이 동작한다. 구성 객체는 페이지 수명 초기에 하나 또는 몇 개만 미리 만들어 두고, 필요할 때마다 재사용하는 것이 의도다. 이는 구현체가 구성을 사전 처리할 수 있게 해준다.

구성 객체는 쿼리하여 구성 딕셔너리를 반환할 수 있고, 직접 수정할 수도 있다.

[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. configurationSanitizerPresets 문자열인 경우:

    1. 단언: configuration"default"임을 확인한다.

    2. configuration내장 안전 기본 구성으로 설정한다.

  2. valid구성 설정configuration과 true, 그리고 this를 넘겨 호출한 반환 값으로 설정한다.

  3. valid가 false라면 TypeError를 throw한다.

get() 메서드 단계는 다음과 같습니다:
참고: get() 메서드 외부에서는 Sanitizer의 요소와 속성의 순서를 관찰할 수 없습니다. 이 메서드의 결과를 명시적으로 정렬함으로써 구현체가 내부적으로 예를 들어, 정렬되지 않은 집합을 사용할 수 있도록 최적화할 기회를 제공합니다.
  1. configthisconfiguration으로 설정한다.

  2. 만약 config["elements"] 존재한다면:

    1. 모든 element에 대해 config["elements"]에서:

      1. 만약 element["attributes"] 존재한다면:

        1. element["attributes"]를 오름차순 정렬의 결과로 설정한다. element["attributes"], attrAless than item attrB일 때.

      2. 만약 element["removeAttributes"] 존재한다면:

        1. element["removeAttributes"]를 오름차순 정렬의 결과로 설정한다. element["removeAttributes"], attrAless than item attrB일 때.

    2. config["elements"]를 오름차순 정렬의 결과로 설정한다. config["elements"], elementAless than item elementB일 때.

  3. 만약 config["removeElements"] 존재한다면:

    1. config["removeElements"]를 오름차순 정렬의 결과로 설정한다. config["removeElements"], elementAless than item elementB일 때.

  4. 만약 config["replaceWithChildrenElements"] 존재한다면:

    1. config["replaceWithChildrenElements"]를 오름차순 정렬의 결과로 설정한다. config["replaceWithChildrenElements"], elementAless than item elementB일 때.

  5. 만약 config["attributes"] 존재한다면:

    1. config["attributes"]를 오름차순 정렬의 결과로 설정한다. config["attributes"], attrAless than item attrB일 때.

  6. 만약 config["removeAttributes"] 존재한다면:

    1. config["removeAttributes"]를 오름차순 정렬의 결과로 설정한다. config["removeAttributes"], attrAless than item attrB일 때.

  7. config를 반환한다.

allowElement(element) 메서드 단계는 요소 허용elementthis구성(configuration)으로 수행한다.
removeElement(element) 메서드 단계는 요소 제거elementthis구성(configuration)으로 수행한다.
replaceElementWithChildren(element) 메서드 단계는 요소를 자식으로 교체elementthis구성(configuration)으로 수행한다.
allowAttribute(attribute) 메서드 단계는 속성 허용attributethis구성(configuration)으로 수행한다.
removeAttribute(attribute) 메서드 단계는 속성 제거attributethis구성(configuration)으로 수행한다.
setComments(allow) 메서드 단계는 주석 설정allowthis구성(configuration)으로 수행한다.
setDataAttributes(allow) 메서드 단계는 data 속성 설정allowthis구성(configuration)으로 수행한다.
removeUnsafe() 메서드 단계는 this구성(configuration)remove unsafethis구성(configuration)에 대해 호출한 결과로 갱신하는 것이다.

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" sanitizer 메서드는 서로 다른 기본값을 가진다는 점에 유의해야 한다.

모든 구성 딕셔너리가 유효한 것은 아닙니다. 유효한 구성은 중복(같은 요소가 두 번 허용되는 경우 등)과 모순(같은 요소가 허용과 제거에 동시에 지정되는 경우 등)을 피해야 합니다.

구성이 유효하려면 여러 조건을 만족해야 합니다:

elements 요소 허용 리스트는 특정 요소에 대한 속성 허용/제거도 지정할 수 있습니다. 이는 [HTML]의 구조를 반영하기 위한 것으로, 글로벌 속성과 특정 요소에만 적용되는 로컬 속성을 모두 인지합니다. 글로벌과 로컬 속성은 혼합될 수 있지만, 특정 속성이 한 리스트에서는 허용되고 다른 리스트에서는 금지되는 모호한 구성은 일반적으로 허용되지 않습니다.

글로벌 attributes 글로벌 removeAttributes
로컬 attributes 속성이 두 리스트 중 하나에 일치하면 허용됩니다. 중복 항목은 허용되지 않습니다. 속성이 로컬 허용 리스트에 있을 때만 허용됩니다. 글로벌 제거와 로컬 허용 리스트 사이에는 중복이 없어야 합니다. 글로벌 제거 리스트는 이 요소에서는 기능이 없지만, 로컬 허용 리스트가 없는 다른 요소에는 적용될 수 있습니다.
로컬 removeAttributes 속성이 글로벌 허용 리스트에는 있고 로컬 제거 리스트에는 없으면 허용됩니다. 로컬 제거 리스트는 글로벌 허용 리스트의 부분집합이어야 합니다. 속성이 어느 리스트에도 없으면 허용됩니다. 글로벌 제거와 로컬 제거 리스트 사이에는 중복이 없어야 합니다.

글로벌과 per-element 리스트 간 중복이 대체로 허용되지 않지만, 글로벌 허용 리스트와 per-element 제거 리스트의 경우에는 후자가 전자의 부분집합이어야 하는 비대칭이 존재합니다. 위 표에서 중복만 집중한 발췌는 다음과 같습니다:

글로벌 attributes 글로벌 removeAttributes
로컬 attributes 중복 항목은 허용되지 않습니다. 중복 항목은 허용되지 않습니다.
로컬 removeAttributes 로컬 제거 리스트는 글로벌 허용 리스트의 부분집합이어야 합니다. 중복 항목은 허용되지 않습니다.

dataAttributes 설정은 커스텀 데이터 속성을 허용합니다. 위 규칙은 dataAttributes 를 허용 리스트로 간주하면 커스텀 데이터 속성에도 쉽게 확장됩니다:

글로벌 attributesdataAttributes 설정
로컬 attributes 모든 커스텀 데이터 속성이 허용됩니다. 어떤 허용 리스트에도 커스텀 데이터 속성이 있으면 중복 항목이 됩니다.
로컬 removeAttributes 커스텀 데이터 속성은 로컬 제거 리스트에 없으면 허용됩니다. 어떤 글로벌 허용 리스트에도 커스텀 데이터 속성이 있으면 중복 항목이 됩니다.

이 규칙을 말로 표현하면 다음과 같습니다:

SanitizerConfig config유효(valid)하려면 다음 조건을 모두 만족해야 합니다:
  1. configelements 또는 removeElements 를 가지며, 둘 다 갖지는 않습니다.

  2. configattributes 또는 removeAttributes 를 가지며, 둘 다 갖지는 않습니다.

  3. Assert: 모든 SanitizerElementNamespaceWithAttributes, SanitizerElementNamespace, 및 SanitizerAttributeNamespace 항목들은 정규화(canonical)되어 있어야 하며, 각각 canonicalize a sanitizer element 또는 canonicalize a sanitizer attribute 를 거쳐야 합니다.

  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. element에 대해 config[elements]:

        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] 존재한다면, element에 대해 config[elements]:

      1. element[attributes] 와 element[removeAttributes] 는 존재한다면 중복 항목이 없어야 합니다.

      2. config[removeAttributes] 와 element[attributes] 의 기본값 « »의 교집합은 빈 집합이어야 합니다.

      3. config[removeAttributes] 와 element[removeAttributes] 의 기본값 « »의 교집합은 빈 집합이어야 합니다.

    2. config[dataAttributes] 는 존재하지 않아야 합니다.

참고: 구성 설정딕셔너리에서 할 때는 약간의 정규화(normalization)가 이루어집니다. 특히 허용/제거 리스트가 모두 없으면, 이것을 빈 제거 리스트로 해석합니다. 그래서 {} 자체는 유효(valid)한 구성은 아니지만, {removeElements:[],removeAttributes:[]}로 정규화되면 유효하게 됩니다. 이 정규화 과정은 비어있는 딕셔너리와 없는 딕셔너리를 일관되게 처리하기 위해 선택되었습니다. 즉, setHTMLUnsafe(txt)setHTMLUnsafe(txt, {sanitizer: {}})가 일관성을 갖도록 하기 위함입니다.

3. 알고리즘

HTML 설정 및 필터링을 하기 위해서는, Element 또는 DocumentFragment target, Element contextElement, string html, dictionary options, 그리고 boolean safe가 주어집니다:
  1. safe가 true이고 contextElementlocal name이 "script"이며, contextElementnamespaceHTML 네임스페이스 또는 SVG 네임스페이스라면, 반환합니다.

  2. sanitizer옵션에서 sanitizer 인스턴스 얻기optionssafe로 호출해서 얻습니다.

  3. newChildrenHTML 프래그먼트 파싱 알고리즘contextElement, html, true로 실행한 결과로 설정합니다.

  4. fragment를 새 DocumentFragment로 생성하고, 노드 documentcontextElement노드 document로 설정합니다.

  5. nodenewChildren에서 fragment에 append합니다.

  6. sanitizefragmentsanitizersafe로 실행합니다.

  7. target 내 모든 자식fragment로 교체합니다.

옵션에서 sanitizer 인스턴스 얻기를 실행하려면, dictionary optionsboolean safe가 필요합니다:

참고: 이 알고리즘은 SetHTMLOptionsSetHTMLUnsafeOptions 모두에 적용됩니다. 두 옵션의 차이는 기본값뿐입니다.

  1. sanitizerSpec를 "default"로 설정합니다.

  2. 만약 options["sanitizer"] 가 존재하면:

    1. sanitizerSpecoptions["sanitizer"]로 설정합니다.

  3. Assert: sanitizerSpecSanitizer 인스턴스이거나, 문자열(string)SanitizerPresets 멤버이거나, 딕셔너리여야 합니다.

  4. sanitizerSpec문자열이라면:

    1. Assert: sanitizerSpec"default"임을 확인합니다.

    2. sanitizerSpec내장 안전 기본 구성으로 설정합니다.

  5. Assert: sanitizerSpecSanitizer 인스턴스이거나, 딕셔너리여야 합니다.

  6. sanitizerSpec딕셔너리라면:

    1. sanitizer를 새 Sanitizer 인스턴스로 생성합니다.

    2. setConfigurationResultset a configurationsanitizerSpecnot safesanitizer에 실행한 결과로 설정합니다.

    3. setConfigurationResult가 false이면, TypeError를 throw합니다.

    4. sanitizerSpecsanitizer로 설정합니다.

  7. Assert: sanitizerSpecSanitizer 인스턴스여야 합니다.

  8. sanitizerSpec을 반환합니다.

3.1. Sanitize

주요 sanitize 작업은 ParentNode node, Sanitizer sanitizer, 그리고 boolean safe을 사용하며, 아래 절차를 따릅니다:
  1. configurationsanitizerconfiguration 값으로 설정합니다.

  2. safe가 true라면, configurationremove unsafeconfiguration에 호출한 결과로 변경합니다.

  3. sanitize corenode, configuration, 그리고 handleJavascriptNavigationUrlssafe로 실행합니다.

sanitize core 작업은 ParentNode node, SanitizerConfig configuration, 그리고 boolean handleJavascriptNavigationUrls를 사용하여 DOM 트리를 node부터 재귀적으로 순회합니다. 절차는 다음과 같습니다:
  1. 각각 child에 대해 nodechildren을 반복합니다:

    1. 단언: child구현함 Text, Comment, Element, 또는 DocumentType.

      참고: 현재 이 알고리즘은 HTML 파서의 출력에만 호출되며, 이 단언이 반드시 만족되어야 합니다. DocumentTypeparseHTMLparseHTMLUnsafe에서만 발생해야 합니다. 만약 앞으로 이 알고리즘이 다른 컨텍스트에서 사용된다면, 이 가정을 재검토해야 합니다.

    2. childDocumentType을 구현하면 continue합니다.

    3. childText를 구현하면 continue합니다.

    4. childComment를 구현하는 경우:

      1. configuration["comments"]가 true가 아니면, child를 제거합니다.

    5. 그 외의 경우:

      1. elementNamechild로컬 이름네임스페이스를 가진 SanitizerElementNamespace 로 설정한다.

      2. 만약 configuration["replaceWithChildrenElements"] 존재하고 configuration["replaceWithChildrenElements"] 포함하는 경우 elementName을:

        1. child에 대해 configurationhandleJavascriptNavigationUrls를 사용하여 sanitize core를 호출한다.

        2. child자식들child 내에서 모두 교체를 호출한다.

        3. 계속 진행한다.

      3. 만약 configuration["removeElements"] 존재하고 configuration["removeElements"] 포함하는 경우 elementName을:

        1. 제거 child.

        2. 계속 진행한다.

      4. 만약 configuration["elements"] 존재하고 configuration["elements"]가 포함하지 않는 경우 elementName을:

        1. 제거 child.

        2. 계속 진행한다.

      5. 만약 elementName이 «[ "name" → "template", "namespace" → HTML namespace ]»와 같으면, childtemplate contents에 대해 configurationhandleJavascriptNavigationUrls를 사용하여 sanitize core를 호출한다.

      6. 만약 child섀도우 호스트라면, child섀도우 루트에 대해 configurationhandleJavascriptNavigationUrls를 사용하여 sanitize core를 호출한다.

      7. elementWithLocalAttributes를 « [] »로 설정한다.

      8. 만약 configuration["elements"] 존재하고 configuration["elements"] 포함하는 경우 elementName을:

        1. elementWithLocalAttributesconfiguration["elements"][elementName]로 설정한다.

      9. attribute에 대해 child속성 리스트에서:

        1. attrNameattribute로컬 이름네임스페이스를 가진 SanitizerAttributeNamespace 로 설정한다.

        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이 true라면:

          1. 만약 «[elementName, attrName]»이 내장 내비게이팅 URL 속성 리스트에 일치하고, attributejavascript: URL을 포함한다면, 제거 attribute.

          2. 만약 child네임스페이스MathML 네임스페이스와 같고, attr로컬 이름이 "href"와 같고, attr네임스페이스null 또는 XLink 네임스페이스이고, attrjavascript: URL을 포함한다면, 제거 attribute.

          3. 만약 내장 애니메이팅 URL 속성 리스트포함하는 경우 «[elementName, attrName]»을, 그리고 attr이 "href" 또는 "xlink:href"와 같으면, 제거 attribute.

      10. child에 대해 configurationhandleJavascriptNavigationUrls를 사용하여 sanitize core를 호출한다.

참고: 현재 브라우저는 javascript: URL을 navigation시에만 지원합니다. navigation 자체는 XSS 위협이 아니므로, javascript: URL로의 navigation만 처리하며 일반 navigation은 처리하지 않습니다.

선언적 navigation은 몇 가지 유형으로 나뉩니다:

  1. 앵커 요소 (<a>, HTML 및 SVG 네임스페이스)

  2. 폼 요소 (폼 action으로 navigation을 트리거)

  3. [MathML]모든 요소가 앵커 역할을 할 수 있도록 허용합니다.

  4. [SVG11] 애니메이션.

첫 두 경우는 내장 navigation URL 속성 리스트로 처리됩니다.

MathML의 경우는 별도 규칙으로 처리됩니다. 이 명세에 "네임스페이스 전역" 규칙이 없기 때문입니다.

SVG 애니메이션의 경우는 내장 애니메이션 URL 속성 리스트로 처리됩니다. SVG 애니메이션 요소의 해석은 애니메이션의 대상에 따라 다르지만, sanitize 중에는 최종 대상 파악이 불가능하므로, sanitize 알고리즘은 모든 애니메이션 href 속성을 차단합니다.

attributejavascript: URL을 포함하는지 판단하려면:
  1. url기본 URL 파서attribute에 실행한 결과로 설정합니다.

  2. urlfailure라면, false를 반환합니다.

  3. urlscheme이 "javascript"인지 반환합니다.

3.2. 구성 수정

구성 수정자 메서드는 Sanitizer에 정의된 메서드로, 해당 인스턴스의 구성을 변경합니다. 이들은 유효성 기준을 유지합니다. 변경 여부를 호출자에게 알리는 boolean 값을 반환합니다.

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>`는 제거됩니다.
    
To 요소를 허용 SanitizerElementWithAttributes elementSanitizerConfig configuration과 함께 처리하려면:
참고: 이 알고리즘은 요소 허용 리스트가 요소별 속성 허용/제거 리스트를 지정할 수 있으므로 다소 복잡합니다. 따라서 다음 네 가지 경우를 구별해야 합니다:
  • 글로벌 허용 리스트인지 제거 리스트인지 여부,

  • 그리고 이러한 리스트가 이미 element를 포함하는지 여부.

  1. elementelement속성을 포함한 sanitizer element 정규화의 결과로 설정한다.

  2. 만약 configuration["elements"] 존재하면:

    1. modifiedelementconfiguration["replaceWithChildrenElements"] 에서 제거한 결과로 설정한다.

    2. 코멘트: per-element 속성이 글로벌 속성과 겹치지 않도록 해야 한다.

    3. 만약 element["attributes"] 존재하면:

      1. element["attributes"] 를 element["attributes"]에서 중복 제거한 결과로 설정한다.

      2. 만약 configuration["attributes"] 존재하면:

        1. element["attributes"] 를 element["attributes"] 와 configuration["attributes"]의 차집합으로 설정한다.

        2. 만약 configuration["dataAttributes"]가 true라면:

          1. element["attributes"] 에서 커스텀 데이터 속성item모두 제거한다.

      3. 만약 configuration["removeAttributes"] 존재하면:

        1. element["attributes"] 를 element["attributes"] 와 configuration["removeAttributes"]의 차집합으로 설정한다.

    4. 만약 element["removeAttributes"] 존재하면:

      1. element["removeAttributes"] 를 element["removeAttributes"]에서 중복 제거한 결과로 설정한다.

      2. 만약 configuration["attributes"] 존재하면:

        1. element["removeAttributes"] 를 element["removeAttributes"] 와 configuration["attributes"]의 교집합으로 설정한다.

      3. 만약 configuration["removeAttributes"] 존재하면:

        1. element["removeAttributes"] 를 element["removeAttributes"] 와 configuration["removeAttributes"]의 차집합으로 설정한다.

    5. 만약 configuration["elements"] 가 포함하지 않는 경우 element을:

      1. 코멘트: 글로벌 허용 리스트에 아직 element가 없는 경우이다.

      2. elementconfiguration["elements"]에 추가한다.

      3. true를 반환한다.

    6. 코멘트: 글로벌 허용 리스트에 이미 element가 있는 경우이다.

    7. current elementconfiguration["elements"]의 itemitem[name]이 element[name]과 같고 item[namespace]가 element[namespace]와 같은 item으로 설정한다.

    8. 만약 elementcurrent element같다면 modified를 반환한다.

    9. elementconfiguration["elements"] 에서 제거한다.

    10. elementconfiguration["elements"] 에 추가한다.

    11. true를 반환한다.

  3. 그 밖의 경우:

    1. 만약 element["attributes"] 존재하거나 element["removeAttributes"] 기본값 « »이 빈 집합이 아니라면:

      1. 이 작업이 지원되지 않음을 콘솔에 경고로 보고할 수 있다.

      2. false를 반환한다.

    2. modifiedelementconfiguration["replaceWithChildrenElements"] 에서 제거한 결과로 설정한다.

    3. 만약 configuration["removeElements"] 가 포함하지 않는 경우 element을:

      1. 코멘트: 글로벌 제거 리스트에 element가 없는 경우이다.

      2. modified를 반환한다.

    4. 코멘트: 글로벌 제거 리스트에 element가 있는 경우이다.

    5. elementconfiguration["removeElements"] 에서 제거한다.

    6. true를 반환한다.

remove an element SanitizerElement elementSanitizerConfig configuration에서 제거하려면:
Note: 이 메서드는 4가지 경우를 구분해야 합니다:
  • 글로벌 허용 목록(allow-list) 또는 제거 목록(remove-list)이 있는지,

  • 그 목록에 이미 element가 포함되어 있는지 여부.

  1. elementsanitizer 요소 정규화의 결과로 설정한다.

  2. modifiedelementconfiguration["replaceWithChildrenElements"] 에서 제거한 결과로 설정한다.

  3. 만약 configuration["elements"]가 존재한다면:

    1. 만약 configuration["elements"]가 포함하는 경우 element을:

      1. 코멘트: 글로벌 허용 리스트에 element가 포함되어 있다.

      2. elementconfiguration["elements"]에서 제거한다.

      3. true를 반환한다.

    2. 코멘트: 글로벌 허용 리스트에 element가 포함되어 있지 않다.

    3. modified를 반환한다.

  4. 그 밖의 경우:

    1. 만약 configuration["removeElements"]가 포함하는 경우 element을:

      1. 코멘트: 글로벌 제거 리스트에 이미 element가 있다.

      2. modified를 반환한다.

    2. 코멘트: 글로벌 제거 리스트에 element가 없다.

    3. elementconfiguration["removeElements"]에 추가한다.

    4. true를 반환한다.

요소를 그 자식으로 대체하기 위해 SanitizerElement elementSanitizerConfig configuration에서:
  1. elementsanitizer 요소 정규화의 결과로 설정한다, 입력은 element.

  2. 만약 configuration["replaceWithChildrenElements"]가 포함한다면 element를:

    1. false를 반환한다.

  3. Remove element from configuration["removeElements"].

  4. Remove element from configuration["elements"] 목록.

  5. Add elementconfiguration["replaceWithChildrenElements"]에.

  6. true를 반환한다.

속성 허용을 위해 SanitizerAttribute attributeSanitizerConfig configuration에 대해:

참고: 이 메서드는 두 가지 경우를 구분한다. 즉, 글로벌 허용 리스트가 있는지 글로벌 제거 리스트가 있는지이다. 글로벌 허용 리스트에 attribute를 추가하면, 유효성 기준을 유지하기 위해 per-element 허용/제거 리스트를 보정하는 추가 작업이 필요할 수 있다.

  1. attributesanitizer 속성 정규화의 결과로 설정한다, 입력은 attribute.

  2. 만약 configuration["attributes"]가 존재한다면:

    1. 주석: 글로벌 허용 리스트가 있다면, attribute를 추가해야 한다.

    2. 만약 configuration["dataAttributes"]가 true이고 attribute커스텀 데이터 속성이라면, false를 반환한다.

    3. 만약 configuration["attributes"]가 포함한다면 attribute를 false를 반환한다.

    4. 주석: per-element 허용/제거 리스트를 보정한다.

    5. 만약 configuration["elements"]가 존재한다면:

      1. element에 대해 configuration["elements"] 안의:

        1. 만약 element["attributes"]가 기본값 « »로 포함한다면 attribute를:

          1. Remove attribute from element["attributes"].

        2. Assert: element["removeAttributes"]가 기본값 « »일 때 포함하지 않는다 attribute를.

    6. Append attribute to configuration["attributes"]

    7. true를 반환한다.

  3. 그렇지 않다면:

    1. 주석: 글로벌 제거 리스트가 있는 경우, attribute를 제거해야 한다.

    2. 만약 configuration["removeAttributes"]가 포함하지 않는다면 attribute를:

      1. false를 반환한다.

    3. Remove attribute from configuration["removeAttributes"].

    4. true를 반환한다.

속성 제거 SanitizerAttributeattribute 를 SanitizerConfig configuration에서:

참고: 이 메서드는 두 가지 경우를 구분한다. 즉, 글로벌 허용 리스트가 있는지 글로벌 제거 리스트가 있는지이다. 글로벌 제거 리스트에 attribute를 추가하면, 유효성 기준을 유지하기 위해 per-element 허용/제거 리스트를 보정하는 추가 작업이 필요할 수 있다. 글로벌 허용 리스트에서 attribute를 제거하는 경우, 로컬 제거 리스트에서도 이를 제거해야 할 수 있다.

  1. attributesanitizer 속성 정규화의 결과로 설정한다, 입력은 attribute.

  2. 만약 configuration["attributes"]가 존재한다면:

    1. 주석: 글로벌 허용 리스트가 있는 경우, attribute를 추가해야 한다.

    2. 만약 configuration["attributes"]가 포함하지 않는다면 attribute를:

      1. false를 반환한다.

    3. 주석: per-element 허용/제거 리스트를 보정한다.

    4. 만약 configuration["elements"]가 존재한다면:

      1. element에 대해 configuration["elements"] 안의:

        1. 만약 element["removeAttributes"]가 기본값 « »로 포함한다면 attribute를:

          1. Remove attribute from element["removeAttributes"].

    5. Remove attribute from configuration["attributes"].

    6. true를 반환한다.

  3. 그렇지 않다면:

    1. 주석: 글로벌 제거 리스트가 있는 경우, attribute를 추가해야 한다.

    2. 만약 configuration["removeAttributes"]가 포함한다면 attribute를 false를 반환한다.

    3. 주석: per-element 허용/제거 리스트를 보정한다.

    4. 만약 configuration["elements"]가 존재한다면:

      1. element에 대해 configuration["elements"] 안의:

        1. 만약 element["attributes"]가 기본값 « »로 포함한다면 attribute를:

          1. Remove attribute from element["attributes"].

        2. 만약 element["removeAttributes"]가 기본값 « »로 포함한다면 attribute를:

          1. Remove attribute from element["removeAttributes"].

    5. Append attribute to configuration["removeAttributes"]

    6. true를 반환한다.

주석 설정을 위해 boolean allowSanitizerConfig configuration에서:
  1. 만약 configuration["comments"]가 존재하고 configuration["comments"]가 allow와 같다면, false를 반환한다;

  2. configuration["comments"]를 allow로 설정한다.

  3. true를 반환한다.

data 속성 설정을 위해 boolean allowSanitizerConfig configuration에서:
  1. 만약 configuration["attributes"]가 존재하지 않으면, false를 반환한다.

  2. 만약 configuration["dataAttributes"]가 allow와 같다면, false를 반환한다.

  3. 만약 allow가 true라면:

    1. Remove 임의의 항목 attrconfiguration["attributes"]에서 제거하되, 그 attr커스텀 데이터 속성인 경우.

    2. 만약 configuration["elements"]가 존재한다면:

      1. element에 대해 configuration["elements"] 안의:

        1. 만약 element[attributes]가 존재한다면:

          1. Remove 임의의 항목 attrelement[attributes]에서 제거하되, 그 attr커스텀 데이터 속성인 경우.

  4. configuration["dataAttributes"]를 allow로 설정한다.

  5. true를 반환한다.

안전하지 않은 항목 제거SanitizerConfig configuration에서 수행하려면:

참고: 이 알고리즘의 이름은 remove unsafe이지만, 우리는 이 명세의 의미에서만 “unsafe”라는 용어를 사용하여, 문서에 삽입될 때 JavaScript가 실행되는 콘텐츠를 나타낸다. 즉, 이 메서드는 XSS의 기회를 제거한다.

  1. Assert: 키 집합내장 안전 기준 구성동치는 «[ "removeElements", "removeAttributes" ] »이다.

  2. result를 false로 둔다.

  3. element에 대해 내장 안전 기준 구성[removeElements]의:

    1. 요소 제거를 호출하여 elementconfiguration에서 제거한다.

    2. 호출이 true를 반환했다면, result를 true로 설정한다.

  4. attribute에 대해 내장 안전 기준 구성[removeAttributes]의:

    1. 속성 제거를 호출하여 attributeconfiguration에서 제거한다.

    2. 호출이 true를 반환했다면, result를 true로 설정한다.

  5. attribute에 대해, 이벤트 핸들러 콘텐츠 속성에 열거된:

    1. 속성 제거를 호출하여 attributeconfiguration에서 제거한다.

    2. 호출이 true를 반환했다면, result를 true로 설정한다.

  6. result를 반환한다.

3.3. 구성 설정

To set a configuration, given a dictionary configuration, a boolean allowCommentsAndDataAttributes, and a Sanitizer sanitizer:
  1. 구성 정규화(Canonicalize) configurationallowCommentsAndDataAttributes와 함께 수행합니다.

  2. If configuration is not valid, then return false.

  3. sanitizerconfigurationconfiguration으로 설정합니다.

  4. True를 반환합니다.

3.4. 구성 정규화

Sanitizer 는 여러 처리 단계를 더 쉽게 하기 위해 configuration을 정규화된 형태로 저장합니다.

elements 목록 {elements: ["div"]}{elements: [{name: "div", namespace: "http://www.w3.org/1999/xhtml"}]}로 저장됩니다).
구성을 정규화(canonicalize)하기 위해 SanitizerConfig configurationboolean allowCommentsAndDataAttributes를 사용합니다:

참고: configuration은 JavaScript 값을 SanitizerConfig로 변환한 [WebIDL]의 결과라고 가정합니다.

  1. configuration["elements"]와 configuration["removeElements"]가 모두 exist하지 않으면, set configuration["removeElements"]를 « »로 설정합니다.

  2. configuration["attributes"]와 configuration["removeAttributes"]가 모두 exist하지 않으면, set configuration["removeAttributes"]를 « »로 설정합니다.

  3. configuration["elements"]가 exists인 경우:

    1. elements를 « »로 둡니다.

    2. For each element of configuration["elements"] 에 대해 다음을 수행합니다:

      1. Append canonicalize a sanitizer element with attributes의 결과인 elementelements에 추가합니다.

    3. configuration["elements"]를 elements로 설정합니다.

  4. configuration["removeElements"]가 exists인 경우:

    1. elements를 « »로 둡니다.

    2. For each element of configuration["removeElements"] 에 대해 다음을 수행합니다:

      1. Append canonicalize a sanitizer element element의 결과를 elements에 추가합니다.

    3. configuration["removeElements"]를 elements로 설정합니다.

  5. configuration["replaceWithChildrenElements"]가 exists인 경우:

    1. elements를 « »로 둡니다.

    2. For each element of configuration["replaceWithChildrenElements"] 에 대해 다음을 수행합니다:

      1. Append canonicalize a sanitizer element element의 결과를 elements에 추가합니다.

    3. configuration["replaceWithChildrenElements"]를 elements로 설정합니다.

  6. configuration["attributes"]가 exists인 경우:

    1. attributes를 « »로 둡니다.

    2. For each attribute of configuration["attributes"] 에 대해 다음을 수행합니다:

      1. Append canonicalize a sanitizer attribute attribute의 결과를 attributes에 추가합니다.

    3. configuration["attributes"]를 attributes로 설정합니다.

  7. configuration["removeAttributes"]가 exists인 경우:

    1. attributes를 « »로 둡니다.

    2. For each attribute of configuration["removeAttributes"] 에 대해 다음을 수행합니다:

      1. Append canonicalize a sanitizer attribute attribute의 결과를 attributes에 추가합니다.

    3. configuration["removeAttributes"]를 attributes로 설정합니다.

  8. configuration["comments"]가 exist하지 않으면, set configuration["comments"]를 allowCommentsAndDataAttributes로 설정합니다.

  9. configuration["attributes"]가 exists이고 configuration["dataAttributes"]가 exist하지 않으면, set configuration["dataAttributes"]를 allowCommentsAndDataAttributes로 설정합니다.

속성이 있는 sanitizer 요소를 정규화하기 위해 SanitizerElementWithAttributes element에 대해 다음을 수행합니다:
  1. resultcanonicalize a sanitizer elementelement로 호출한 결과로 둡니다.

  2. elementdictionary인 경우:

    1. element["attributes"]가 exists이면:

      1. attributes를 « »로 둡니다.

      2. For each attribute of element["attributes"]:

        1. Append canonicalize a sanitizer attributeattribute로 호출한 결과를 attributes에 추가합니다.

      3. Set result["attributes"]를 attributes로 설정합니다.

    2. element["removeAttributes"]가 exists이면:

      1. attributes를 « »로 둡니다.

      2. For each attribute of element["removeAttributes"]:

        1. Append canonicalize a sanitizer attributeattribute로 호출한 결과를 attributes에 추가합니다.

      3. Set result["removeAttributes"]를 attributes로 설정합니다.

  3. result["attributes"]와 result["removeAttributes"]가 모두 exist하지 않으면:

    1. Set result["removeAttributes"]를 « »로 설정합니다.

  4. result를 반환합니다.

sanitizer 요소를 정규화하기 위해 SanitizerElement element에 대해, canonicalize a sanitizer nameelementHTML namespace를 기본 네임스페이스로 하여 호출한 결과를 반환합니다.
sanitizer 속성을 정규화하기 위해 SanitizerAttribute attribute에 대해, canonicalize a sanitizer nameattribute와 기본 네임스페이스로 null을 사용하여 호출한 결과를 반환합니다.
sanitizer 이름을 정규화하기 위해 name과 기본 네임스페이스 defaultNamespace로 다음 단계를 수행합니다:
  1. Assert: nameDOMString이거나 dictionary여야 합니다.

  2. nameDOMString이면, «[ "name" → name, "namespace" → defaultNamespace]»를 반환합니다.

  3. Assert: namedictionary이며 name["name"]과 name["namespace"]가 모두 exist합니다.

  4. name["namespace"]가 빈 문자열이면, 이를 null로 설정합니다.

  5. «[
    "name" → name["name"],
    "namespace" → name["namespace"]
    ]»를 반환합니다.

3.5. 보조 알고리즘

이 명세에서 사용하는 정규화된 요소속성 이름 목록에서, 목록 멤버십은 "name"과 "namespace" 두 항목의 매칭을 기반으로 합니다:

Sanitizer 이름 listcontains item을 포함하는 경우는, listordered mapentry가 존재하고, item["name"]이 equals entry["name"]이고, item["namespace"]가 equals entry["namespace"]일 때입니다.
remove를 수행하여 itemlist에서 제거할 때, listordered map인 경우, remove를 사용하여 item["name"]이 equals entry["name"]이고, item["namespace"]가 equals entry["namespace"]인 모든 entrylist에서 제거합니다.
addnamecanonicalized된 것이고, listordered map일 때, namelist에 추가하는 절차입니다:
  1. 만약 list이미 name를 포함하면 반환합니다.

  2. Appendnamelist에 추가합니다.

항목 itemA보다 작은 항목 itemB인 경우는 다음과 같다:
  1. itemA["namespace"]가 null인 경우:

    1. itemB["namespace"]가 null이 아니면, true를 반환한다.

  2. 그렇지 않으면:

    1. itemB["namespace"]가 null이면, false를 반환한다.

    2. itemA["namespace"]가 code unit less than itemB["namespace"]이면, true를 반환한다.

  3. itemA["name"]이 code unit less than itemB["name"]인지 반환한다.

순서 있는 집합(ordered sets)의 동등성은 멤버의 동등성만을 고려하며, 순서는 고려하지 않는다: 순서 있는 집합 AB동등하다고 한다. 이는 Asuperset이며 Bsuperset일 경우이다.
순서 있는 맵(ordered map)튜플 시퀀스이다. 순서 있는 맵의 동등성은 이 튜플 시퀀스를 순서 있는 집합으로 취급하여 동등성으로 판단한다. 순서 있는 맵 AB동등하다는 것은 A엔트리(entries)로 이루어진 순서 있는 집합과, B엔트리로 이루어진 순서 있는 집합동등할 때이다.
list list중복을 갖는다는 것은, 임의의 itemlist에 대해, list 안에 item["name"]이 entry["name"]이고 item["namespace"]가 entry["namespace"]인 entry가 둘 이상 존재하는 경우를 말한다.
중복을 제거하기 위해 list list에서,
  1. result를 « »로 둔다.

  2. entrylist에 대해, 추가를 사용해 entryresult에 추가한다.

  3. result를 반환한다.

교집합lists ABSanitizerElement 를 담고 있을 때, 집합의 교집합과 동일하되, 그 전에 집합의 항목들을 미리 정규화한 것으로 정의된다:
  1. set A를 « [] »로 둔다.

  2. set B를 « [] »로 둔다.

  3. entryA에 대해, append를 사용해 canonicalize a sanitizer name의 결과로 얻은 entryset A에 추가한다.

  4. entryB에 대해, append를 사용해 canonicalize a sanitizer name의 결과로 얻은 entryset B에 추가한다.

  5. set Aset B교집합을 반환한다.

not을 판단하기 위해 boolean bool에 대해, bool이 true이면 false를, 그렇지 않으면 true를 반환한다.
Comment는 알고리즘 안의 특정 지점에 적용되는 설명 텍스트를 포함한다.

3.6. 내장 항목

내장 항목은 네 가지가 있습니다:

내장 안전 기본 구성(built-in safe default configuration)는 다음과 같습니다:

{
  "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": "rel",
          "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는 DOM 기반 크로스 사이트 스크립팅(XSS)을 방지하기 위해 고안되었습니다. 제공받은 HTML 콘텐츠를 순회하며, 구성(configuration)에 따라 요소와 속성을 제거합니다. 명세된 API는 스크립트 실행이 가능한 마크업이 남아있는 Sanitizer 객체를 생성하는 것을 지원해서는 안 되며, 만약 그렇게 된다면 이는 위협 모델의 버그입니다.

그럼에도 불구하고 Sanitizer API의 올바른 사용으로도 보호할 수 없는 보안 이슈들이 있으며, 그 시나리오들은 아래의 섹션에서 설명됩니다.

4.1. 서버 측 반사 및 저장 XSS

이 섹션은 규범적이지 않습니다.

Sanitizer API는 DOM 내에서만 동작하며, 기존 DocumentFragment를 순회하고 필터링하는 기능을 제공합니다. Sanitizer는 서버 측 반사(reflected) 또는 저장(stored) XSS를 다루지 않습니다.

4.2. DOM 클로버링 공격

이 섹션은 규범적이지 않습니다.

DOM 클로버링(clobbering)은 악의적인 HTML이 id 또는 name 속성으로 요소의 이름을 지정하여, DOM 내 HTML 요소의 children과 같은 프로퍼티가 악성 콘텐츠로 인해 덮어써지는 공격입니다.

Sanitizer API는 기본 상태에서는 DOM 클로버링 공격을 방지하지 않지만, idname 속성을 제거하도록 구성할 수 있습니다.

4.3. 스크립트 가젯을 이용한 XSS

이 섹션은 규범적이지 않습니다.

스크립트 가젯(script gadgets)은 공격자가 인기 있는 JavaScript 라이브러리의 기존 애플리케이션 코드를 악용하여, 자신의 코드를 실행시키는 기술입니다. 이는 보기에는 무해한 코드 또는 동작하지 않을 것 같은 DOM 노드를 삽입하고, 프레임워크가 그 입력을 해석하여 JavaScript 실행을 유도하는 방식으로 이루어집니다.

Sanitizer API는 이러한 공격을 방지할 수 없지만, 페이지 작성자가 일반적으로 알려지지 않은 요소를 명시적으로 허용해야 하며, 또한 알려지지 않은 속성과 요소, 그리고 템플릿이나 프레임워크 전용 코드에 자주 사용되는 마크업(예: data-, slot 속성 및 <slot>, <template> 요소 등)을 명시적으로 구성해야 합니다. 이러한 제한이 충분하지 않다고 생각하며, 페이지 작성자는 해당 동작을 위해 사용하는 서드파티 라이브러리를 반드시 검토해야 합니다.

4.4. Mutated XSS

이 섹션은 규범적이지 않습니다.

Mutated XSS 또는 mXSS는 HTML 스니펫을 올바르지 않은 문맥(context)에서 파싱할 때 발생하는 파서 컨텍스트 불일치에 기반한 공격입니다. 특히 파싱된 HTML 프래그먼트를 문자열로 직렬화한 후, 그 문자열을 다른 부모 요소에 삽입하여 다시 파싱할 때 원래와 정확히 동일하게 해석된다는 보장이 없습니다. 예를 들어, 외래 콘텐츠(foreign content) 또는 잘못 중첩된 태그(mis-nested tags)에 대한 파싱 방식이 바뀌는 점을 악용할 수 있습니다.

Sanitizer API는 문자열을 노드 트리로 변환하는 기능만 제공합니다. 모든 sanitizer 함수는 암묵적으로 컨텍스트를 제공합니다: Element.setHTML()은 현재 요소를 사용하고, Document.parseHTML()은 새 문서를 만듭니다. 따라서 Sanitizer API는 mutated XSS에 직접적으로 영향을 받지 않습니다.

만약 개발자가 Sanitizer로 정화된 노드 트리를 .innerHTML 등으로 문자열로 얻은 후, 다시 파싱한다면 mutated XSS가 발생할 수 있습니다. 우리는 이러한 사용을 권장하지 않습니다. HTML을 문자열로 처리하거나 전달해야만 하는 경우, 해당 문자열은 신뢰할 수 없는 것으로 간주하고 DOM에 삽입할 때 반드시 다시 한 번 Sanitizer로 정화해야 합니다. 즉, Sanitizer로 정화한 후 직렬화된 HTML 트리는 더 이상 "정화된" 것으로 간주할 수 없습니다.

mXSS에 대한 보다 완전한 설명은 [MXSS]에서 확인할 수 있습니다.

5. 감사의 글

이 작업은 cure53의 [DOMPURIFY], Internet Explorer의 window.toStaticHTML() 그리고 Ben Bucksch의 오리지널 [HTMLSanitizer]에 영감을 받았습니다. Anne van Kesteren, Krzysztof Kotowicz, 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; et al. 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 공격: innerHTML 변형을 이용한 잘 보호된 웹 애플리케이션 공격. URL: https://cure53.de/fp170.pdf
[SafeMathML]
MathML 안전 목록. URL: https://w3c.github.io/mathml-docs/mathml-safe-list
[SVG11]
Erik Dahlström; et al. Scalable Vector Graphics (SVG) 1.1 (Second Edition). 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;
};