Commit 6b381082 authored by Yifan Luo's avatar Yifan Luo Committed by Commit Bot

[Sanitizer API] Add Document and DocumentFragment as inputs on all methods

Add Document and DocumentFragment as inputs on both sanitize and
sanitizeToString methods.

Bug: 1116418
Change-Id: I06cd10dde9a5ca637140f7c4a71a302faa77abc2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2487085Reviewed-by: default avatarDaniel Vogelheim <vogelheim@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Commit-Queue: Yifan Luo <lyf@chromium.org>
Cr-Commit-Position: refs/heads/master@{#822147}
parent 28032a02
...@@ -97,6 +97,8 @@ bindings_modules_generated_union_type_files = [ ...@@ -97,6 +97,8 @@ bindings_modules_generated_union_type_files = [
"$bindings_modules_v8_output_dir/string_or_array_buffer_or_array_buffer_view_or_ndef_message_init.h", "$bindings_modules_v8_output_dir/string_or_array_buffer_or_array_buffer_view_or_ndef_message_init.h",
"$bindings_modules_v8_output_dir/string_or_canvas_gradient_or_canvas_pattern.cc", "$bindings_modules_v8_output_dir/string_or_canvas_gradient_or_canvas_pattern.cc",
"$bindings_modules_v8_output_dir/string_or_canvas_gradient_or_canvas_pattern.h", "$bindings_modules_v8_output_dir/string_or_canvas_gradient_or_canvas_pattern.h",
"$bindings_modules_v8_output_dir/string_or_document_fragment_or_document.cc",
"$bindings_modules_v8_output_dir/string_or_document_fragment_or_document.h",
"$bindings_modules_v8_output_dir/string_or_string_sequence_or_constrain_dom_string_parameters.cc", "$bindings_modules_v8_output_dir/string_or_string_sequence_or_constrain_dom_string_parameters.cc",
"$bindings_modules_v8_output_dir/string_or_string_sequence_or_constrain_dom_string_parameters.h", "$bindings_modules_v8_output_dir/string_or_string_sequence_or_constrain_dom_string_parameters.h",
"$bindings_modules_v8_output_dir/string_or_unsigned_long.cc", "$bindings_modules_v8_output_dir/string_or_unsigned_long.cc",
......
...@@ -5,7 +5,9 @@ ...@@ -5,7 +5,9 @@
#include "sanitizer.h" #include "sanitizer.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_node_filter.h" #include "third_party/blink/renderer/bindings/core/v8/v8_node_filter.h"
#include "third_party/blink/renderer/bindings/modules/v8/string_or_document_fragment_or_document.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_sanitizer_config.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_fragment.h"
#include "third_party/blink/renderer/core/dom/element.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.h"
...@@ -84,25 +86,35 @@ void Sanitizer::AttrFormatter( ...@@ -84,25 +86,35 @@ void Sanitizer::AttrFormatter(
Sanitizer::~Sanitizer() = default; Sanitizer::~Sanitizer() = default;
String Sanitizer::sanitizeToString(ScriptState* script_state, String Sanitizer::sanitizeToString(ScriptState* script_state,
const String& input, StringOrDocumentFragmentOrDocument& input,
ExceptionState& exception_state) { ExceptionState& exception_state) {
return CreateMarkup(sanitize(script_state, input, exception_state), return CreateMarkup(sanitize(script_state, input, exception_state),
kChildrenOnly); kChildrenOnly);
} }
DocumentFragment* Sanitizer::sanitize(ScriptState* script_state, DocumentFragment* Sanitizer::sanitize(ScriptState* script_state,
const String& input, StringOrDocumentFragmentOrDocument& input,
ExceptionState& exception_state) { ExceptionState& exception_state) {
DocumentFragment* fragment = nullptr;
LocalDOMWindow* window = LocalDOMWindow::From(script_state); LocalDOMWindow* window = LocalDOMWindow::From(script_state);
if (!window) { if (input.IsDocumentFragment()) {
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 {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Cannot find current DOM window."); "Cannot find current DOM window.");
return nullptr; return nullptr;
} }
Document* document = window->document();
DocumentFragment* fragment = document->createDocumentFragment();
DCHECK(document->QuerySelector("body"));
fragment->ParseHTML(input, document->QuerySelector("body"));
Node* node = fragment->firstChild(); Node* node = fragment->firstChild();
...@@ -155,6 +167,8 @@ DocumentFragment* Sanitizer::sanitize(ScriptState* script_state, ...@@ -155,6 +167,8 @@ DocumentFragment* Sanitizer::sanitize(ScriptState* script_state,
} }
} else { } else {
for (const auto& name : element->getAttributeNames()) { 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) && bool drop = (drop_attributes_.Contains(name) &&
(drop_attributes_.at(name).Contains("*") || (drop_attributes_.at(name).Contains("*") ||
drop_attributes_.at(name).Contains(node_name))) || drop_attributes_.at(name).Contains(node_name))) ||
......
...@@ -11,10 +11,12 @@ ...@@ -11,10 +11,12 @@
namespace blink { namespace blink {
class Document;
class DocumentFragment; class DocumentFragment;
class ExceptionState; class ExceptionState;
class SanitizerConfig; class SanitizerConfig;
class ScriptState; class ScriptState;
class StringOrDocumentFragmentOrDocument;
class MODULES_EXPORT Sanitizer final : public ScriptWrappable { class MODULES_EXPORT Sanitizer final : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO(); DEFINE_WRAPPERTYPEINFO();
...@@ -24,8 +26,12 @@ class MODULES_EXPORT Sanitizer final : public ScriptWrappable { ...@@ -24,8 +26,12 @@ class MODULES_EXPORT Sanitizer final : public ScriptWrappable {
explicit Sanitizer(const SanitizerConfig*); explicit Sanitizer(const SanitizerConfig*);
~Sanitizer() override; ~Sanitizer() override;
String sanitizeToString(ScriptState*, const String&, ExceptionState&); String sanitizeToString(ScriptState*,
DocumentFragment* sanitize(ScriptState*, const String&, ExceptionState&); StringOrDocumentFragmentOrDocument&,
ExceptionState&);
DocumentFragment* sanitize(ScriptState*,
StringOrDocumentFragmentOrDocument&,
ExceptionState&);
void Trace(Visitor*) const override; void Trace(Visitor*) const override;
......
...@@ -4,11 +4,13 @@ ...@@ -4,11 +4,13 @@
// https://github.com/WICG/sanitizer-api // https://github.com/WICG/sanitizer-api
typedef (DOMString or DocumentFragment or Document) SanitizerInput;
[ [
Exposed=Window, Exposed=Window,
RuntimeEnabled=SanitizerAPI RuntimeEnabled=SanitizerAPI
] interface Sanitizer { ] interface Sanitizer {
[RaisesException] constructor(optional SanitizerConfig config = {}); [RaisesException] constructor(optional SanitizerConfig config = {});
[CallWith=ScriptState, RaisesException] DOMString sanitizeToString(DOMString input); [CallWith=ScriptState, RaisesException] DOMString sanitizeToString(SanitizerInput input);
[CallWith=ScriptState, RaisesException] DocumentFragment sanitize(DOMString input); [CallWith=ScriptState, RaisesException] DocumentFragment sanitize(SanitizerInput input);
}; };
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
Exposed=Window Exposed=Window
] interface Sanitizer { ] interface Sanitizer {
constructor(optional SanitizerConfig sanitizerConfig = {}); constructor(optional SanitizerConfig sanitizerConfig = {});
DocumentFragment sanitize(DOMString input); DocumentFragment sanitize((DOMString or DocumentFragment or Document) input);
DOMString sanitizeToString(DOMString input); DOMString sanitizeToString((DOMString or DocumentFragment or Document) input);
}; };
dictionary SanitizerConfig { dictionary SanitizerConfig {
...@@ -13,6 +13,5 @@ dictionary SanitizerConfig { ...@@ -13,6 +13,5 @@ dictionary SanitizerConfig {
sequence<DOMString> blockElements; sequence<DOMString> blockElements;
sequence<DOMString> dropElements; sequence<DOMString> dropElements;
sequence<DOMString> allowAttributes; sequence<DOMString> allowAttributes;
sequence<DOMString> blockAttributes;
sequence<DOMString> dropAttributes; sequence<DOMString> dropAttributes;
}; };
...@@ -19,12 +19,46 @@ ...@@ -19,12 +19,46 @@
assert_throws_js(TypeError, _ => s.sanitize()); assert_throws_js(TypeError, _ => s.sanitize());
}, "SanitizerAPI sanitize function without argument should throw an error."); }, "SanitizerAPI sanitize function without argument should throw an error.");
test(t => {
let s = new Sanitizer({});
fragment = s.sanitize(null);
assert_true(fragment instanceof DocumentFragment);
assert_equals(getString(fragment), "null");
}, "SanitizerAPI sanitize function for null.");
testcases.forEach(c => test(t => { testcases.forEach(c => test(t => {
let s = new Sanitizer(c.config_input); let s = new Sanitizer(c.config_input);
fragment = s.sanitize(c.value); fragment = s.sanitize(c.value);
assert_true(fragment instanceof DocumentFragment); assert_true(fragment instanceof DocumentFragment);
assert_equals(getString(fragment), c.result); assert_equals(getString(fragment), c.result);
}, "SanitizerAPI with config: " + c.message + ", sanitize function for " + c.message)); }, "SanitizerAPI with config: " + c.message + ", sanitize from string function for " + c.message));
testcases.forEach(c => test(t => {
let s = new Sanitizer(c.config_input);
var dom = document.implementation.createHTMLDocument();
dom.documentElement.innerHTML = c.value;
fragment = s.sanitize(dom);
assert_true(fragment instanceof DocumentFragment);
let result = getString(fragment);
// Remove <html> and </html>
if (c.message != "document" && result.slice(0,12) == "<html><body>" && result.slice(result.length-14, result.length) == "</body></html>")
result = result.substr(12, result.length - 26);
if (c.message == "document") {
assert_equals(result, "<html><body>test</body></html>");
} else {
assert_equals(result, c.result);
}
}, "SanitizerAPI with config: " + c.message + ", sanitize from document function for " + c.message));
testcases.forEach(c => test(t => {
let s = new Sanitizer(c.config_input);
let tpl = document.createElement("template");
tpl.innerHTML = c.value;
fragment = s.sanitize(tpl.content);
assert_true(fragment instanceof DocumentFragment);
assert_equals(getString(fragment), c.result);
}, "SanitizerAPI with config: " + c.message + ", sanitize from document fragment function for " + c.message));
</script> </script>
</body> </body>
</html> </html>
...@@ -13,10 +13,38 @@ ...@@ -13,10 +13,38 @@
assert_throws_js(TypeError, _ => s.sanitizeToString()); assert_throws_js(TypeError, _ => s.sanitizeToString());
}, "SanitizerAPI sanitize function without argument should throw an error."); }, "SanitizerAPI sanitize function without argument should throw an error.");
test(t => {
let s = new Sanitizer({});
fragment = s.sanitize(null);
assert_equals(s.sanitizeToString(null), "null");
}, "SanitizerAPI sanitizeToString function for null.");
testcases.forEach(c => test(t => { testcases.forEach(c => test(t => {
let s = new Sanitizer(c.config_input); let s = new Sanitizer(c.config_input);
assert_equals(s.sanitizeToString(c.value), c.result); assert_equals(s.sanitizeToString(c.value), c.result);
}, "SanitizerAPI config: " + c.message + ", sanitizeToString function for " + c.message)); }, "SanitizerAPI config: " + c.message + ", sanitizeToString from string function for " + c.message));
testcases.forEach(c => test(t => {
let s = new Sanitizer(c.config_input);
var dom = document.implementation.createHTMLDocument();
dom.documentElement.innerHTML = c.value;
let result = s.sanitizeToString(dom);
// Remove <html> and </html>
if (c.message != "document" && result.slice(0,12) == "<html><body>" && result.slice(result.length-14, result.length) == "</body></html>")
result = result.substr(12, result.length - 26);
if (c.message == "document") {
assert_equals(result, "<html><body>test</body></html>");
} else {
assert_equals(result, c.result);
}
}, "SanitizerAPI with config: " + c.message + ", sanitizeToString from document function for " + c.message));
testcases.forEach(c => test(t => {
let s = new Sanitizer(c.config_input);
let tpl = document.createElement("template");
tpl.innerHTML = c.value;
assert_equals(s.sanitizeToString(tpl.content), c.result);
}, "SanitizerAPI with config: " + c.message + ", sanitizeToString from document fragment function for " + c.message));
</script> </script>
</body> </body>
</html> </html>
...@@ -8,7 +8,6 @@ const testcases = [ ...@@ -8,7 +8,6 @@ const testcases = [
{config_input: {}, value: 1+2, result: "3", message: "arithmetic"}, {config_input: {}, value: 1+2, result: "3", message: "arithmetic"},
{config_input: {}, value: "", result: "", message: "empty string"}, {config_input: {}, value: "", result: "", message: "empty string"},
{config_input: {}, value: undefined, result: "undefined", message: "undefined"}, {config_input: {}, value: undefined, result: "undefined", message: "undefined"},
{config_input: {}, value: null, result: "null", message: "null"},
{config_input: {}, value: "<html><head></head><body>test</body></html>", result: "test", message: "document"}, {config_input: {}, value: "<html><head></head><body>test</body></html>", result: "test", message: "document"},
{config_input: {}, value: "<div>test", result: "<div>test</div>", message: "html without close tag"}, {config_input: {}, value: "<div>test", result: "<div>test</div>", message: "html without close tag"},
{config_input: {}, value: "<script>alert('i am a test')<\/script>", result: "", message: "scripts for default configs"}, {config_input: {}, value: "<script>alert('i am a test')<\/script>", result: "", message: "scripts for default configs"},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment