Skip to content

Commit

Permalink
[Santiizer API] Change default list
Browse files Browse the repository at this point in the history
Change the default lists and sanitizing logics according to:
WICG/sanitizer-api#38 and
WICG/sanitizer-api#60

Bug: 1116418
Change-Id: Ie4866b09ec3cd94a6bb38075ba7068ddd2a448a6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2698917
Commit-Queue: Yifan Luo <[email protected]>
Reviewed-by: Daniel Vogelheim <[email protected]>
Cr-Commit-Position: refs/heads/master@{#856648}
  • Loading branch information
iVanlIsh authored and Chromium LUCI CQ committed Feb 23, 2021
1 parent e13fe01 commit 716ecae
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 73 deletions.
155 changes: 96 additions & 59 deletions third_party/blink/renderer/modules/sanitizer_api/sanitizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/html/custom/custom_element.h"
#include "third_party/blink/renderer/core/html/html_collection.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/html/html_head_element.h"
#include "third_party/blink/renderer/core/trustedtypes/trusted_html.h"
#include "third_party/blink/renderer/core/trustedtypes/trusted_types_util.h"
Expand Down Expand Up @@ -83,8 +84,8 @@ void Sanitizer::AttrFormatter(
const Vector<std::pair<String, Vector<String>>>& attrs) {
for (const std::pair<String, Vector<String>>& pair : attrs) {
const String& lower_attr = pair.first.LowerASCII();
if (pair.second.Contains("*")) {
attr_map.insert(lower_attr, Vector<String>({"*"}));
if (pair.second == kVectorStar || pair.second.Contains("*")) {
attr_map.insert(lower_attr, kVectorStar);
} else {
Vector<String> elements;
for (const String& s : pair.second) {
Expand Down Expand Up @@ -172,70 +173,106 @@ DocumentFragment* Sanitizer::SanitizeImpl(
}

// TODO(crbug.com/1126936): Review the sanitising algorithm for non-HTMLs.
String node_name = node->nodeName().UpperASCII();
// If the current element is dropped, remove current element entirely and
// proceed to its next sibling.
if (drop_elements_.Contains(node_name) ||
(!allow_custom_elements_ &&
CustomElement::IsValidName(AtomicString(node_name.LowerASCII()),
false))) {
Node* tmp = node;
node = NodeTraversal::NextSkippingChildren(*node, fragment);
tmp->remove();
} else if (block_elements_.Contains(node_name) ||
(has_allow_elements_ && !allow_elements_.Contains(node_name))) {
// If the current element is blocked, append its children after current
// node to parent node, remove current element and proceed to the next
// node.
Node* parent = node->parentNode();
Node* next_sibling = node->nextSibling();
while (node->hasChildren()) {
Node* n = node->firstChild();
if (next_sibling) {
parent->insertBefore(n, next_sibling, exception_state);
} else {
parent->appendChild(n, exception_state);
}
if (exception_state.HadException()) {
return nullptr;
}
}
Node* tmp = node;
node = NodeTraversal::Next(*node, fragment);
tmp->remove();
// 1. Let |name| be |element|'s tag name.
String name = node->nodeName().UpperASCII();
// 2. Classify elements into one of three kinds: kCustom, kUnknown, kRegular
ElementKind kind = ElementKind::kRegular;
if (CustomElement::IsValidName(AtomicString(name.LowerASCII()), false)) {
kind = ElementKind::kCustom;
} else if (To<HTMLElement>(node)->IsHTMLUnknownElement()) {
kind = ElementKind::kUnknown;
}

// 3. If |kind| is `regular` and if |name| is not contained in the
// default element allow list, then 'drop'
if (kind == ElementKind::kRegular &&
!default_allow_elements_.Contains(name)) {
node = DropElement(node, fragment);
} else if (kind == ElementKind::kCustom && !allow_custom_elements_) {
// 4. If |kind| is `custom` and if allow_custom_elements_ is unset or set
// to anything other than `true`, then 'drop'.
node = DropElement(node, fragment);
} else if (drop_elements_.Contains(name)) {
// 5. If |name| is in |config|'s [=element drop list=] then 'drop'.
node = DropElement(node, fragment);
} else if (block_elements_.Contains(name)) {
// 6. If |name| is in |config|'s [=element block list=] then 'block'.
node = BlockElement(node, fragment, exception_state);
} else if (has_allow_elements_ && !allow_elements_.Contains(name)) {
// 7. if |config| has a non-empty [=element allow list=] and |name| is
// not in |config|'s [=element allow list=] then 'block'.
node = BlockElement(node, fragment, exception_state);
} else {
// Otherwise, remove any attributes to be dropped from the current
// element, and proceed to the next node (preorder, depth-first
// traversal).
Element* element = To<Element>(node);
if (has_allow_attributes_ &&
allow_attributes_.at("*").Contains(node_name)) {
} else if (drop_attributes_.at("*").Contains(node_name)) {
for (const auto& name : element->getAttributeNames()) {
element->removeAttribute(name);
}
} else {
for (const auto& name : element->getAttributeNames()) {
// Attributes in drop list or not in allow list while allow list
// exists will be dropped.
bool drop = (drop_attributes_.Contains(name) &&
(drop_attributes_.at(name).Contains("*") ||
drop_attributes_.at(name).Contains(node_name))) ||
(has_allow_attributes_ &&
!(allow_attributes_.Contains(name) &&
(allow_attributes_.at(name).Contains("*") ||
allow_attributes_.at(name).Contains(node_name))));
if (drop)
element->removeAttribute(name);
}
}
node = NodeTraversal::Next(*node, fragment);
node = KeepElement(node, fragment, name);
}
}

return fragment;
}

// If the current element needs to be dropped, remove current element entirely
// and proceed to its next sibling.
Node* Sanitizer::DropElement(Node* node, DocumentFragment* fragment) {
Node* tmp = node;
node = NodeTraversal::NextSkippingChildren(*node, fragment);
tmp->remove();
return node;
}

// If the current element should be blocked, append its children after current
// node to parent node, remove current element and proceed to the next node.
Node* Sanitizer::BlockElement(Node* node,
DocumentFragment* fragment,
ExceptionState& exception_state) {
Node* parent = node->parentNode();
Node* next_sibling = node->nextSibling();
while (node->hasChildren()) {
Node* n = node->firstChild();
if (next_sibling) {
parent->insertBefore(n, next_sibling, exception_state);
} else {
parent->appendChild(n, exception_state);
}
if (exception_state.HadException()) {
return nullptr;
}
}
Node* tmp = node;
node = NodeTraversal::Next(*node, fragment);
tmp->remove();
return node;
}

// Remove any attributes to be dropped from the current element, and proceed to
// the next node (preorder, depth-first traversal).
Node* Sanitizer::KeepElement(Node* node,
DocumentFragment* fragment,
String& node_name) {
Element* element = To<Element>(node);
if (has_allow_attributes_ && allow_attributes_.at("*").Contains(node_name)) {
} else if (drop_attributes_.at("*").Contains(node_name)) {
for (const auto& name : element->getAttributeNames()) {
element->removeAttribute(name);
}
} else {
for (const auto& name : element->getAttributeNames()) {
// Attributes in drop list or not in allow list while allow list
// exists will be dropped.
bool drop = (drop_attributes_.Contains(name) &&
(drop_attributes_.at(name) == kVectorStar ||
drop_attributes_.at(name).Contains(node_name))) ||
(has_allow_attributes_ &&
!(allow_attributes_.Contains(name) &&
(allow_attributes_.at(name) == kVectorStar ||
allow_attributes_.at(name).Contains(node_name))));
if (drop)
element->removeAttribute(name);
}
}
node = NodeTraversal::Next(*node, fragment);
return node;
}

void Sanitizer::Trace(Visitor* visitor) const {
ScriptWrappable::Trace(visitor);
}
Expand Down
164 changes: 150 additions & 14 deletions third_party/blink/renderer/modules/sanitizer_api/sanitizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ class ScriptState;
class StringOrDocumentFragmentOrDocument;
class StringOrTrustedHTMLOrDocumentFragmentOrDocument;

enum ElementKind {
kCustom,
kUnknown,
kRegular,
};

class MODULES_EXPORT Sanitizer final : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();

Expand All @@ -37,6 +43,10 @@ class MODULES_EXPORT Sanitizer final : public ScriptWrappable {
void Trace(Visitor*) const override;

private:
Node* DropElement(Node*, DocumentFragment*);
Node* BlockElement(Node*, DocumentFragment*, ExceptionState&);
Node* KeepElement(Node*, DocumentFragment*, String&);

void ElementFormatter(HashSet<String>&, const Vector<String>&);
void AttrFormatter(HashMap<String, Vector<String>>&,
const Vector<std::pair<String, Vector<String>>>&);
Expand All @@ -56,22 +66,148 @@ class MODULES_EXPORT Sanitizer final : public ScriptWrappable {
bool has_allow_elements_ = false;
bool has_allow_attributes_ = false;

// TODO(lyf): make it all-ailpan.
const HashSet<String> default_block_elements_ = {};
const HashSet<String> default_drop_elements_ = {"SCRIPT", "ANNOTATION-XML",
"AUDIO", "COLGROUP",
"DESC", "FOREIGNOBJECT",
"HEAD", "IFRAME",
"MATH", "MI",
"MN", "MO",
"MS", "MTEXT",
"NOEMBED", "NOFRAMES",
"PLAINTEXT", "STYLE",
"SVG", "TEMPLATE",
"THEAD", "TITLE",
"VIDEO", "XMP"};
const HashSet<String> default_drop_elements_ = {};
const HashSet<String> default_allow_elements_ = {
"A", "ABBR", "ACRONYM", "ADDRESS", "AREA", "ARTICLE",
"ASIDE", "AUDIO", "B", "BDI", "BDO", "BIG",
"BLOCKQUOTE", "BODY", "BR", "BUTTON", "CANVAS", "CAPTION",
"CENTER", "CITE", "CODE", "COL", "COLGROUP", "DATALIST",
"DD", "DEL", "DETAILS", "DIALOG", "DFN", "DIR",
"DIV", "DL", "DT", "EM", "FIELDSET", "FIGCAPTION",
"FIGURE", "FONT", "FOOTER", "FORM", "H1", "H2",
"H3", "H4", "H5", "H6", "HEAD", "HEADER",
"HGROUP", "HR", "HTML", "I", "IMG", "INPUT",
"INS", "KBD", "KEYGEN", "LABEL", "LEGEND", "LI",
"LINK", "LISTING", "MAP", "MARK", "MENU", "META",
"METER", "NAV", "NOBR", "NOSCRIPT", "OL", "OPTGROUP",
"OPTION", "OUTPUT", "P", "PICTURE", "PRE", "PROGRESS",
"Q", "RB", "RP", "RT", "RTC", "RUBY",
"S", "SAMP", "SECTION", "SELECT", "SLOT", "SMALL",
"SOURCE", "SPAN", "STRIKE", "STRONG", "SUB", "SUMMARY",
"SUP", "STYLE", "TABLE", "TBODY", "TD", "TEXTAREA",
"TFOOT", "TH", "THEAD", "TIME", "TR", "TRACK",
"TT", "U", "UL", "VAR", "VIDEO", "WBR"};
const Vector<String> kVectorStar = Vector<String>({"*"});
const HashMap<String, Vector<String>> default_drop_attributes_ = {
{"onclick", Vector<String>({"*"})},
{"onsubmit", Vector<String>({"*"})}};
{"onabort", kVectorStar},
{"onafterprint", kVectorStar},
{"onanimationstart", kVectorStar},
{"onanimationiteration", kVectorStar},
{"onanimationend", kVectorStar},
{"onauxclick", kVectorStar},
{"onbeforecopy", kVectorStar},
{"onbeforecut", kVectorStar},
{"onbeforepaste", kVectorStar},
{"onbeforeprint", kVectorStar},
{"onbeforeunload", kVectorStar},
{"onblur", kVectorStar},
{"oncancel", kVectorStar},
{"oncanplay", kVectorStar},
{"oncanplaythrough", kVectorStar},
{"onchange", kVectorStar},
{"onclick", kVectorStar},
{"onclose", kVectorStar},
{"oncontextmenu", kVectorStar},
{"oncopy", kVectorStar},
{"oncuechange", kVectorStar},
{"oncut", kVectorStar},
{"ondblclick", kVectorStar},
{"ondrag", kVectorStar},
{"ondragend", kVectorStar},
{"ondragenter", kVectorStar},
{"ondragleave", kVectorStar},
{"ondragover", kVectorStar},
{"ondragstart", kVectorStar},
{"ondrop", kVectorStar},
{"ondurationchange", kVectorStar},
{"onemptied", kVectorStar},
{"onended", kVectorStar},
{"onerror", kVectorStar},
{"onfocus", kVectorStar},
{"onfocusin", kVectorStar},
{"onfocusout", kVectorStar},
{"onformdata", kVectorStar},
{"ongotpointercapture", kVectorStar},
{"onhashchange", kVectorStar},
{"oninput", kVectorStar},
{"oninvalid", kVectorStar},
{"onkeydown", kVectorStar},
{"onkeypress", kVectorStar},
{"onkeyup", kVectorStar},
{"onlanguagechange", kVectorStar},
{"onload", kVectorStar},
{"onloadeddata", kVectorStar},
{"onloadedmetadata", kVectorStar},
{"onloadstart", kVectorStar},
{"onlostpointercapture", kVectorStar},
{"onmessage", kVectorStar},
{"onmessageerror", kVectorStar},
{"onmousedown", kVectorStar},
{"onmouseenter", kVectorStar},
{"onmouseleave", kVectorStar},
{"onmousemove", kVectorStar},
{"onmouseout", kVectorStar},
{"onmouseover", kVectorStar},
{"onmouseup", kVectorStar},
{"onmousewheel", kVectorStar},
{"ononline", kVectorStar},
{"onoffline", kVectorStar},
{"onorientationchange", kVectorStar},
{"onoverscroll", kVectorStar},
{"onpagehide", kVectorStar},
{"onpageshow", kVectorStar},
{"onpaste", kVectorStar},
{"onpause", kVectorStar},
{"onplay", kVectorStar},
{"onplaying", kVectorStar},
{"onpointercancel", kVectorStar},
{"onpointerdown", kVectorStar},
{"onpointerenter", kVectorStar},
{"onpointerleave", kVectorStar},
{"onpointermove", kVectorStar},
{"onpointerout", kVectorStar},
{"onpointerover", kVectorStar},
{"onpointerrawupdate", kVectorStar},
{"onpointerup", kVectorStar},
{"onpopstate", kVectorStar},
{"onportalactivate", kVectorStar},
{"onprogress", kVectorStar},
{"onratechange", kVectorStar},
{"onreset", kVectorStar},
{"onresize", kVectorStar},
{"onscroll", kVectorStar},
{"onscrollend", kVectorStar},
{"onsearch", kVectorStar},
{"onseeked", kVectorStar},
{"onseeking", kVectorStar},
{"onselect", kVectorStar},
{"onselectstart", kVectorStar},
{"onselectionchange", kVectorStar},
{"onshow", kVectorStar},
{"onstalled", kVectorStar},
{"onstorage", kVectorStar},
{"onsuspend", kVectorStar},
{"onsubmit", kVectorStar},
{"ontimeupdate", kVectorStar},
{"ontimezonechange", kVectorStar},
{"ontoggle", kVectorStar},
{"ontouchstart", kVectorStar},
{"ontouchmove", kVectorStar},
{"ontouchend", kVectorStar},
{"ontouchcancel", kVectorStar},
{"ontransitionend", kVectorStar},
{"onunload", kVectorStar},
{"onvolumechange", kVectorStar},
{"onwaiting", kVectorStar},
{"onwebkitanimationstart", kVectorStar},
{"onwebkitanimationiteration", kVectorStar},
{"onwebkitanimationend", kVectorStar},
{"onwebkitfullscreenchange", kVectorStar},
{"onwebkitfullscreenerror", kVectorStar},
{"onwebkittransitionend", kVectorStar},
{"onwheel", kVectorStar}};
};

} // namespace blink
Expand Down

0 comments on commit 716ecae

Please sign in to comment.