Skip to content

Commit

Permalink
Add more tests for trusted-types-sink violation reports.
Browse files Browse the repository at this point in the history
This covers all Window-only sinks listed in
w3c/trusted-types#494 (comment)

This also adds small improvements to csp-violations.js:

- Rely on connect-src / EventSource / self.add/removeEventListner, so
  that the file could be usable in Workers in the future.

- Also force a connect-src violation before executing fn, so that we
  can flush previously reported violations. Although this is not
  necessary for existing tests, I noticed this can sometimes happen
  when I was trying to write tests.
  • Loading branch information
fred-wang committed Jan 29, 2025
1 parent 48b7733 commit a2c185e
Show file tree
Hide file tree
Showing 31 changed files with 536 additions and 123 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<script src="support/helper.sub.js"></script>
<script src="./support/csp-violations.js"></script>
<meta http-equiv="Content-Security-Policy" content="trusted-types 'none'">
<meta http-equiv="Content-Security-Policy" content="object-src 'none'">
<meta http-equiv="Content-Security-Policy" content="connect-src 'none'">
<body>
<script>
promise_test(async t => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<script src="./support/csp-violations.js"></script>

<meta http-equiv="Content-Security-Policy" content="trusted-types SomeName JustOneMoreName AnotherName">
<meta http-equiv="Content-Security-Policy" content="object-src 'none'">
<meta http-equiv="Content-Security-Policy" content="connect-src 'none'">
<body>
<script>
// Allowed name test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'
Content-Security-Policy: object-src 'none'
Content-Security-Policy: connect-src 'none'
2 changes: 1 addition & 1 deletion trusted-types/require-trusted-types-for.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<script src="/resources/testharnessreport.js"></script>
<script src="./support/csp-violations.js"></script>
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'">
<meta http-equiv="Content-Security-Policy" content="object-src 'none'">
<meta http-equiv="Content-Security-Policy" content="connect-src 'none'">
</head>
<body>
<script>
Expand Down
39 changes: 22 additions & 17 deletions trusted-types/support/csp-violations.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,45 @@ const cspDirectives = [
"script-src",
];

// A generic helper that runs function fn and return a promise resolving with
// an array of reported violations for trusted type directives and a possible
// exception thrown.
// A generic helper that runs function fn and returns a promise resolving with
// an array of reported violations and a possible exception thrown. This forces
// a "connect-src" violation before and after calling fn, to make sure we are
// not gathering violations reported before fn, and that all the violations
// reported by fn have been delivered. This assumes that the test file contains
// the CSP directive connect-src 'none' and that fn is not itself triggering
// a "connect-src" violation report.
function trusted_type_violations_and_exception_for(fn) {
return new Promise((resolve, reject) => {
// Listen for security policy violations.
let receivedInitialConnectSrcViolation = false;
let result = { violations: [], exception: null };
let handler = e => {
if (cspDirectives.includes(e.effectiveDirective)) {
result.violations.push(e);
} else if (e.effectiveDirective === "object-src") {
document.removeEventListener("securitypolicyviolation", handler);
e.stopPropagation();
resolve(result);
if (receivedInitialConnectSrcViolation) {
result.violations.push(e);
}
} else if (e.effectiveDirective === "connect-src") {
if (receivedInitialConnectSrcViolation) {
self.removeEventListener("securitypolicyviolation", handler);
e.stopPropagation();
resolve(result);
} else {
receivedInitialConnectSrcViolation = true;
}
} else {
reject(`Unexpected violation for directive ${e.effectiveDirective}`);
}
}
document.addEventListener("securitypolicyviolation", handler);
self.addEventListener("securitypolicyviolation", handler);

// Run the specified function and record any exception.
new EventSource("/common/blank.html");
try {
fn();
} catch(e) {
result.exception = e;
}

// Force an "object-src" violation, to make sure all the previous violations
// have been delivered. This assumes the test file's associated .headers
// file contains Content-Security-Policy: object-src 'none'.
var o = document.createElement('object');
o.type = "video/mp4";
o.data = "dummy.webm";
document.body.appendChild(o);
new EventSource("/common/blank.html");
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
// headers are set in the .headers file:
//
// Content-Security-Policy: script-src 'unsafe-inline'; report-uri ...
// Content-Security-Policy: object-src 'none'
// Content-Security-Policy: connect-src 'none'
// Content-Security-Policy: require-trusted-types-for 'script'
//
// The second rule is there so we can provoke a CSP violation report at will.
// The intent is that in order to test that a violation has *not* been thrown
// (and without resorting to abominations like timeouts), we force a *another*
// CSP violation (by violating the object-src rule) and when that event is
// CSP violation (by violating the connect-src rule) and when that event is
// processed we can we sure that an earlier event - if it indeed occurred -
// must have already been processed.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Content-Security-Policy: script-src http: https: 'nonce-123' 'report-sample'
Content-Security-Policy: object-src 'none'
Content-Security-Policy: connect-src 'none'
Content-Security-Policy: require-trusted-types-for 'script'
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// headers are set in the .headers file:
//
// Content-Security-Policy: script-src 'unsafe-inline' 'unsafe-eval'; report-uri ...
// Content-Security-Policy: object-src 'none'
// Content-Security-Policy: connect-src 'none'
// Content-Security-Policy-Report-Only: require-trusted-types-for 'script'
//
// The last rule is there so we can provoke a CSP violation report at will.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Content-Security-Policy: script-src http: https: 'nonce-123' 'unsafe-eval'
Content-Security-Policy: object-src 'none'
Content-Security-Policy: connect-src 'none'
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'

2 changes: 1 addition & 1 deletion trusted-types/trusted-types-eval-reporting.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// headers are set in the .headers file:
//
// Content-Security-Policy: script-src 'unsafe-inline' 'unsafe-eval'; report-uri ...
// Content-Security-Policy: object-src 'none'
// Content-Security-Policy: connect-src 'none'
// Content-Security-Policy: require-trusted-types-for 'script'
//
// The last rule is there so we can provoke a CSP violation report at will.
Expand Down
2 changes: 1 addition & 1 deletion trusted-types/trusted-types-eval-reporting.html.headers
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Content-Security-Policy: script-src http: https: 'nonce-123' 'unsafe-eval'
Content-Security-Policy: object-src 'none'
Content-Security-Policy: connect-src 'none'
Content-Security-Policy: require-trusted-types-for 'script'

2 changes: 1 addition & 1 deletion trusted-types/trusted-types-report-only.html.headers
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Content-Security-Policy-Report-Only: trusted-types two; report-uri /content-security-policy/resources/dummy-report.php; require-trusted-types-for 'script';
Content-Security-Policy: object-src 'none'
Content-Security-Policy: connect-src 'none'
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/csp-violations.js"></script>
<meta http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'; connect-src 'none';">
<body>
<script>
const policy = trustedTypes.createPolicy("dummy", { createHTML: x => x });
const input = `<p>${'A'.repeat(100)}</p>`;

promise_test(async t => {
await no_trusted_type_violation_for(_ =>
(new DOMParser()).parseFromString(policy.createHTML(input), "text/html")
);
}, "No violation reported for TrustedHTML.");

promise_test(async t => {
let violation = await trusted_type_violation_for(TypeError, _ =>
(new DOMParser()).parseFromString(input, "text/html")
);
assert_equals(violation.blockedURI, "trusted-types-sink");
assert_equals(violation.sample, `DOMParser parseFromString|${clipSampleIfNeeded(input)}`);
}, "Violation report for plain string.");
</script>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/csp-violations.js"></script>
<meta http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'; connect-src 'none';">
<body>
<script>
const policy = trustedTypes.createPolicy("dummy", { createHTML: x => x });
const input = `<p>${'A'.repeat(100)}</p>`;

promise_test(async t => {
await no_trusted_type_violation_for(_ => {
["insertHTML", "paste"].forEach(command => {
document.execCommand(command, false, policy.createHTML(input));
});
});
}, "No violation reported for TrustedHTML.");

promise_test(async t => {
await no_trusted_type_violation_for(_ =>
document.execCommand("paste", false, input)
);
}, "No violation reported (paste command).");

promise_test(async t => {
let violation = await trusted_type_violation_for(TypeError, _ =>
document.execCommand("insertHTML", false, input)
);
assert_equals(violation.blockedURI, "trusted-types-sink");
assert_equals(violation.sample, `Document execCommand|${clipSampleIfNeeded(input)}`);
}, "Violation report for plain string (insertHTML command).");
</script>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/csp-violations.js"></script>
<meta http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'; connect-src 'none';">
<body>
<script>
const policy = trustedTypes.createPolicy("dummy", { createHTML: x => x });
const input = `<p>${'A'.repeat(100)}</p>`;

promise_test(async t => {
await no_trusted_type_violation_for(_ =>
Document.parseHTMLUnsafe(policy.createHTML(input))
);
}, "No violation reported for TrustedHTML.");

promise_test(async t => {
let violation = await trusted_type_violation_for(TypeError, _ =>
Document.parseHTMLUnsafe(input)
);
assert_equals(violation.blockedURI, "trusted-types-sink");
assert_equals(violation.sample, `Document parseHTMLUnsafe|${clipSampleIfNeeded(input)}`);
}, "Violation report for plain string.");
</script>
</body>
38 changes: 38 additions & 0 deletions trusted-types/trusted-types-reporting-for-Document-write.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/csp-violations.js"></script>
<meta http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'; connect-src 'none';">
<body>
<div id="log"></div>
<script>
const policy = trustedTypes.createPolicy("dummy", { createHTML: x => x });
const input = [`<p>${'A'.repeat(50)}</p>`,
`<p>${'B'.repeat(30)}</p>`,
`<p>${'C'.repeat(20)}</p>`];
const joinedInput = input.join('');
const trustedInput = input.map(x => policy.createHTML(input));

// Create a separate document for testing, so write calls don't mess up with
// how testharness writes its output in the main document.
const doc = (new DOMParser()).
parseFromString(policy.createHTML("<body></body>"), "text/html");

["write", "writeln"].forEach(methodName => {
promise_test(async t => {
await no_trusted_type_violation_for(_ =>
doc[methodName](...trustedInput)
);
}, `No violation reported for ${methodName}() with TrustedHTML arguments.`);

promise_test(async t => {
let violation = await trusted_type_violation_for(TypeError, _ =>
doc[methodName](trustedInput[0], input[1], trustedInput[2])
);
assert_equals(violation.blockedURI, "trusted-types-sink");
assert_equals(violation.sample, `Document ${methodName}|${clipSampleIfNeeded(joinedInput)}`);
}, `Violation report for plain string for ${methodName}() with at least one plain string argument.`);
});
</script>
</body>
26 changes: 26 additions & 0 deletions trusted-types/trusted-types-reporting-for-Element-innerHTML.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/csp-violations.js"></script>
<meta http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'; connect-src 'none';">
<body>
<script>
const policy = trustedTypes.createPolicy("dummy", { createHTML: x => x });
const input = `<p>${'A'.repeat(100)}</p>`;

promise_test(async t => {
await no_trusted_type_violation_for(_ =>
document.createElement("div").innerHTML = policy.createHTML(input)
);
}, "No violation reported for TrustedHTML.");

promise_test(async t => {
let violation = await trusted_type_violation_for(TypeError, _ =>
document.createElement("div").innerHTML = input
);
assert_equals(violation.blockedURI, "trusted-types-sink");
assert_equals(violation.sample, `Element innerHTML|${clipSampleIfNeeded(input)}`);
}, "Violation report for plain string.");
</script>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/csp-violations.js"></script>
<meta http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'; connect-src 'none';">
<body>
<script>
const policy = trustedTypes.createPolicy("dummy", { createHTML: x => x });
const input = `<p>${'A'.repeat(100)}</p>`;

promise_test(async t => {
await no_trusted_type_violation_for(_ =>
document.createElement("div").
insertAdjacentHTML("beforeend", policy.createHTML(input))
);
}, "No violation reported for TrustedHTML.");

promise_test(async t => {
let violation = await trusted_type_violation_for(TypeError, _ =>
document.createElement("div").insertAdjacentHTML("beforeend", input)
);
assert_equals(violation.blockedURI, "trusted-types-sink");
assert_equals(violation.sample, `Element insertAdjacentHTML|${clipSampleIfNeeded(input)}`);
}, "Violation report for plain string.");
</script>
</body>
26 changes: 26 additions & 0 deletions trusted-types/trusted-types-reporting-for-Element-outerHTML.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/csp-violations.js"></script>
<meta http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'; connect-src 'none';">
<body>
<script>
const policy = trustedTypes.createPolicy("dummy", { createHTML: x => x });
const input = `<p>${'A'.repeat(100)}</p>`;

promise_test(async t => {
await no_trusted_type_violation_for(_ =>
document.createElement("div").outerHTML = policy.createHTML(input)
);
}, "No violation reported for TrustedHTML.");

promise_test(async t => {
let violation = await trusted_type_violation_for(TypeError, _ =>
document.createElement("div").outerHTML = input
);
assert_equals(violation.blockedURI, "trusted-types-sink");
assert_equals(violation.sample, `Element outerHTML|${clipSampleIfNeeded(input)}`);
}, "Violation report for plain string.");
</script>
</body>
Loading

0 comments on commit a2c185e

Please sign in to comment.