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 = [
"$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.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.h",
"$bindings_modules_v8_output_dir/string_or_unsigned_long.cc",
......
......@@ -5,7 +5,9 @@
#include "sanitizer.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/core/dom/document.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/node.h"
......@@ -84,25 +86,35 @@ void Sanitizer::AttrFormatter(
Sanitizer::~Sanitizer() = default;
String Sanitizer::sanitizeToString(ScriptState* script_state,
const String& input,
StringOrDocumentFragmentOrDocument& input,
ExceptionState& exception_state) {
return CreateMarkup(sanitize(script_state, input, exception_state),
kChildrenOnly);
}
DocumentFragment* Sanitizer::sanitize(ScriptState* script_state,
const String& input,
StringOrDocumentFragmentOrDocument& input,
ExceptionState& exception_state) {
DocumentFragment* fragment = nullptr;
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,
"Cannot find current DOM window.");
return nullptr;
}
Document* document = window->document();
DocumentFragment* fragment = document->createDocumentFragment();
DCHECK(document->QuerySelector("body"));
fragment->ParseHTML(input, document->QuerySelector("body"));
Node* node = fragment->firstChild();
......@@ -155,6 +167,8 @@ DocumentFragment* Sanitizer::sanitize(ScriptState* script_state,
}
} 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))) ||
......
......@@ -11,10 +11,12 @@
namespace blink {
class Document;
class DocumentFragment;
class ExceptionState;
class SanitizerConfig;
class ScriptState;
class StringOrDocumentFragmentOrDocument;
class MODULES_EXPORT Sanitizer final : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
......@@ -24,8 +26,12 @@ class MODULES_EXPORT Sanitizer final : public ScriptWrappable {
explicit Sanitizer(const SanitizerConfig*);
~Sanitizer() override;
String sanitizeToString(ScriptState*, const String&, ExceptionState&);
DocumentFragment* sanitize(ScriptState*, const String&, ExceptionState&);
String sanitizeToString(ScriptState*,
StringOrDocumentFragmentOrDocument&,
ExceptionState&);
DocumentFragment* sanitize(ScriptState*,
StringOrDocumentFragmentOrDocument&,
ExceptionState&);
void Trace(Visitor*) const override;
......
......@@ -4,11 +4,13 @@
// https://github.com/WICG/sanitizer-api
typedef (DOMString or DocumentFragment or Document) SanitizerInput;
[
Exposed=Window,
RuntimeEnabled=SanitizerAPI
] interface Sanitizer {
[RaisesException] constructor(optional SanitizerConfig config = {});
[CallWith=ScriptState, RaisesException] DOMString sanitizeToString(DOMString input);
[CallWith=ScriptState, RaisesException] DocumentFragment sanitize(DOMString input);
[CallWith=ScriptState, RaisesException] DOMString sanitizeToString(SanitizerInput input);
[CallWith=ScriptState, RaisesException] DocumentFragment sanitize(SanitizerInput input);
};
......@@ -4,8 +4,8 @@
Exposed=Window
] interface Sanitizer {
constructor(optional SanitizerConfig sanitizerConfig = {});
DocumentFragment sanitize(DOMString input);
DOMString sanitizeToString(DOMString input);
DocumentFragment sanitize((DOMString or DocumentFragment or Document) input);
DOMString sanitizeToString((DOMString or DocumentFragment or Document) input);
};
dictionary SanitizerConfig {
......@@ -13,6 +13,5 @@ dictionary SanitizerConfig {
sequence<DOMString> blockElements;
sequence<DOMString> dropElements;
sequence<DOMString> allowAttributes;
sequence<DOMString> blockAttributes;
sequence<DOMString> dropAttributes;
};
......@@ -19,12 +19,46 @@
assert_throws_js(TypeError, _ => s.sanitize());
}, "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 => {
let s = new Sanitizer(c.config_input);
fragment = s.sanitize(c.value);
assert_true(fragment instanceof DocumentFragment);
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>
</body>
</html>
......@@ -13,10 +13,38 @@
assert_throws_js(TypeError, _ => s.sanitizeToString());
}, "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 => {
let s = new Sanitizer(c.config_input);
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>
</body>
</html>
......@@ -8,7 +8,6 @@ const testcases = [
{config_input: {}, value: 1+2, result: "3", message: "arithmetic"},
{config_input: {}, value: "", result: "", message: "empty string"},
{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: "<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"},
......
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