summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/modules/sanitizer_api/sanitizer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/modules/sanitizer_api/sanitizer.cc')
-rw-r--r--chromium/third_party/blink/renderer/modules/sanitizer_api/sanitizer.cc244
1 files changed, 175 insertions, 69 deletions
diff --git a/chromium/third_party/blink/renderer/modules/sanitizer_api/sanitizer.cc b/chromium/third_party/blink/renderer/modules/sanitizer_api/sanitizer.cc
index 320636766ff..adcc31578a2 100644
--- a/chromium/third_party/blink/renderer/modules/sanitizer_api/sanitizer.cc
+++ b/chromium/third_party/blink/renderer/modules/sanitizer_api/sanitizer.cc
@@ -4,61 +4,92 @@
#include "sanitizer.h"
+#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_node_filter.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_parse_from_string_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/string_or_document_fragment_or_document.h"
#include "third_party/blink/renderer/bindings/modules/v8/string_or_trusted_html_or_document_fragment_or_document.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_sanitizer_config.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/document_fragment.h"
+#include "third_party/blink/renderer/core/dom/document_init.h"
+#include "third_party/blink/renderer/core/dom/dom_implementation.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/dom/node_traversal.h"
+#include "third_party/blink/renderer/core/dom/range.h"
#include "third_party/blink/renderer/core/editing/serializers/serialization.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#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"
+#include "third_party/blink/renderer/core/xml/dom_parser.h"
#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
-Sanitizer* Sanitizer::Create(const SanitizerConfig* config,
+Sanitizer* Sanitizer::Create(ExecutionContext* execution_context,
+ const SanitizerConfig* config,
ExceptionState& exception_state) {
- return MakeGarbageCollected<Sanitizer>(config);
+ return MakeGarbageCollected<Sanitizer>(execution_context, config);
}
-Sanitizer::Sanitizer(const SanitizerConfig* config) {
+Sanitizer::Sanitizer(ExecutionContext* execution_context,
+ const SanitizerConfig* config)
+ : allow_custom_elements_(config->allowCustomElements()) {
+ bool use_default_config = true;
+ if (config->allowCustomElements()) {
+ use_default_config = false;
+ }
+
// Format dropElements to uppercase.
drop_elements_ = default_drop_elements_;
if (config->hasDropElements()) {
ElementFormatter(drop_elements_, config->dropElements());
+ use_default_config = false;
}
// Format blockElements to uppercase.
block_elements_ = default_block_elements_;
if (config->hasBlockElements()) {
ElementFormatter(block_elements_, config->blockElements());
+ use_default_config = false;
}
// Format allowElements to uppercase.
if (config->hasAllowElements()) {
has_allow_elements_ = true;
ElementFormatter(allow_elements_, config->allowElements());
+ use_default_config = false;
}
// Format dropAttributes to lowercase.
drop_attributes_ = default_drop_attributes_;
if (config->hasDropAttributes()) {
AttrFormatter(drop_attributes_, config->dropAttributes());
+ use_default_config = false;
}
// Format allowAttributes to lowercase.
if (config->hasAllowAttributes()) {
has_allow_attributes_ = true;
AttrFormatter(allow_attributes_, config->allowAttributes());
+ use_default_config = false;
+ }
+
+ if (use_default_config) {
+ // TODO(lyf): Add unit tests for counters.
+ UseCounter::Count(execution_context,
+ WebFeature::kSanitizerAPIDefaultConfiguration);
}
}
@@ -74,8 +105,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) {
@@ -129,18 +160,37 @@ DocumentFragment* Sanitizer::SanitizeImpl(
DocumentFragment* fragment = nullptr;
LocalDOMWindow* window = LocalDOMWindow::From(script_state);
+ if (!window) {
+ exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+ "Cannot find current DOM window.");
+ return nullptr;
+ }
if (input.IsDocumentFragment()) {
+ UseCounter::Count(window->GetExecutionContext(),
+ WebFeature::kSanitizerAPIFromFragment);
fragment = input.GetAsDocumentFragment();
- } else if (window) {
- Document* document = window->document();
- if (input.IsString() || input.IsNull()) {
- fragment = document->createDocumentFragment();
- DCHECK(document->QuerySelector("body"));
- fragment->ParseHTML(input.GetAsString(), document->QuerySelector("body"));
- } else {
- fragment = document->createDocumentFragment();
- fragment->appendChild(input.GetAsDocument()->documentElement());
- }
+ } else if (input.IsString() || input.IsNull()) {
+ UseCounter::Count(window->GetExecutionContext(),
+ WebFeature::kSanitizerAPIFromString);
+
+ Document* document =
+ window->document()
+ ? window->document()->implementation().createHTMLDocument()
+ : DOMParser::Create(script_state)
+ ->parseFromString("<!DOCTYPE html><html><body></body></html>",
+ "text/html",
+ ParseFromStringOptions::Create());
+ // TODO(https://crbug.com/1178774): Behavior difference need further
+ // investgate.
+ fragment = document->createRange()->createContextualFragment(
+ input.GetAsString(), exception_state);
+ } else if (input.IsDocument()) {
+ UseCounter::Count(window->GetExecutionContext(),
+ WebFeature::kSanitizerAPIFromDocument);
+
+ fragment = input.GetAsDocument()->createDocumentFragment();
+ fragment->CloneChildNodesFrom(*(input.GetAsDocument()->body()),
+ CloneChildrenFlag::kClone);
} else {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Cannot find current DOM window.");
@@ -157,67 +207,123 @@ 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)) {
- 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 (IsA<HTMLElement>(node) &&
+ 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);
+ UseCounter::Count(window->GetExecutionContext(),
+ WebFeature::kSanitizerAPIActionTaken);
+ } 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);
+ UseCounter::Count(window->GetExecutionContext(),
+ WebFeature::kSanitizerAPIActionTaken);
+ } else if (drop_elements_.Contains(name)) {
+ // 5. If |name| is in |config|'s [=element drop list=] then 'drop'.
+ node = DropElement(node, fragment);
+ UseCounter::Count(window->GetExecutionContext(),
+ WebFeature::kSanitizerAPIActionTaken);
+ } else if (block_elements_.Contains(name)) {
+ // 6. If |name| is in |config|'s [=element block list=] then 'block'.
+ node = BlockElement(node, fragment, exception_state);
+ UseCounter::Count(window->GetExecutionContext(),
+ WebFeature::kSanitizerAPIActionTaken);
+ } 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);
+ UseCounter::Count(window->GetExecutionContext(),
+ WebFeature::kSanitizerAPIActionTaken);
} 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, window);
}
}
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,
+ LocalDOMWindow* window) {
+ 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);
+ UseCounter::Count(window->GetExecutionContext(),
+ WebFeature::kSanitizerAPIActionTaken);
+ }
+ } 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);
+ UseCounter::Count(window->GetExecutionContext(),
+ WebFeature::kSanitizerAPIActionTaken);
+ }
+ }
+ }
+ node = NodeTraversal::Next(*node, fragment);
+ return node;
+}
+
void Sanitizer::Trace(Visitor* visitor) const {
ScriptWrappable::Trace(visitor);
}