diff --git a/content-security-policy/report-hash/default-src.https.window.js b/content-security-policy/report-hash/default-src.https.window.js
new file mode 100644
index 00000000000000..c8edc03fd3fe61
--- /dev/null
+++ b/content-security-policy/report-hash/default-src.https.window.js
@@ -0,0 +1,3 @@
+// META: script=/reporting/resources/report-helper.js
+// META: script=resources/report-hash-test-runner.sub.js
+run_tests();
diff --git a/content-security-policy/report-hash/default-src.https.window.js.sub.headers b/content-security-policy/report-hash/default-src.https.window.js.sub.headers
new file mode 100644
index 00000000000000..3566b037308b70
--- /dev/null
+++ b/content-security-policy/report-hash/default-src.https.window.js.sub.headers
@@ -0,0 +1,3 @@
+Reporting-Endpoints: csp-endpoint="/reporting/resources/report.py?reportID={{$id:uuid()}}"
+Content-Security-Policy: default-src 'self' {{hosts[alt][www]}}:{{ports[https][0]}} 'unsafe-inline' 'report-sha256'; report-to csp-endpoint
+Server-Timing: uuid;desc="{{$id}}",hash;desc="sha256-1XF/E08XndkoxwN6eIa5J89hYn3OVZ/UyB8BrU5jgzk="
diff --git a/content-security-policy/report-hash/multiple-policies.https.sub.html b/content-security-policy/report-hash/multiple-policies.https.sub.html
new file mode 100644
index 00000000000000..ae4f88864d8266
--- /dev/null
+++ b/content-security-policy/report-hash/multiple-policies.https.sub.html
@@ -0,0 +1,18 @@
+
+
+
+
+ Test that reports for same-origin subresources are sent with hashes
+
+
+
+
+
+
+
+
+
+
+
diff --git a/content-security-policy/report-hash/multiple-policies.https.sub.html.sub.headers b/content-security-policy/report-hash/multiple-policies.https.sub.html.sub.headers
new file mode 100644
index 00000000000000..17c313da488b4a
--- /dev/null
+++ b/content-security-policy/report-hash/multiple-policies.https.sub.html.sub.headers
@@ -0,0 +1,7 @@
+Reporting-Endpoints: csp-endpoint="/reporting/resources/report.py?reportID={{$id:uuid()}}"
+Content-Security-Policy: script-src-elem 'self' {{hosts[alt][www]}}:{{ports[https][0]}} 'unsafe-inline' 'report-sha256'; report-to csp-endpoint
+Reporting-Endpoints: csp-endpoint2="/reporting/resources/report.py?reportID={{$id2:uuid()}}"
+Content-Security-Policy: script-src-elem 'self' {{hosts[alt][www]}}:{{ports[https][0]}} 'unsafe-inline' 'report-sha512'; report-to csp-endpoint2
+Server-Timing: uuid;desc="{{$id}}",hash;desc="sha256-1XF/E08XndkoxwN6eIa5J89hYn3OVZ/UyB8BrU5jgzk="
+Server-Timing: uuid2;desc="{{$id2}}",hash2;desc="sha512-hG4x56V5IhUUepZdYU/lX7UOQJ2M7f6ud2EI7os4JV3OwXSZ002P3zkb9tXQkjpOO8UbtjuEufvdcU67Qt2tlw=="
+
diff --git a/content-security-policy/report-hash/reportonly-default-src.https.window.js b/content-security-policy/report-hash/reportonly-default-src.https.window.js
new file mode 100644
index 00000000000000..c8edc03fd3fe61
--- /dev/null
+++ b/content-security-policy/report-hash/reportonly-default-src.https.window.js
@@ -0,0 +1,3 @@
+// META: script=/reporting/resources/report-helper.js
+// META: script=resources/report-hash-test-runner.sub.js
+run_tests();
diff --git a/content-security-policy/report-hash/reportonly-default-src.https.window.js.sub.headers b/content-security-policy/report-hash/reportonly-default-src.https.window.js.sub.headers
new file mode 100644
index 00000000000000..0490f22ed47e0a
--- /dev/null
+++ b/content-security-policy/report-hash/reportonly-default-src.https.window.js.sub.headers
@@ -0,0 +1,3 @@
+Reporting-Endpoints: csp-endpoint="/reporting/resources/report.py?reportID={{$id:uuid()}}"
+Content-Security-Policy-Report-Only: default-src 'self' {{hosts[alt][www]}}:{{ports[https][0]}} 'unsafe-inline' 'report-sha256'; report-to csp-endpoint
+Server-Timing: uuid;desc="{{$id}}",hash;desc="sha256-1XF/E08XndkoxwN6eIa5J89hYn3OVZ/UyB8BrU5jgzk="
diff --git a/content-security-policy/report-hash/reportonly-script-src-elem.https.window.js b/content-security-policy/report-hash/reportonly-script-src-elem.https.window.js
new file mode 100644
index 00000000000000..c8edc03fd3fe61
--- /dev/null
+++ b/content-security-policy/report-hash/reportonly-script-src-elem.https.window.js
@@ -0,0 +1,3 @@
+// META: script=/reporting/resources/report-helper.js
+// META: script=resources/report-hash-test-runner.sub.js
+run_tests();
diff --git a/content-security-policy/report-hash/reportonly-script-src-elem.https.window.js.sub.headers b/content-security-policy/report-hash/reportonly-script-src-elem.https.window.js.sub.headers
new file mode 100644
index 00000000000000..b7929547a09084
--- /dev/null
+++ b/content-security-policy/report-hash/reportonly-script-src-elem.https.window.js.sub.headers
@@ -0,0 +1,3 @@
+Reporting-Endpoints: csp-endpoint="/reporting/resources/report.py?reportID={{$id:uuid()}}"
+Content-Security-Policy-Report-Only: script-src-elem 'self' {{hosts[alt][www]}}:{{ports[https][0]}} 'unsafe-inline' 'report-sha256'; report-to csp-endpoint
+Server-Timing: uuid;desc="{{$id}}",hash;desc="sha256-1XF/E08XndkoxwN6eIa5J89hYn3OVZ/UyB8BrU5jgzk="
diff --git a/content-security-policy/report-hash/reportonly-script-src-none.https.window.js b/content-security-policy/report-hash/reportonly-script-src-none.https.window.js
new file mode 100644
index 00000000000000..d708e989b70c5a
--- /dev/null
+++ b/content-security-policy/report-hash/reportonly-script-src-none.https.window.js
@@ -0,0 +1,4 @@
+// META: script=/reporting/resources/report-helper.js
+// META: script=resources/report-hash-test-runner.sub.js
+run_tests();
+
diff --git a/content-security-policy/report-hash/reportonly-script-src-none.https.window.js.sub.headers b/content-security-policy/report-hash/reportonly-script-src-none.https.window.js.sub.headers
new file mode 100644
index 00000000000000..9c6d3efd27489a
--- /dev/null
+++ b/content-security-policy/report-hash/reportonly-script-src-none.https.window.js.sub.headers
@@ -0,0 +1,4 @@
+Reporting-Endpoints: csp-endpoint="/reporting/resources/report.py?reportID={{$id:uuid()}}"
+Content-Security-Policy-Report-Only: script-src 'none' {{hosts[alt][www]}}:{{ports[https][0]}} 'unsafe-inline' 'report-sha256'; report-to csp-endpoint
+Server-Timing: uuid;desc="{{$id}}",hash;desc="sha256-1XF/E08XndkoxwN6eIa5J89hYn3OVZ/UyB8BrU5jgzk="
+
diff --git a/content-security-policy/report-hash/reportonly-script-src.https.window.js b/content-security-policy/report-hash/reportonly-script-src.https.window.js
new file mode 100644
index 00000000000000..c8edc03fd3fe61
--- /dev/null
+++ b/content-security-policy/report-hash/reportonly-script-src.https.window.js
@@ -0,0 +1,3 @@
+// META: script=/reporting/resources/report-helper.js
+// META: script=resources/report-hash-test-runner.sub.js
+run_tests();
diff --git a/content-security-policy/report-hash/reportonly-script-src.https.window.js.sub.headers b/content-security-policy/report-hash/reportonly-script-src.https.window.js.sub.headers
new file mode 100644
index 00000000000000..ee7eb6f7f53b42
--- /dev/null
+++ b/content-security-policy/report-hash/reportonly-script-src.https.window.js.sub.headers
@@ -0,0 +1,3 @@
+Reporting-Endpoints: csp-endpoint="/reporting/resources/report.py?reportID={{$id:uuid()}}"
+Content-Security-Policy-Report-Only: script-src 'self' {{hosts[alt][www]}}:{{ports[https][0]}} 'unsafe-inline' 'report-sha256'; report-to csp-endpoint
+Server-Timing: uuid;desc="{{$id}}",hash;desc="sha256-1XF/E08XndkoxwN6eIa5J89hYn3OVZ/UyB8BrU5jgzk="
diff --git a/content-security-policy/report-hash/resources/report-hash-test-runner.sub.js b/content-security-policy/report-hash/resources/report-hash-test-runner.sub.js
new file mode 100644
index 00000000000000..f336cf14bfb586
--- /dev/null
+++ b/content-security-policy/report-hash/resources/report-hash-test-runner.sub.js
@@ -0,0 +1,129 @@
+function find_server_timing(name) {
+ const server_timing = performance.getEntriesByType("navigation")[0].serverTiming;
+ for (entry of server_timing) {
+ if (entry.name == name) {
+ return entry.description;
+ }
+ }
+ return null;
+}
+
+const ORIGIN = "https://{{host}}:{{ports[https][0]}}";
+const REMOTE_ORIGIN = "https://{{hosts[alt][www]}}:{{ports[https][0]}}";
+const endpoint = `${ORIGIN}/reporting/resources/report.py`;
+const id = find_server_timing("uuid");
+const id2 = find_server_timing("uuid2");
+const subresource_url = `${ORIGIN}/reporting/resources/comment.js`;
+const crossorigin_subresource_url = `${REMOTE_ORIGIN}/reporting/resources/comment.js`;
+const subresource_hash = find_server_timing("hash");
+const subresource_hash2 = find_server_timing("hash2");
+let counter = 0;
+
+function reporting_observer_setup(expected_url, expected_hash) {
+ return new Promise(resolve => {
+ new ReportingObserver((reports, observer) => {
+ assert_unreached();
+ observer.disconnect();
+ }, { types: ["csp-hash"] }).observe();
+ step_timeout(resolve, 100);
+ });
+}
+
+async function check_reports(uuid, expected_hash, url) {
+ const reports = await pollReports(endpoint, uuid);
+ checkReportExists(reports, 'csp-hash', location.href);
+ const report = getReport(reports, 'csp-hash', location.href, url);
+ assert_not_equals(report, null);
+ assert_equals(report.body.hash, expected_hash);
+ assert_equals(report.body.type, "subresource");
+ assert_equals(report.body.destination, "script");
+}
+
+function report_hash_test(url, populate_script_attributes, expected_hash, expected_hash2, description) {
+ promise_test(async t => {
+ const unique_subresource_url = `${url}?${++counter}`;
+ const observer_promise = reporting_observer_setup(unique_subresource_url, subresource_hash);
+ // Trigger a script load
+ await new Promise(resolve => {
+ const script = document.createElement('script');
+ script.src = unique_subresource_url;
+ populate_script_attributes(script);
+ script.addEventListener('load', resolve);
+ document.head.appendChild(script);
+ });
+
+ await check_reports(id, expected_hash, unique_subresource_url);
+ if (id2) {
+ await check_reports(id2, expected_hash2, unique_subresource_url);
+ }
+ await observer_promise;
+ }, description);
+}
+
+function no_report_test(create_element, description) {
+ promise_test(async t => {
+ const unique_subresource_url = `${subresource_url}?${++counter}`;
+ // Trigger a script load
+ await new Promise(resolve => {
+ const elem = create_element(unique_subresource_url);
+ elem.addEventListener('load', resolve);
+ elem.addEventListener('error', resolve);
+ document.head.appendChild(elem);
+ });
+
+ // Wait for report to be received.
+ const reports = await pollReports(endpoint, id);
+ const report = getReport(reports, 'csp-hash', location.href, unique_subresource_url);
+ assert_equals(report, null);
+ }, description);
+};
+
+function run_tests() {
+ report_hash_test(subresource_url, script => {
+ script.crossOrigin = "anonymous";
+ }, subresource_hash, subresource_hash2,
+ "Reporting endpoints received hash for same-origin CORS script.");
+
+ report_hash_test(subresource_url, script => {
+ }, subresource_hash, subresource_hash2,
+ "Reporting endpoints received hash for same-origin no-CORS script.");
+
+ report_hash_test(crossorigin_subresource_url, script => {
+ script.crossOrigin = "anonymous";
+ }, subresource_hash, subresource_hash2,
+ "Reporting endpoints received hash for cross-origin CORS script.");
+
+ report_hash_test(crossorigin_subresource_url, script => {
+ }, /*expected_hash=*/"", /*expected_hash2=*/"",
+ "Reporting endpoints received no hash for cross-origin no-CORS script.");
+
+ report_hash_test(subresource_url, script => {
+ script.crossOrigin = "anonymous";
+ script.integrity = "sha512-hG4x56V5IhUUepZdYU/lX7UOQJ2M7f6ud2EI7os4JV3OwXSZ002P3zkb9tXQkjpOO8UbtjuEufvdcU67Qt2tlw==";
+ }, subresource_hash, subresource_hash2,
+ "Reporting endpoints received the right hash for same-origin CORS script with integrity.");
+
+ no_report_test(url => {
+ const script = document.createElement('script');
+ script.src = url;
+ script.crossOrigin = "anonymous"
+ script.integrity = "sha256-foobar";
+ return script;
+ }, "Reporting endpoints received no report for failed integrity check with sha256.");
+
+ no_report_test(url => {
+ const script = document.createElement('script');
+ script.src = url;
+ script.crossOrigin = "anonymous"
+ script.integrity = "sha512-foobar";
+ return script;
+ }, "Reporting endpoints received no report for failed integrity check with sha512.");
+
+ no_report_test(url => {
+ const link = document.createElement('link');
+ link.href = url;
+ link.crossOrigin = "anonymous"
+ link.rel = "stylesheet"
+ return link;
+ }, "Reporting endpoints received no report for CORS stylesheet.");
+}
diff --git a/content-security-policy/report-hash/script-src-elem.https.window.js b/content-security-policy/report-hash/script-src-elem.https.window.js
new file mode 100644
index 00000000000000..c8edc03fd3fe61
--- /dev/null
+++ b/content-security-policy/report-hash/script-src-elem.https.window.js
@@ -0,0 +1,3 @@
+// META: script=/reporting/resources/report-helper.js
+// META: script=resources/report-hash-test-runner.sub.js
+run_tests();
diff --git a/content-security-policy/report-hash/script-src-elem.https.window.js.sub.headers b/content-security-policy/report-hash/script-src-elem.https.window.js.sub.headers
new file mode 100644
index 00000000000000..d9c04180310c85
--- /dev/null
+++ b/content-security-policy/report-hash/script-src-elem.https.window.js.sub.headers
@@ -0,0 +1,3 @@
+Reporting-Endpoints: csp-endpoint="/reporting/resources/report.py?reportID={{$id:uuid()}}"
+Content-Security-Policy: script-src-elem 'self' {{hosts[alt][www]}}:{{ports[https][0]}} 'unsafe-inline' 'report-sha256'; report-to csp-endpoint
+Server-Timing: uuid;desc="{{$id}}",hash;desc="sha256-1XF/E08XndkoxwN6eIa5J89hYn3OVZ/UyB8BrU5jgzk="
diff --git a/content-security-policy/report-hash/script-src-sha512.https.window.js b/content-security-policy/report-hash/script-src-sha512.https.window.js
new file mode 100644
index 00000000000000..c8edc03fd3fe61
--- /dev/null
+++ b/content-security-policy/report-hash/script-src-sha512.https.window.js
@@ -0,0 +1,3 @@
+// META: script=/reporting/resources/report-helper.js
+// META: script=resources/report-hash-test-runner.sub.js
+run_tests();
diff --git a/content-security-policy/report-hash/script-src-sha512.https.window.js.sub.headers b/content-security-policy/report-hash/script-src-sha512.https.window.js.sub.headers
new file mode 100644
index 00000000000000..eaa0873582b78a
--- /dev/null
+++ b/content-security-policy/report-hash/script-src-sha512.https.window.js.sub.headers
@@ -0,0 +1,3 @@
+Reporting-Endpoints: csp-endpoint="/reporting/resources/report.py?reportID={{$id:uuid()}}"
+Content-Security-Policy: script-src 'self' {{hosts[alt][www]}}:{{ports[https][0]}} 'unsafe-inline' 'report-sha512' 'report-sha384' 'report-sha256'; report-to csp-endpoint
+Server-Timing: uuid;desc="{{$id}}",hash;desc="sha512-hG4x56V5IhUUepZdYU/lX7UOQJ2M7f6ud2EI7os4JV3OwXSZ002P3zkb9tXQkjpOO8UbtjuEufvdcU67Qt2tlw=="
diff --git a/content-security-policy/report-hash/script-src.https.window.js b/content-security-policy/report-hash/script-src.https.window.js
new file mode 100644
index 00000000000000..c8edc03fd3fe61
--- /dev/null
+++ b/content-security-policy/report-hash/script-src.https.window.js
@@ -0,0 +1,3 @@
+// META: script=/reporting/resources/report-helper.js
+// META: script=resources/report-hash-test-runner.sub.js
+run_tests();
diff --git a/content-security-policy/report-hash/script-src.https.window.js.sub.headers b/content-security-policy/report-hash/script-src.https.window.js.sub.headers
new file mode 100644
index 00000000000000..bd92f728c858fb
--- /dev/null
+++ b/content-security-policy/report-hash/script-src.https.window.js.sub.headers
@@ -0,0 +1,3 @@
+Reporting-Endpoints: csp-endpoint="/reporting/resources/report.py?reportID={{$id:uuid()}}"
+Content-Security-Policy: script-src 'self' {{hosts[alt][www]}}:{{ports[https][0]}} 'unsafe-inline' 'report-sha256'; report-to csp-endpoint
+Server-Timing: uuid;desc="{{$id}}",hash;desc="sha256-1XF/E08XndkoxwN6eIa5J89hYn3OVZ/UyB8BrU5jgzk="
diff --git a/reporting/resources/comment.js b/reporting/resources/comment.js
new file mode 100644
index 00000000000000..7fa15a06da7798
--- /dev/null
+++ b/reporting/resources/comment.js
@@ -0,0 +1 @@
+// Ceci n'est pas un commentaire
diff --git a/reporting/resources/comment.js.headers b/reporting/resources/comment.js.headers
new file mode 100644
index 00000000000000..cb762eff806849
--- /dev/null
+++ b/reporting/resources/comment.js.headers
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: *
diff --git a/reporting/resources/report-helper.js b/reporting/resources/report-helper.js
index 4d3c3fbdee800c..5b5438903de11c 100644
--- a/reporting/resources/report-helper.js
+++ b/reporting/resources/report-helper.js
@@ -36,3 +36,12 @@ function checkReportExists(reports, type, url) {
}
assert_unreached(`A report of ${type} from ${url} is not found.`);
}
+
+function getReport(reports, type, document_url, subresource_url) {
+ for (const report of reports) {
+ if (report.type !== type) continue;
+ if (report.body.documentURL === document_url && report.body.subresourceURL == subresource_url) return report;
+ }
+ return null;
+}
+