// 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