blob: 50fb54d485dd548623b843fc7073c73b12f0ddcd [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/containers/contains.h"
#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/test/bind.h"
#include "content/public/browser/network_service_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/url_loader_monitor.h"
#include "content/shell/browser/shell.h"
#include "content/shell/browser/shell_content_browser_client.h"
#include "net/dns/mock_host_resolver.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/trust_tokens.mojom.h"
#include "services/network/public/mojom/url_loader.mojom-shared.h"
#include "services/network/test/trust_token_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
// These integration tests verify that calling the Fetch API with Trust Tokens
// parameters results in the parameters' counterparts appearing downstream in
// network::ResourceRequest.
//
// Separately, Blink layout tests check that the API correctly rejects invalid
// input.
namespace content {
class TrustTokenParametersBrowsertest
: public ::testing::WithParamInterface<network::TrustTokenTestParameters>,
public ContentBrowserTest {
public:
TrustTokenParametersBrowsertest() = default;
};
INSTANTIATE_TEST_SUITE_P(
WithIssuanceParameters,
TrustTokenParametersBrowsertest,
testing::ValuesIn(network::kIssuanceTrustTokenTestParameters));
INSTANTIATE_TEST_SUITE_P(
WithRedemptionParameters,
TrustTokenParametersBrowsertest,
testing::ValuesIn(network::kRedemptionTrustTokenTestParameters));
INSTANTIATE_TEST_SUITE_P(
WithSigningParameters,
TrustTokenParametersBrowsertest,
testing::ValuesIn(network::kSigningTrustTokenTestParameters));
IN_PROC_BROWSER_TEST_P(TrustTokenParametersBrowsertest,
PopulatesResourceRequestViaFetch) {
ASSERT_TRUE(embedded_test_server()->Start());
network::TrustTokenParametersAndSerialization
expected_params_and_serialization =
network::SerializeTrustTokenParametersAndConstructExpectation(
GetParam());
GURL url(embedded_test_server()->GetURL("/title1.html"));
GURL trust_token_url(embedded_test_server()->GetURL("/title2.html"));
URLLoaderMonitor monitor({trust_token_url});
EXPECT_TRUE(NavigateToURL(shell(), url));
ExecuteScriptAsync(
shell(), JsReplace("fetch($1, {privateToken: ", trust_token_url) +
expected_params_and_serialization.serialized_params + "});");
monitor.WaitForUrls();
std::optional<network::ResourceRequest> request =
monitor.GetRequestInfo(trust_token_url);
ASSERT_TRUE(request);
ASSERT_TRUE(request->trust_token_params);
EXPECT_TRUE(request->trust_token_params.as_ptr().Equals(
expected_params_and_serialization.params));
}
IN_PROC_BROWSER_TEST_P(TrustTokenParametersBrowsertest,
PopulatesResourceRequestViaIframe) {
ASSERT_TRUE(embedded_test_server()->Start());
network::TrustTokenParametersAndSerialization
expected_params_and_serialization =
network::SerializeTrustTokenParametersAndConstructExpectation(
GetParam());
// In the iframe interface to private state tokens, we only accept the
// kSigning variant, i.e. the send-redemption-record operation.
if (expected_params_and_serialization.params->operation !=
network::mojom::TrustTokenOperationType::kSigning) {
return;
}
GURL url(embedded_test_server()->GetURL("/title1.html"));
GURL trust_token_url(embedded_test_server()->GetURL("/title2.html"));
URLLoaderMonitor monitor({trust_token_url});
EXPECT_TRUE(NavigateToURL(shell(), url));
EXPECT_TRUE(ExecJs(
shell(), JsReplace("let iframe = document.createElement('iframe');"
"iframe.src = $1;"
"iframe.privateToken = $2;"
"document.body.appendChild(iframe);",
trust_token_url,
expected_params_and_serialization.serialized_params)));
monitor.WaitForUrls();
std::optional<network::ResourceRequest> request =
monitor.GetRequestInfo(trust_token_url);
ASSERT_TRUE(request);
ASSERT_TRUE(request->trust_token_params);
EXPECT_TRUE(request->trust_token_params.as_ptr().Equals(
expected_params_and_serialization.params));
}
IN_PROC_BROWSER_TEST_P(TrustTokenParametersBrowsertest,
PopulatesResourceRequestViaXhr) {
ASSERT_TRUE(embedded_test_server()->Start());
network::TrustTokenParametersAndSerialization
expected_params_and_serialization =
network::SerializeTrustTokenParametersAndConstructExpectation(
GetParam());
GURL url(embedded_test_server()->GetURL("/title1.html"));
GURL trust_token_url(embedded_test_server()->GetURL("/title2.html"));
URLLoaderMonitor monitor({trust_token_url});
EXPECT_TRUE(NavigateToURL(shell(), url));
EXPECT_TRUE(ExecJs(
shell(),
base::StrCat({"let request = new XMLHttpRequest(); ",
JsReplace("request.open(\"GET\", $1);", trust_token_url),
"request.setPrivateToken(",
expected_params_and_serialization.serialized_params,
"); request.send();"})));
monitor.WaitForUrls();
std::optional<network::ResourceRequest> request =
monitor.GetRequestInfo(trust_token_url);
ASSERT_TRUE(request);
EXPECT_TRUE(request->trust_token_params.as_ptr().Equals(
expected_params_and_serialization.params));
}
class TrustTokenPermissionsPolicyBrowsertest : public ContentBrowserTest {
public:
TrustTokenPermissionsPolicyBrowsertest() = default;
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
}
};
IN_PROC_BROWSER_TEST_F(TrustTokenPermissionsPolicyBrowsertest,
PassesDefaultValueToFactoryParams) {
// Since the private-state-token-redemption Permissions Policy feature is
// enabled by default in cross-site frames, the child's
// URLLoaderFactoryParams should be populated with
// TrustTokenOperationPolicyVerdict::kPotentiallyPermit.
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)"));
base::RunLoop run_loop;
ShellContentBrowserClient::Get()->set_url_loader_factory_params_callback(
base::BindLambdaForTesting(
[&](const network::mojom::URLLoaderFactoryParams* params,
const url::Origin& origin, bool unused_is_for_isolated_world,
bool unused_is_for_service_worker) {
if (base::Contains(origin.host(), 'b')) {
ASSERT_TRUE(params);
ASSERT_THAT(params->trust_token_redemption_policy,
network::mojom::TrustTokenOperationPolicyVerdict::
kPotentiallyPermit);
ASSERT_THAT(params->trust_token_issuance_policy,
network::mojom::TrustTokenOperationPolicyVerdict::
kPotentiallyPermit);
run_loop.Quit();
}
}));
EXPECT_TRUE(NavigateToURL(shell(), url));
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(TrustTokenPermissionsPolicyBrowsertest,
PassesNegativeRedemptionValueToFactoryParams) {
// Even though the private-state-token-redemption Permissions Policy feature
// is enabled by default in cross-site frames, the allow attribute on the
// iframe can disable it for the b.com frame, so the child's
// URLLoaderFactoryParams should be populated with
// TrustTokenOperationPolicyVerdict::kForbid.
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(
"a.com",
"/cross_site_iframe_factory.html?a(b{"
"disallow-private-state-token-redemption})"));
base::RunLoop run_loop;
ShellContentBrowserClient::Get()->set_url_loader_factory_params_callback(
base::BindLambdaForTesting(
[&](const network::mojom::URLLoaderFactoryParams* params,
const url::Origin& origin, bool unused_is_for_isolated_world,
bool unused_is_for_service_worker) {
if (base::Contains(origin.host(), "b")) {
ASSERT_TRUE(params);
ASSERT_THAT(
params->trust_token_redemption_policy,
network::mojom::TrustTokenOperationPolicyVerdict::kForbid);
ASSERT_THAT(params->trust_token_issuance_policy,
network::mojom::TrustTokenOperationPolicyVerdict::
kPotentiallyPermit);
run_loop.Quit();
}
}));
EXPECT_TRUE(NavigateToURL(shell(), url));
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(TrustTokenPermissionsPolicyBrowsertest,
PassesNegativeIssuanceValueToFactoryParams) {
// Even though the private-state-token-issuance Permissions Policy feature is
// enabled by default in cross-site frames, the allow attribute on the iframe
// can disable it for the b.com frame, so the child's URLLoaderFactoryParams
// should be populated with
// TrustTokenOperationPolicyVerdict::kForbid.
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(
"a.com",
"/cross_site_iframe_factory.html?a(b{"
"disallow-private-state-token-issuance})"));
base::RunLoop run_loop;
ShellContentBrowserClient::Get()->set_url_loader_factory_params_callback(
base::BindLambdaForTesting(
[&](const network::mojom::URLLoaderFactoryParams* params,
const url::Origin& origin, bool unused_is_for_isolated_world,
bool unused_is_for_service_worker) {
if (base::Contains(origin.host(), "b")) {
ASSERT_TRUE(params);
ASSERT_THAT(params->trust_token_redemption_policy,
network::mojom::TrustTokenOperationPolicyVerdict::
kPotentiallyPermit);
ASSERT_THAT(
params->trust_token_issuance_policy,
network::mojom::TrustTokenOperationPolicyVerdict::kForbid);
run_loop.Quit();
}
}));
EXPECT_TRUE(NavigateToURL(shell(), url));
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(TrustTokenPermissionsPolicyBrowsertest,
PassesDefaultValueToFactoryParamsAfterCrash) {
// Since the private-state-token-redemption Permissions Policy feature is
// enabled by default in cross-site frames, the child's
// URLLoaderFactoryParams should be populated with
// TrustTokenOperationPolicyVerdict::kPotentiallyPermit.
//
// In particular, this should be true for factory params repopulated after a
// network service crash!
// Can't test this on bots that use an in-process network service.
if (IsInProcessNetworkService())
return;
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)"));
EXPECT_TRUE(NavigateToURL(shell(), url));
base::RunLoop run_loop;
ShellContentBrowserClient::Get()->set_url_loader_factory_params_callback(
base::BindLambdaForTesting(
[&](const network::mojom::URLLoaderFactoryParams* params,
const url::Origin& origin, bool unused_is_for_isolated_world,
bool unused_is_for_service_worker) {
if (base::Contains(origin.host(), 'b')) {
ASSERT_TRUE(params);
ASSERT_THAT(params->trust_token_redemption_policy,
network::mojom::TrustTokenOperationPolicyVerdict::
kPotentiallyPermit);
ASSERT_THAT(params->trust_token_issuance_policy,
network::mojom::TrustTokenOperationPolicyVerdict::
kPotentiallyPermit);
run_loop.Quit();
}
}));
SimulateNetworkServiceCrash();
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(TrustTokenPermissionsPolicyBrowsertest,
PassesNegativeRedemptionValueToFactoryParamsAfterCrash) {
// Even though the private-state-token-redemption Permissions Policy feature
// is enabled by default in cross-site frames, the allow attribute on the
// iframe can disable it for the b.com frame, so the child's
// URLLoaderFactoryParams should be populated with
// TrustTokenOperationPolicyVerdict::kForbid.
//
// In particular, this should be true for factory params repopulated after a
// network service crash!
// Can't test this on bots that use an in-process network service.
if (IsInProcessNetworkService())
return;
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(
"a.com",
"/cross_site_iframe_factory.html?a(b{"
"disallow-private-state-token-redemption})"));
EXPECT_TRUE(NavigateToURL(shell(), url));
base::RunLoop run_loop;
ShellContentBrowserClient::Get()->set_url_loader_factory_params_callback(
base::BindLambdaForTesting(
[&](const network::mojom::URLLoaderFactoryParams* params,
const url::Origin& origin, bool unused_is_for_isolated_world,
bool unused_is_for_service_worker) {
if (base::Contains(origin.host(), "b")) {
ASSERT_TRUE(params);
ASSERT_THAT(
params->trust_token_redemption_policy,
network::mojom::TrustTokenOperationPolicyVerdict::kForbid);
ASSERT_THAT(params->trust_token_issuance_policy,
network::mojom::TrustTokenOperationPolicyVerdict::
kPotentiallyPermit);
run_loop.Quit();
}
}));
SimulateNetworkServiceCrash();
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(TrustTokenPermissionsPolicyBrowsertest,
PassesNegativeIssuanceValueToFactoryParamsAfterCrash) {
// Even though the private-state-token-issuance Permissions Policy feature is
// enabled by default in cross-site frames, the allow attribute on the iframe
// can disable it for the b.com frame, so the child's URLLoaderFactoryParams
// should be populated with
// TrustTokenOperationPolicyVerdict::kForbid.
//
// In particular, this should be true for factory params repopulated after a
// network service crash!
// Can't test this on bots that use an in-process network service.
if (IsInProcessNetworkService()) {
return;
}
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(
"a.com",
"/cross_site_iframe_factory.html?a(b{"
"disallow-private-state-token-issuance})"));
EXPECT_TRUE(NavigateToURL(shell(), url));
base::RunLoop run_loop;
ShellContentBrowserClient::Get()->set_url_loader_factory_params_callback(
base::BindLambdaForTesting(
[&](const network::mojom::URLLoaderFactoryParams* params,
const url::Origin& origin, bool unused_is_for_isolated_world,
bool unused_is_for_service_worker) {
if (base::Contains(origin.host(), "b")) {
ASSERT_TRUE(params);
ASSERT_THAT(params->trust_token_redemption_policy,
network::mojom::TrustTokenOperationPolicyVerdict::
kPotentiallyPermit);
ASSERT_THAT(
params->trust_token_issuance_policy,
network::mojom::TrustTokenOperationPolicyVerdict::kForbid);
run_loop.Quit();
}
}));
SimulateNetworkServiceCrash();
run_loop.Run();
}
constexpr char kPrivateStateTokenRedemptionPolicyHeader[] =
"/set-header?Feature-Policy: private-state-token-redemption 'self'";
constexpr char kPrivateStateTokenIssuancePolicyHeader[] =
"/set-header?Feature-Policy: private-state-token-issuance 'self'";
class TrustTokenPermissionsPolicyFencedFrameTest
: public TrustTokenPermissionsPolicyBrowsertest,
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
public:
TrustTokenPermissionsPolicyFencedFrameTest()
: policy_header_in_primary_page_(std::get<0>(GetParam())),
policy_header_in_fenced_frame_page_(std::get<1>(GetParam())) {}
content::test::FencedFrameTestHelper& fenced_frame_test_helper() {
return fenced_frame_helper_;
}
protected:
const bool policy_header_in_primary_page_;
const bool policy_header_in_fenced_frame_page_;
private:
content::test::FencedFrameTestHelper fenced_frame_helper_;
};
INSTANTIATE_TEST_SUITE_P(All,
TrustTokenPermissionsPolicyFencedFrameTest,
::testing::Combine(::testing::Bool(),
::testing::Bool()));
IN_PROC_BROWSER_TEST_P(TrustTokenPermissionsPolicyFencedFrameTest,
PassesNegativeRedemptionValueToFactoryParams) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL primary_url(embedded_test_server()->GetURL(
"a.com", policy_header_in_primary_page_
? kPrivateStateTokenRedemptionPolicyHeader
: "/title1.html"));
GURL fenced_frame_url(embedded_test_server()->GetURL(
"b.com", policy_header_in_fenced_frame_page_
? std::string(kPrivateStateTokenRedemptionPolicyHeader) +
"&Supports-Loading-Mode: fenced-frame"
: "/fenced_frames/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), primary_url));
base::RunLoop run_loop;
ShellContentBrowserClient::Get()->set_url_loader_factory_params_callback(
base::BindLambdaForTesting(
[&](const network::mojom::URLLoaderFactoryParams* params,
const url::Origin& origin, bool unused_is_for_isolated_world,
bool unused_is_for_service_worker) {
if (origin.host() != "b.com")
return;
EXPECT_TRUE(params);
EXPECT_THAT(
params->trust_token_redemption_policy,
network::mojom::TrustTokenOperationPolicyVerdict::kForbid);
run_loop.Quit();
}));
ASSERT_TRUE(fenced_frame_test_helper().CreateFencedFrame(
shell()->web_contents()->GetPrimaryMainFrame(), fenced_frame_url));
run_loop.Run();
}
IN_PROC_BROWSER_TEST_P(TrustTokenPermissionsPolicyFencedFrameTest,
PassesNegativeIssuanceValueToFactoryParams) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL primary_url(embedded_test_server()->GetURL(
"a.com", policy_header_in_primary_page_
? kPrivateStateTokenIssuancePolicyHeader
: "/title1.html"));
GURL fenced_frame_url(embedded_test_server()->GetURL(
"b.com", policy_header_in_fenced_frame_page_
? std::string(kPrivateStateTokenIssuancePolicyHeader) +
"&Supports-Loading-Mode: fenced-frame"
: "/fenced_frames/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), primary_url));
base::RunLoop run_loop;
ShellContentBrowserClient::Get()->set_url_loader_factory_params_callback(
base::BindLambdaForTesting(
[&](const network::mojom::URLLoaderFactoryParams* params,
const url::Origin& origin, bool unused_is_for_isolated_world,
bool unused_is_for_service_worker) {
if (origin.host() != "b.com") {
return;
}
EXPECT_TRUE(params);
EXPECT_THAT(
params->trust_token_issuance_policy,
network::mojom::TrustTokenOperationPolicyVerdict::kForbid);
run_loop.Quit();
}));
ASSERT_TRUE(fenced_frame_test_helper().CreateFencedFrame(
shell()->web_contents()->GetPrimaryMainFrame(), fenced_frame_url));
run_loop.Run();
}
} // namespace content