Skip to content

Commit

Permalink
[Trusted Types] Fix bypass with multiple-argument DOM accessors
Browse files Browse the repository at this point in the history
Some DOM methods accept multiple DOM nodes or text strings as arguments.
When applied to a <script> node, these (presently) allow string
insertion into <script> elements without Trusted Types checking.

Bug: 948614
Change-Id: I18dd0f3c0652bda2c6256cdba3359e476ada8b09
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1688030
Reviewed-by: Mason Freed <masonfreed@chromium.org>
Reviewed-by: Mike West <mkwst@chromium.org>
Commit-Queue: Daniel Vogelheim <vogelheim@chromium.org>
Cr-Commit-Position: refs/heads/master@{#678633}
  • Loading branch information
otherdaniel authored and chromium-wpt-export-bot committed Jul 18, 2019
1 parent f04df0d commit c71415b
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 0 deletions.
65 changes: 65 additions & 0 deletions trusted-types/Node-multiple-arguments.tentative.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<!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>
<div id="container"></div>
<script>
const container = document.querySelector("#container");
const policy = window.TrustedTypes.createPolicy("policy", {
createScript: t => t,
});
function stringify(arg) {
return "textContent" in arg.__proto__ ? arg.textContent : arg.toString()
}

// This test case mirrors the block-Node-multiple-arguments case except
// that, because Trusted Types is not enabled, no exceptions should be
// thrown anyhwere.
const targets = ["div", "script"];
const all_args = [
[ policy.createScript("'createScript';") ],
[ policy.createScript("'cresteScript #1';"), policy.createScript("'#2;'") ],
[ "'plain text';" ],
[ "'plain text #1';", "'plain text #2';" ],
[ document.createTextNode("'node';") ],
[ document.createTextNode("'node #1';"),
document.createTextNode("'node #2';") ],
[ "'mixed';", document.createTextNode("'node';") ],
[ "'mixed';", policy.createScript("'script';") ],
[ document.createTextNode("'node';"),
policy.createScript("'script';") ],
];

for (target of targets) {
for (args of all_args) {

for (setter of [container.replaceWith, container.after, container.before]) {
test(t => {
var outer = document.createElement(target);
container.appendChild(outer);
var inner = document.createElement("p");
outer.appendChild(inner);
setter.apply(inner, args);
assert_equals(outer.textContent, args.map(stringify).join(""));

}, `${setter.name}(${args.toString()}) on <${target}> should pass`);
}

for (setter of [container.append, container.prepend]) {
test(t => {
let outer = document.createElement(target);
container.appendChild(outer);
setter.apply(outer, args);
assert_equals(outer.textContent, args.map(stringify).join(""));
}, `${setter.name}(${args.toString()}) on <${target}> should pass`);
}

}
}
</script>
</body>
</html>
90 changes: 90 additions & 0 deletions trusted-types/block-Node-multiple-arguments.tentative.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/helper.sub.js"></script>
<meta http-equiv="Content-Security-Policy" content="trusted-types *">
</head>
<body>
<div id="container"></div>
<script>
const container = document.querySelector("#container");
const policy = window.TrustedTypes.createPolicy("policy", {
createScript: t => t,
});
function stringify(arg) {
return "textContent" in arg.__proto__ ? arg.textContent : arg.toString()
}

// Test all combinations of:
// - DOM methods: append, prepend, replaceWith, after, before
// - one or two parameters, string, Text node, Trusted Script
// - into regular container or <script> element.
//
// Test arguments are all string literals with a semicolon, because -
// depending on target - it might be injected into a <script> and shouldn't
// cause syntax errors there.
const targets = ["div", "script"];
const pass_args = [
[ policy.createScript("'createScript';") ],
[ policy.createScript("'cresteScript #1';"), policy.createScript("'#2;'") ],
];
const fail_args = [
[ "'plain text';" ],
[ "'plain text #1';", "'plain text #2';" ],
[ document.createTextNode("'node';") ],
[ document.createTextNode("'node #1';"),
document.createTextNode("'node #2';") ],
[ "'mixed';", document.createTextNode("'node';") ],
[ "'mixed';", policy.createScript("'script';") ],
[ document.createTextNode("'node';"),
policy.createScript("'script';") ],
];
const all_args = [].concat(pass_args).concat(fail_args);

for (target of targets) {
for (args of all_args) {
var should_fail = target == "script" && fail_args.indexOf(args) != -1;
var fail_string = should_fail ? "fail" : "pass";

for (setter of [container.replaceWith, container.after, container.before]) {
test(t => {
var outer = document.createElement(target);
container.appendChild(outer);
var inner = document.createElement("p");
outer.appendChild(inner);
var test_fn = _ => { setter.apply(inner, args); };
var expected;
if (should_fail) {
assert_throws(new TypeError(), test_fn, "This should throw.");
expected = "";
} else {
test_fn();
expected = args.map(stringify).join("");
}
assert_equals(outer.textContent, expected);
}, `${setter.name}(${args.toString()}) on <${target}> should ${fail_string}`);
}

for (setter of [container.append, container.prepend]) {
test(t => {
let outer = document.createElement(target);
container.appendChild(outer);
var test_fn = _ => { setter.apply(outer, args); };
var expected;
if (should_fail) {
assert_throws(new TypeError(), test_fn, "This should throw.");
expected = "";
} else {
test_fn();
expected = args.map(stringify).join("");
}
assert_equals(outer.textContent, expected);
}, `${setter.name}(${args.toString()}) on <${target}> should ${fail_string}`);
}
}
}
</script>
</body>
</html>

0 comments on commit c71415b

Please sign in to comment.