HTML 정리자 API

커뮤니티 그룹 초안 보고서,

이 버전:
https://wicg.github.io/sanitizer-api/
이슈 추적:
GitHub
편집자:
Frederik Braun (Mozilla)
Mario Heiderich (Cure53)
Daniel Vogelheim (Google LLC)
Tom Schuster (Mozilla)

요약

이 문서는 개발자가 신뢰할 수 없는 HTML 입력을 받아 문서의 DOM에 안전하게 삽입할 수 있도록 정리(sanitize)할 수 있게 하는 일련의 API를 규정합니다.

이 문서의 상태

이 명세는 웹 플랫폼 인큐베이터 커뮤니티 그룹에서 발행했습니다. 이는 W3C 표준이 아니며 W3C 표준화 트랙에도 있지 않습니다. 다음에 유의하세요: W3C 커뮤니티 기여자 라이선스 계약(CLA)에 따라 제한적인 옵트아웃이 가능하며 기타 조건이 적용됩니다. 자세히 알아보기: W3C 커뮤니티 및 비즈니스 그룹.

1. 소개

이 절은 비규범적입니다.

웹 애플리케이션은 클라이언트 측에서 HTML 문자열을 다뤄야 하는 경우가 자주 있습니다. 클라이언트 측 템플릿 솔루션의 일부일 수도 있고, 사용자 생성 콘텐츠를 렌더링하는 과정의 일부일 수도 있습니다. 이를 안전한 방식으로 수행하는 것은 어렵습니다. 문자열을 단순히 이어 붙여 ElementinnerHTML 에 밀어 넣는 순진한 접근은 수많은 예상치 못한 방식으로 JavaScript 실행을 유발할 수 있어 매우 위험합니다.

[DOMPURIFY]와 같은 라이브러리는 삽입 전에 문자열을 신중히 파싱하고 정리(sanitize)하여 문제를 다루려 합니다. 즉, DOM을 구성한 다음 허용 목록을 통해 그 구성원을 필터링합니다. 하지만 이는 취약한 접근법으로 드러났습니다. 웹에 노출된 파싱 API는 실제로 문자열을 "실제" DOM으로 렌더링할 때의 브라우저 동작과 항상 합리적으로 매핑되지 않기 때문입니다. 게다가 라이브러리는 시간이 지나며 변화하는 브라우저 동작을 계속 따라가야 합니다. 한때 안전했던 것이 플랫폼 수준의 새로운 기능으로 인해 시한폭탄이 될 수 있습니다.

브라우저는 언제 코드를 실행할지에 대해 꽤 잘 알고 있습니다. 사용자 공간 라이브러리보다 더 나은 방법은, 브라우저가 임의 문자열로부터 HTML을 안전한 방식으로 렌더링하도록 가르치고, 브라우저 자체의 파서 구현이 변함에 따라 함께 유지·업데이트될 가능성이 훨씬 높은 방식으로 이를 수행하는 것입니다. 이 문서는 바로 그러한 목적을 달성하려는 API를 개략적으로 설명합니다.

1.1. 목표

1.2. API 요약

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

2. 프레임워크

2.1. 정리자 API

Element 인터페이스는 setHTML()setHTMLUnsafe() 두 가지 메서드를 정의합니다. 둘 다 HTML 마크업이 담긴 DOMString과 선택적 구성을 받습니다.

partial interface Element {
  [CEReactions] undefined setHTMLUnsafe((TrustedHTML or DOMString) html, optional SetHTMLUnsafeOptions options = {});
  [CEReactions] undefined setHTML(DOMString html, optional SetHTMLOptions options = {});
};
ElementsetHTMLUnsafe(html, options) 메서드 단계는 다음과 같습니다:
  1. compliantHTML신뢰된 타입 호환 문자열 얻기 알고리즘에 TrustedHTML, this관련 전역 객체, html, "Element setHTMLUnsafe", 그리고 "script"를 전달하여 호출한 결과로 한다.

  2. target을 다음으로 둡니다: thistemplate 요소인 경우 template contents, 그 외에는 this.

  3. Set and filter HTMLtarget, this, compliantHTML, options, false를 인수로 하여 수행합니다.

ElementsetHTML(html, options) 메서드 단계는 다음과 같습니다:
  1. target을 다음으로 둡니다: thistemplate인 경우 template contents, 그 외에는 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. compliantHTML신뢰된 타입 호환 문자열 가져오기 알고리즘에 TrustedHTML, this관련 전역 객체, html, "ShadowRoot setHTMLUnsafe", 그리고 "script"를 전달하여 호출한 결과로 한다.

  2. Set and filter HTMLthis, thisshadow host(컨텍스트 요소로 사용), compliantHTML, options, false를 인수로 하여 수행합니다.

ShadowRootsetHTML(html, options) 메서드 단계는 다음과 같습니다:
  1. Set and filter HTMLthis(대상)와 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, 현재 전역 객체, html, "Document parseHTMLUnsafe", 그리고 "script"를 전달하여 호출한 결과로 한다.

  2. document를 새 Document로 둡니다. 이때 content type은 "text/html"입니다.

    참고: document에는 탐색 컨텍스트가 없으므로 스크립팅이 비활성화됩니다.

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

  4. Parse HTML from a stringdocumentcompliantHTML을 인수로 하여 수행합니다.

  5. sanitizerget a sanitizer instance from optionsoptions와 false로 호출한 결과로 둡니다.

  6. sanitizedocument에 대해 sanitizer 및 false로 호출합니다.

  7. document를 반환합니다.

parseHTML(html, options) 메서드 단계는 다음과 같습니다:
  1. document를 새 Document로 둡니다. 이때 content type은 "text/html"입니다.

    참고: document에는 탐색 컨텍스트가 없으므로 스크립팅이 비활성화됩니다.

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

  3. Parse HTML from a stringdocumenthtml을 인수로 하여 수행합니다.

  4. sanitizerget a sanitizer instance from optionsoptions와 true로 호출한 결과로 둡니다.

  5. sanitizedocument에 대해 sanitizer 및 true로 호출합니다.

  6. document를 반환합니다.

2.2. SetHTML 옵션과 구성 객체.

setHTML() 계열의 메서드는 모두 옵션 딕셔너리를 받습니다. 현재는 이 딕셔너리의 구성원 한 개만 정의되어 있습니다:

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

Sanitizer 구성 객체는 필터 구성을 캡슐화합니다. 동일한 구성은 "안전" 또는 "비안전" 메서드 모두에서 사용할 수 있으며, "안전" 메서드는 전달된 구성에 대해 묵시적으로 removeUnsafe 연산을 수행하고, 구성이 전달되지 않으면 기본 구성을 가집니다. 기본값은 "안전"과 "비안전" 메서드 간에 다릅니다: "안전" 메서드는 기본적으로 안전하도록 목표하며 제한적인 기본값을 가지는 반면, "비안전" 메서드는 기본적으로 제한이 없습니다. 구성 사용 의도는 페이지 수명 초기에 하나(또는 몇 개)의 구성을 구축하여 필요할 때마다 사용하는 것입니다. 이렇게 하면 구현에서 구성을 사전 처리할 수 있습니다.

구성 객체는 구성 딕셔너리를 반환하도록 질의할 수 있으며, 직접 수정할 수도 있습니다.

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

  // Query configuration:
  SanitizerConfig get();

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

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

Sanitizer에는 연관된 SanitizerConfig 구성이 있습니다.

constructor(configuration) 메서드 단계는 다음과 같습니다:
  1. configurationSanitizerPresets 문자열인 경우:

    1. 단정: configuration다음과 같다: default.

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

  2. valid구성 설정configuration과 true로 this에 대해 호출한 반환값으로 둡니다.

  3. valid가 false이면 TypeError를 던집니다.

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

  2. 단언: config유효하다.

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

    1. 모든 config["elements"]의 element에 대하여:

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

        1. attrA항목보다 작음 attrB이 되도록, element["attributes"]를 오름차순으로 정렬한 결과로 element["attributes"]를 설정한다.

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

        1. attrA항목보다 작음 attrB이 되도록, element["removeAttributes"]를 오름차순으로 정렬한 결과로 element["removeAttributes"]를 설정한다.

    2. elementA항목보다 작음 elementB가 되도록, config["elements"]를 오름차순으로 정렬한 결과로 설정한다.

  4. 그렇지 않으면:

    1. elementA항목보다 작음 elementB가 되도록, config["removeElements"]를 오름차순으로 정렬한 결과로 설정한다.

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

    1. elementA항목보다 작음 elementB가 되도록, config["replaceWithChildrenElements"]를 오름차순으로 정렬한 결과로 설정한다.

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

    1. attrA항목보다 작음 attrB가 되도록, config["attributes"]를 오름차순으로 정렬한 결과로 설정한다.

  7. 그렇지 않으면:

    1. attrA항목보다 작음 attrB가 되도록, config["removeAttributes"]를 오름차순으로 정렬한 결과로 설정한다.

  8. config를 반환한다.

allowElement(element) 메서드 단계는 다음과 같다:
참고: 이 알고리즘은 비교적 복잡한데, 요소 허용 목록이 속성에 대해 요소별 허용 또는 제거 목록을 지정할 수 있기 때문이다. 이는 다음 네 가지 경우를 구분해야 함을 요구한다:
  • 전역 허용 목록인지 전역 제거 목록인지, 그리고

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

  1. configurationthisconfiguration으로 둔다.

  2. 단언: configuration유효하다.

  3. element속성이 있는 Sanitizer 요소를 정규화한 결과로 설정한다.

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

    1. modified제거의 결과로 설정하는데, configuration["replaceWithChildrenElements"]에서 element를 제거한 결과로 한다.

    2. 주석: 요소별 속성이 전역 속성과 겹치지 않도록 해야 한다.

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

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

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

        2. element["attributes"]를 차집합의 결과로 설정하는데, element["attributes"]에서 configuration["attributes"]를 뺀 값으로 한다.

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

          1. 모든 항목 itemelement["attributes"]에서 제거하되, item사용자 정의 데이터 속성인 경우에 한한다.

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

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

        2. element["removeAttributes"]를 교집합의 결과로 설정하는데, element["removeAttributes"]와 configuration["attributes"]의 교집합으로 한다.

    4. 그렇지 않으면:

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

        1. 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"]를 중복 제거한 결과로 설정한다.

        2. element["removeAttributes"]를 차집합의 결과로 설정하는데, element["removeAttributes"]에서 configuration["removeAttributes"]를 뺀 값으로 한다.

    5. 만약 configuration["elements"]가 포함하지 않는다면 element를:

      1. 주석: 전역 허용 목록에 element가 아직 없는 경우이다.

      2. 추가: configuration["elements"]에 element를 추가한다.

      3. true를 반환한다.

    6. 주석: 전역 허용 목록에 이미 element가 있는 경우이다.

    7. current elementconfiguration["elements"]의 item 가운데에서, item[name]이 같고 element[name]과, item[namespace]가 같은 element[namespace]인 항목으로 둔다.

    8. 만약 element동일하다면 current element와, modified를 반환한다.

    9. 제거: configuration["elements"]에서 element를 제거한다.

    10. 추가: configuration["elements"]에 element를 추가한다.

    11. true를 반환한다.

  5. 그렇지 않으면:

    1. 만약 element["attributes"]가 존재하거나 element["removeAttributes"]를 기본값과 함께 « »로 보았을 때 비어 있지 않다면:

      1. 사용자 에이전트는 이 동작이 지원되지 않음을 콘솔에 경고로 보고할 수 있다.

      2. false를 반환한다.

    2. modified제거의 결과로 설정하는데, configuration["replaceWithChildrenElements"]에서 element를 제거한 결과로 한다.

    3. 만약 configuration["removeElements"]가 포함하지 않는다면 element를:

      1. 주석: 전역 제거 목록에 element가 없는 경우이다.

      2. modified를 반환한다.

    4. 주석: 전역 제거 목록에 element가 있는 경우이다.

    5. 제거: configuration["removeElements"]에서 element를 제거한다.

    6. true를 반환한다.

removeElement(element) 메서드 단계는, 요소를 제거elementthisconfiguration으로 수행하는 것이다.
replaceElementWithChildren(element) 메서드 단계는 다음과 같다:
  1. configurationthisconfiguration으로 둔다.

  2. 단언: configuration유효하다.

  3. elementSanitizer 요소를 정규화한 결과로 설정한다.

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

    1. false를 반환한다.

  5. 제거: configuration["removeElements"]에서 element를 제거한다.

  6. 제거: configuration["elements"] 목록에서 element를 제거한다.

  7. 추가: configuration["replaceWithChildrenElements"]에 element를 추가한다.

  8. true를 반환한다.

allowAttribute(attribute) 메서드 단계는 다음과 같다:
참고: 이 메서드는 전역 허용 목록이 있는 경우와 전역 제거 목록이 있는 경우 두 가지를 구분한다. 전역 허용 목록에 attribute를 추가하는 경우, 유효성 기준을 유지하기 위해 요소별 허용/제거 목록을 정리하는 추가 작업이 필요할 수 있다.
  1. configurationthisconfiguration으로 둔다.

  2. 단언: configuration유효하다.

  3. attributeSanitizer 속성을 정규화한 결과로 설정한다.

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

    1. 주석: 전역 허용 목록이 있다면 attribute를 추가해야 한다.

    2. 만약 configuration["dataAttributes"]가 true이고, attribute사용자 정의 데이터 속성이라면, false를 반환한다.

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

    4. 주석: 요소별 허용 및 제거 목록을 정리한다.

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

      1. element에 대해, configuration["elements"]에 있는:

        1. 만약 element["attributes"]를 기본값과 함께 « »로 보았을 때 포함한다면 attribute를:

          1. 제거: element["attributes"]에서 attribute를 제거한다.

        2. 단언: element["removeAttributes"]를 기본값과 함께 « »로 보았을 때 포함하지 않아야 한다 attribute.

    6. 추가: configuration["attributes"]에 attribute를 추가한다.

    7. true를 반환한다.

  5. 그렇지 않으면:

    1. 주석: 전역 제거 목록이 있는 경우, attribute를 제거해야 한다.

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

      1. false를 반환한다.

    3. 제거: configuration["removeAttributes"]에서 attribute를 제거한다.

    4. true를 반환한다.

removeAttribute(attribute) 메서드 단계는 속성을 제거attributethisconfiguration으로 수행하는 것이다.
setComments(allow) 메서드 단계는 다음과 같다:
  1. configurationthisconfiguration으로 둔다.

  2. 단언: configuration유효하다.

  3. 만약 configuration["comments"]가 존재하고 configuration["comments"]가 allow와 같다면, false를 반환한다;

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

  5. true를 반환한다.

setDataAttributes(allow) 메서드 단계는 다음과 같다:
  1. configurationthisconfiguration으로 둔다.

  2. 단언: configuration유효하다.

  3. 만약 configuration["attributes"]가 존재하지 않는다면, false를 반환한다.

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

  5. 만약 allow가 true라면:

    1. 제거: 모든 항목 attrconfiguration["attributes"]에서 제거하되, attr사용자 정의 데이터 속성인 경우에 한한다.

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

      1. element에 대해, configuration["elements"]에 있는:

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

          1. 제거: 모든 항목 attrelement[attributes]에서 제거하되, attr사용자 정의 데이터 속성인 경우에 한한다.

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

  7. true를 반환한다.

removeUnsafe() 메서드 단계는 thisconfiguration안전하지 않은 것을 제거thisconfiguration에 대해 호출한 결과로 업데이트하는 것이다.

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 속성은 전역 허용 목록에 있고 로컬 제거 목록에는 없는 경우 허용된다. 로컬 제거 목록은 전역 허용 목록의 부분집합이어야 한다. 속성은 어느 목록에도 없을 때 허용된다. 전역 제거 목록과 로컬 제거 목록 사이에는 중복 항목이 허용되지 않는다.

전역 목록과 요소별 목록 사이에서는 대체로 중복이 허용되지 않지만, 전역 허용 목록과 요소별 제거 목록의 조합에서는 후자가 전자에 대한 부분집합이어야 한다는 비대칭성이 있음을 유의하라. 위 표에서 중복에만 초점을 맞춘 발췌는 다음과 같다:

전역 attributes 전역 removeAttributes
로컬 attributes 중복은 허용되지 않는다. 중복은 허용되지 않는다.
로컬 removeAttributes 로컬 제거 목록은 전역 허용 목록의 부분집합이어야 한다. 중복은 허용되지 않는다.

dataAttributes 설정은 사용자 정의 데이터 속성을 허용한다. 위 규칙은 dataAttributes 를 허용 목록으로 간주하면 사용자 정의 데이터 속성에도 쉽게 확장된다:

전역 attributesdataAttributes 가 설정됨
로컬 attributes 모든 사용자 정의 데이터 속성이 허용된다. 사용자 정의 데이터 속성은 중복 항목이 되므로 어떤 허용 목록에도 기재되어서는 안 된다.
로컬 removeAttributes 사용자 정의 데이터 속성은 로컬 제거 목록에 기재되지 않은 한 허용된다. 중복 항목이 되므로 전역 허용 목록에는 사용자 정의 데이터 속성을 기재해서는 안 된다.

이 규칙들을 문장으로 정리하면:

SanitizerConfig config가 다음 모든 조건을 만족하면 유효하다:
  1. config에는 elements 또는 removeElements 가 있어야 하며, 둘 다 동시에 있어서는 안 된다.

  2. config에는 attributes 또는 removeAttributes 가 있어야 하며, 둘 다 동시에 있어서는 안 된다.

  3. 단언: config 내의 모든 SanitizerElementNamespaceWithAttributes, SanitizerElementNamespace, 및 SanitizerAttributeNamespace 항목들은 정규형이어야 하며, 이는 상황에 맞게 Sanitizer 요소 정규화 또는 Sanitizer 속성 정규화를 거쳤음을 의미한다.

  4. config[elements], config[removeElements], config[replaceWithChildrenElements], config[attributes], 또는 config[removeAttributes] 가 존재한다면, 중복 항목이 없어야 한다.

  5. config[elements] 와 config[replaceWithChildrenElements] 가 모두 존재한다면, config[elements] 와 config[replaceWithChildrenElements] 의 교집합비어 있어야 한다.

  6. config[removeElements] 와 config[replaceWithChildrenElements] 가 모두 존재한다면, config[removeElements] 와 config[replaceWithChildrenElements] 의 교집합비어 있어야 한다.

  7. config[attributes] 가 존재한다면:

    1. config[elements] 가 존재한다면:

      1. config[elements]의 element에 대해:

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

        2. config[attributes] 와 element[attributes] 기본값 « » 을 사용한 교집합비어 있어야 한다.

        3. element[removeAttributes] 기본값 « » 은 config[attributes] 의 부분집합이어야 한다.

        4. dataAttributes존재하고 dataAttributes 가 true라면:

          1. element[attributes] 에는 사용자 정의 데이터 속성이 포함되지 않아야 한다.

    2. Assert: config[dataAttributes] 존재함.

    3. dataAttributes 가 true라면:

      1. config[attributes] 에는 사용자 정의 데이터 속성이 포함되지 않아야 한다.

  8. config[removeAttributes] 가 존재한다면:

    1. config[elements] 가 존재한다면, config[elements] 의 element에 대해:

      1. element[attributes] 와 element[removeAttributes] 가 동시에 존재해서는 안 된다.

      2. element[attributes] 와 element[removeAttributes] 는(존재한다면) 중복 항목이 없어야 한다.

      3. config[removeAttributes] 와 element[attributes] 기본값 « » 을 사용한 교집합비어 있어야 한다.

      4. config[removeAttributes] 와 element[removeAttributes] 기본값 « » 을 사용한 교집합비어 있어야 한다.

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

Note: 구성 설정dictionary에서 수행하면 약간의 정규화가 이루어진다. 특히 허용/제거 목록이 모두 없으면 이를 빈 제거 목록로 해석한다. 따라서 {} 자체는 유효한 구성은 아니지만, {removeElements:[],removeAttributes:[]}로 정규화되며 이는 유효하다. 이 정규화 단계는 누락된 dictionary가 빈 dictionary와 일관되도록(즉, setHTMLUnsafe(txt)setHTMLUnsafe(txt, {sanitizer: {}}) 와 일치하도록) 선택된 것이다.

3. 알고리즘

HTML 설정 및 필터링을(를) 수행하려면, Element 또는 DocumentFragment target, Element contextElement, 문자열 html, 딕셔너리 options, 불리언 safe가 주어진다:
  1. safe이고 contextElementlocal name이 "script"이며, contextElementnamespaceHTML 네임스페이스 또는 SVG 네임스페이스라면, 반환한다.

  2. sanitizer옵션에서 sanitizer 인스턴스 얻기의 결과로 optionssafe를 넘겨 호출한 값으로 둔다.

  3. newChildrenHTML 프래그먼트 파싱 알고리즘의 결과로, contextElement, html, true를 넘겨 호출한 값으로 둔다.

  4. fragmentDocumentFragment의 새 인스턴스로 두고, node documentcontextElementnode document로 한다.

  5. newChildren의 각 node에 대해, fragment에 node 추가를 수행한다.

  6. sanitizefragment, sanitizer, safe로 호출한다.

  7. Replace all을 사용해 target 내에 fragment로 교체한다.

dictionary options에서 boolean safe와 함께 get a sanitizer instance from options을 수행하려면:

참고: 이 알고리즘은 SetHTMLOptionsSetHTMLUnsafeOptions 모두에 적용된다. 기본값만 다르다.

  1. sanitizerSpec를 "default"로 둔다.

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

    1. sanitizerSpecoptions["sanitizer"]로 설정한다.

  3. 단언: sanitizerSpecSanitizer 인스턴스이거나, string으로서 SanitizerPresets 멤버이거나, dictionary여야 한다.

  4. 만약 sanitizerSpecstring이라면:

    1. 단언: sanitizerSpec다음과 같다: "default"

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

  5. 단언: sanitizerSpecSanitizer 인스턴스이거나, dictionary여야 한다.

  6. 만약 sanitizerSpecdictionary라면:

    1. sanitizer를 새로운 Sanitizer 인스턴스로 둔다.

    2. setConfigurationResultset a configurationsanitizer에서 sanitizerSpecnot safe와 함께 호출한 결과로 둔다.

    3. 만약 setConfigurationResult가 false라면, throw를 사용해 TypeError를 발생시킨다.

    4. sanitizerSpecsanitizer로 설정한다.

  7. 단언: sanitizerSpecSanitizer 인스턴스이다.

  8. sanitizerSpec를 반환한다.

3.1. 정리(Sanitize)

주요 sanitize 작업에서, ParentNode node, Sanitizer sanitizer, 그리고 boolean safe를 사용하여 다음 단계를 수행한다:
  1. configurationsanitizerconfiguration 값으로 둔다.

  2. 단언: configuration유효하다.

  3. 만약 safe가 true라면, configurationremove unsafeconfiguration에 대해 호출한 결과로 설정한다.

  4. sanitize corenode, configuration에 대해 호출하고, handleJavascriptNavigationUrlssafe로 설정한다.

sanitize core 작업은 ParentNode node, SanitizerConfig configuration, 그리고 boolean handleJavascriptNavigationUrls를 사용하여 node를 시작점으로 DOM 트리를 재귀적으로 순회한다. 다음 단계로 구성된다:
  1. child에 대하여, nodechildren 중에서:

    1. 단언: childimplements를 만족하는 Text, Comment, Element, 또는 DocumentType이다.

      참고: 현재 이 알고리즘은 HTML 파서의 출력에 대해서만 호출되며, 위 단언이 성립해야 한다. DocumentTypeparseHTMLparseHTMLUnsafe 에서만 나타나야 한다. 향후 이 알고리즘이 다른 문맥에서 사용된다면, 이 가정은 재검토가 필요하다.

    2. 만약 childimplements DocumentType라면, continue 한다.

    3. 만약 childimplements Text라면, continue 한다.

    4. 만약 childimplements Comment라면:

      1. 만약 configuration["comments"] 가 true가 아니면, removechild를 제거한다.

    5. 그 밖의 경우:

      1. elementNameSanitizerElementNamespace로 두되, childlocal namenamespace를 사용한다.

      2. 만약 configuration["replaceWithChildrenElements"] 가 존재하고, configuration["replaceWithChildrenElements"] 가 포함한다면 elementName을:

        1. sanitize corechild에 대해 configurationhandleJavascriptNavigationUrls와 함께 호출한다.

        2. replace all을 호출하여 childchildrenchild 내부에 대체한다.

        3. Continue.

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

        1. 만약 configuration["elements"] 가 포함하지 않는다면 elementName을:

          1. Removechild를 제거한다.

          2. Continue.

      4. 그렇지 않다면:

        1. 만약 configuration["removeElements"] 가 포함한다면 elementName을:

          1. Removechild를 제거한다.

          2. Continue.

      5. 만약 elementName같다면 «[ "name" → "template", "namespace" → HTML namespace ]», sanitize corechildtemplate contents에 대해 configurationhandleJavascriptNavigationUrls와 함께 호출한다.

      6. 만약 childshadow host라면, sanitize corechildshadow root에 대해 configurationhandleJavascriptNavigationUrls와 함께 호출한다.

      7. elementWithLocalAttributes를 « [] »로 둔다.

      8. 만약 configuration["elements"] 가 존재하고, configuration["elements"] 가 포함한다면 elementName을:

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

      9. attribute에 대해, childattribute list에서:

        1. attrNameSanitizerAttributeNamespace로 두되, attributelocal namenamespace를 사용한다.

        2. 만약 elementWithLocalAttributes["removeAttributes"] 가 with default « » 로 간주될 때 포함한다면 attrName을:

          1. Removeattribute를 제거한다.

        3. 그렇지 않고, configuration["attributes"] 가 존재한다면:

          1. 만약 configuration["attributes"] 가 포함하지 않는다면 attrName을, 그리고 elementWithLocalAttributes["attributes"] 가 with default « » 로 간주될 때에도 포함하지 않는다면 attrName을, 그리고 "data-"가 code unit prefix가 아니고 attributelocal name 에 대해, namespacenull이 아니거나 configuration["dataAttributes"] 가 true가 아니라면:

            1. Removeattribute를 제거한다.

        4. 그 밖의 경우:

          1. 만약 elementWithLocalAttributes["attributes"] 가 존재하고, elementWithLocalAttributes["attributes"] 가 포함하지 않는다면 attrName을:

            1. Removeattribute를 제거한다.

          2. 그렇지 않고, configuration["removeAttributes"] 가 포함한다면 attrName을:

            1. Removeattribute를 제거한다.

        5. 만약 handleJavascriptNavigationUrls라면:

          1. 만약 «[elementName, attrName]»가 내장 탐색 URL 속성 목록의 항목과 일치하고, attributejavascript: URL을 포함한다면, removeattribute를 제거한다.

          2. 만약 childnamespace같고 MathML Namespace이며, attrlocal name다음과 같고 "href"이며, attrnamespacenull이거나 XLink namespace이고, attrjavascript: URL을 포함한다면, removeattribute를 제거한다.

          3. 만약 내장 애니메이팅 URL 속성 목록포함하고 «[elementName, attrName]»을, 그리고 attrvalue다음과 같다면 "href" 또는 "xlink:href", removeattribute를 제거한다.

      10. sanitize corechild에 대해 configurationhandleJavascriptNavigationUrls와 함께 호출한다.

참고: 현재 브라우저는 javascript: URL을 navigation 시에만 지원한다. navigation 자체는 XSS 위협이 아니므로, navigation에서 javascript: URL만 막고 일반 navigation은 막지 않는다.

선언적 navigation은 다음과 같은 몇 가지 범주로 나뉜다:

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

  2. form action에 따라 네비게이션을 트리거하는 폼 요소

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

  4. [SVG11] 애니메이션.

첫 두 가지는 내장 navigating URL 속성 목록에서 다룬다.

MathML의 경우, 이 명세에 "네임스페이스 전역" 규칙이 없으므로 별도의 규칙으로 다룬다.

SVG 애니메이션의 경우는 내장 animating 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>`는 제거됨.
요소를 제거하기 위해 SanitizerElement elementSanitizerConfig configuration에서 다음과 같이 처리한다:
참고: 이 메서드는 4가지 경우를 구분해야 한다:
  • 전역 허용 목록인지 전역 제거 목록인지,

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

  1. 단언: configuration유효하다.

  2. elementsanitizer 요소를 정규화element로 수행한 결과로 설정한다.

  3. modifiedremove를 호출해 configuration["replaceWithChildrenElements"]에서 element를 제거한 결과로 설정한다.

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

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

      1. 주석: 전역 허용 목록이 있으며 그 목록에 element가 포함되어 있다.

      2. Remove를 호출해 configuration["elements"]에서 element를 제거한다.

      3. true를 반환한다.

    2. 주석: 전역 허용 목록이 있지만 element는 포함되어 있지 않다.

    3. modified를 반환한다.

  5. 그렇지 않다면:

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

      1. 주석: 전역 제거 목록이 있으며 이미 element가 포함되어 있다.

      2. modified를 반환한다.

    2. 주석: 전역 제거 목록이 있으나 element는 포함되어 있지 않다.

    3. Add를 호출해 elementconfiguration["removeElements"]에 추가한다.

    4. true를 반환한다.

속성을 제거하기 위해 SanitizerAttributeattributeSanitizerConfig configuration에서 다음과 같이 처리한다:

참고: 이 메서드는 두 가지 경우를 구분한다. 전역 허용 목록이 있는지, 전역 제거 목록이 있는지이다. 전역 제거 목록에 attribute를 추가하는 경우, 유효성 기준을 유지하기 위해 요소별 허용/제거 목록을 정리하는 추가 작업이 필요할 수 있다. 전역 허용 목록에서 attribute를 제거하는 경우, 로컬 제거 목록에서도 이를 제거해야 할 수 있다.

  1. 단언: configuration유효하다.

  2. attributesanitizer 속성을 정규화attribute로 수행한 결과로 설정한다.

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

    1. 주석: 전역 허용 목록이 있는 경우, attribute를 추가해야 한다.

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

      1. false를 반환한다.

    3. 주석: 요소별 허용 및 제거 목록을 정리한다.

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

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

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

          1. Remove를 호출해 element["removeAttributes"]에서 attribute를 제거한다.

    5. Remove를 호출해 configuration["attributes"]에서 attribute를 제거한다.

    6. true를 반환한다.

  4. 그렇지 않다면:

    1. 주석: 전역 제거 목록이 있는 경우, attribute를 추가해야 한다.

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

    3. 주석: 요소별 허용 및 제거 목록을 정리한다.

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

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

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

          1. Remove를 호출해 element["attributes"]에서 attribute를 제거한다.

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

          1. Remove를 호출해 element["removeAttributes"]에서 attribute를 제거한다.

    5. Append를 호출해 attributeconfiguration["removeAttributes"]에 추가한다.

    6. true를 반환한다.

안전하지 않은 항목 제거SanitizerConfig configuration에서 수행하려면, 다음을 실행한다:

참고: 이 알고리즘의 이름은 remove unsafe이지만, 우리는 이 명세의 의미에서 엄격하게 "unsafe"라는 용어를 사용하여, 문서에 삽입될 때 JavaScript가 실행될 수 있는 콘텐츠를 지칭한다. 다시 말해, 이 메서드는 XSS의 기회를 제거한다.

  1. 단언: 키 집합내장 안전 기준 구성동일함: «[ "removeElements", "removeAttributes" ] ».

  2. 단언: configuration유효하다.

  3. result를 false로 둔다.

  4. element에 대하여, 내장 안전 기준 구성[removeElements]의 항목을 순회한다:

    1. 요소를 제거를 호출해 configuration에서 element를 제거한다.

    2. 호출 결과가 true이면, result를 true로 설정한다.

  5. attribute에 대하여, 내장 안전 기준 구성[removeAttributes]의 항목을 순회한다:

    1. 속성을 제거를 호출해 configuration에서 attribute를 제거한다.

    2. 호출 결과가 true이면, result를 true로 설정한다.

  6. attribute에 대하여, 이벤트 핸들러 콘텐츠 속성에 나열된 항목을 순회한다:

    1. 속성을 제거를 호출해 configuration에서 attribute를 제거한다.

    2. 호출 결과가 true이면, result를 true로 설정한다.

  7. result를 반환한다.

3.3. 구성 설정

구성 설정을 하려면 딕셔너리 configuration, 불리언 allowCommentsAndDataAttributes, Sanitizer sanitizer가 주어진다:
  1. 구성 정규화 configurationallowCommentsAndDataAttributes를 사용해 수행.

  2. configuration유효하지 않으면 false 반환.

  3. sanitizerconfigurationconfiguration으로 설정.

  4. true 반환.

3.4. 구성 정규화

Sanitizerconfiguration을 정규화된 형태로 저장하며, 이로 인해 여러 처리 단계가 더 쉬워집니다.

elements 목록 {elements: ["div"]}는 내부적으로 {elements: [{name: "div", namespace: "http://www.w3.org/1999/xhtml"}]로 저장됩니다.
SanitizerConfig configuration 을(를) boolean allowCommentsAndDataAttributes와 함께 구성을 정규화(canonicalize)하려면:

참고: configuration이 JavaScript 값을 SanitizerConfig(으)로 변환한 [WebIDL]의 결과라고 가정한다.

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

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

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

    1. elements를 « »로 둔다.

    2. configuration["elements"]의 element에 대하여 다음을 수행한다:

      1. 속성이 있는 Sanitizer 요소 정규화의 결과 elementAppendelements에 추가한다.

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

  4. configuration["removeElements"]가 존재한다면:

    1. elements를 « »로 둔다.

    2. configuration["removeElements"]의 element에 대하여 다음을 수행한다:

      1. Sanitizer 요소 정규화의 결과 elementAppendelements에 추가한다.

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

  5. configuration["replaceWithChildrenElements"]가 존재한다면:

    1. elements를 « »로 둔다.

    2. configuration["replaceWithChildrenElements"]의 element에 대하여 다음을 수행한다:

      1. Sanitizer 요소 정규화의 결과 elementAppendelements에 추가한다.

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

  6. configuration["attributes"]가 존재한다면:

    1. attributes를 « »로 둔다.

    2. configuration["attributes"]의 attribute에 대하여 다음을 수행한다:

      1. Sanitizer 속성 정규화의 결과 attributeAppendattributes에 추가한다.

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

  7. configuration["removeAttributes"]가 존재한다면:

    1. attributes를 « »로 둔다.

    2. configuration["removeAttributes"]의 attribute에 대하여 다음을 수행한다:

      1. Sanitizer 속성 정규화의 결과 attributeAppendattributes에 추가한다.

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

  8. configuration["comments"]가 존재하지 않으면, set으로 configuration["comments"]를 allowCommentsAndDataAttributes로 설정한다.

  9. configuration["attributes"]가 존재하고, configuration["dataAttributes"]가 존재하지 않으면, set으로 configuration["dataAttributes"]를 allowCommentsAndDataAttributes로 설정한다.

SanitizerElementWithAttributes element에 대해 속성이 있는 Sanitizer 요소를 정규화하려면:
  1. resultSanitizer 요소 정규화element에 대해 수행한 결과로 둔다.

  2. elementdictionary라면:

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

      1. attributes를 « »로 둔다.

      2. element["attributes"]의 attribute에 대하여:

        1. Sanitizer 속성 정규화attribute에 대해 수행한 결과를 Appendattributes에 추가한다.

      3. Set으로 result["attributes"]를 attributes로 설정한다.

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

      1. attributes를 « »로 둔다.

      2. element["removeAttributes"]의 attribute에 대하여:

        1. Sanitizer 속성 정규화attribute에 대해 수행한 결과를 Appendattributes에 추가한다.

      3. Set으로 result["removeAttributes"]를 attributes로 설정한다.

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

    1. Set으로 result["removeAttributes"]를 « »로 설정한다.

  4. result를 반환한다.

SanitizerElement element에 대해 Sanitizer 요소를 정규화하려면, element와 기본 네임스페이스로 HTML namespace를 사용하여 Sanitizer 이름 정규화의 결과를 반환한다.
SanitizerAttribute attribute에 대해 Sanitizer 속성을 정규화하려면, attribute와 기본 네임스페이스로 null을 사용하여 Sanitizer 이름 정규화의 결과를 반환한다.
기본 네임스페이스 defaultNamespace와 함께 name에 대해 Sanitizer 이름을 정규화하려면, 다음 단계를 수행한다:
  1. 단언: nameDOMString 이거나 dictionary여야 한다.

  2. 만약 nameDOMString이라면, «[ "name" → name, "namespace" → defaultNamespace]»를 반환한다.

  3. 단언: namedictionary이며 name["name"]과 name["namespace"]가 모두 존재한다.

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

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

3.5. 지원 알고리즘

이 명세에서 사용하는 정규화된 elementattribute name 목록의 경우, 목록 멤버십은 "name"과 "namespace" 항목이 모두 일치하는지에 기반한다:

Sanitizer name listcontains item을 포함하는 경우는, listentry 중에 ordered map이고, item["name"]이 entry["name"]과 같다 그리고 item["namespace"]가 entry["namespace"]와 같다entry가 존재하는 경우이다.
remove를 사용해 itemlist list에서 제거하려면:
  1. removed를 false로 둔다.

  2. list의 각 entry에 대해:

    1. item["name"]이 entry["name"]와 같고 item["namespace"]가 entry["namespace"]와 같다면:

      1. entrylist에서 제거한다.

      2. removed를 true로 설정한다.

  3. removed를 반환한다.

addnamelist에 추가한다. 여기서 name정규화된 값이고 listordered map이다:
  1. listname을 이미 포함하면 반환한다.

  2. appendnamelist에 추가한다.

아이템 itemAless than item 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["namespace"]가 같지 않다면 itemB["namespace"], false를 반환한다.

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

ordered set의 동등성은 멤버의 동등성이며, 순서는 고려하지 않는다: Ordered set ABequal이며, AB의 superset이고 BA의 superset인 경우이다.
ordered mapkeyvalue 튜플의 시퀀스이다. ordered map의 동등성은 이 튜플 시퀀스를 ordered set으로 보았을 때의 동등성이다. Ordered map ABequal이며, A엔트리로 이루어진 ordered setB엔트리로 이루어진 ordered setequal일 때 동등하다고 본다.
list listhas duplicates가 있다는 것은, list의 어떤 item에 대해서, list 내에서 item["name"]이 entry["name"]이고 item["namespace"]가 entry["namespace"]인 entry가 2개 이상 존재하는 경우이다.
remove duplicateslist list에서 중복을 제거한다.
  1. result를 « »로 둔다.

  2. list의 각 entry에 대해, add entryresult에 추가한다.

  3. result를 반환한다.

list ABintersection (교집합)은 SanitizerElement를 담고, set intersection과 동일하되, set의 엔트리는 미리 정규화되어야 한다:
  1. set A를 « [] »로 둔다.

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

  3. A의 각 entry에 대해, append정규화 entryset A에 추가한다.

  4. B의 각 entry에 대해, append정규화 entryset B에 추가한다.

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

boolean boolnot을 구하려면, bool이 true이면 false를, 그렇지 않으면 true를 반환한다.
Comment는 알고리즘 내 특정 지점에 대한 설명 텍스트를 담는다.

3.6. 내장값

내장값(builtin)은 네 가지가 있다:

내장 안전 기본 구성은 다음과 같다:

{
  "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
        }
      ]
    }
  ],
  "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"
  ]
}
"javascript:" 탐색이 "unsafe"로 간주되는 내장 navigating URL 속성 목록은 다음과 같다:

«[
[ { "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 네임스페이스 } ],

내장 animating URL 속성 목록[SVG11]에서 선언적으로 네비게이션 요소를 "javascript:" URL로 변형하는 데 사용될 수 있으며, 다음과 같다:

«[
[ { "name" → "animate", "namespace" → SVG 네임스페이스 }, { "name" → "attributeName", "namespace" → null] } ],
[ { "name" → "animateMotion", "namespace" → SVG 네임스페이스 }, { "name" → "attributeName", "namespace" → null } ],
[ { "name" → "animateTransform", "namespace" → SVG 네임스페이스 }, { "name" → "attributeName", "namespace" → null } ],
[ { "name" → "set", "namespace" → SVG 네임스페이스 }, { "name" → "attributeName", "namespace" → null } ],

4. 보안 고려사항

Sanitizer API는 주어진 HTML 콘텐츠를 순회하며 구성에 따라 요소와 속성을 제거함으로써 DOM 기반 크로스 사이트 스크립팅(XSS)을 방지하기 위한 것입니다. 이 명세의 API는 스크립트 실행이 가능한 마크업이 남게 되는 Sanitizer 객체의 생성을 지원해서는 안 되며, 그렇게 된다면 이는 위협 모델 상의 버그입니다.

그렇다 하더라도, Sanitizer API의 올바른 사용만으로는 방지할 수 없는 보안 이슈들이 있으며, 이러한 시나리오들이 다음 절에서 설명됩니다.

4.1. 서버 사이드 반사 및 저장 XSS

이 절은 규범(normative)이 아닙니다.

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

4.2. DOM 클로버링

이 절은 규범(normative)이 아닙니다.

DOM 클로버링은 악의적인 HTML이 idname 속성을 통해 요소 이름을 지정함으로써, HTML 요소의 children과 같은 프로퍼티가 악성 콘텐츠에 의해 가려지는 공격을 의미합니다.

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

4.3. 스크립트 gadget을 이용한 XSS

이 절은 규범(normative)이 아닙니다.

스크립트 gadget은 공격자가 인기있는 JavaScript 라이브러리의 기존 애플리케이션 코드를 활용하여 자신의 코드를 실행하게 만드는 기법입니다. 종종 무해해 보이는 코드나 inert DOM 노드를 삽입해 프레임워크가 이를 파싱 및 해석하여 JavaScript 실행에 이용하게 하는 방식입니다.

Sanitizer API만으로는 이러한 공격을 막을 수 없으며, 페이지 저자는 일반적으로 알 수 없는 요소를 명시적으로 허용해야 하고, data-slot 속성, <slot>, <template>과 같은 템플릿/프레임워크 전용 마크업에 대해서도 명시적으로 허용/제거 구성을 해야 합니다. 이러한 제한은 완전하지 않으므로, 페이지 저자에게는 사용하는 서드파티 라이브러리의 동작을 면밀히 검토할 것을 권장합니다.

4.4. Mutated XSS

이 절은 규범(normative)이 아닙니다.

Mutated XSS(mXSS)는 HTML 조각을 올바른 컨텍스트 없이 파싱할 때 발생하는 파서 컨텍스트 불일치에 기반한 공격입니다. 특히, 파싱된 HTML 프래그먼트를 문자열로 직렬화한 뒤, 다른 부모 요소에 삽입하여 재파싱하면 파싱 및 해석 결과가 동일하다고 보장할 수 없습니다. 예를 들어 foreign content나 잘못 중첩된 태그의 파싱 동작 변화에 의존하여 공격이 이루어질 수 있습니다.

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

개발자가 정리된(sanitized) 노드 트리를 .innerHTML 등으로 문자열로 변환한 뒤, 이를 다시 파싱한다면 mutated XSS가 발생할 수 있습니다. 이러한 방식은 권장하지 않습니다. HTML을 어쩔 수 없이 문자열로 처리/전달해야 한다면, 해당 문자열은 신뢰할 수 없는 것으로 간주하고 DOM에 삽입할 때 반드시 다시 한 번 정리해야 합니다. 즉, 정리되고 직렬화된 HTML 트리는 더 이상 정리된 것으로 간주할 수 없습니다.

mXSS에 대해 보다 자세한 내용은 [MXSS]를 참고하십시오.

5. 감사의 글

이 작업은 cure53의 [DOMPURIFY], Internet Explorer의 window.toStaticHTML() 그리고 Ben Bucksch의 [HTMLSanitizer]에서 영감을 얻었습니다. Anne van Kesteren, Krzysztof Kotowicz, Andrew C. H. Mc Millan, Tom Schuster, Luke Warlow, Guillaume Weghsteen, 그리고 Mike West가 소중한 피드백을 주셨습니다.

색인

이 명세서가 정의하는 용어

참조로 정의된 용어

참고 문헌

규범적 참고 문헌

[DOM]
Anne van Kesteren. DOM 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. 7 March 2023. 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; et al. Scalable Vector Graphics (SVG) 1.1 (Second Edition). 16 August 2011. 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;
};