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) {
|
mojom::CrossOriginEmbedderPolicyReporter* reporter) {
|
||||||
constexpr auto kBlockedDueToCoep = mojom::BlockedByResponseReason::
|
constexpr auto kBlockedDueToCoep = mojom::BlockedByResponseReason::
|
||||||
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep;
|
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep;
|
||||||
if (embedder_policy.report_only_value ==
|
if ((embedder_policy.report_only_value ==
|
||||||
mojom::CrossOriginEmbedderPolicyValue::kRequireCorp &&
|
mojom::CrossOriginEmbedderPolicyValue::kRequireCorp ||
|
||||||
|
(embedder_policy.report_only_value ==
|
||||||
|
mojom::CrossOriginEmbedderPolicyValue::kCredentialless &&
|
||||||
|
request_mode == mojom::RequestMode::kNavigate)) &&
|
||||||
reporter) {
|
reporter) {
|
||||||
const auto result = IsBlockedInternal(
|
const auto result = IsBlockedInternal(
|
||||||
policy, request_url, request_initiator, request_mode,
|
policy, request_url, request_initiator, request_mode,
|
||||||
|
@ -6,9 +6,11 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/memory/ref_counted.h"
|
#include "base/memory/ref_counted.h"
|
||||||
|
#include "base/test/scoped_feature_list.h"
|
||||||
#include "net/http/http_response_headers.h"
|
#include "net/http/http_response_headers.h"
|
||||||
#include "net/http/http_util.h"
|
#include "net/http/http_util.h"
|
||||||
#include "services/network/public/cpp/cross_origin_resource_policy.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"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
namespace network {
|
namespace network {
|
||||||
@ -155,6 +157,10 @@ TEST(CrossOriginResourcePolicyTest, ShouldAllowSameSite) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(CrossOriginResourcePolicyTest, WithCOEP) {
|
TEST(CrossOriginResourcePolicyTest, WithCOEP) {
|
||||||
|
base::test::ScopedFeatureList scoped_feature_list;
|
||||||
|
scoped_feature_list.InitWithFeatures(
|
||||||
|
{features::kCrossOriginEmbedderPolicyCredentialless}, {});
|
||||||
|
|
||||||
mojom::URLResponseHead corp_none;
|
mojom::URLResponseHead corp_none;
|
||||||
mojom::URLResponseHead corp_same_origin;
|
mojom::URLResponseHead corp_same_origin;
|
||||||
mojom::URLResponseHead corp_cross_origin;
|
mojom::URLResponseHead corp_cross_origin;
|
||||||
@ -189,29 +195,34 @@ TEST(CrossOriginResourcePolicyTest, WithCOEP) {
|
|||||||
expectation_with_coep_none;
|
expectation_with_coep_none;
|
||||||
const absl::optional<mojom::BlockedByResponseReason>
|
const absl::optional<mojom::BlockedByResponseReason>
|
||||||
expectation_with_coep_require_corp;
|
expectation_with_coep_require_corp;
|
||||||
|
const absl::optional<mojom::BlockedByResponseReason>
|
||||||
|
expectation_with_coep_credentialless;
|
||||||
} test_cases[] = {
|
} test_cases[] = {
|
||||||
// We don't have a cross-origin-resource-policy header on a response. That
|
// We don't have a cross-origin-resource-policy header on a response. That
|
||||||
// leads to blocking when COEP: kRequireCorp is used.
|
// leads to blocking when COEP: kRequireCorp is used.
|
||||||
{RequestMode::kNoCors, another_origin, corp_none.Clone(), kAllow,
|
{RequestMode::kNoCors, another_origin, corp_none.Clone(), kAllow,
|
||||||
|
mojom::BlockedByResponseReason::
|
||||||
|
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep,
|
||||||
mojom::BlockedByResponseReason::
|
mojom::BlockedByResponseReason::
|
||||||
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep},
|
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep},
|
||||||
// We have "cross-origin-resource-policy: same-origin", so regardless of
|
// We have "cross-origin-resource-policy: same-origin", so regardless of
|
||||||
// COEP the response is blocked.
|
// COEP the response is blocked.
|
||||||
{RequestMode::kNoCors, another_origin, corp_same_origin.Clone(),
|
{RequestMode::kNoCors, another_origin, corp_same_origin.Clone(),
|
||||||
|
mojom::BlockedByResponseReason::kCorpNotSameOrigin,
|
||||||
mojom::BlockedByResponseReason::kCorpNotSameOrigin,
|
mojom::BlockedByResponseReason::kCorpNotSameOrigin,
|
||||||
mojom::BlockedByResponseReason::kCorpNotSameOrigin},
|
mojom::BlockedByResponseReason::kCorpNotSameOrigin},
|
||||||
// We have "cross-origin-resource-policy: cross-origin", so regardless of
|
// We have "cross-origin-resource-policy: cross-origin", so regardless of
|
||||||
// COEP the response is allowed.
|
// COEP the response is allowed.
|
||||||
{RequestMode::kNoCors, another_origin, corp_cross_origin.Clone(), kAllow,
|
{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
|
// The origin of the request URL and request's origin match, so regardless
|
||||||
// of COEP the response is allowed.
|
// of COEP the response is allowed.
|
||||||
{RequestMode::kNoCors, destination_origin, corp_same_origin.Clone(),
|
{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
|
// The request mode is "cors", so so regardless of COEP the response is
|
||||||
// allowed.
|
// allowed.
|
||||||
{RequestMode::kCors, another_origin, corp_same_origin.Clone(), kAllow,
|
{RequestMode::kCors, another_origin, corp_same_origin.Clone(), kAllow,
|
||||||
kAllow},
|
kAllow, kAllow},
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto& test_case : test_cases) {
|
for (const auto& test_case : test_cases) {
|
||||||
@ -291,10 +302,70 @@ TEST(CrossOriginResourcePolicyTest, WithCOEP) {
|
|||||||
} else {
|
} else {
|
||||||
EXPECT_TRUE(reporter.reports().empty());
|
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) {
|
TEST(CrossOriginResourcePolicyTest, NavigationWithCOEP) {
|
||||||
|
base::test::ScopedFeatureList scoped_feature_list;
|
||||||
|
scoped_feature_list.InitWithFeatures(
|
||||||
|
{features::kCrossOriginEmbedderPolicyCredentialless}, {});
|
||||||
|
|
||||||
mojom::URLResponseHead corp_none;
|
mojom::URLResponseHead corp_none;
|
||||||
mojom::URLResponseHead corp_same_origin;
|
mojom::URLResponseHead corp_same_origin;
|
||||||
mojom::URLResponseHead corp_cross_origin;
|
mojom::URLResponseHead corp_cross_origin;
|
||||||
@ -328,22 +399,27 @@ TEST(CrossOriginResourcePolicyTest, NavigationWithCOEP) {
|
|||||||
expectation_with_coep_none;
|
expectation_with_coep_none;
|
||||||
const absl::optional<mojom::BlockedByResponseReason>
|
const absl::optional<mojom::BlockedByResponseReason>
|
||||||
expectation_with_coep_require_corp;
|
expectation_with_coep_require_corp;
|
||||||
|
const absl::optional<mojom::BlockedByResponseReason>
|
||||||
|
expectation_with_coep_credentialless;
|
||||||
} test_cases[] = {
|
} test_cases[] = {
|
||||||
// We don't have a cross-origin-resource-policy header on a response. That
|
// We don't have a cross-origin-resource-policy header on a response. That
|
||||||
// leads to blocking when COEP: kRequireCorp is used.
|
// leads to blocking when COEP: kRequireCorp is used.
|
||||||
{another_origin, corp_none.Clone(), kAllow,
|
{another_origin, corp_none.Clone(), kAllow,
|
||||||
|
mojom::BlockedByResponseReason::
|
||||||
|
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep,
|
||||||
mojom::BlockedByResponseReason::
|
mojom::BlockedByResponseReason::
|
||||||
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep},
|
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep},
|
||||||
// We have "cross-origin-resource-policy: same-origin",
|
// We have "cross-origin-resource-policy: same-origin",
|
||||||
// COEP the response is blocked.
|
// COEP the response is blocked.
|
||||||
{another_origin, corp_same_origin.Clone(), kAllow,
|
{another_origin, corp_same_origin.Clone(), kAllow,
|
||||||
|
mojom::BlockedByResponseReason::kCorpNotSameOrigin,
|
||||||
mojom::BlockedByResponseReason::kCorpNotSameOrigin},
|
mojom::BlockedByResponseReason::kCorpNotSameOrigin},
|
||||||
// We have "cross-origin-resource-policy: cross-origin", so regardless of
|
// We have "cross-origin-resource-policy: cross-origin", so regardless of
|
||||||
// COEP the response is allowed.
|
// 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
|
// The origin of the request URL and request's origin match, so regardless
|
||||||
// of COEP the response is allowed.
|
// 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) {
|
for (const auto& test_case : test_cases) {
|
||||||
@ -352,6 +428,9 @@ TEST(CrossOriginResourcePolicyTest, NavigationWithCOEP) {
|
|||||||
const bool should_be_blocked_due_to_coep =
|
const bool should_be_blocked_due_to_coep =
|
||||||
(test_case.expectation_with_coep_none !=
|
(test_case.expectation_with_coep_none !=
|
||||||
test_case.expectation_with_coep_require_corp);
|
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
|
// COEP: none, COEP-report-only: none
|
||||||
EXPECT_EQ(test_case.expectation_with_coep_none,
|
EXPECT_EQ(test_case.expectation_with_coep_none,
|
||||||
@ -419,6 +498,69 @@ TEST(CrossOriginResourcePolicyTest, NavigationWithCOEP) {
|
|||||||
} else {
|
} else {
|
||||||
EXPECT_TRUE(reporter.reports().empty());
|
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
|
} // 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` +
|
const WORKER_URL = `${ORIGIN}${BASE}/reporting-worker.js` +
|
||||||
'?pipe=header(cross-origin-embedder-policy,require-corp)' +
|
'?pipe=header(cross-origin-embedder-policy,require-corp)' +
|
||||||
`|header(cross-origin-embedder-policy-report-only,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) {
|
function wait(ms) {
|
||||||
return new Promise(resolve => step_timeout(resolve, ms));
|
return new Promise(resolve => step_timeout(resolve, ms));
|
||||||
@ -117,18 +120,17 @@ const ENVIRONMENTS = [{
|
|||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
tag: 'between service worker and page',
|
tag: 'between service worker and page',
|
||||||
contextUrl: `${ORIGIN}${BASE}/reporting-empty-frame.html`,
|
contextUrl: REPORTING_FRAME_URL,
|
||||||
run: async (test, url) => {
|
run: async (test, url) => {
|
||||||
const SCOPE = `${BASE}/reporting-empty-frame.html`;
|
|
||||||
// Here we use a Service Worker without COEP.
|
// Here we use a Service Worker without COEP.
|
||||||
const WORKER_URL = `${ORIGIN}${BASE}/sw.js`;
|
const WORKER_URL = `${ORIGIN}${BASE}/sw.js`;
|
||||||
const reg =
|
const reg = await service_worker_unregister_and_register(
|
||||||
await service_worker_unregister_and_register(test, WORKER_URL, SCOPE);
|
test, WORKER_URL, REPORTING_FRAME_URL);
|
||||||
test.add_cleanup(() => reg.unregister());
|
test.add_cleanup(() => reg.unregister());
|
||||||
const worker = reg.installing || reg.waiting || reg.active;
|
const worker = reg.installing || reg.waiting || reg.active;
|
||||||
worker.addEventListener('error', test.unreached_func('Worker.onerror'));
|
worker.addEventListener('error', test.unreached_func('Worker.onerror'));
|
||||||
return await fetchInFrame(
|
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 { REMOTE_ORIGIN } = get_host_info();
|
||||||
const BASE = new URL("resources", location).pathname
|
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) {
|
function wait(ms) {
|
||||||
return new Promise(resolve => step_timeout(resolve, ms));
|
return new Promise(resolve => step_timeout(resolve, ms));
|
||||||
@ -65,7 +68,6 @@ async function checkNavigationReportExistence(endpoint, blockedUrl, contextUrl,
|
|||||||
const retryDelay = 200;
|
const retryDelay = 200;
|
||||||
for (let i = 0; i * retryDelay < timeout; i++) {
|
for (let i = 0; i * retryDelay < timeout; i++) {
|
||||||
const reports = await fetchReports(endpoint);
|
const reports = await fetchReports(endpoint);
|
||||||
|
|
||||||
for (const report of reports) {
|
for (const report of reports) {
|
||||||
if (report.type !== 'coep' || report.url !== contextUrl ||
|
if (report.type !== 'coep' || report.url !== contextUrl ||
|
||||||
report.body.type !== 'navigation') {
|
report.body.type !== 'navigation') {
|
||||||
@ -85,7 +87,7 @@ promise_test(async t => {
|
|||||||
const iframe = document.createElement('iframe');
|
const iframe = document.createElement('iframe');
|
||||||
t.add_cleanup(() => iframe.remove());
|
t.add_cleanup(() => iframe.remove());
|
||||||
|
|
||||||
iframe.src = `resources/reporting-empty-frame.html`
|
iframe.src = FRAME_URL
|
||||||
document.body.appendChild(iframe);
|
document.body.appendChild(iframe);
|
||||||
await new Promise(resolve => {
|
await new Promise(resolve => {
|
||||||
iframe.addEventListener('load', resolve, {once: true});
|
iframe.addEventListener('load', resolve, {once: true});
|
||||||
@ -106,7 +108,7 @@ promise_test(async t => {
|
|||||||
const iframe = document.createElement('iframe');
|
const iframe = document.createElement('iframe');
|
||||||
t.add_cleanup(() => iframe.remove());
|
t.add_cleanup(() => iframe.remove());
|
||||||
|
|
||||||
iframe.src = `resources/reporting-empty-frame.html`
|
iframe.src = FRAME_URL
|
||||||
document.body.appendChild(iframe);
|
document.body.appendChild(iframe);
|
||||||
await new Promise(resolve => {
|
await new Promise(resolve => {
|
||||||
iframe.addEventListener('load', resolve, {once: true});
|
iframe.addEventListener('load', resolve, {once: true});
|
||||||
@ -135,7 +137,7 @@ promise_test(async (t) => {
|
|||||||
const iframe = document.createElement('iframe');
|
const iframe = document.createElement('iframe');
|
||||||
t.add_cleanup(() => iframe.remove());
|
t.add_cleanup(() => iframe.remove());
|
||||||
|
|
||||||
iframe.src = 'resources/reporting-empty-frame.html';
|
iframe.src = FRAME_URL;
|
||||||
const targetUrl = `/common/blank.html?${token()}`;
|
const targetUrl = `/common/blank.html?${token()}`;
|
||||||
iframe.addEventListener('load', t.step_func(() => {
|
iframe.addEventListener('load', t.step_func(() => {
|
||||||
const nested = iframe.contentDocument.createElement('iframe');
|
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