Commit 668359aa authored by Daniel Vogelheim's avatar Daniel Vogelheim Committed by Commit Bot

[Trusted Types] Rework default policy error handling.

This adapts Trusted Types to recently proposed spec changes, where a
regular error that occurs during default policy application will simply
be passed on to the app, while a return value of null or undefined
signals that the default policy does not wish to deal with that value
(and hence default policy application failed, in the Trusted Types sense
of 'failed').

Change-Id: I41fd23019137e640b1c1b2df7d02c0b8e69932a6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1778485
Commit-Queue: Daniel Vogelheim <vogelheim@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Reviewed-by: default avatarYuki Shiino <yukishiino@chromium.org>
Cr-Commit-Position: refs/heads/master@{#693688}
parent 1970e861
......@@ -1091,6 +1091,8 @@ IdlArrayOrSequenceType.literal_cpp_value = array_or_sequence_literal_cpp_value
_IDL_TYPE_TO_NATIVE_VALUE_TRAITS_TAG_MAP = {
'DOMString': 'IDLString',
'USVString': 'IDLUSVString',
'DOMStringOrNull': 'IDLStringOrNull',
'USVStringOrNull': 'IDLUSVStringOrNull',
'any': 'ScriptValue',
'boolean': 'IDLBoolean',
'long': 'IDLLong',
......@@ -1102,6 +1104,8 @@ _IDL_TYPE_TO_NATIVE_VALUE_TRAITS_TAG_MAP = {
def idl_type_to_native_value_traits_tag(idl_type):
idl_type_str = str(idl_type)
if idl_type.is_nullable:
idl_type_str += "OrNull"
if idl_type_str in _IDL_TYPE_TO_NATIVE_VALUE_TRAITS_TAG_MAP:
return _IDL_TYPE_TO_NATIVE_VALUE_TRAITS_TAG_MAP[idl_type_str]
else:
......
......@@ -11,6 +11,6 @@ dictionary TrustedTypePolicyOptions {
CreateURLCallback createURL;
};
callback CreateHTMLCallback = DOMString (DOMString input);
callback CreateScriptCallback = DOMString (DOMString input);
callback CreateURLCallback = USVString (DOMString input);
callback CreateHTMLCallback = DOMString? (DOMString input);
callback CreateScriptCallback = DOMString? (DOMString input);
callback CreateURLCallback = USVString? (DOMString input);
......@@ -286,7 +286,10 @@ String GetStringFromTrustedHTML(const String& string,
TrustedHTML* result = default_policy->CreateHTML(
execution_context->GetIsolate(), string, exception_state);
if (exception_state.HadException()) {
exception_state.ClearException();
return g_empty_string;
}
if (result->toString().IsNull()) {
TrustedTypeFail(kTrustedHTMLAssignmentAndDefaultPolicyFailed,
execution_context, exception_state, string);
return g_empty_string;
......@@ -339,7 +342,10 @@ String GetStringFromTrustedScript(const String& potential_script,
execution_context->GetIsolate(), potential_script, exception_state);
DCHECK_EQ(!result, exception_state.HadException());
if (exception_state.HadException()) {
exception_state.ClearException();
return g_empty_string;
}
if (result->toString().IsNull()) {
TrustedTypeFail(kTrustedScriptAssignmentAndDefaultPolicyFailed,
execution_context, exception_state, potential_script);
return g_empty_string;
......@@ -380,7 +386,10 @@ String GetStringFromTrustedScriptURL(
execution_context->GetIsolate(), string, exception_state);
if (exception_state.HadException()) {
exception_state.ClearException();
return g_empty_string;
}
if (result->toString().IsNull()) {
TrustedTypeFail(kTrustedScriptURLAssignmentAndDefaultPolicyFailed,
execution_context, exception_state, string);
return g_empty_string;
......@@ -417,7 +426,10 @@ String GetStringFromTrustedURL(USVStringOrTrustedURL string_or_trusted_url,
TrustedURL* result = default_policy->CreateURL(
execution_context->GetIsolate(), string, exception_state);
if (exception_state.HadException()) {
exception_state.ClearException();
return g_empty_string;
}
if (result->toString().IsNull()) {
TrustedTypeFail(kTrustedURLAssignmentAndDefaultPolicyFailed,
execution_context, exception_state, string);
return g_empty_string;
......@@ -444,7 +456,10 @@ Node* TrustedTypesCheckForHTMLScriptElement(Node* child,
TrustedScript* result = default_policy->CreateScript(
doc->GetIsolate(), child->textContent(), exception_state);
if (exception_state.HadException()) {
exception_state.ClearException();
return nullptr;
}
if (result->toString().IsNull()) {
return TrustedTypeFail(kTextNodeScriptAssignmentAndDefaultPolicyFailed, doc,
exception_state, child->textContent())
? nullptr
......
......@@ -60,7 +60,7 @@
const stringTestCases = [
[ s => s, "whatever" ],
[ s => null, "null" ],
[ s => null, "" ],
[ s => "well, " + s, "well, whatever" ],
[ s => { throw new Error() }, new Error() ],
[ s => { aGlobalVarForSideEffectTesting = s; return s }, "whatever" ],
......@@ -71,7 +71,7 @@
const urlTestCases = [
[ s => s, INPUTS.SCRIPTURL ],
[ s => null, "null" ],
[ s => null, "" ],
[ s => s + "#duck", INPUTS.SCRIPTURL + "#duck" ],
[ s => { throw new Error() }, new Error() ],
[ s => s + "#" + aGlobalVarForSideEffectTesting,
......
......@@ -18,7 +18,7 @@
}, "html = identity function");
test(t => {
createHTMLTest('TestPolicyHTML2', { createHTML: s => null }, "null", t);
createHTMLTest('TestPolicyHTML2', { createHTML: s => null }, "", t);
}, "html = null");
var HTMLstr = 'well, ';
......@@ -90,7 +90,7 @@
}, "script = identity function");
test(t => {
createScriptTest('TestPolicyScript2', { createScript: s => null }, "null", t);
createScriptTest('TestPolicyScript2', { createScript: s => null }, "", t);
}, "script = null");
var Scriptstr = 'well, ';
......@@ -165,7 +165,7 @@
}, "script_url = identity function");
test(t => {
createScriptURLTest('TestPolicyScriptURL2', { createScriptURL: s => null }, "null", t);
createScriptURLTest('TestPolicyScriptURL2', { createScriptURL: s => null }, "", t);
}, "script_url = null");
var scriptURLstr = '#duck';
......@@ -240,7 +240,7 @@
}, "url = identity function");
test(t => {
createURLTest('TestPolicyURL2', { createURL: s => null }, "null", t);
createURLTest('TestPolicyURL2', { createURL: s => null }, "", t);
}, "url = null");
var URLstr = '#x';
......
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/helper.sub.js"></script>
</head>
<body>
<script>
// We expect to run this test in two instances, enforcing and report-only
// Trusted Type policies. We'll infer from our URL which one we are.
//
// The expected file names/headers are:
// - default-policy.tentative.html:
// Content-Security-Policy: trusted-types *
// - default-policy-report-only.tentative.html:
// Content-Security-Policy-Report-Only: trusted-types *
//
// The behaviour of the tests should be _mostly_ identical, except that
// Trusted Types relevant assignments should only throw in the enforced
// case. We will use assert_throws for things that should always throw
// (i.e., regular exceptions), and maybe_throws for tests that should only
// throw in TT-enforcing mode.
const is_report_only = document.location.pathname.includes("report-only");
const maybe_throws = (is_report_only
? (error, fn, message) => fn()
: assert_throws);
// Ensure that only the right events trigger violation reports.
// The Promise will resolve, when an event including the string "done" is
// received. The last line of this test file will cause this trigger.
promise_test(t => {
let count = { "null": 0, "undefined": 0, "nodefault": 0 };
return new Promise((resolve, reject) => {
document.addEventListener("securitypolicyviolation", e => {
e.stopPropagation();
// We count the violation reports. We expect one each for "null" and
// "undefined", one each for the "no default" test case above, and one
// for the "done" line at the end, which signals the end of the test run.
if (e.sample.includes("done")) {
resolve(count);
} else if (e.sample.includes("null")) {
count["null"]++;
} else if (e.sample.includes("undefined")) {
count["undefined"]++;
} else if (e.sample.includes("nodefault")) {
count["nodefault"]++;
} else {
reject();
}
});
}).then(counters => {
for (const counter of ["null", "undefined", "nodefault"]) {
assert_equals(counters[counter], testCases.length,
"event count of " + counter);
}
});
}, "Count SecurityPolicyViolation events.");
const testCases = [
[ "a", "href"],
[ "script", "src" ],
[ "div", "innerHTML" ],
[ "script", "text" ],
];
// Try each test case _without_ a default policy.
testCases.forEach(c => {
test(t => {
const element = document.createElement(c[0]);
maybe_throws(TypeError(), _ => element[c[1]] = "nodefault");
}, `${c[0]}.${c[1]} no default policy`);
});
// A trusted type policy that forces a number of edge cases.
function policy(str) {
if (str == "throw")
throw RangeError();
else if (str == "null")
return null;
else if (str == "undefined")
return undefined;
else if (str == "typeerror")
return document.bla();
else if (str == "done")
return null;
else
return "sanitized: " + str;
}
TrustedTypes.createPolicy("default", {
createURL: policy,
createScriptURL: policy,
createHTML: policy,
createScript: policy
});
testCases.forEach(c => {
const name = `${c[0]}.${c[1]} `;
test(t => {
const element = document.createElement(c[0]);
element[c[1]] = "abc";
assert_equals(element[c[1]], "sanitized: abc");
}, name + "default");
test(t => {
const element = document.createElement(c[0]);
maybe_throws(TypeError(), _ => element[c[1]] = "null");
}, name + "null");
test(t => {
const element = document.createElement(c[0]);
assert_throws(RangeError(), _ => element[c[1]] = "throw");
}, name + "throw");
test(t => {
const element = document.createElement(c[0]);
maybe_throws(TypeError(), _ => element[c[1]] = "undefined");
}, name + "undefined");
test(t => {
const element = document.createElement(c[0]);
assert_throws(TypeError(), _ => element[c[1]] = "typeerror");
}, name + "typeerror");
});
// Trigger the exit condition in the "Count" promise test above.
try { document.createElement("a").href = "done"; } catch (e) {}
</script>
</body>
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