Commit c875a504 authored by Yifan Luo's avatar Yifan Luo Committed by Commit Bot

[Sanitizer API] Fix Trusted Types bypass in Sanitizer API.

`sanitize` as a DOMString to Fragment API need to go through Trusted Types.

Bug: 1116418
Change-Id: Icf73ff9f7a8f115d0f9f6e32ae9673c7475691e2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2506046
Commit-Queue: Yifan Luo <lyf@chromium.org>
Reviewed-by: default avatarDaniel Vogelheim <vogelheim@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Cr-Commit-Position: refs/heads/master@{#825303}
parent 7822836c
......@@ -101,6 +101,8 @@ bindings_modules_generated_union_type_files = [
"$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_trusted_html_or_document_fragment_or_document.cc",
"$bindings_modules_v8_output_dir/string_or_trusted_html_or_document_fragment_or_document.h",
"$bindings_modules_v8_output_dir/string_or_unsigned_long.cc",
"$bindings_modules_v8_output_dir/string_or_unsigned_long.h",
"$bindings_modules_v8_output_dir/uint32_array_or_unsigned_long_sequence.cc",
......
......@@ -6,6 +6,7 @@
#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/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"
......@@ -14,6 +15,8 @@
#include "third_party/blink/renderer/core/dom/node_traversal.h"
#include "third_party/blink/renderer/core/editing/serializers/serialization.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.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/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"
......@@ -88,11 +91,39 @@ Sanitizer::~Sanitizer() = default;
String Sanitizer::sanitizeToString(ScriptState* script_state,
StringOrDocumentFragmentOrDocument& input,
ExceptionState& exception_state) {
return CreateMarkup(sanitize(script_state, input, exception_state),
return CreateMarkup(SanitizeImpl(script_state, input, exception_state),
kChildrenOnly);
}
DocumentFragment* Sanitizer::sanitize(ScriptState* script_state,
DocumentFragment* Sanitizer::sanitize(
ScriptState* script_state,
StringOrTrustedHTMLOrDocumentFragmentOrDocument& input,
ExceptionState& exception_state) {
StringOrDocumentFragmentOrDocument new_input;
if (input.IsString() || input.IsNull()) {
LocalDOMWindow* window = LocalDOMWindow::From(script_state);
if (!window) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Cannot find current DOM window.");
return nullptr;
}
new_input.SetString(TrustedTypesCheckForHTML(
input.GetAsString(), window->GetExecutionContext(), exception_state));
if (exception_state.HadException()) {
return nullptr;
}
} else if (input.IsTrustedHTML()) {
new_input.SetString(input.GetAsTrustedHTML()->toString());
} else if (input.IsDocument()) {
new_input.SetDocument(input.GetAsDocument());
} else if (input.IsDocumentFragment()) {
new_input.SetDocumentFragment(input.GetAsDocumentFragment());
}
return SanitizeImpl(script_state, new_input, exception_state);
}
DocumentFragment* Sanitizer::SanitizeImpl(
ScriptState* script_state,
StringOrDocumentFragmentOrDocument& input,
ExceptionState& exception_state) {
DocumentFragment* fragment = nullptr;
......
......@@ -17,6 +17,7 @@ class ExceptionState;
class SanitizerConfig;
class ScriptState;
class StringOrDocumentFragmentOrDocument;
class StringOrTrustedHTMLOrDocumentFragmentOrDocument;
class MODULES_EXPORT Sanitizer final : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
......@@ -30,7 +31,7 @@ class MODULES_EXPORT Sanitizer final : public ScriptWrappable {
StringOrDocumentFragmentOrDocument&,
ExceptionState&);
DocumentFragment* sanitize(ScriptState*,
StringOrDocumentFragmentOrDocument&,
StringOrTrustedHTMLOrDocumentFragmentOrDocument&,
ExceptionState&);
void Trace(Visitor*) const override;
......@@ -40,6 +41,10 @@ class MODULES_EXPORT Sanitizer final : public ScriptWrappable {
void AttrFormatter(HashMap<String, Vector<String>>&,
const Vector<std::pair<String, Vector<String>>>&);
DocumentFragment* SanitizeImpl(ScriptState*,
StringOrDocumentFragmentOrDocument&,
ExceptionState&);
HashSet<String> allow_elements_ = {};
HashSet<String> block_elements_ = {};
HashSet<String> drop_elements_ = {};
......
......@@ -5,6 +5,7 @@
// https://github.com/WICG/sanitizer-api
typedef (DOMString or DocumentFragment or Document) SanitizerInput;
typedef (DOMString or TrustedHTML or DocumentFragment or Document) SanitizerInputWithTrustedHTML;
[
Exposed=Window,
......@@ -12,5 +13,5 @@ typedef (DOMString or DocumentFragment or Document) SanitizerInput;
] interface Sanitizer {
[RaisesException] constructor(optional SanitizerConfig config = {});
[CallWith=ScriptState, RaisesException] DOMString sanitizeToString(SanitizerInput input);
[CallWith=ScriptState, RaisesException] DocumentFragment sanitize(SanitizerInput input);
[CallWith=ScriptState, RaisesException] DocumentFragment sanitize(SanitizerInputWithTrustedHTML input);
};
......@@ -75,4 +75,26 @@
resolve();
});
}, "Set require trusted types for 'script' without CSP for trusted types don't block policy creation and using.");
promise_test(t => {
let p = Promise.resolve()
.then(promise_violation("require-trusted-types-for 'script'"));
let s = new Sanitizer();
assert_throws_js(TypeError,
_ => {
fragment = s.sanitize("testHTML");
});
return p;
}, "Require trusted types for 'script' block Sanitizer sanitize from string API.");
promise_test(t => {
let p = Promise.resolve()
.then(promise_violation("require-trusted-types-for 'script'"));
let s = new Sanitizer();
assert_throws_js(TypeError,
_ => {
fragment = s.sanitize(null);
});
return p;
}, "Require trusted types for 'script' block Sanitizer sanitize null from string API.");
</script>
This is a testharness.js-based test.
Found 104 tests; 101 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 106 tests; 103 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS SanitizerAPI sanitize function without argument should throw an error.
PASS SanitizerAPI sanitize function for null.
PASS SanitizerAPI with config: string, sanitize from string function for string
......@@ -104,5 +104,7 @@ PASS SanitizerAPI with config: allowAttributes list {"id": ["div"]} with id attr
PASS SanitizerAPI with config: allowAttributes list {"id": ["*"]} with id attribute and onclick scripts, sanitize from document fragment function for allowAttributes list {"id": ["*"]} with id attribute and onclick scripts
PASS SanitizerAPI with config: allowAttributes list {"*": ["a"]} with style attribute, sanitize from document fragment function for allowAttributes list {"*": ["a"]} with style attribute
PASS SanitizerAPI with config: allowAttributes list has no influence to dropAttributes, sanitize from document fragment function for allowAttributes list has no influence to dropAttributes
PASS SanitizerAPI sanitize from TrustedHTML.
PASS SanitizerAPI sanitize from string with default policy.
Harness: the test ran to completion.
......@@ -59,6 +59,24 @@
assert_true(fragment instanceof DocumentFragment);
assert_equals(getString(fragment), c.result);
}, "SanitizerAPI with config: " + c.message + ", sanitize from document fragment function for " + c.message));
test(t => {
let s = new Sanitizer();
let policy = trustedTypes.createPolicy("myPolicy", {createHTML: s=>s+1});
let html_string = policy.createHTML("testHTML");
fragment = s.sanitize(html_string);
assert_true(fragment instanceof DocumentFragment);
assert_equals(getString(fragment), "testHTML1");
}, "SanitizerAPI sanitize from TrustedHTML.");
test(t => {
let s = new Sanitizer();
let policy = trustedTypes.createPolicy("default", {createHTML: s=>s+2});
let html_string = policy.createHTML("testHTML");
fragment = s.sanitize(html_string);
assert_true(fragment instanceof DocumentFragment);
assert_equals(getString(fragment), "testHTML2");
}, "SanitizerAPI sanitize from string with default policy.");
</script>
</body>
</html>
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