NEL report for subresource sxg header integrity mismatch
When there is a mismatch between the 'header-integrity' value of
'allowed-alt-sxg' link header of the cached main resource and the header
integrity value of the cached subresource, Chrome will send a NEL report
with "sxg.header_integrity_mismatch" type when navigating to the main
resource if NEL reporting was setup.
Bug: 1025074
Change-Id: I7c0ae0904a211e11b72bae95cfe2274eb9c5550a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2237212
Reviewed-by: Kunihiko Sakamoto <ksakamoto@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Commit-Queue: Tsuyoshi Horo <horo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#788891}
diff --git a/signed-exchange/resources/generate-test-sxgs.sh b/signed-exchange/resources/generate-test-sxgs.sh
index 73c2080..e3988ce 100755
--- a/signed-exchange/resources/generate-test-sxgs.sh
+++ b/signed-exchange/resources/generate-test-sxgs.sh
@@ -577,6 +577,22 @@
-miRecordSize 100 \
-responseHeader "link:<$inner_url_origin/signed-exchange/resources/sxg-subresource-script.js>;rel=allowed-alt-sxg;header-integrity=\"$header_integrity\",<$inner_url_origin/signed-exchange/resources/sxg-subresource-script.js>;rel=preload;as=script"
+# Generate the signed exchange file of signed exchange subresource test with
+# header integrity mismatch.
+gen-signedexchange \
+ -version $sxg_version \
+ -uri $inner_url_origin/signed-exchange/resources/sxg-subresource-sxg.html \
+ -status 200 \
+ -content sxg-subresource-sxg-inner.html \
+ -certificate $certfile \
+ -certUrl $cert_url_origin/signed-exchange/resources/$certfile.cbor \
+ -validityUrl $inner_url_origin/signed-exchange/resources/resource.validity.msg \
+ -privateKey $keyfile \
+ -date 2030-04-01T00:00:00Z \
+ -expire 168h \
+ -o sxg/sxg-subresource-header-integrity-mismatch.sxg \
+ -miRecordSize 100 \
+ -responseHeader "link:<$inner_url_origin/signed-exchange/resources/sxg-subresource-script.js>;rel=allowed-alt-sxg;header-integrity=\"sha256-$dummy_sha256\",<$inner_url_origin/signed-exchange/resources/sxg-subresource-script.js>;rel=preload;as=script"
# A Signed Exchange for testing prefetch.
# The id query value "XXX..." of prefetch-test-cert.py will be replaced with
diff --git a/signed-exchange/resources/sxg-subresource-mismatch-iframe.html b/signed-exchange/resources/sxg-subresource-mismatch-iframe.html
new file mode 100644
index 0000000..f05fcc9
--- /dev/null
+++ b/signed-exchange/resources/sxg-subresource-mismatch-iframe.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<body>
+<script>
+(async () => {
+ const sxg_path = 'sxg/sxg-subresource-header-integrity-mismatch.sxg';
+ const scipt_sxg_path = 'sxg/sxg-subresource-script.sxg';
+ const scipt_path = 'sxg-subresource-script.js';
+ const wait_for_prefetch = new Promise((resolve) => {
+ new PerformanceObserver((list) => {
+ for (let e of list.getEntries()) {
+ if (e.name.endsWith(scipt_sxg_path)) {
+ resolve();
+ } else if (e.name.endsWith(scipt_path)) {
+ window.parent.postMessage(
+ scipt_path + ' should not be prefetched', '*');
+ }
+ }
+ }).observe({ entryTypes: ['resource'] });
+ });
+
+ const link = document.createElement('link');
+ link.rel = 'prefetch';
+ link.href = sxg_path;
+ document.body.appendChild(link);
+ await wait_for_prefetch;
+ location.href = sxg_path;
+})()
+</script>
+</body>
diff --git a/signed-exchange/resources/sxg/sxg-subresource-header-integrity-mismatch.sxg b/signed-exchange/resources/sxg/sxg-subresource-header-integrity-mismatch.sxg
new file mode 100644
index 0000000..ace89dd
--- /dev/null
+++ b/signed-exchange/resources/sxg/sxg-subresource-header-integrity-mismatch.sxg
Binary files differ
diff --git a/signed-exchange/resources/sxg/sxg-subresource-header-integrity-mismatch.sxg.sub.headers b/signed-exchange/resources/sxg/sxg-subresource-header-integrity-mismatch.sxg.sub.headers
new file mode 100644
index 0000000..8f1b47e
--- /dev/null
+++ b/signed-exchange/resources/sxg/sxg-subresource-header-integrity-mismatch.sxg.sub.headers
@@ -0,0 +1 @@
+Link: <https://{{hosts[alt][]}}:{{ports[https][0]}}/signed-exchange/resources/sxg/sxg-subresource-script.sxg>;rel=alternate;type="application/signed-exchange;v=b3";anchor="https://127.0.0.1:8444/signed-exchange/resources/sxg-subresource-script.js";
diff --git a/signed-exchange/subresource/sxg-subresource-header-integrity-mismatch.tentative.html b/signed-exchange/subresource/sxg-subresource-header-integrity-mismatch.tentative.html
new file mode 100644
index 0000000..8a26b21
--- /dev/null
+++ b/signed-exchange/subresource/sxg-subresource-header-integrity-mismatch.tentative.html
@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<title>Subresource signed exchange prefetch.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/network-error-logging/support/nel.sub.js"></script>
+<script src="../resources/sxg-util.js"></script>
+<body>
+<script>
+nel_test(async t => {
+ const alt_origin = get_host_info().HTTPS_NOTSAMESITE_ORIGIN;
+ const test_origin = get_host_info().HTTPS_ORIGIN;
+ await fetchResourceWithBasicPolicy();
+ const iframe_path =
+ alt_origin +
+ '/signed-exchange/resources/sxg-subresource-mismatch-iframe.html';
+ const wait_message = (new Promise((resolve) => {
+ const on_message = (event) => {
+ window.removeEventListener('message', on_message);
+ resolve(event.data);
+ };
+ window.addEventListener('message', on_message);
+ }));
+ withIframe(iframe_path);
+ const message = await wait_message;
+ assert_equals(message, 'from server');
+ const cert_url = test_origin + '/signed-exchange/resources/127.0.0.1.sxg.pem.cbor';
+
+ const main_outer_url = alt_origin + '/signed-exchange/resources/sxg/sxg-subresource-header-integrity-mismatch.sxg';
+ const main_inner_url = innerURLOrigin() + '/signed-exchange/resources/sxg-subresource-sxg.html';
+ const sub_outer_url = alt_origin + '/signed-exchange/resources/sxg/sxg-subresource-script.sxg';
+ const sub_inner_url = innerURLOrigin() + '/signed-exchange/resources/sxg-subresource-script.js';
+ const iframe_url = alt_origin + '/signed-exchange/resources/sxg-subresource-mismatch-iframe.html';
+ assert_true(await reportsExist([
+ // Normal NEL report for the iframe's HTML.
+ {
+ url: iframe_url,
+ user_agent: navigator.userAgent,
+ type: "network-error",
+ body: {
+ phase: "application",
+ type: "ok",
+ status_code: 200,
+ referrer: location.href
+ },
+ metadata: {
+ content_type: "application/reports+json",
+ },
+ },
+ // Normal NEL report for the main resource signed exchange.
+ {
+ url: main_outer_url,
+ user_agent: navigator.userAgent,
+ type: "network-error",
+ body: {
+ phase: "application",
+ type: "ok",
+ status_code: 200,
+ referrer: iframe_url,
+ },
+ metadata: {
+ content_type: "application/reports+json",
+ },
+ },
+ // Signed Exchange NEL report for the main resource signed exchange.
+ {
+ url: main_outer_url,
+ user_agent: navigator.userAgent,
+ type: "network-error",
+ body: {
+ phase: "sxg",
+ type: "ok",
+ status_code: 200,
+ referrer: iframe_url,
+ sxg: {
+ outer_url: main_outer_url,
+ inner_url: main_inner_url,
+ cert_url: [cert_url]
+ }
+ },
+ metadata: {
+ content_type: "application/reports+json",
+ },
+ },
+ // Signed Exchange NEL report for the subresource signed exchange header
+ // integrity mismatch.
+ {
+ url: sub_outer_url,
+ user_agent: navigator.userAgent,
+ type: "network-error",
+ body: {
+ phase: "sxg",
+ type: "sxg.header_integrity_mismatch",
+ status_code: 200,
+ referrer: main_outer_url,
+ sxg: {
+ outer_url: sub_outer_url,
+ inner_url: sub_inner_url,
+ cert_url: [cert_url]
+ }
+ },
+ metadata: {
+ content_type: "application/reports+json",
+ },
+ },
+ // Normal NEL report for the main resource signed exchange.
+ {
+ url: sub_outer_url,
+ user_agent: navigator.userAgent,
+ type: "network-error",
+ body: {
+ phase: "application",
+ type: "ok",
+ status_code: 200,
+ referrer: iframe_url,
+ },
+ metadata: {
+ content_type: "application/reports+json",
+ },
+ },
+ // Signed Exchange NEL report for the sub resource signed exchange.
+ {
+ url: sub_outer_url,
+ user_agent: navigator.userAgent,
+ type: "network-error",
+ body: {
+ phase: "sxg",
+ type: "ok",
+ status_code: 200,
+ referrer: iframe_url,
+ sxg: {
+ outer_url: sub_outer_url,
+ inner_url: sub_inner_url,
+ cert_url: [cert_url]
+ }
+ },
+ metadata: {
+ content_type: "application/reports+json",
+ },
+ },
+ ]));
+}, 'Subresource signed exchange prefetch.');
+</script>
+</body>