Bug 1564550 [wpt PR 17732] - [Trusted Types] Fix bypass with multiple-argument DOM accessors, a=testonly
authorDaniel Vogelheim <vogelheim@chromium.org>
Mon, 22 Jul 2019 11:12:03 +0000
changeset 484570 163a7e23ba56e6c9bbf41a4329c27470b8d9fc73
parent 484569 c827676abd8c1854d54628f3d3a966882bc5bdae
child 484571 8d45d6ddacfac12cbe5b2ed2146b97e0f9e6df35
push id36340
push userapavel@mozilla.com
push dateThu, 25 Jul 2019 06:05:14 +0000
treeherdermozilla-central@efa086e28780 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1564550, 17732, 948614, 1688030, 678633
milestone70.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1564550 [wpt PR 17732] - [Trusted Types] Fix bypass with multiple-argument DOM accessors, a=testonly Automatic update from web-platform-tests [Trusted Types] Fix bypass with multiple-argument DOM accessors 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} -- wpt-commits: c71415b4f94f14a4f689436df50061f78113726a wpt-pr: 17732
testing/web-platform/tests/trusted-types/Node-multiple-arguments.tentative.html
testing/web-platform/tests/trusted-types/block-Node-multiple-arguments.tentative.html
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/trusted-types/Node-multiple-arguments.tentative.html
@@ -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>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/trusted-types/block-Node-multiple-arguments.tentative.html
@@ -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>