Skip to content

Commit

Permalink
Bug 1942119 [wpt PR 50124] - Rewrite Trusted types tests for CSP viol…
Browse files Browse the repository at this point in the history
…ations, a=testonly

Automatic update from web-platform-tests
Rewrite Trusted types tests for CSP violations (#50124)

* Rewrite Trusted types tests for CSP violations

Currently the listener to "securitypolicyviolation" is added before
actually running the statement that triggers violations, so it could
be possible that some violations are not caught. This bad pattern is
duplicated in several `trusted-types*reporting*` tests.

This patch adds a new helper file to properly wrap the
listener registration and statement execution in a promise, and
reuses it in existing tests.

w3c/trusted-types#576

--

wpt-commits: bb5f8351e2b19b1f4cfd16ca891ca638461c1b4b
wpt-pr: 50124
  • Loading branch information
fred-wang authored and moz-wptsync-bot committed Jan 27, 2025
1 parent 1f873a0 commit a0d8a57
Show file tree
Hide file tree
Showing 16 changed files with 392 additions and 612 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,22 @@
<script src="/resources/testharness.js" ></script>
<script src="/resources/testharnessreport.js"></script>
<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'">
<body>
<script>
function promise_violation(t, filter_arg) {
return _ => new Promise((resolve) => {
function handler(e) {
let matches = e.originalPolicy.includes(filter_arg);
if (matches) {
document.removeEventListener("securitypolicyviolation", handler);
e.stopPropagation();
resolve(e);
}
}

document.addEventListener("securitypolicyviolation", handler);
t.add_cleanup(() => {
document.removeEventListener("securitypolicyviolation", handler);
});
});
}

promise_test(t => {
let p = Promise.resolve()
.then(promise_violation(t, "trusted-types 'none'"));

assert_throws_js(TypeError, _ => {
window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } );
});
return p;
promise_test(async t => {
let violation = await trusted_type_violation_for(TypeError, _ =>
window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } )
);
assert_true(violation.originalPolicy.includes("trusted-types 'none'"));
}, "Cannot create policy with name 'SomeName' - policy creation throws");

promise_test(t => {
let p = Promise.resolve()
.then(promise_violation(t, "trusted-types 'none'"));

assert_throws_js(TypeError, _ => {
window.trustedTypes.createPolicy('default', { createHTML: s => s } );
});
return p;
promise_test(async t => {
let violation = await trusted_type_violation_for(TypeError, _ =>
window.trustedTypes.createPolicy('default', { createHTML: s => s } )
);
assert_true(violation.originalPolicy.includes("trusted-types 'none'"));
}, "Cannot create policy with name 'default' - policy creation throws");
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
<script src="/resources/testharness.js" ></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/helper.sub.js"></script>
<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'">
<body>
<script>
// Allowed name test
Expand All @@ -20,46 +22,22 @@
assert_equals(policy.name, 'JustOneMoreName');
}, "Another allowed-name policy creation works.");

function promise_violation(t, filter_arg) {
return _ => new Promise((resolve) => {
function handler(e) {
let matches = e.originalPolicy.includes(filter_arg);
if (matches) {
document.removeEventListener("securitypolicyviolation", handler);
e.stopPropagation();
resolve(e);
}
}

document.addEventListener("securitypolicyviolation", handler);
t.add_cleanup(() => {
document.removeEventListener("securitypolicyviolation", handler);
});
});
}

// Non-allowed names test
promise_test(t => {
let p = Promise.resolve()
.then(promise_violation(t, "trusted-types SomeName JustOneMoreName AnotherName"));

assert_throws_js(TypeError, _ => {
window.trustedTypes.createPolicy('SomeOtherName', { createHTML: s => s } );
});
return p;
promise_test(async t => {
let violation = await trusted_type_violation_for(TypeError, _ =>
window.trustedTypes.createPolicy('SomeOtherName', { createHTML: s => s } )
);
assert_true(violation.originalPolicy.includes("trusted-types SomeName JustOneMoreName AnotherName"));
}, "Non-allowed name policy creation throws.");

// Duplicate names test
promise_test(t => {
let p = Promise.resolve()
.then(promise_violation(t, "trusted-types SomeName JustOneMoreName AnotherName"));

promise_test(async t => {
let policy = window.trustedTypes.createPolicy('AnotherName', { createHTML: s => s } );
assert_equals(policy.name, 'AnotherName');

assert_throws_js(TypeError, _ => {
window.trustedTypes.createPolicy('AnotherName', { createHTML: s => s } );
});
return p;
let violation = await trusted_type_violation_for(TypeError, _ =>
window.trustedTypes.createPolicy('AnotherName', { createHTML: s => s } )
);
assert_true(violation.originalPolicy.includes("trusted-types SomeName JustOneMoreName AnotherName"));
}, "Duplicate name policy creation throws.");
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,35 @@
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="./support/csp-violations.js"></script>
</head>
<body>
<script>

function promise_violation(filter_arg) {
return _ => new Promise((resolve, reject) => {
function handler(e) {
let matches = (filter_arg instanceof Function)
? filter_arg(e)
: (e.originalPolicy.includes(filter_arg));
if (matches) {
document.removeEventListener("securitypolicyviolation", handler);
e.stopPropagation();
resolve(e);
}
}

document.addEventListener("securitypolicyviolation", handler);
});
}

promise_test(t => {
let p = Promise.resolve()
.then(promise_violation("require-trusted-types-for 'script'"));

promise_test(async t => {
d = document.createElement("div");
d.innerHTML = "a";
let violation = await trusted_type_violation_without_exception_for(_ =>
d.innerHTML = "a"
);
assert_true(violation.originalPolicy.includes("require-trusted-types-for 'script'"));
assert_equals("a", d.innerHTML);
return p;
}, "Require trusted types for 'script' block create HTML.");

promise_test(t => {
let p = Promise.resolve()
.then(promise_violation("require-trusted-types-for 'script'"));

promise_test(async t => {
d = document.createElement("script");
d.innerText = "a";
let violation = await trusted_type_violation_without_exception_for(_ =>
d.innerText = "a"
);
assert_true(violation.originalPolicy.includes("require-trusted-types-for 'script'"));
assert_equals("a", d.innerText);
return p;
}, "Require trusted types for 'script' block create script.");

promise_test(t => {
let p = Promise.resolve()
.then(promise_violation("require-trusted-types-for 'script'"));

promise_test(async t => {
s = document.createElement("script");
s.src = "a";
let violation = await trusted_type_violation_without_exception_for(_ =>
s.src = "a"
);
assert_true(violation.originalPolicy.includes("require-trusted-types-for 'script'"));
assert_true(s.src.includes("/trusted-types/a"));
return p;
}, "Require trusted types for 'script' block create script URL.");

promise_test(t => {
Expand All @@ -68,4 +48,4 @@
resolve();
});
}, "Set require trusted types for 'script' without CSP for trusted types don't block policy creation and using.");
</script>
</script>
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'
Content-Security-Policy: object-src 'none'
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,37 @@
<head>
<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'">
<meta http-equiv="Content-Security-Policy" content="object-src 'none'">
</head>
<body>
<script>

function promise_violation(filter_arg) {
return _ => new Promise((resolve, reject) => {
function handler(e) {
let matches = (filter_arg instanceof Function)
? filter_arg(e)
: (e.originalPolicy.includes(filter_arg));
if (matches) {
document.removeEventListener("securitypolicyviolation", handler);
e.stopPropagation();
resolve(e);
}
}

document.addEventListener("securitypolicyviolation", handler);
});
}

promise_test(t => {
let p = Promise.resolve()
.then(promise_violation("require-trusted-types-for 'script'"));
promise_test(async t => {
d = document.createElement("div");
assert_throws_js(TypeError,
_ => {
d.innerHTML = "a";
});
let violation = await trusted_type_violation_for(TypeError, _ =>
d.innerHTML = "a"
);
assert_true(violation.originalPolicy.includes("require-trusted-types-for 'script'"));
assert_equals("", d.innerHTML);
return p;
}, "Require trusted types for 'script' block create HTML.");

promise_test(t => {
let p = Promise.resolve()
.then(promise_violation("require-trusted-types-for 'script'"));
promise_test(async t => {
d = document.createElement("script");
assert_throws_js(TypeError,
_ => {
d.innerText = "a";
});
let violation = await trusted_type_violation_for(TypeError, _ =>
d.innerText = "a"
);
assert_true(violation.originalPolicy.includes("require-trusted-types-for 'script'"));
assert_equals("", d.innerText);
return p;
}, "Require trusted types for 'script' block create script.");

promise_test(t => {
let p = Promise.resolve()
.then(promise_violation("require-trusted-types-for 'script'"));
promise_test(async t => {
s = document.createElement("script");
assert_throws_js(TypeError,
_ => {
s.src = "a";
});
let violation = await trusted_type_violation_for(TypeError, _ =>
s.src = "a"
);
assert_true(violation.originalPolicy.includes("require-trusted-types-for 'script'"));
assert_equals("", s.src);
return p;
}, "Require trusted types for 'script' block create script URL.");

promise_test(t => {
Expand Down
70 changes: 70 additions & 0 deletions testing/web-platform/tests/trusted-types/support/csp-violations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const cspDirectives = [
// https://w3c.github.io/trusted-types/dist/spec/#require-trusted-types-for-csp-directive
"require-trusted-types-for",
// https://w3c.github.io/trusted-types/dist/spec/#trusted-types-csp-directive
"trusted-types",
// https://w3c.github.io/webappsec-csp/#script-src
"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.
function trusted_type_violations_and_exception_for(fn) {
return new Promise((resolve, reject) => {
// Listen for security policy violations.
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);
} else {
reject(`Unexpected violation for directive ${e.effectiveDirective}`);
}
}
document.addEventListener("securitypolicyviolation", handler);

// Run the specified function and record any exception.
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);
});
}

// Helper function when we expect one violation and exception.
async function trusted_type_violation_for(expectedException, fn) {
let {violations, exception} =
await trusted_type_violations_and_exception_for(fn);
assert_equals(violations.length, 1, "a single violation reported");
assert_true(exception instanceof expectedException, `${expectedException.prototype} exception reported`);
return violations[0];
}

// Helper function when we expect no violation or exception.
async function no_trusted_type_violation_for(fn) {
let {violations, exception} =
await trusted_type_violations_and_exception_for(fn);
assert_equals(violations.length, 0, "no violation reported");
assert_equals(exception, null, "no exception thrown");
}

async function trusted_type_violation_without_exception_for(fn) {
let {violations, exception} =
await trusted_type_violations_and_exception_for(fn);
assert_equals(violations.length, 1, "a single violation reported");
assert_equals(exception, null, "no exception thrown");
return violations[0];
}

This file was deleted.

Loading

0 comments on commit a0d8a57

Please sign in to comment.