blob: acf7cb270a0f7059f5a1ea237fdb9b9197f44c25 [file] [log] [blame]
// Copyright 2021 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/memory/raw_ptr.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "base/command_line.h"
#include "base/test/scoped_feature_list.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_base.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/content_mock_cert_verifier.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
namespace content {
namespace {
class MockContentBrowserClient : public ContentBrowserTestContentBrowserClient {
public:
bool ShouldDisableOriginAgentClusterDefault(BrowserContext*) override {
return should_disable_origin_agent_cluster_default_;
}
bool should_disable_origin_agent_cluster_default_ = false;
};
} // anonymous namespace
// Test the effect of the OriginAgentCluster: header on document.domain
// settability and how it (doesn't) affect process assignment.
class OriginAgentClusterBrowserTest : public ContentBrowserTest {
public:
OriginAgentClusterBrowserTest()
: OriginAgentClusterBrowserTest(
/* origin_cluster_default_enabled */ false,
/* secure_context */ true) {}
~OriginAgentClusterBrowserTest() override = default;
void SetUp() override {
mock_cert_verifier_.SetUpCommandLine(
base::CommandLine::ForCurrentProcess());
ContentBrowserTest::SetUp();
}
protected:
enum OriginAgentClusterState { kUnset, kSetTrue, kSetFalse, kMalformed };
OriginAgentClusterBrowserTest(bool origin_cluster_default_enabled,
bool secure_context)
: server_(net::EmbeddedTestServer::TYPE_HTTPS),
origin_cluster_default_enabled_(origin_cluster_default_enabled),
secure_context_(secure_context) {
server_.AddDefaultHandlers(GetTestDataFilePath());
// InitWithFeatures needs to be called in the constructor in multi-threaded
// tests.
std::vector<base::test::FeatureRef> enabled, disabled;
(origin_cluster_default_enabled_ ? enabled : disabled)
.push_back(blink::features::kOriginAgentClusterDefaultEnabled);
// TODO(https://crbug.com/40259221): update this test to be parameterized on
// kOriginKeyedProcessesByDefault, and then make sure all the tests have
// correct expectations both with and without. This will assist in removing
// the kOriginAgentClusterDefaultEnabled flag.
disabled.push_back(features::kOriginKeyedProcessesByDefault);
features_.InitWithFeatures(enabled, disabled);
}
void SetUpOnMainThread() override {
mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
host_resolver()->AddRule("*", "127.0.0.1");
SetupCrossSiteRedirector(server());
ASSERT_TRUE(server()->Start());
browser_client_ = std::make_unique<MockContentBrowserClient>();
}
void TearDownOnMainThread() override { browser_client_.reset(); }
void SetUpInProcessBrowserTestFixture() override {
ContentBrowserTest::SetUpInProcessBrowserTestFixture();
mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
}
void TearDownInProcessBrowserTestFixture() override {
ContentBrowserTest::TearDownInProcessBrowserTestFixture();
mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
}
net::EmbeddedTestServer* server() {
return secure_context_ ? &server_ : embedded_test_server();
}
RenderProcessHost* NavigateAndGetProcess(const char* domain,
OriginAgentClusterState oac_state) {
DCHECK(domain);
WebContents* contents = Navigate(domain, oac_state);
DCHECK(contents);
return static_cast<WebContentsImpl*>(contents)
->GetPrimaryMainFrame()
->GetProcess();
}
bool CanDocumentDomain(std::string from,
std::string to,
OriginAgentClusterState oac_state) {
WebContents* contents = Navigate(from, oac_state);
DCHECK(contents);
return EvalJs(contents, SetDocumentDomainTo(to)).ExtractBool();
}
// This tries to set document.domain. But instead of checking whether setting
// succeeded (which is what CanDocumentDomain above does), it checks whether
// a warning message is posted to the console.
bool CanDocumentDomainMessage(std::string from,
std::string to,
OriginAgentClusterState oac_state) {
WebContents* contents = Navigate(from, oac_state);
DCHECK(contents);
WebContentsConsoleObserver console(contents);
console.SetPattern("document.domain mutation is ignored*");
CHECK(ExecJs(contents, SetDocumentDomainTo(to)));
return !console.messages().size();
}
// Simulate setting the OriginAgentClusterDefaultEnabled enterprise policy.
void SetEnterprisePolicy(bool value) {
// Note that the enterprise policy has different 'polarity', and true
// means Chromium picks the default and false is legacy behaviour, while
// ContentBrowserClientShould::DisableOriginAgentClusterDefault is a
// disable switch, meaning that false means Chromium picks the default
// and true is legacy behaviour.
browser_client_->should_disable_origin_agent_cluster_default_ = !value;
}
private:
std::string SetDocumentDomainTo(std::string to) const {
// Assign document.domain and check whether it changed.
// Wrap the statement in a try-catch, since since document.domain setting
// may throw.
return JsReplace(
"try { "
"document.domain = $1; "
"document.domain == $1; "
"} catch (e) { false; }",
to);
}
WebContents* Navigate(std::string domain, std::string path) {
GURL url(server()->GetURL(domain, path));
EXPECT_TRUE(NavigateToURL(shell(), url));
return shell()->web_contents();
}
// For the purpose of this test, we only care about the Origin-Agent-Cluster:
// header. The test setup has four pages corresponding to the three valid
// states (absent, true ("?1"), and false ("?0"), and one malformed one
// ("?2"). For ease of use, we have an enum to designate the appropriate
// test page.
WebContents* Navigate(std::string domain,
const OriginAgentClusterState state) {
switch (state) {
case kUnset:
return Navigate(domain, "/empty.html");
case kSetTrue:
return Navigate(domain, "/set-header?Origin-Agent-Cluster: ?1");
case kSetFalse:
return Navigate(domain, "/set-header?Origin-Agent-Cluster: ?0");
case kMalformed:
return Navigate(domain, "/set-header?Origin-Agent-Cluster: potato");
}
}
protected:
// https:-embedded test server.
// The BrowserTestBase::embedded_test_server_ is a private member and is
// constructed as http:-only, and so we cannot change or replace it.
// The setup of server_ emulates that of embedded_test_server_.
net::EmbeddedTestServer server_;
content::ContentMockCertVerifier mock_cert_verifier_;
std::unique_ptr<MockContentBrowserClient> browser_client_;
const bool origin_cluster_default_enabled_;
const bool secure_context_;
base::test::ScopedFeatureList features_;
};
// Test fixture wih the default behaviour change enabled.
// (blink::features::kOriginAgentClusterDefaultEnabled)
class OriginAgentClusterEnabledBrowserTest
: public OriginAgentClusterBrowserTest {
public:
OriginAgentClusterEnabledBrowserTest()
: OriginAgentClusterBrowserTest(
/* origin_cluster_default_enabled */ true,
/* secure_context */ true) {}
~OriginAgentClusterEnabledBrowserTest() override = default;
};
// Test fixture wih the default behaviour change enabled, but using an insecure
// context. (blink::features::kOriginAgentClusterDefaultEnabled + http:)
class OriginAgentClusterInsecureEnabledBrowserTest
: public OriginAgentClusterBrowserTest {
public:
OriginAgentClusterInsecureEnabledBrowserTest()
: OriginAgentClusterBrowserTest(
/* origin_cluster_default_enabled */ true,
/* secure_context */ false) {}
~OriginAgentClusterInsecureEnabledBrowserTest() override = default;
};
// DocumentDomain: Can we set document.domain?
//
// Tests are for each Origin-Agent-Cluster: header state
// (enabled/disabled/default/malformed), and flag being enabled/disabled.
//
// These tests ensure that the flag will change the default behaviour only.
IN_PROC_BROWSER_TEST_F(OriginAgentClusterBrowserTest, DocumentDomain_Default) {
EXPECT_TRUE(CanDocumentDomain("a.domain.test", "domain.test", kUnset));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterBrowserTest, DocumentDomain_Enabled) {
EXPECT_FALSE(CanDocumentDomain("a.domain.test", "domain.test", kSetTrue));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterBrowserTest, DocumentDomain_Disabled) {
EXPECT_TRUE(CanDocumentDomain("a.domain.test", "domain.test", kSetFalse));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterBrowserTest,
DocumentDomain_Malformed) {
EXPECT_TRUE(CanDocumentDomain("a.domain.test", "domain.test", kMalformed));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
DocumentDomain_Default) {
EXPECT_FALSE(CanDocumentDomain("a.domain.test", "domain.test", kUnset));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
DocumentDomain_Enabled) {
EXPECT_FALSE(CanDocumentDomain("a.domain.test", "domain.test", kSetTrue));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
DocumentDomain_Disabled) {
EXPECT_TRUE(CanDocumentDomain("a.domain.test", "domain.test", kSetFalse));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
DocumentDomain_Malformed) {
EXPECT_FALSE(CanDocumentDomain("a.domain.test", "domain.test", kMalformed));
}
// Process: Will two pages (same site, different origin) be assigned to the
// same process?
//
// Tests are for each Origin-Agent-Cluster: header state
// (enabled/disabled/default/malformed), and the flag being enabled/disabled.
//
// These tests mainly ensure that the enabled-flag will not actually change
// this behaviour, since we use same-process clustering. (Unlike some earlier
// plans, where we were trying to change the process model as well.)
IN_PROC_BROWSER_TEST_F(OriginAgentClusterBrowserTest, SameProcess_Default) {
EXPECT_EQ(NavigateAndGetProcess("a.domain.test", kUnset),
NavigateAndGetProcess("b.domain.test", kUnset));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterBrowserTest, SameProcess_Enabled) {
EXPECT_NE(NavigateAndGetProcess("a.domain.test", kSetTrue),
NavigateAndGetProcess("b.domain.test", kSetTrue));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterBrowserTest, SameProcess_Disabled) {
EXPECT_EQ(NavigateAndGetProcess("a.domain.test", kSetFalse),
NavigateAndGetProcess("b.domain.test", kSetFalse));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterBrowserTest, SameProcess_Malformed) {
EXPECT_EQ(NavigateAndGetProcess("a.domain.test", kMalformed),
NavigateAndGetProcess("b.domain.test", kMalformed));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
SameProcess_Default) {
EXPECT_EQ(NavigateAndGetProcess("a.domain.test", kUnset),
NavigateAndGetProcess("b.domain.test", kUnset));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
SameProcess_Enabled) {
EXPECT_NE(NavigateAndGetProcess("a.domain.test", kSetTrue),
NavigateAndGetProcess("b.domain.test", kSetTrue));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
SameProcess_Disabled) {
EXPECT_EQ(NavigateAndGetProcess("a.domain.test", kSetFalse),
NavigateAndGetProcess("b.domain.test", kSetFalse));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
SameProcess_Malformed) {
EXPECT_EQ(NavigateAndGetProcess("a.domain.test", kMalformed),
NavigateAndGetProcess("b.domain.test", kMalformed));
}
// WarningMessage: Test whether setting document.domain triggers a console
// message, for each Origin-Agent-Cluster: header state
// (enabled/disabled/default/malformed), and each flag (none/enable/message).
IN_PROC_BROWSER_TEST_F(OriginAgentClusterBrowserTest, WarningMessage_Default) {
EXPECT_TRUE(CanDocumentDomainMessage("a.domain.test", "domain.test", kUnset));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterBrowserTest, WarningMessage_Enabled) {
EXPECT_FALSE(
CanDocumentDomainMessage("a.domain.test", "domain.test", kSetTrue));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterBrowserTest, WarningMessage_Disabled) {
EXPECT_TRUE(
CanDocumentDomainMessage("a.domain.test", "domain.test", kSetFalse));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterBrowserTest,
WarningMessage_Malformed) {
EXPECT_TRUE(
CanDocumentDomainMessage("a.domain.test", "domain.test", kMalformed));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
WarningMessage_Default) {
EXPECT_FALSE(
CanDocumentDomainMessage("a.domain.test", "domain.test", kUnset));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
WarningMessage_Enabled) {
EXPECT_FALSE(
CanDocumentDomainMessage("a.domain.test", "domain.test", kSetTrue));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
WarningMessage_Disabled) {
EXPECT_TRUE(
CanDocumentDomainMessage("a.domain.test", "domain.test", kSetFalse));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
WarningMessage_Malformed) {
EXPECT_FALSE(
CanDocumentDomainMessage("a.domain.test", "domain.test", kMalformed));
}
// Policy: Ensure that the legacy behaviour remains if the appropriate
// enterprise policy is set.
//
// (The case without policy is adequately covered by the tests above, since
// none of them modify the policy.)
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
Policy_SetTrue_Default) {
SetEnterprisePolicy(false);
EXPECT_TRUE(CanDocumentDomain("a.domain.test", "domain.test", kUnset));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
Policy_SetFalse_Default) {
SetEnterprisePolicy(true);
EXPECT_FALSE(CanDocumentDomain("a.domain.test", "domain.test", kUnset));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
Policy_SetTrue_Enabled) {
SetEnterprisePolicy(false);
EXPECT_FALSE(CanDocumentDomain("a.domain.test", "domain.test", kSetTrue));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
Policy_SetFalse_Enabled) {
SetEnterprisePolicy(true);
EXPECT_FALSE(CanDocumentDomain("a.domain.test", "domain.test", kSetTrue));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
Policy_SetTrue_Disabled) {
SetEnterprisePolicy(false);
EXPECT_TRUE(CanDocumentDomain("a.domain.test", "domain.test", kSetFalse));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
Policy_SetFalse_Disabled) {
SetEnterprisePolicy(true);
EXPECT_TRUE(CanDocumentDomain("a.domain.test", "domain.test", kSetFalse));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
Policy_SetTrue_Malformed) {
SetEnterprisePolicy(false);
EXPECT_TRUE(CanDocumentDomain("a.domain.test", "domain.test", kMalformed));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
Policy_SetFalse_Malformed) {
SetEnterprisePolicy(true);
EXPECT_FALSE(CanDocumentDomain("a.domain.test", "domain.test", kMalformed));
}
// Make sure that the default isolation state is correctly generated with
// respect to the enterprise policy.
IN_PROC_BROWSER_TEST_F(OriginAgentClusterEnabledBrowserTest,
DefaultIsolationStateEnterprisePolicyTest) {
// OAC by default not disabled by enterprise policy.
SetEnterprisePolicy(true);
{
OriginAgentClusterIsolationState default_isolation_state =
OriginAgentClusterIsolationState::CreateForDefaultIsolation(nullptr);
EXPECT_TRUE(default_isolation_state.is_origin_agent_cluster());
}
// OAC by default disabled by enterprise policy.
SetEnterprisePolicy(false);
{
OriginAgentClusterIsolationState default_isolation_state =
OriginAgentClusterIsolationState::CreateForDefaultIsolation(nullptr);
EXPECT_FALSE(default_isolation_state.is_origin_agent_cluster());
}
}
// Test that the legacy behaviour continues working in insecure contexts.
//
// Origin-Agent-Cluster: is supposed to be a "powerful feature", which is
// restricted to secure contexts. But disabling it is certainly not powerful,
// and is specifically geared towards legacy applications, which means we
// should ensure that one can still set document.domain in an insecure context
// when Origin-Agent-Cluster: ?0 is set.
IN_PROC_BROWSER_TEST_F(OriginAgentClusterInsecureEnabledBrowserTest,
DocumentDomain_Default) {
EXPECT_TRUE(CanDocumentDomain("a.domain.test", "domain.test", kUnset));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterInsecureEnabledBrowserTest,
DocumentDomain_Disabled) {
EXPECT_TRUE(CanDocumentDomain("a.domain.test", "domain.test", kSetFalse));
}
IN_PROC_BROWSER_TEST_F(OriginAgentClusterInsecureEnabledBrowserTest,
DocumentDomain_Malformed) {
EXPECT_TRUE(CanDocumentDomain("a.domain.test", "domain.test", kMalformed));
}
// We are (deliberately) not testing the kSetTrue case in this set of tests,
// since that is bound to a secure context. But the disable cases should
// continue to remain compatible with legacy behaviour.
} // namespace content