Skip to content

Commit

Permalink
Origin policy: update CSP parsing to match the latest spec
Browse files Browse the repository at this point in the history
This updates the parsing of the feature policy parts of the origin policy manifest to mostly match the latest spec draft at https://wicg.github.io/origin-policy/, in particular https://wicg.github.io/origin-policy/#parsing. That is, it moves away from "content-security-policy": [{ "policy": "string", "report-only": boolean }] to "content_security": { "policies": ["...CSP strings"], "policies_report_only": ["...CSP strings"] }.

Additionally, it removes the failure on parsing errors, as those are no longer in the spec.

This does not yet properly parse the CSP string as a CSP; instead it still treats it as a header (so, commas are allowed inside). A failing test is added for that case, which will be addressed in a followup CL.

Bug: 751996
Change-Id: I8d14815b486afd4a5622bc4b25874c81418fd38c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1977148
Commit-Queue: Domenic Denicola <domenic@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Reviewed-by: Daniel Vogelheim <vogelheim@chromium.org>
Cr-Commit-Position: refs/heads/master@{#732666}
  • Loading branch information
domenic authored and chromium-wpt-export-bot committed Jan 17, 2020
1 parent 87107b4 commit d2251f7
Show file tree
Hide file tree
Showing 39 changed files with 300 additions and 38 deletions.
@@ -0,0 +1,5 @@
{
"content_security": {
"policies": ["script-src 'self' 'unsafe-inline', img-src 'none'"]
}
}
@@ -0,0 +1,8 @@
{
"content_security": {
"policies": ["script-src 'self' 'unsafe-inline'"]
},
"content_security": {
"policies": ["img-src 'none'"]
}
}
@@ -0,0 +1,6 @@
{
"content_security": {
"policies": ["script-src 'self' 'unsafe-inline'"],
"policies": ["img-src 'none'"]
}
}
5 changes: 5 additions & 0 deletions .well-known/origin-policy/policy-content-security-noimg
@@ -0,0 +1,5 @@
{
"content_security": {
"policies": ["img-src 'none'"]
}
}
@@ -0,0 +1,5 @@
{
"content_security": {
"policies_report_only": ["img-src 'none'"]
}
}
5 changes: 5 additions & 0 deletions .well-known/origin-policy/policy-content-security-non-array
@@ -0,0 +1,5 @@
{
"content_security": {
"policies": "script-src 'self' 'unsafe-inline'"
}
}
3 changes: 3 additions & 0 deletions .well-known/origin-policy/policy-content-security-non-object
@@ -0,0 +1,3 @@
{
"content_security": ["script-src 'self' 'unsafe-inline'"]
}
5 changes: 5 additions & 0 deletions .well-known/origin-policy/policy-content-security-non-string
@@ -0,0 +1,5 @@
{
"content_security": {
"policies": [["script-src 'self' 'unsafe-inline'"]]
}
}
5 changes: 5 additions & 0 deletions .well-known/origin-policy/policy-content-security-valid
@@ -0,0 +1,5 @@
{
"content_security": {
"policies": ["script-src 'self' 'unsafe-inline'"]
}
}
@@ -0,0 +1,5 @@
{
"content_security": {
"policies": ["script-src 'self' 'unsafe-inline'", "img-src 'none'"]
}
}
@@ -0,0 +1,5 @@
{
"content_security": {
"policies": ["script-src 'self' 'unsafe-inline'; img-src 'none'"]
}
}
4 changes: 3 additions & 1 deletion .well-known/origin-policy/policy-csp-1
@@ -1,3 +1,5 @@
{
"content-security-policy": [{ "policy": "script-src 'self' 'unsafe-inline'" }]
"content_security": {
"policies": ["script-src 'self' 'unsafe-inline'"]
}
}
6 changes: 3 additions & 3 deletions .well-known/origin-policy/policy-csp-2
@@ -1,5 +1,5 @@
{
"content-security-policy": [{
"policy": "script-src 'self' 'nonce-test'"
}]
"content_security": {
"policies": ["script-src 'self' 'nonce-test'"]
}
}
3 changes: 0 additions & 3 deletions .well-known/origin-policy/policy-noimg

This file was deleted.

11 changes: 11 additions & 0 deletions origin-policy/content-security/comma-in-policy.https.html
@@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>Commas in "content_security/policy" cause parse errors and thus no CSP</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="helper.js"></script>

<script>
"use strict";
runCSPTest({ unsafeEval: true });
</script>
@@ -0,0 +1 @@
Sec-Origin-Policy: policy=policy-content-security-comma-in-policy
13 changes: 13 additions & 0 deletions origin-policy/content-security/double-content-security.https.html
@@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>Of two "content_security" items only the second counts</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="helper.js"></script>

<body>

<script>
"use strict";
runCSPTest({ unsafeEval: true, img: false });
</script>
@@ -0,0 +1 @@
Sec-Origin-Policy: policy=policy-content-security-double-content-security
13 changes: 13 additions & 0 deletions origin-policy/content-security/double-policies.https.html
@@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>Of two "content_security/policies" items only the second counts</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="helper.js"></script>

<body>

<script>
"use strict";
runCSPTest({ unsafeEval: true, img: false });
</script>
@@ -0,0 +1 @@
Sec-Origin-Policy: policy=policy-content-security-double-policies
73 changes: 73 additions & 0 deletions origin-policy/content-security/helper.js
@@ -0,0 +1,73 @@
window.waitForOneSecurityPolicyViolationEvent = expectedBlockedURI => {
return new Promise(resolve => {
let eventCount = 0;
let blockedURI = null;

document.addEventListener("securitypolicyviolation", e => {
++eventCount;
blockedURI = e.blockedURI;

// We want to test that only one event is fired, but we want to do so
// without waiting indefinitely. By waiting for one tick, we at least
// ensure that there's no bug that leads to two securitypolicyviolation
// events being fired at the same time, as a result of the one violation.
step_timeout(() => {
assert_equals(eventCount, 1);
resolve(blockedURI);
});
});
});
};

window.waitForImgFail = imgSrc => {
return new Promise((resolve, reject) => {
const img = document.createElement("img");
img.onload = () => reject(new Error("Must not load the image"));
img.onerror = () => resolve();

img.src = imgSrc;
document.body.append(img);
});
};


window.waitForImgSuccess = imgSrc => {
return new Promise((resolve, reject) => {
const img = document.createElement("img");
img.onload = () => resolve();
img.onerror = () => reject(new Error("Must load the image"));

img.src = imgSrc;
document.body.append(img);
});
};

// Both params are optional; if they are not given as booleans then we will not test that aspect.
window.runCSPTest = ({ unsafeEval, img }) => {
if (unsafeEval === true) {
test(() => {
eval("window.evalAllowed = true;");
assert_equals(window.evalAllowed, true);
}, "eval must be allowed");
} else if (unsafeEval === false) {
test(() => {
try {
eval("window.evalAllowed = true;");
} catch (e) { }

assert_equals(window.evalAllowed, undefined);
}, "eval must be disallowed");
}

if (img === true) {
promise_test(
() => waitForImgSuccess("/common/security-features/subresource/image.py"),
"img loading must be allowed"
);
} else if (img === false) {
promise_test(
() => waitForImgFail("/common/security-features/subresource/image.py"),
"img loading must be disallowed"
);
}
};
11 changes: 11 additions & 0 deletions origin-policy/content-security/non-array.https.html
@@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>Non-array "content_security/policies" member must be ignored</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="helper.js"></script>

<script>
"use strict";
runCSPTest({ unsafeEval: true });
</script>
@@ -0,0 +1 @@
Sec-Origin-Policy: policy=policy-content-security-non-array
11 changes: 11 additions & 0 deletions origin-policy/content-security/non-object.https.html
@@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>Non-object "content_security" member must be ignored</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="helper.js"></script>

<script>
"use strict";
runCSPTest({ unsafeEval: true });
</script>
@@ -0,0 +1 @@
Sec-Origin-Policy: policy=policy-content-security-non-object
11 changes: 11 additions & 0 deletions origin-policy/content-security/non-string.https.html
@@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>Non-string "content_security/policies" array member must be ignored</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="helper.js"></script>

<script>
"use strict";
runCSPTest({ unsafeEval: true });
</script>
@@ -0,0 +1 @@
Sec-Origin-Policy: policy=policy-content-security-non-string
@@ -0,0 +1,23 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>CSP via origin policy must trigger a securitypolicyviolation event even when the CSP is report-only</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="helper.js"></script>

<body>

<script>
"use strict";

promise_test(() => {
const imgURL = (new URL("/common/security-features/subresource/image.py", document.location)).href;

return Promise.all([
waitForOneSecurityPolicyViolationEvent(imgURL).then(blockedURI => {
assert_equals(blockedURI, imgURL);
}),
waitForImgSuccess(imgURL)
]);
});
</script>
@@ -0,0 +1 @@
Sec-Origin-Policy: policy=policy-content-security-noimg-report-only
23 changes: 23 additions & 0 deletions origin-policy/content-security/trigger-violation-report.https.html
@@ -0,0 +1,23 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>CSP via origin policy must trigger a securitypolicyviolation event</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="helper.js"></script>

<body>

<script>
"use strict";

promise_test(() => {
const imgURL = (new URL("/common/security-features/subresource/image.py", document.location)).href;

return Promise.all([
waitForOneSecurityPolicyViolationEvent(imgURL).then(blockedURI => {
assert_equals(blockedURI, imgURL);
}),
waitForImgFail(imgURL)
]);
});
</script>
@@ -0,0 +1 @@
Sec-Origin-Policy: policy=policy-content-security-noimg
@@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>"content_security/policy" can contain multiple array items to enforce multiple CSPs</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="helper.js"></script>

<body>

<script>
"use strict";
runCSPTest({ unsafeEval: false, img: false });
</script>
@@ -0,0 +1 @@
Sec-Origin-Policy: policy=policy-content-security-valid-with-semicolon
13 changes: 13 additions & 0 deletions origin-policy/content-security/valid-with-semicolon.https.html
@@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>"content_security/policy" array items can contain semicolons to enforce multiple CSP directives</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="helper.js"></script>

<body>

<script>
"use strict";
runCSPTest({ unsafeEval: false, img: false });
</script>
@@ -0,0 +1 @@
Sec-Origin-Policy: policy=policy-content-security-valid-with-semicolon
11 changes: 11 additions & 0 deletions origin-policy/content-security/valid.https.html
@@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>Valid "content_security" member disallows eval</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="helper.js"></script>

<script>
"use strict";
runCSPTest({ unsafeEval: false });
</script>
1 change: 1 addition & 0 deletions origin-policy/content-security/valid.https.html.headers
@@ -0,0 +1 @@
Sec-Origin-Policy: policy=policy-content-security-valid
30 changes: 0 additions & 30 deletions origin-policy/origin-policy-single-report.https.tentative.html

This file was deleted.

This file was deleted.

0 comments on commit d2251f7

Please sign in to comment.