chromium/components/site_isolation/site_isolation_policy_unittest.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/site_isolation/site_isolation_policy.h"

#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/json/values_util.h"
#include "base/memory/raw_ptr.h"
#include "base/no_destructor.h"
#include "base/system/sys_info.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_entropy_provider.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/prefs/testing_pref_service.h"
#include "components/site_isolation/features.h"
#include "components/site_isolation/pref_names.h"
#include "components/site_isolation/preloaded_isolated_origins.h"
#include "components/user_prefs/user_prefs.h"
#include "components/variations/variations_switches.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/site_isolation_policy.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_task_environment.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/web_contents_tester.h"
#include "net/http/http_response_headers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace site_isolation {
namespace {

IsolatedOriginSource;

// Some command-line switches override field trials - the tests need to be
// skipped in this case.
bool ShouldSkipBecauseOfConflictingCommandLineSwitches() {}

}  // namespace

// Base class for site isolation tests which handles setting a
// ContentBrowserClient with logic for enabling/disabling site isolation.
class BaseSiteIsolationTest : public testing::Test {};

// Tests with OriginKeyedProcessesByDefault enabled.
class OriginKeyedProcessesByDefaultSiteIsolationPolicyTest
    : public BaseSiteIsolationTest {};

// Make sure AreOriginKeyedProcessesEnabledByDefault() only returns true when
// StrictSiteIsolation is enabled.
TEST_F(OriginKeyedProcessesByDefaultSiteIsolationPolicyTest,
       RequiresStrictSiteIsolation) {}

class SiteIsolationPolicyTest : public BaseSiteIsolationTest {};

class WebTriggeredIsolatedOriginsPolicyTest : public SiteIsolationPolicyTest {};

// Verify that persisting web-triggered isolated origins properly saves the
// origins to prefs and respects the maximum number of entries (3 in this
// test).
TEST_F(WebTriggeredIsolatedOriginsPolicyTest, PersistIsolatedOrigin) {}

// Verify that when origins stored in prefs contain more than the current
// maximum number of entries, we clean up older entries when adding a new one
// to go back under the size limit.
TEST_F(WebTriggeredIsolatedOriginsPolicyTest, UpdatedMaxSize) {}

// Verify that when origins stored in prefs expire, we don't apply them when
// loading persisted isolated origins, and we remove them from prefs.
TEST_F(WebTriggeredIsolatedOriginsPolicyTest, Expiration) {}

// Helper class that enables site isolation for password sites.
class PasswordSiteIsolationPolicyTest : public SiteIsolationPolicyTest {};

// Verifies that SiteIsolationPolicy::ApplyPersistedIsolatedOrigins applies
// stored isolated origins correctly when using site isolation for password
// sites.
TEST_F(PasswordSiteIsolationPolicyTest, ApplyPersistedIsolatedOrigins) {}

// Helper class that disables strict site isolation as well as site isolation
// for password sites.
class NoPasswordSiteIsolationPolicyTest : public SiteIsolationPolicyTest {};

// Verifies that SiteIsolationPolicy::ApplyPersistedIsolatedOrigins ignores
// stored isolated origins when site isolation for password sites is off.
TEST_F(NoPasswordSiteIsolationPolicyTest,
       PersistedIsolatedOriginsIgnoredWithoutPasswordIsolation) {}

enum class SitePerProcessMemoryThreshold {};

enum class SitePerProcessMode {};

struct SitePerProcessMemoryThresholdBrowserTestParams {};

const url::Origin& GetTrialOrigin() {}

// Helper class to run tests on a simulated 512MB low-end device.
class SitePerProcessMemoryThresholdBrowserTest
    : public BaseSiteIsolationTest,
      public ::testing::WithParamInterface<
          SitePerProcessMemoryThresholdBrowserTestParams> {};

SitePerProcessMemoryThresholdBrowserTestNoIsolation;
TEST_P(SitePerProcessMemoryThresholdBrowserTestNoIsolation, NoIsolation) {}

SitePerProcessMemoryThresholdBrowserTestIsolation;
TEST_P(SitePerProcessMemoryThresholdBrowserTestIsolation, Isolation) {}

INSTANTIATE_TEST_SUITE_P();

INSTANTIATE_TEST_SUITE_P();

SitePerProcessMemoryThresholdBrowserTestNoIsolatedOrigin;
TEST_P(SitePerProcessMemoryThresholdBrowserTestNoIsolatedOrigin,
       TrialNoIsolatedOrigin) {}

SitePerProcessMemoryThresholdBrowserTestIsolatedOrigin;
TEST_P(SitePerProcessMemoryThresholdBrowserTestIsolatedOrigin,
       TrialIsolatedOrigin) {}

INSTANTIATE_TEST_SUITE_P();

INSTANTIATE_TEST_SUITE_P();

// Helper class to run tests with password-triggered site isolation initialized
// via a regular field trial and *not* via a command-line override.  It
// creates a new field trial (with 100% probability of being in the group), and
// initializes the test class's ScopedFeatureList using it.  Two derived
// classes below control are used to initialize the feature to either enabled
// or disabled state.
class PasswordSiteIsolationFieldTrialTest : public BaseSiteIsolationTest {};

class EnabledPasswordSiteIsolationFieldTrialTest
    : public PasswordSiteIsolationFieldTrialTest {};

class DisabledPasswordSiteIsolationFieldTrialTest
    : public PasswordSiteIsolationFieldTrialTest {};

TEST_F(EnabledPasswordSiteIsolationFieldTrialTest, BelowThreshold) {}

TEST_F(EnabledPasswordSiteIsolationFieldTrialTest, AboveThreshold) {}

// This test verifies that when password-triggered site isolation is disabled
// via field trials but force-enabled via command line, it takes effect even
// when below the memory threshold.  See https://crbug.com/1009828.
TEST_F(DisabledPasswordSiteIsolationFieldTrialTest,
       CommandLineOverride_BelowThreshold) {}

// Similar to the test above, but with device memory being above memory
// threshold.
TEST_F(DisabledPasswordSiteIsolationFieldTrialTest,
       CommandLineOverride_AboveThreshold) {}

// Helper class to run tests with strict origin isolation initialized via
// a regular field trial and *not* via a command-line override.  It creates a
// new field trial (with 100% probability of being in the group), and
// initializes the test class's ScopedFeatureList using it.  Two derived
// classes below control are used to initialize the feature to either enabled
// or disabled state.
class StrictOriginIsolationFieldTrialTest : public BaseSiteIsolationTest {};

class EnabledStrictOriginIsolationFieldTrialTest
    : public StrictOriginIsolationFieldTrialTest {};

class DisabledStrictOriginIsolationFieldTrialTest
    : public StrictOriginIsolationFieldTrialTest {};

// Check that when strict origin isolation is enabled via a field trial, and
// the device is above the memory threshold, disabling it via the command line
// takes precedence.
TEST_F(EnabledStrictOriginIsolationFieldTrialTest,
       DisabledViaCommandLineOverride) {}

// This test verifies that when strict origin isolation is disabled
// via field trials but force-enabled via command line, it takes effect even
// when below the memory threshold.  See https://crbug.com/1009828.
TEST_F(DisabledStrictOriginIsolationFieldTrialTest,
       EnabledViaCommandLineOverride_BelowThreshold) {}

// The following tests verify that the list of Android's built-in isolated
// origins takes effect. This list is only used in official builds, and only
// when above the memory threshold.
#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && BUILDFLAG(IS_ANDROID)
class BuiltInIsolatedOriginsTest : public SiteIsolationPolicyTest {
 public:
  BuiltInIsolatedOriginsTest() = default;

  BuiltInIsolatedOriginsTest(const BuiltInIsolatedOriginsTest&) = delete;
  BuiltInIsolatedOriginsTest& operator=(const BuiltInIsolatedOriginsTest&) =
      delete;

 protected:
  void SetUp() override {
    // Simulate a 512MB device.
    base::CommandLine::ForCurrentProcess()->AppendSwitch(
        switches::kEnableLowEndDeviceMode);
    EXPECT_EQ(512, base::SysInfo::AmountOfPhysicalMemoryMB());
    SiteIsolationPolicyTest::SetUp();
  }
};

// Check that the list of preloaded isolated origins is properly applied when
// device RAM is above the site isolation memory threshold.
TEST_F(BuiltInIsolatedOriginsTest, DefaultThreshold) {
  if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
    return;

  // Define a memory threshold at 128MB.  This is below the 512MB of physical
  // memory that this test simulates, so preloaded isolated origins should take
  // effect.
  base::test::ScopedFeatureList memory_feature;
  memory_feature.InitAndEnableFeatureWithParameters(
      features::kSiteIsolationMemoryThresholds,
      {{features::kPartialSiteIsolationMemoryThresholdParamName, "128"}});

  // Ensure that isolated origins that are normally loaded on browser
  // startup are applied.
  content::SiteIsolationPolicy::ApplyGlobalIsolatedOrigins();

  EXPECT_TRUE(
      content::SiteIsolationPolicy::ArePreloadedIsolatedOriginsEnabled());

  auto* cpsp = content::ChildProcessSecurityPolicy::GetInstance();
  std::vector<url::Origin> isolated_origins = cpsp->GetIsolatedOrigins(
      content::ChildProcessSecurityPolicy::IsolatedOriginSource::BUILT_IN);

  // The list of built-in origins is fairly large; we don't want to hardcode
  // the size here as it might change, so just check that there are at least 10
  // origins.
  EXPECT_GT(isolated_origins.size(), 10u);

  // Check that a couple of well-known origins are on the list.
  EXPECT_THAT(
      isolated_origins,
      ::testing::Contains(url::Origin::Create(GURL("https://google.com/"))));
  EXPECT_THAT(
      isolated_origins,
      ::testing::Contains(url::Origin::Create(GURL("https://amazon.com/"))));
  EXPECT_THAT(
      isolated_origins,
      ::testing::Contains(url::Origin::Create(GURL("https://facebook.com/"))));

  cpsp->ClearIsolatedOriginsForTesting();
}

TEST_F(BuiltInIsolatedOriginsTest, BelowThreshold) {
  if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
    return;

  // Define a memory threshold at 768MB.  This is above the 512MB of physical
  // memory that this test simulates, so preloaded isolated origins shouldn't
  // take effect.
  base::test::ScopedFeatureList memory_feature;
  memory_feature.InitAndEnableFeatureWithParameters(
      features::kSiteIsolationMemoryThresholds,
      {{features::kPartialSiteIsolationMemoryThresholdParamName, "768"}});

  // Ensure that isolated origins that are normally loaded on browser
  // startup are applied.
  content::SiteIsolationPolicy::ApplyGlobalIsolatedOrigins();

  EXPECT_FALSE(
      content::SiteIsolationPolicy::ArePreloadedIsolatedOriginsEnabled());

  auto* cpsp = content::ChildProcessSecurityPolicy::GetInstance();
  std::vector<url::Origin> isolated_origins = cpsp->GetIsolatedOrigins(
      content::ChildProcessSecurityPolicy::IsolatedOriginSource::BUILT_IN);

  // There shouldn't be any built-in origins on Android. (Note that desktop has
  // some built-in origins that are applied regardless of memory threshold.)
  EXPECT_EQ(isolated_origins.size(), 0u);

  cpsp->ClearIsolatedOriginsForTesting();
}

// Check that the list of preloaded isolated origins is not applied when full
// site isolation is used, since in that case the list is redundant.
TEST_F(BuiltInIsolatedOriginsTest, NotAppliedWithFullSiteIsolation) {
  // Force full site-per-process mode.
  content::IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());

  // Define a memory threshold at 128MB.  This is below the 512MB of physical
  // memory that this test simulates, so preloaded isolated origins shouldn't
  // be disabled by the memory threshold.
  base::test::ScopedFeatureList memory_feature;
  memory_feature.InitAndEnableFeatureWithParameters(
      features::kSiteIsolationMemoryThresholds,
      {{features::kPartialSiteIsolationMemoryThresholdParamName, "128"}});

  // Ensure that isolated origins that are normally loaded on browser
  // startup are applied.
  content::SiteIsolationPolicy::ApplyGlobalIsolatedOrigins();

  // Because full site-per-process is used, the preloaded isolated origins are
  // redundant and should not be applied.
  EXPECT_FALSE(
      content::SiteIsolationPolicy::ArePreloadedIsolatedOriginsEnabled());

  auto* cpsp = content::ChildProcessSecurityPolicy::GetInstance();
  std::vector<url::Origin> isolated_origins = cpsp->GetIsolatedOrigins(
      content::ChildProcessSecurityPolicy::IsolatedOriginSource::BUILT_IN);
  EXPECT_EQ(isolated_origins.size(), 0u);
}
#endif

// Helper class for tests that use header-based opt-in origin isolation and
// simulate a 512MB device, while turning off strict site isolation.  This is
// used for checking how opt-in origin isolation behaves with site isolation
// memory thresholds.
class OptInOriginIsolationPolicyTest : public BaseSiteIsolationTest {};

// Check that opt-in origin isolation is not applied when below the memory
// threshold (and when full site isolation is not used).
TEST_F(OptInOriginIsolationPolicyTest, BelowThreshold) {}

// Counterpart to the test above, but verifies that opt-in origin isolation is
// enabled when above the memory threshold.
TEST_F(OptInOriginIsolationPolicyTest, AboveThreshold) {}

}  // namespace site_isolation