Reland "[Credentialless] Add credentialless reporting"
This is a reland of 9787b3957b
Diff here:
https://chromium-review.googlesource.com/c/chromium/src/+/2940593/1..8
which add the report link and fix wpt tests in
`reporting-to-endpoint.https.html` and unit test in
`cross_origin_resource_policy_unittest.cc` alone with
a diff of test in
`credentialless/reporting-subresource-corp.tentative.https.html`
which related to CORP issue landed in
https://chromium-review.googlesource.com/c/chromium/src/+/2886899
Original change's description:
> [Credentialless] Add credentialless reporting
>
> This CL adds support for navigational COEP:credentialless
> requests to cross-origin-resource-policy reporting.
> When a navigational response is blocked, COEP:credentialless
> report will be sent.
>
> Bug: 1200849, 1215583
> Change-Id: I3ab8235190597b6292f99afe5daffd059435d369
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2867085
> Reviewed-by: Yifan Luo <lyf@chromium.org>
> Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org>
> Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
> Commit-Queue: Yifan Luo <lyf@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#888454}
Bug: 1200849
Change-Id: Ib1fe3721de61cbea1b9284d07bc0a09b5923f474
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2940593
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org>
Commit-Queue: Yifan Luo <lyf@chromium.org>
Cr-Commit-Position: refs/heads/master@{#890720}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
a70e32b72c
commit
a9b5392e1a
services/network/public/cpp
third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy
@ -238,8 +238,11 @@ absl::optional<mojom::BlockedByResponseReason> IsBlockedInternalWithReporting(
|
||||
mojom::CrossOriginEmbedderPolicyReporter* reporter) {
|
||||
constexpr auto kBlockedDueToCoep = mojom::BlockedByResponseReason::
|
||||
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep;
|
||||
if (embedder_policy.report_only_value ==
|
||||
mojom::CrossOriginEmbedderPolicyValue::kRequireCorp &&
|
||||
if ((embedder_policy.report_only_value ==
|
||||
mojom::CrossOriginEmbedderPolicyValue::kRequireCorp ||
|
||||
(embedder_policy.report_only_value ==
|
||||
mojom::CrossOriginEmbedderPolicyValue::kCredentialless &&
|
||||
request_mode == mojom::RequestMode::kNavigate)) &&
|
||||
reporter) {
|
||||
const auto result = IsBlockedInternal(
|
||||
policy, request_url, request_initiator, request_mode,
|
||||
|
@ -6,9 +6,11 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "net/http/http_response_headers.h"
|
||||
#include "net/http/http_util.h"
|
||||
#include "services/network/public/cpp/cross_origin_resource_policy.h"
|
||||
#include "services/network/public/cpp/features.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace network {
|
||||
@ -155,6 +157,10 @@ TEST(CrossOriginResourcePolicyTest, ShouldAllowSameSite) {
|
||||
}
|
||||
|
||||
TEST(CrossOriginResourcePolicyTest, WithCOEP) {
|
||||
base::test::ScopedFeatureList scoped_feature_list;
|
||||
scoped_feature_list.InitWithFeatures(
|
||||
{features::kCrossOriginEmbedderPolicyCredentialless}, {});
|
||||
|
||||
mojom::URLResponseHead corp_none;
|
||||
mojom::URLResponseHead corp_same_origin;
|
||||
mojom::URLResponseHead corp_cross_origin;
|
||||
@ -189,29 +195,34 @@ TEST(CrossOriginResourcePolicyTest, WithCOEP) {
|
||||
expectation_with_coep_none;
|
||||
const absl::optional<mojom::BlockedByResponseReason>
|
||||
expectation_with_coep_require_corp;
|
||||
const absl::optional<mojom::BlockedByResponseReason>
|
||||
expectation_with_coep_credentialless;
|
||||
} test_cases[] = {
|
||||
// We don't have a cross-origin-resource-policy header on a response. That
|
||||
// leads to blocking when COEP: kRequireCorp is used.
|
||||
{RequestMode::kNoCors, another_origin, corp_none.Clone(), kAllow,
|
||||
mojom::BlockedByResponseReason::
|
||||
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep,
|
||||
mojom::BlockedByResponseReason::
|
||||
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep},
|
||||
// We have "cross-origin-resource-policy: same-origin", so regardless of
|
||||
// COEP the response is blocked.
|
||||
{RequestMode::kNoCors, another_origin, corp_same_origin.Clone(),
|
||||
mojom::BlockedByResponseReason::kCorpNotSameOrigin,
|
||||
mojom::BlockedByResponseReason::kCorpNotSameOrigin,
|
||||
mojom::BlockedByResponseReason::kCorpNotSameOrigin},
|
||||
// We have "cross-origin-resource-policy: cross-origin", so regardless of
|
||||
// COEP the response is allowed.
|
||||
{RequestMode::kNoCors, another_origin, corp_cross_origin.Clone(), kAllow,
|
||||
kAllow},
|
||||
kAllow, kAllow},
|
||||
// The origin of the request URL and request's origin match, so regardless
|
||||
// of COEP the response is allowed.
|
||||
{RequestMode::kNoCors, destination_origin, corp_same_origin.Clone(),
|
||||
kAllow, kAllow},
|
||||
kAllow, kAllow, kAllow},
|
||||
// The request mode is "cors", so so regardless of COEP the response is
|
||||
// allowed.
|
||||
{RequestMode::kCors, another_origin, corp_same_origin.Clone(), kAllow,
|
||||
kAllow},
|
||||
kAllow, kAllow},
|
||||
};
|
||||
|
||||
for (const auto& test_case : test_cases) {
|
||||
@ -291,10 +302,70 @@ TEST(CrossOriginResourcePolicyTest, WithCOEP) {
|
||||
} else {
|
||||
EXPECT_TRUE(reporter.reports().empty());
|
||||
}
|
||||
|
||||
reporter.ClearReports();
|
||||
// COEP: credentialless, COEP-report-only: none
|
||||
embedder_policy.value =
|
||||
mojom::CrossOriginEmbedderPolicyValue::kCredentialless;
|
||||
EXPECT_EQ(
|
||||
test_case.expectation_with_coep_credentialless,
|
||||
CrossOriginResourcePolicy::IsBlocked(
|
||||
final_url, original_url, test_case.origin, *test_case.response_info,
|
||||
test_case.request_mode, test_case.origin,
|
||||
RequestDestination::kImage, embedder_policy, &reporter));
|
||||
if (should_be_blocked_due_to_coep) {
|
||||
ASSERT_EQ(2u, reporter.reports().size());
|
||||
EXPECT_TRUE(reporter.reports()[0].report_only);
|
||||
EXPECT_EQ(reporter.reports()[0].blocked_url, original_url);
|
||||
EXPECT_EQ(reporter.reports()[0].destination, RequestDestination::kImage);
|
||||
EXPECT_FALSE(reporter.reports()[1].report_only);
|
||||
EXPECT_EQ(reporter.reports()[1].blocked_url, original_url);
|
||||
EXPECT_EQ(reporter.reports()[1].destination, RequestDestination::kImage);
|
||||
} else {
|
||||
EXPECT_TRUE(reporter.reports().empty());
|
||||
}
|
||||
|
||||
reporter.ClearReports();
|
||||
// COEP: none, COEP-report-only: credentialless
|
||||
embedder_policy.value = mojom::CrossOriginEmbedderPolicyValue::kNone;
|
||||
embedder_policy.report_only_value =
|
||||
mojom::CrossOriginEmbedderPolicyValue::kCredentialless;
|
||||
EXPECT_EQ(
|
||||
test_case.expectation_with_coep_none,
|
||||
CrossOriginResourcePolicy::IsBlocked(
|
||||
final_url, original_url, test_case.origin, *test_case.response_info,
|
||||
test_case.request_mode, test_case.origin,
|
||||
RequestDestination::kScript, embedder_policy, &reporter));
|
||||
EXPECT_TRUE(reporter.reports().empty());
|
||||
|
||||
reporter.ClearReports();
|
||||
// COEP: credentialless, COEP-report-only: credentialless
|
||||
embedder_policy.value =
|
||||
mojom::CrossOriginEmbedderPolicyValue::kCredentialless;
|
||||
embedder_policy.report_only_value =
|
||||
mojom::CrossOriginEmbedderPolicyValue::kCredentialless;
|
||||
EXPECT_EQ(
|
||||
test_case.expectation_with_coep_credentialless,
|
||||
CrossOriginResourcePolicy::IsBlocked(
|
||||
final_url, original_url, test_case.origin, *test_case.response_info,
|
||||
test_case.request_mode, test_case.origin,
|
||||
RequestDestination::kEmpty, embedder_policy, &reporter));
|
||||
if (should_be_blocked_due_to_coep) {
|
||||
ASSERT_EQ(1u, reporter.reports().size());
|
||||
EXPECT_FALSE(reporter.reports()[0].report_only);
|
||||
EXPECT_EQ(reporter.reports()[0].blocked_url, original_url);
|
||||
EXPECT_EQ(reporter.reports()[0].destination, RequestDestination::kEmpty);
|
||||
} else {
|
||||
EXPECT_TRUE(reporter.reports().empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CrossOriginResourcePolicyTest, NavigationWithCOEP) {
|
||||
base::test::ScopedFeatureList scoped_feature_list;
|
||||
scoped_feature_list.InitWithFeatures(
|
||||
{features::kCrossOriginEmbedderPolicyCredentialless}, {});
|
||||
|
||||
mojom::URLResponseHead corp_none;
|
||||
mojom::URLResponseHead corp_same_origin;
|
||||
mojom::URLResponseHead corp_cross_origin;
|
||||
@ -328,22 +399,27 @@ TEST(CrossOriginResourcePolicyTest, NavigationWithCOEP) {
|
||||
expectation_with_coep_none;
|
||||
const absl::optional<mojom::BlockedByResponseReason>
|
||||
expectation_with_coep_require_corp;
|
||||
const absl::optional<mojom::BlockedByResponseReason>
|
||||
expectation_with_coep_credentialless;
|
||||
} test_cases[] = {
|
||||
// We don't have a cross-origin-resource-policy header on a response. That
|
||||
// leads to blocking when COEP: kRequireCorp is used.
|
||||
{another_origin, corp_none.Clone(), kAllow,
|
||||
mojom::BlockedByResponseReason::
|
||||
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep,
|
||||
mojom::BlockedByResponseReason::
|
||||
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep},
|
||||
// We have "cross-origin-resource-policy: same-origin",
|
||||
// COEP the response is blocked.
|
||||
{another_origin, corp_same_origin.Clone(), kAllow,
|
||||
mojom::BlockedByResponseReason::kCorpNotSameOrigin,
|
||||
mojom::BlockedByResponseReason::kCorpNotSameOrigin},
|
||||
// We have "cross-origin-resource-policy: cross-origin", so regardless of
|
||||
// COEP the response is allowed.
|
||||
{another_origin, corp_cross_origin.Clone(), kAllow, kAllow},
|
||||
{another_origin, corp_cross_origin.Clone(), kAllow, kAllow, kAllow},
|
||||
// The origin of the request URL and request's origin match, so regardless
|
||||
// of COEP the response is allowed.
|
||||
{destination_origin, corp_same_origin.Clone(), kAllow, kAllow},
|
||||
{destination_origin, corp_same_origin.Clone(), kAllow, kAllow, kAllow},
|
||||
};
|
||||
|
||||
for (const auto& test_case : test_cases) {
|
||||
@ -352,6 +428,9 @@ TEST(CrossOriginResourcePolicyTest, NavigationWithCOEP) {
|
||||
const bool should_be_blocked_due_to_coep =
|
||||
(test_case.expectation_with_coep_none !=
|
||||
test_case.expectation_with_coep_require_corp);
|
||||
const bool should_be_blocked_due_to_credentialless =
|
||||
(test_case.expectation_with_coep_none !=
|
||||
test_case.expectation_with_coep_credentialless);
|
||||
|
||||
// COEP: none, COEP-report-only: none
|
||||
EXPECT_EQ(test_case.expectation_with_coep_none,
|
||||
@ -419,6 +498,69 @@ TEST(CrossOriginResourcePolicyTest, NavigationWithCOEP) {
|
||||
} else {
|
||||
EXPECT_TRUE(reporter.reports().empty());
|
||||
}
|
||||
|
||||
reporter.ClearReports();
|
||||
// COEP: credentialless, COEP-report-only: none
|
||||
embedder_policy.value =
|
||||
mojom::CrossOriginEmbedderPolicyValue::kCredentialless;
|
||||
EXPECT_EQ(test_case.expectation_with_coep_credentialless,
|
||||
CrossOriginResourcePolicy::IsNavigationBlocked(
|
||||
final_url, original_url, test_case.origin,
|
||||
*test_case.response_info, test_case.origin,
|
||||
RequestDestination::kImage, embedder_policy, &reporter));
|
||||
if (should_be_blocked_due_to_credentialless) {
|
||||
ASSERT_EQ(2u, reporter.reports().size());
|
||||
EXPECT_TRUE(reporter.reports()[0].report_only);
|
||||
EXPECT_EQ(reporter.reports()[0].blocked_url, original_url);
|
||||
EXPECT_EQ(reporter.reports()[0].destination, RequestDestination::kImage);
|
||||
EXPECT_FALSE(reporter.reports()[1].report_only);
|
||||
EXPECT_EQ(reporter.reports()[1].blocked_url, original_url);
|
||||
EXPECT_EQ(reporter.reports()[1].destination, RequestDestination::kImage);
|
||||
} else {
|
||||
EXPECT_TRUE(reporter.reports().empty());
|
||||
}
|
||||
|
||||
reporter.ClearReports();
|
||||
// COEP: none, COEP-report-only: credentialless
|
||||
embedder_policy.value = mojom::CrossOriginEmbedderPolicyValue::kNone;
|
||||
embedder_policy.report_only_value =
|
||||
mojom::CrossOriginEmbedderPolicyValue::kCredentialless;
|
||||
EXPECT_EQ(test_case.expectation_with_coep_none,
|
||||
CrossOriginResourcePolicy::IsNavigationBlocked(
|
||||
final_url, original_url, test_case.origin,
|
||||
*test_case.response_info, test_case.origin,
|
||||
RequestDestination::kScript, embedder_policy, &reporter));
|
||||
if (should_be_blocked_due_to_credentialless) {
|
||||
ASSERT_EQ(1u, reporter.reports().size());
|
||||
EXPECT_TRUE(reporter.reports()[0].report_only);
|
||||
EXPECT_EQ(reporter.reports()[0].blocked_url, original_url);
|
||||
EXPECT_EQ(reporter.reports()[0].destination, RequestDestination::kScript);
|
||||
} else {
|
||||
EXPECT_TRUE(reporter.reports().empty());
|
||||
}
|
||||
|
||||
reporter.ClearReports();
|
||||
// COEP: credentialless, COEP-report-only: credentialless
|
||||
embedder_policy.value =
|
||||
mojom::CrossOriginEmbedderPolicyValue::kCredentialless;
|
||||
embedder_policy.report_only_value =
|
||||
mojom::CrossOriginEmbedderPolicyValue::kCredentialless;
|
||||
EXPECT_EQ(test_case.expectation_with_coep_credentialless,
|
||||
CrossOriginResourcePolicy::IsNavigationBlocked(
|
||||
final_url, original_url, test_case.origin,
|
||||
*test_case.response_info, test_case.origin,
|
||||
RequestDestination::kEmpty, embedder_policy, &reporter));
|
||||
if (should_be_blocked_due_to_credentialless) {
|
||||
ASSERT_EQ(2u, reporter.reports().size());
|
||||
EXPECT_TRUE(reporter.reports()[0].report_only);
|
||||
EXPECT_EQ(reporter.reports()[0].blocked_url, original_url);
|
||||
EXPECT_EQ(reporter.reports()[0].destination, RequestDestination::kEmpty);
|
||||
EXPECT_FALSE(reporter.reports()[1].report_only);
|
||||
EXPECT_EQ(reporter.reports()[1].blocked_url, original_url);
|
||||
EXPECT_EQ(reporter.reports()[1].destination, RequestDestination::kEmpty);
|
||||
} else {
|
||||
EXPECT_TRUE(reporter.reports().empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace network
|
||||
|
136
third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/reporting-navigation.tentative.https.html
vendored
Normal file
136
third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/reporting-navigation.tentative.https.html
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<meta name="timeout" content="long">
|
||||
<body>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="./resources/common.js"></script>
|
||||
<script>
|
||||
const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
|
||||
const COEP = '|header(cross-origin-embedder-policy,credentialless)';
|
||||
const COEP_RO =
|
||||
'|header(cross-origin-embedder-policy-report-only,credentialless)';
|
||||
const CORP_CROSS_ORIGIN =
|
||||
'|header(cross-origin-resource-policy,cross-origin)';
|
||||
const FRAME_URL = `${ORIGIN}/common/blank.html?pipe=`;
|
||||
const REMOTE_FRAME_URL = `${REMOTE_ORIGIN}/common/blank.html?pipe=`;
|
||||
|
||||
function checkCorpReport(report, contextUrl, blockedUrl, disposition) {
|
||||
assert_equals(report.type, 'coep');
|
||||
assert_equals(report.url, contextUrl);
|
||||
assert_equals(report.body.type, 'corp');
|
||||
assert_equals(report.body.blockedURL, blockedUrl);
|
||||
assert_equals(report.body.disposition, disposition);
|
||||
assert_equals(report.body.destination, 'iframe');
|
||||
}
|
||||
|
||||
function checkCoepMismatchReport(report, contextUrl, blockedUrl, disposition) {
|
||||
assert_equals(report.type, 'coep');
|
||||
assert_equals(report.url, contextUrl);
|
||||
assert_equals(report.body.type, 'navigation');
|
||||
assert_equals(report.body.blockedURL, blockedUrl);
|
||||
assert_equals(report.body.disposition, disposition);
|
||||
}
|
||||
|
||||
function loadFrame(document, url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const frame = document.createElement('iframe');
|
||||
frame.src = url;
|
||||
frame.onload = () => resolve(frame);
|
||||
frame.onerror = reject;
|
||||
document.body.appendChild(frame);
|
||||
});
|
||||
}
|
||||
|
||||
// |parentSuffix| is a suffix for the parent frame URL.
|
||||
// |targetUrl| is a URL for the target frame.
|
||||
async function loadFrames(test, parentSuffix, targetUrl) {
|
||||
const frame = await loadFrame(document, FRAME_URL + parentSuffix);
|
||||
test.add_cleanup(() => frame.remove());
|
||||
// Here we don't need "await". This loading may or may not succeed, and
|
||||
// we're not interested in the result.
|
||||
loadFrame(frame.contentDocument, targetUrl);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
async function observeReports(global) {
|
||||
const reports = [];
|
||||
const observer = new global.ReportingObserver((rs) => {
|
||||
for (const r of rs) {
|
||||
reports.push(r.toJSON());
|
||||
}
|
||||
});
|
||||
observer.observe();
|
||||
|
||||
// Wait 1000ms for reports to settle.
|
||||
await new Promise(r => step_timeout(r, 1000));
|
||||
return reports;
|
||||
}
|
||||
|
||||
function desc(headers) {
|
||||
return headers === '' ? '(none)' : headers;
|
||||
}
|
||||
|
||||
// CASES is a list of test case. Each test case consists of:
|
||||
// parent_headers: the suffix of the URL of the parent frame.
|
||||
// target_headers: the suffix of the URL of the target frame.
|
||||
// expected_reports: one of:
|
||||
// 'CORP': CORP violation
|
||||
// 'CORP-RO': CORP violation (report only)
|
||||
// 'NAV': COEP mismatch between the frames.
|
||||
// 'NAV-RO': COEP mismatch between the frames (report only).
|
||||
const reportingTest = function(
|
||||
parent_headers, target_headers, expected_reports) {
|
||||
// These tests are very slow, so they must be run in parallel using
|
||||
// async_test.
|
||||
promise_test_parallel(async t => {
|
||||
const targetUrl = REMOTE_FRAME_URL + target_headers;
|
||||
const parent = await loadFrames(t, parent_headers, targetUrl);
|
||||
const contextUrl = parent.src ? parent.src : 'about:blank';
|
||||
const reports = await observeReports(parent.contentWindow);
|
||||
assert_equals(reports.length, expected_reports.length);
|
||||
for (let i = 0; i < reports.length; i += 1) {
|
||||
const report = reports[i];
|
||||
switch (expected_reports[i]) {
|
||||
case 'CORP':
|
||||
checkCorpReport(report, contextUrl, targetUrl, 'enforce');
|
||||
break;
|
||||
case 'CORP-RO':
|
||||
checkCorpReport(report, contextUrl, targetUrl, 'reporting');
|
||||
break;
|
||||
case 'NAV':
|
||||
checkCoepMismatchReport(report, contextUrl, targetUrl, 'enforce');
|
||||
break;
|
||||
case 'NAV-RO':
|
||||
checkCoepMismatchReport(report, contextUrl, targetUrl, 'reporting');
|
||||
break;
|
||||
default:
|
||||
assert_unreached(
|
||||
'Unexpected report exception: ' + expected_reports[i]);
|
||||
}
|
||||
}
|
||||
}, `parent: ${desc(parent_headers)}, target: ${desc(target_headers)}, `);
|
||||
}
|
||||
|
||||
reportingTest('', '', []);
|
||||
reportingTest('', COEP, []);
|
||||
reportingTest(COEP, COEP, ['CORP']);
|
||||
reportingTest(COEP, '', ['CORP']);
|
||||
|
||||
reportingTest('', CORP_CROSS_ORIGIN, []);
|
||||
reportingTest(COEP, CORP_CROSS_ORIGIN, ['NAV']);
|
||||
|
||||
reportingTest('', COEP + CORP_CROSS_ORIGIN, []);
|
||||
reportingTest(COEP, COEP + CORP_CROSS_ORIGIN, []);
|
||||
|
||||
reportingTest(COEP_RO, COEP, ['CORP-RO']);
|
||||
reportingTest(COEP_RO, '', ['CORP-RO', 'NAV-RO']);
|
||||
reportingTest(COEP_RO, CORP_CROSS_ORIGIN, ['NAV-RO']);
|
||||
reportingTest(COEP_RO, COEP + CORP_CROSS_ORIGIN, []);
|
||||
|
||||
reportingTest(COEP, COEP_RO + CORP_CROSS_ORIGIN, ['NAV']);
|
||||
|
||||
</script>
|
||||
</body></html>
|
202
third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/reporting-subresource-corp.tentative.https.html
vendored
Normal file
202
third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/reporting-subresource-corp.tentative.https.html
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<meta name="timeout" content="long">
|
||||
<body>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/utils.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
|
||||
<script>
|
||||
const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
|
||||
const BASE = "/html/cross-origin-embedder-policy/resources";
|
||||
const FRAME_URL = `${ORIGIN}/common/blank.html` +
|
||||
'?pipe=header(cross-origin-embedder-policy,credentialless)' +
|
||||
`|header(cross-origin-embedder-policy-report-only,credentialless)`;
|
||||
const WORKER_URL = `${ORIGIN}${BASE}/reporting-worker.js` +
|
||||
'?pipe=header(cross-origin-embedder-policy,credentialless)' +
|
||||
`|header(cross-origin-embedder-policy-report-only,credentialless)`;
|
||||
const REPORTING_FRAME_URL = `${ORIGIN}${BASE}/reporting-empty-frame.html` +
|
||||
'?pipe=header(cross-origin-embedder-policy,credentialless)' +
|
||||
`|header(cross-origin-embedder-policy-report-only,credentialless)`;
|
||||
|
||||
function wait(ms) {
|
||||
return new Promise(resolve => step_timeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function fetchInFrame(t, frameUrl, url) {
|
||||
const reports = [];
|
||||
const frame = await with_iframe(frameUrl);
|
||||
t.add_cleanup(() => frame.remove());
|
||||
|
||||
const observer = new frame.contentWindow.ReportingObserver((rs) => {
|
||||
for (const report of rs) {
|
||||
reports.push(report.toJSON());
|
||||
}
|
||||
});
|
||||
observer.observe();
|
||||
const init = { mode: 'no-cors', cache: 'no-store' };
|
||||
await frame.contentWindow.fetch(url, init).catch(() => {});
|
||||
|
||||
// Wait 1000ms for reports to settle.
|
||||
await new Promise(r => step_timeout(r, 1000));
|
||||
return reports;
|
||||
}
|
||||
|
||||
async function fetchInWorker(workerOrPort, url) {
|
||||
const script =
|
||||
`fetch('${url}', {mode: 'no-cors', cache: 'no-store'}).catch(() => {});`;
|
||||
const mc = new MessageChannel();
|
||||
workerOrPort.postMessage({script, port: mc.port2}, [mc.port2]);
|
||||
return (await new Promise(r => mc.port1.onmessage = r)).data;
|
||||
}
|
||||
|
||||
function checkReport(report, contextUrl, blockedUrl, disposition, destination) {
|
||||
assert_equals(report.type, 'coep');
|
||||
assert_equals(report.url, contextUrl);
|
||||
assert_equals(report.body.type, 'corp');
|
||||
assert_equals(report.body.blockedURL, blockedUrl);
|
||||
assert_equals(report.body.disposition, disposition);
|
||||
assert_equals(report.body.destination, destination);
|
||||
}
|
||||
|
||||
// We want to test several URLs in various environments (document,
|
||||
// dedicated worker, shared worker, service worker). As expectations
|
||||
// are independent of environment except for the context URLs in reports,
|
||||
// we define ENVIRONMENTS and CASES to reduce the code duplication.
|
||||
//
|
||||
// ENVIRONMENTS is a list of dictionaries. Each dictionary consists of:
|
||||
// - tag: the name of the environment
|
||||
// - contextUrl: the URL of the environment settings object
|
||||
// - run: an async function which generates reports
|
||||
// - test: a testharness Test object
|
||||
// - url: the URL for a test case (see below)
|
||||
//
|
||||
// CASES is a list of test cases. Each test case consists of:
|
||||
// - name: the name of the test case
|
||||
// - url: the URL of the test case
|
||||
// - check: a function to check the results
|
||||
// - reports: the generated reports
|
||||
// - url: the URL of the test case
|
||||
// - contextUrl: the URL of the environment settings object (see
|
||||
// ENVORONMENTS)
|
||||
|
||||
const ENVIRONMENTS = [{
|
||||
tag: 'document',
|
||||
contextUrl: FRAME_URL,
|
||||
run: async (test, url) => {
|
||||
return await fetchInFrame(test, FRAME_URL, url);
|
||||
},
|
||||
}, {
|
||||
tag: 'dedicated worker',
|
||||
contextUrl: WORKER_URL,
|
||||
run: async (test, url) => {
|
||||
const worker = new Worker(WORKER_URL);
|
||||
worker.addEventListener('error', test.unreached_func('Worker.onerror'));
|
||||
test.add_cleanup(() => worker.terminate());
|
||||
return await fetchInWorker(worker, url);
|
||||
},
|
||||
}, {
|
||||
tag: 'shared worker',
|
||||
contextUrl: WORKER_URL,
|
||||
run: async (test, url) => {
|
||||
const worker = new SharedWorker(WORKER_URL);
|
||||
worker.addEventListener('error', test.unreached_func('Worker.onerror'));
|
||||
return await fetchInWorker(worker.port, url);
|
||||
},
|
||||
}, {
|
||||
tag: 'service worker',
|
||||
contextUrl: WORKER_URL,
|
||||
run: async (test, url) => {
|
||||
// Generate a one-time scope for service workeer.
|
||||
const SCOPE = `${BASE}/${token()}.html`;
|
||||
const reg =
|
||||
await service_worker_unregister_and_register(test, WORKER_URL, SCOPE);
|
||||
test.add_cleanup(() => reg.unregister());
|
||||
const worker = reg.installing || reg.waiting || reg.active;
|
||||
worker.addEventListener('error', test.unreached_func('Worker.onerror'));
|
||||
return await fetchInWorker(worker, url);
|
||||
},
|
||||
}, {
|
||||
tag: 'between service worker and page',
|
||||
contextUrl: REPORTING_FRAME_URL,
|
||||
run: async (test, url) => {
|
||||
// Service Worker without COEP.
|
||||
const WORKER_URL = `${ORIGIN}${BASE}/sw.js`;
|
||||
const reg = await service_worker_unregister_and_register(
|
||||
test, WORKER_URL, REPORTING_FRAME_URL);
|
||||
test.add_cleanup(() => reg.unregister());
|
||||
const worker = reg.installing || reg.waiting || reg.active;
|
||||
worker.addEventListener('error', test.unreached_func('Worker.onerror'));
|
||||
return await fetchInFrame(
|
||||
test, REPORTING_FRAME_URL, url);
|
||||
},
|
||||
}];
|
||||
|
||||
const CASES = [{
|
||||
name: 'same-origin',
|
||||
url: '/common/text-plain.txt',
|
||||
check: (reports, url, contextUrl) => {
|
||||
assert_equals(reports.length, 0);
|
||||
}
|
||||
}, {
|
||||
name: 'blocked by CORP: same-origin',
|
||||
url: `${REMOTE_ORIGIN}${BASE}/nothing-same-origin-corp.txt`,
|
||||
check: (reports, url, contextUrl) => {
|
||||
assert_equals(reports.length, 0);
|
||||
}
|
||||
}, {
|
||||
name: 'blocked due to COEP',
|
||||
url: `${REMOTE_ORIGIN}/common/text-plain.txt`,
|
||||
check: (reports, contextUrl, url) => {
|
||||
assert_equals(reports.length, 0);
|
||||
}
|
||||
}, {
|
||||
name: 'blocked during redirect',
|
||||
url: `${ORIGIN}/common/redirect.py?location=` +
|
||||
encodeURIComponent(`${REMOTE_ORIGIN}/common/text-plain.txt`),
|
||||
check: (reports, contextUrl, url) => {
|
||||
// The redirection is blocked because CORP is required on the response here
|
||||
// according to https://github.com/w3c/ServiceWorker/issues/1592
|
||||
if (contextUrl === REPORTING_FRAME_URL) {
|
||||
assert_equals(reports.length, 1);
|
||||
checkReport(reports[0], contextUrl, url, 'enforce', '');
|
||||
} else {
|
||||
assert_equals(reports.length, 0);
|
||||
}
|
||||
},
|
||||
}];
|
||||
|
||||
for (const env of ENVIRONMENTS) {
|
||||
for (const testcase of CASES) {
|
||||
promise_test(async (t) => {
|
||||
const reports = await env.run(t, testcase.url);
|
||||
testcase.check(reports, env.contextUrl, testcase.url);
|
||||
}, `[${env.tag}] ${testcase.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
// A test for a non-empty destination.
|
||||
promise_test(async (t) => {
|
||||
const reports = [];
|
||||
const frame = await with_iframe(FRAME_URL);
|
||||
t.add_cleanup(() => frame.remove());
|
||||
|
||||
const observer = new frame.contentWindow.ReportingObserver((rs) => {
|
||||
for (const report of rs) {
|
||||
reports.push(report.toJSON());
|
||||
}
|
||||
});
|
||||
observer.observe();
|
||||
const url = `${REMOTE_ORIGIN}/common/utils.js`;
|
||||
const script = frame.contentDocument.createElement('script');
|
||||
script.src = url;
|
||||
frame.contentDocument.body.appendChild(script);
|
||||
|
||||
// Wait 200ms for reports to settle.
|
||||
await t.step_timeout(200);
|
||||
|
||||
assert_equals(reports.length, 0);
|
||||
}, 'destination: script');
|
||||
|
||||
</script>
|
@ -16,6 +16,9 @@ const FRAME_URL = `${ORIGIN}/common/blank.html` +
|
||||
const WORKER_URL = `${ORIGIN}${BASE}/reporting-worker.js` +
|
||||
'?pipe=header(cross-origin-embedder-policy,require-corp)' +
|
||||
`|header(cross-origin-embedder-policy-report-only,require-corp)`;
|
||||
const REPORTING_FRAME_URL = `${ORIGIN}${BASE}/reporting-empty-frame.html` +
|
||||
'?pipe=header(cross-origin-embedder-policy,require-corp)' +
|
||||
`|header(cross-origin-embedder-policy-report-only,require-corp)`;
|
||||
|
||||
function wait(ms) {
|
||||
return new Promise(resolve => step_timeout(resolve, ms));
|
||||
@ -117,18 +120,17 @@ const ENVIRONMENTS = [{
|
||||
},
|
||||
}, {
|
||||
tag: 'between service worker and page',
|
||||
contextUrl: `${ORIGIN}${BASE}/reporting-empty-frame.html`,
|
||||
contextUrl: REPORTING_FRAME_URL,
|
||||
run: async (test, url) => {
|
||||
const SCOPE = `${BASE}/reporting-empty-frame.html`;
|
||||
// Here we use a Service Worker without COEP.
|
||||
const WORKER_URL = `${ORIGIN}${BASE}/sw.js`;
|
||||
const reg =
|
||||
await service_worker_unregister_and_register(test, WORKER_URL, SCOPE);
|
||||
const reg = await service_worker_unregister_and_register(
|
||||
test, WORKER_URL, REPORTING_FRAME_URL);
|
||||
test.add_cleanup(() => reg.unregister());
|
||||
const worker = reg.installing || reg.waiting || reg.active;
|
||||
worker.addEventListener('error', test.unreached_func('Worker.onerror'));
|
||||
return await fetchInFrame(
|
||||
test, `${ORIGIN}${BASE}/reporting-empty-frame.html`, url);
|
||||
test, REPORTING_FRAME_URL, url);
|
||||
},
|
||||
}];
|
||||
|
||||
|
10
third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html
vendored
10
third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html
vendored
@ -21,6 +21,9 @@
|
||||
// .
|
||||
const { REMOTE_ORIGIN } = get_host_info();
|
||||
const BASE = new URL("resources", location).pathname
|
||||
const FRAME_URL = `resources/reporting-empty-frame.html` +
|
||||
`?pipe=header(cross-origin-embedder-policy,require-corp;report-to="endpoint")` +
|
||||
`|header(cross-origin-embedder-policy-report-only,require-corp;report-to="report-only-endpoint")`;
|
||||
|
||||
function wait(ms) {
|
||||
return new Promise(resolve => step_timeout(resolve, ms));
|
||||
@ -65,7 +68,6 @@ async function checkNavigationReportExistence(endpoint, blockedUrl, contextUrl,
|
||||
const retryDelay = 200;
|
||||
for (let i = 0; i * retryDelay < timeout; i++) {
|
||||
const reports = await fetchReports(endpoint);
|
||||
|
||||
for (const report of reports) {
|
||||
if (report.type !== 'coep' || report.url !== contextUrl ||
|
||||
report.body.type !== 'navigation') {
|
||||
@ -85,7 +87,7 @@ promise_test(async t => {
|
||||
const iframe = document.createElement('iframe');
|
||||
t.add_cleanup(() => iframe.remove());
|
||||
|
||||
iframe.src = `resources/reporting-empty-frame.html`
|
||||
iframe.src = FRAME_URL
|
||||
document.body.appendChild(iframe);
|
||||
await new Promise(resolve => {
|
||||
iframe.addEventListener('load', resolve, {once: true});
|
||||
@ -106,7 +108,7 @@ promise_test(async t => {
|
||||
const iframe = document.createElement('iframe');
|
||||
t.add_cleanup(() => iframe.remove());
|
||||
|
||||
iframe.src = `resources/reporting-empty-frame.html`
|
||||
iframe.src = FRAME_URL
|
||||
document.body.appendChild(iframe);
|
||||
await new Promise(resolve => {
|
||||
iframe.addEventListener('load', resolve, {once: true});
|
||||
@ -135,7 +137,7 @@ promise_test(async (t) => {
|
||||
const iframe = document.createElement('iframe');
|
||||
t.add_cleanup(() => iframe.remove());
|
||||
|
||||
iframe.src = 'resources/reporting-empty-frame.html';
|
||||
iframe.src = FRAME_URL;
|
||||
const targetUrl = `/common/blank.html?${token()}`;
|
||||
iframe.addEventListener('load', t.step_func(() => {
|
||||
const nested = iframe.contentDocument.createElement('iframe');
|
||||
|
@ -1,2 +0,0 @@
|
||||
cross-origin-embedder-policy: require-corp; report-to="endpoint"
|
||||
cross-origin-embedder-policy-report-only: require-corp; report-to="report-only-endpoint"
|
Reference in New Issue
Block a user