chromium/chrome/browser/policy/policy_extension_controlling_proxy_lacros_browsertest.cc

// Copyright 2023 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/functional/callback_helpers.h"
#include "base/path_service.h"
#include "base/test/gtest_tags.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/chrome_content_verifier_delegate.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/mixin_based_extension_apitest.h"
#include "chrome/browser/policy/extension_force_install_mixin.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/prefs/pref_service.h"
#include "components/proxy_config/proxy_config_dictionary.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/test_management_policy.h"
#include "extensions/common/extension.h"
#include "extensions/test/extension_test_message_listener.h"

namespace policy {

namespace {

using extensions::mojom::ManifestLocation;

// A proxy test extension which is setting proxy for chromium primary profile:
const char kProxySettingExtensionId[] = "cmppkjmdihjjoenffkfoejbjdgnlgnbm";
const char kProxySettingExtensionExtensionPath[] =
    "extensions/browsertest/proxy_setting_extension/";
const char kProxySettingExtensionPemPath[] =
    "extensions/browsertest/proxy_setting_extension.pem";

}  // namespace

class PolicyExtensionControllingProxyTest
    : public extensions::MixinBasedExtensionApiTest {
 public:
  PolicyExtensionControllingProxyTest() = default;

  std::string GetKeyFromProxyPrefs(const PrefService::Preference* prefs,
                                   const std::string& key) {
    return *prefs->GetValue()->GetDict().FindString(key);
  }

  const PrefService::Preference* GetOriginalProxyPrefs() {
    PrefService* prefs = browser()->profile()->GetOriginalProfile()->GetPrefs();
    return prefs->FindPreference(proxy_config::prefs::kProxy);
  }

  const PrefService::Preference* GetIncognitoProxyPrefs() {
    PrefService* prefs =
        browser()
            ->profile()
            ->GetOffTheRecordProfile(
                Profile::OTRProfileID::CreateUniqueForTesting(),
                /*create_if_needed=*/true)
            ->GetPrefs();
    return prefs->FindPreference(proxy_config::prefs::kProxy);
  }

  policy::MockConfigurationPolicyProvider* policy_provider() {
    return &mock_policy_provider_;
  }

  ExtensionForceInstallMixin* force_mixin() {
    return &extension_force_install_mixin_;
  }

  base::FilePath GetTestDataDir() {
    return base::PathService::CheckedGet(chrome::DIR_TEST_DATA);
  }

 protected:
  void SetUp() override {
    extensions::ChromeContentVerifierDelegate::SetDefaultModeForTesting(
        extensions::ChromeContentVerifierDelegate::VerifyInfo::Mode::
            ENFORCE_STRICT);

    // This test modifies ash state without restoring to the default. It will
    // fail many other tests if it runs with shared Ash. Run it with unique Ash
    // before the issue is fixed.
    StartUniqueAshChrome(
        /*enabled_features=*/{},
        /*disabled_features=*/{},
        /*additional_cmdline_switches=*/{},
        "crbug.com/1489718 Switch to shared ash when the issue is fixed");

    extensions::MixinBasedExtensionApiTest::SetUp();
  }

  void SetUpInProcessBrowserTestFixture() override {
    extensions::MixinBasedExtensionApiTest::SetUpInProcessBrowserTestFixture();

    mock_policy_provider_.SetDefaultReturns(
        true /* is_initialization_complete_return */,
        true /* is_first_policy_load_complete_return */);
    mock_policy_provider_.SetAutoRefresh();
    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
        &mock_policy_provider_);
  }

  void SetUpOnMainThread() override {
    extensions::MixinBasedExtensionApiTest::SetUpOnMainThread();
    extension_force_install_mixin_.InitWithMockPolicyProvider(
        profile(), policy_provider());
  }

  void AddScreenplayTag() {
    base::AddTagToTestResult("feature_id",
                             "screenplay-3c0007b0-8082-4b7d-bdeb-675a4dc1bbb4");
  }

  ExtensionForceInstallMixin extension_force_install_mixin_{&mixin_host_};

 private:
  testing::NiceMock<policy::MockConfigurationPolicyProvider>
      mock_policy_provider_;
  web_app::OsIntegrationManager::ScopedSuppressForTesting os_hooks_suppress_;
};

// Verifies that proxy which is configured by force-installed extensions
// will not affect incognito profile.
IN_PROC_BROWSER_TEST_F(PolicyExtensionControllingProxyTest,
                       ForcedProxyExtensionHasNoEffectInIncognitoMode) {
  AddScreenplayTag();

  {
    // Check that extension is not loaded.
    EXPECT_FALSE(
        force_mixin()->GetInstalledExtension(kProxySettingExtensionId));
    // Check that in main profile default proxy mode is system.
    const PrefService::Preference* proxy_prefs = GetOriginalProxyPrefs();
    ASSERT_TRUE(proxy_prefs);
    EXPECT_FALSE(proxy_prefs->IsExtensionControlled());
    std::string proxy_mode = GetKeyFromProxyPrefs(proxy_prefs, "mode");
    EXPECT_EQ(proxy_mode, "system");

    // Check that in incognito profile default proxy mode is system.
    const PrefService::Preference* incognito_proxy_prefs =
        GetIncognitoProxyPrefs();
    ASSERT_TRUE(incognito_proxy_prefs);
    EXPECT_FALSE(proxy_prefs->IsExtensionControlled());
    std::string incognito_proxy_mode =
        GetKeyFromProxyPrefs(incognito_proxy_prefs, "mode");
    EXPECT_EQ(proxy_mode, "system");
  }

  // Force load extension from the source dir and wait until message "ready"
  // is received.
  // As PEM file is provided, we are expecting same extension ID always.
  EXPECT_TRUE(force_mixin()->ForceInstallFromSourceDir(
      GetTestDataDir().AppendASCII(kProxySettingExtensionExtensionPath),
      GetTestDataDir().AppendASCII(kProxySettingExtensionPemPath),
      ExtensionForceInstallMixin::WaitMode::kReadyMessageReceived));

  // Verify extension is installed and enabled.
  ASSERT_TRUE(force_mixin()->GetInstalledExtension(kProxySettingExtensionId));
  EXPECT_TRUE(force_mixin()->GetEnabledExtension(kProxySettingExtensionId));
  EXPECT_EQ(force_mixin()
                ->GetInstalledExtension(kProxySettingExtensionId)
                ->location(),
            ManifestLocation::kExternalPolicyDownload);

  {
    // Verify extension has changed proxy setting for Main profile.
    const PrefService::Preference* proxy_prefs = GetOriginalProxyPrefs();
    ASSERT_TRUE(proxy_prefs);
    EXPECT_TRUE(proxy_prefs->IsExtensionControlled());
    std::string proxy_mode = GetKeyFromProxyPrefs(proxy_prefs, "mode");
    std::string proxy_server = GetKeyFromProxyPrefs(proxy_prefs, "server");
    std::string proxy_bypass_list =
        GetKeyFromProxyPrefs(proxy_prefs, "bypass_list");

    EXPECT_EQ(proxy_mode, "fixed_servers");
    EXPECT_EQ(proxy_server, "https=google.com:5555");
    EXPECT_EQ(proxy_bypass_list, "127.0.0.1");

    // Verify extension has not changed proxy setting for Incognito profile.
    const PrefService::Preference* incognito_proxy_prefs =
        GetIncognitoProxyPrefs();
    ASSERT_TRUE(proxy_prefs);
    EXPECT_FALSE(incognito_proxy_prefs->IsExtensionControlled());
    std::string incognito_proxy_mode =
        GetKeyFromProxyPrefs(incognito_proxy_prefs, "mode");
    EXPECT_EQ(incognito_proxy_mode, "system");
  }
}

}  // namespace policy