blob: 9426995a25151d9b7863589ae1c658ba33810101 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/public/browser/site_isolation_policy.h"
#include "base/command_line.h"
#include "base/containers/span.h"
#include "base/feature_list.h"
#include "base/test/scoped_amount_of_physical_memory_override.h"
#include "base/test/scoped_feature_list.h"
#include "build/android_buildflags.h"
#include "build/build_config.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "chrome/test/base/platform_browser_test.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
#include "components/site_isolation/features.h"
#include "components/site_isolation/site_isolation_policy.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/site_instance.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_ANDROID)
#include "chrome/browser/safe_browsing/android/advanced_protection_status_manager_test_util.h"
#endif
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/ui_test_utils.h"
#endif
class SiteIsolationPolicyBrowserTest : public PlatformBrowserTest {
public:
SiteIsolationPolicyBrowserTest(const SiteIsolationPolicyBrowserTest&) =
delete;
SiteIsolationPolicyBrowserTest& operator=(
const SiteIsolationPolicyBrowserTest&) = delete;
protected:
SiteIsolationPolicyBrowserTest() = default;
struct Expectations {
const char* url;
bool isolated;
};
void CheckExpectations(base::span<Expectations> expectations,
size_t spanification_suspected_redundant_count) {
// TODO(crbug.com/431824301): Remove unneeded parameter once validated to be
// redundant in M143.
CHECK(spanification_suspected_redundant_count == expectations.size(),
base::NotFatalUntil::M143);
content::BrowserContext* context = chrome_test_utils::GetProfile(this);
for (size_t i = 0; i < spanification_suspected_redundant_count; ++i) {
const GURL url(expectations[i].url);
auto instance = content::SiteInstance::CreateForURL(context, url);
EXPECT_EQ(expectations[i].isolated, instance->RequiresDedicatedProcess())
<< "; url = " << url;
}
}
void CheckIsolatedOriginExpectations(
base::span<Expectations> expectations,
size_t spanification_suspected_redundant_count) {
// TODO(crbug.com/431824301): Remove unneeded parameter once validated to be
// redundant in M143.
CHECK(spanification_suspected_redundant_count == expectations.size(),
base::NotFatalUntil::M143);
if (!content::AreAllSitesIsolatedForTesting()) {
CheckExpectations(expectations, spanification_suspected_redundant_count);
}
auto* policy = content::ChildProcessSecurityPolicy::GetInstance();
for (size_t i = 0; i < spanification_suspected_redundant_count; ++i) {
const GURL url(expectations[i].url);
const url::Origin origin = url::Origin::Create(url);
EXPECT_EQ(expectations[i].isolated,
policy->IsGloballyIsolatedOriginForTesting(origin))
<< "; origin = " << origin;
}
}
testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
};
template <bool policy_value>
class SitePerProcessPolicyBrowserTest : public SiteIsolationPolicyBrowserTest {
public:
SitePerProcessPolicyBrowserTest(const SitePerProcessPolicyBrowserTest&) =
delete;
SitePerProcessPolicyBrowserTest& operator=(
const SitePerProcessPolicyBrowserTest&) = delete;
protected:
SitePerProcessPolicyBrowserTest() = default;
void SetUpInProcessBrowserTestFixture() override {
// We setup the policy here, because the policy must be 'live' before
// the renderer is created, since the value for this policy is passed
// to the renderer via a command-line. Setting the policy in the test
// itself or in SetUpOnMainThread works for update-able policies, but
// is too late for this one.
provider_.SetDefaultReturns(
true /* is_initialization_complete_return */,
true /* is_first_policy_load_complete_return */);
policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
policy::PolicyMap values;
#if BUILDFLAG(IS_ANDROID)
const char* kPolicyName = policy::key::kSitePerProcessAndroid;
#else
const char* kPolicyName = policy::key::kSitePerProcess;
#endif
values.Set(kPolicyName, policy::POLICY_LEVEL_MANDATORY,
policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
base::Value(policy_value), nullptr);
provider_.UpdateChromePolicy(values);
}
};
typedef SitePerProcessPolicyBrowserTest<true>
SitePerProcessPolicyBrowserTestEnabled;
typedef SitePerProcessPolicyBrowserTest<false>
SitePerProcessPolicyBrowserTestDisabled;
// Ensure that --disable-site-isolation-trials and/or
// --disable-site-isolation-for-enterprise-policy do not override policies.
class NoOverrideSitePerProcessPolicyBrowserTest
: public SitePerProcessPolicyBrowserTestEnabled {
public:
NoOverrideSitePerProcessPolicyBrowserTest(
const NoOverrideSitePerProcessPolicyBrowserTest&) = delete;
NoOverrideSitePerProcessPolicyBrowserTest& operator=(
const NoOverrideSitePerProcessPolicyBrowserTest&) = delete;
protected:
NoOverrideSitePerProcessPolicyBrowserTest() = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kDisableSiteIsolation);
#if BUILDFLAG(IS_ANDROID)
command_line->AppendSwitch(switches::kDisableSiteIsolationForPolicy);
#endif
}
};
IN_PROC_BROWSER_TEST_F(SitePerProcessPolicyBrowserTestEnabled, Simple) {
Expectations expectations[] = {
{"https://foo.com/noodles.html", true},
{"http://foo.com/", true},
{"http://example.org/pumpkins.html", true},
};
CheckExpectations(expectations, std::size(expectations));
}
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
// The policy is not supported on Android
class IsolateOriginsPolicyBrowserTest : public SiteIsolationPolicyBrowserTest {
public:
IsolateOriginsPolicyBrowserTest(const IsolateOriginsPolicyBrowserTest&) =
delete;
IsolateOriginsPolicyBrowserTest& operator=(
const IsolateOriginsPolicyBrowserTest&) = delete;
protected:
IsolateOriginsPolicyBrowserTest() = default;
void SetUpInProcessBrowserTestFixture() override {
// We setup the policy here, because the policy must be 'live' before
// the renderer is created, since the value for this policy is passed
// to the renderer via a command-line. Setting the policy in the test
// itself or in SetUpOnMainThread works for update-able policies, but
// is too late for this one.
provider_.SetDefaultReturns(
true /* is_initialization_complete_return */,
true /* is_first_policy_load_complete_return */);
policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
policy::PolicyMap values;
values.Set(
policy::key::kIsolateOrigins, policy::POLICY_LEVEL_MANDATORY,
policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
base::Value("https://policy1.example.org/,http://policy2.example.com"),
nullptr);
provider_.UpdateChromePolicy(values);
}
};
IN_PROC_BROWSER_TEST_F(IsolateOriginsPolicyBrowserTest, Simple) {
// Verify that the policy present at browser startup is correctly applied.
Expectations expectations[] = {
{"https://foo.com/noodles.html", false},
{"http://foo.com/", false},
{"https://policy1.example.org/pumpkins.html", true},
{"http://policy2.example.com/index.php", true},
};
CheckIsolatedOriginExpectations(expectations, std::size(expectations));
// Simulate updating the policy at "browser runtime".
policy::PolicyMap values;
values.Set(
policy::key::kIsolateOrigins, policy::POLICY_LEVEL_MANDATORY,
policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
base::Value("https://policy3.example.org/,http://policy4.example.com"),
nullptr);
provider_.UpdateChromePolicy(values);
// Verify that the policy update above has taken effect:
// - policy3 and policy4 origins should become isolated
// - policy1 and policy2 origins will remain isolated, even though they were
// removed from the policy (this is an artifact caused by limitations of
// the current implementation, not something that is a hard requirement).
Expectations expectations2[] = {
{"https://foo.com/noodles.html", false},
{"http://foo.com/", false},
{"https://policy1.example.org/pumpkins.html", true},
{"http://policy2.example.com/index.php", true},
{"https://policy3.example.org/pumpkins.html", true},
{"http://policy4.example.com/index.php", true},
};
CheckIsolatedOriginExpectations(expectations2, std::size(expectations2));
}
#endif
IN_PROC_BROWSER_TEST_F(NoOverrideSitePerProcessPolicyBrowserTest, Simple) {
Expectations expectations[] = {
{"https://foo.com/noodles.html", true},
{"http://example.org/pumpkins.html", true},
};
CheckExpectations(expectations, std::size(expectations));
}
// After https://crbug.com/910273 was fixed, enterprise policy can only be used
// to disable Site Isolation on Android - the
// SitePerProcessPolicyBrowserTestFieldTrialTest tests should not be run on any
// other platform. Note that browser_tests won't run on Android until
// https://crbug.com/611756 is fixed.
#if BUILDFLAG(IS_ANDROID)
class SitePerProcessPolicyBrowserTestFieldTrialTest
: public SitePerProcessPolicyBrowserTestDisabled {
public:
SitePerProcessPolicyBrowserTestFieldTrialTest() {
scoped_feature_list_.InitAndEnableFeature(features::kSitePerProcess);
}
SitePerProcessPolicyBrowserTestFieldTrialTest(
const SitePerProcessPolicyBrowserTestFieldTrialTest&) = delete;
SitePerProcessPolicyBrowserTestFieldTrialTest& operator=(
const SitePerProcessPolicyBrowserTestFieldTrialTest&) = delete;
~SitePerProcessPolicyBrowserTestFieldTrialTest() override = default;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(SitePerProcessPolicyBrowserTestFieldTrialTest, Simple) {
// Skip this test if the --site-per-process switch is present (e.g. on Site
// Isolation Android chromium.fyi bot). The test is still valid if
// SitePerProcess is the default (e.g. via ContentBrowserClient's
// ShouldEnableStrictSiteIsolation method) - don't skip the test in such case.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kSitePerProcess)) {
return;
}
// Policy should inject kDisableSiteIsolationForPolicy rather than
// kDisableSiteIsolation switch.
EXPECT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableSiteIsolation));
ASSERT_TRUE(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableSiteIsolationForPolicy));
EXPECT_FALSE(
content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites());
Expectations expectations[] = {
{"https://foo.com/noodles.html", false},
{"http://example.org/pumpkins.html", false},
};
CheckExpectations(expectations, std::size(expectations));
}
#endif
#if BUILDFLAG(IS_ANDROID)
namespace {
bool CheckUseDedicatedProcessesForAllSitesWithAndroidState(
bool is_under_advanced_protection,
base::ByteCount ram) {
safe_browsing::SetAdvancedProtectionStateForTesting(
is_under_advanced_protection);
ChromeContentBrowserClient::DisableAdvancedProtectionCachingForTests();
base::test::ScopedAmountOfPhysicalMemoryOverride memory_override(ram);
site_isolation::SiteIsolationPolicy::
SetDisallowMemoryThresholdCachingForTesting(true);
return content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites();
}
} // anonymous namespace
#endif // BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(SiteIsolationPolicyBrowserTest,
NoPolicyNoTrialsFlags_NoAdvancedProtection_HighRam) {
// The switch to disable Site Isolation should be missing by default (i.e.
// without an explicit enterprise policy).
EXPECT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableSiteIsolation));
#if BUILDFLAG(IS_ANDROID) && !BUILDFLAG(ENABLE_ANDROID_SITE_ISOLATION)
EXPECT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableSiteIsolationForPolicy));
EXPECT_EQ(CheckUseDedicatedProcessesForAllSitesWithAndroidState(
/*is_under_advanced_protection=*/false,
// TODO(crbug.com/429140103): Comments in the original code
// suggested that this was in KiB, but it was in fact in MiB.
// Needs investigation.
/*ram=*/base::MiB(8000)),
base::FeatureList::IsEnabled(features::kSitePerProcess));
#else
EXPECT_TRUE(content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites());
#endif // BUILDFLAG(IS_ANDROID) && !BUILDFLAG(ENABLE_ANDROID_SITE_ISOLATION)
}
#if BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(SiteIsolationPolicyBrowserTest,
NoPolicy_AdvancedProtection_HighRam) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
EXPECT_FALSE(command_line->HasSwitch(switches::kDisableSiteIsolation));
EXPECT_FALSE(
command_line->HasSwitch(switches::kDisableSiteIsolationForPolicy));
EXPECT_TRUE(CheckUseDedicatedProcessesForAllSitesWithAndroidState(
/*is_under_advanced_protection=*/true,
// TODO(crbug.com/429140103): Comments in the original code suggested that
// this was in KiB, but it was in fact in MiB. Needs investigation.
/*ram=*/base::MiB(8000)));
}
IN_PROC_BROWSER_TEST_F(SiteIsolationPolicyBrowserTest,
NoPolicy_AdvancedProtection_LowRam) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
// Skip this test if the --site-per-process switch is present (e.g. on Site
// Isolation Android chromium.fyi bot).
if (command_line->HasSwitch(switches::kSitePerProcess)) {
return;
}
EXPECT_FALSE(command_line->HasSwitch(switches::kDisableSiteIsolation));
EXPECT_FALSE(
command_line->HasSwitch(switches::kDisableSiteIsolationForPolicy));
EXPECT_FALSE(CheckUseDedicatedProcessesForAllSitesWithAndroidState(
/*is_under_advanced_protection=*/true,
// TODO(crbug.com/429140103): Comments in the original code suggested that
// this was in KiB, but it was in fact in MiB. Needs investigation.
/*ram=*/base::MiB(1000)));
}
#endif
#if !BUILDFLAG(IS_ANDROID)
// Parameterized test class to check that an enterprise policy can set the
// origin-keyed processes by default feature, but a user can override the value
// that was set by the enterprise policy (via either the command-line flags or
// about:flags).
class OriginKeyedProcessesEnabledPolicyBrowserTest
: public SiteIsolationPolicyBrowserTest,
public testing::WithParamInterface<bool> {
public:
OriginKeyedProcessesEnabledPolicyBrowserTest(
const OriginKeyedProcessesEnabledPolicyBrowserTest&) = delete;
OriginKeyedProcessesEnabledPolicyBrowserTest& operator=(
const OriginKeyedProcessesEnabledPolicyBrowserTest&) = delete;
net::EmbeddedTestServer* https_server() { return &https_server_; }
protected:
OriginKeyedProcessesEnabledPolicyBrowserTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
if (GetParam()) {
// Turn off the origin-keyed processes feature via user intervention. This
// will override the enterprise setting which will have it set to be
// enabled.
scoped_feature_list_.InitWithFeaturesAndParameters(
{/*enabled_features*/},
{/*disabled_features=*/features::kOriginKeyedProcessesByDefault});
} else {
// Without user override, set the memory threshold to a high value that
// would not be reached, normally causing the feature to remain disabled.
// This will check that overrides will ignore memory threshold limits.
scoped_feature_list_.InitWithFeaturesAndParameters(
{{site_isolation::features::kOriginIsolationMemoryThreshold,
{{site_isolation::features::
kOriginIsolationMemoryThresholdParamName,
std::string("524288")}}}},
{/*disabled_features*/});
}
}
void SetUpInProcessBrowserTestFixture() override {
// We setup the policy here, because the policy must be 'live' before
// the renderer is created, since the value for this policy is passed
// to the renderer via a command-line. Setting the policy in the test
// itself or in SetUpOnMainThread works for update-able policies, but
// is too late for this one.
provider_.SetDefaultReturns(
true /* is_initialization_complete_return */,
true /* is_first_policy_load_complete_return */);
policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
policy::PolicyMap values;
values.Set(policy::key::kOriginKeyedProcessesEnabled,
policy::POLICY_LEVEL_RECOMMENDED, policy::POLICY_SCOPE_MACHINE,
policy::POLICY_SOURCE_CLOUD, base::Value(true), nullptr);
provider_.UpdateChromePolicy(values);
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
https_server()->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
https_server()->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
content::SetupCrossSiteRedirector(https_server());
net::test_server::RegisterDefaultHandlers(https_server());
ASSERT_TRUE(https_server()->Start());
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
net::EmbeddedTestServer https_server_;
};
INSTANTIATE_TEST_SUITE_P(All,
OriginKeyedProcessesEnabledPolicyBrowserTest,
testing::Bool(),
[](auto& info) {
return info.param ? "WithUserOverride"
: "WithoutUserOverride";
});
IN_PROC_BROWSER_TEST_P(OriginKeyedProcessesEnabledPolicyBrowserTest, Simple) {
GURL start_url(https_server()->GetURL("a.test", "/iframe.html"));
GURL cross_origin_url(
https_server()->GetURL("crossorigin.a.test", "/simple.html"));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), start_url));
EXPECT_TRUE(NavigateIframeToURL(web_contents, "test", cross_origin_url));
content::RenderFrameHost* root_rfh = web_contents->GetPrimaryMainFrame();
content::RenderFrameHost* child_rfh = ChildFrameAt(root_rfh, 0);
if (GetParam()) {
// The user overrode the enterprise policy to turn the feature off.
EXPECT_EQ(root_rfh->GetProcess(), child_rfh->GetProcess())
<< "The root frame and child iframe should be in the same process.";
} else {
// There is no user override. The enterprise policy has highest priority and
// turns the feature on, even if the memory threshold is higher than this
// machine's physical memory.
EXPECT_NE(root_rfh->GetProcess(), child_rfh->GetProcess())
<< "The root frame and child iframe should be in separate processes.";
}
}
#endif // !BUILDFLAG(IS_ANDROID)