blob: fbb69a6dfd7ffcf0b9b5fbb2040e42a525b7d4cf [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/extension_allowlist.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_management_test_util.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_service_test_base.h"
#include "chrome/browser/extensions/test_blocklist.h"
#include "chrome/test/base/testing_profile.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "extensions/browser/allowlist_state.h"
#include "extensions/browser/blocklist_extension_prefs.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/extension_features.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
namespace {
// Extension ids used during testing.
constexpr char kExtensionId1[] = "behllobkkfkfnphdnhnkndlbkcpglgmj";
constexpr char kExtensionId2[] = "hpiknbiabeeppbpihjehijgoemciehgk";
constexpr char kExtensionId3[] = "bjafgdebaacbbbecmhlhpofkepfkgcpa";
constexpr char kInstalledCrx[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
using ManagementPrefUpdater = ExtensionManagementPrefUpdater<
sync_preferences::TestingPrefServiceSyncable>;
} // namespace
// Test suite to test safe browsing allowlist enforcement.
//
// Features EnforceSafeBrowsingExtensionAllowlist and
// DisableMalwareExtensionsRemotely are enabled.
class ExtensionAllowlistUnitTestBase : public ExtensionServiceTestBase {
protected:
// Creates a test extension service with 3 installed extensions.
void CreateExtensionService(bool enhanced_protection_enabled) {
InitializeGoodInstalledExtensionService();
extension_prefs_ = ExtensionPrefs::Get(profile());
if (enhanced_protection_enabled) {
safe_browsing::SetSafeBrowsingState(
profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION);
}
}
void CreateEmptyExtensionService() {
ExtensionServiceTestBase::ExtensionServiceInitParams params =
CreateDefaultInitParams();
params.pref_file = base::FilePath();
InitializeExtensionService(params);
extension_prefs_ = ExtensionPrefs::Get(profile());
safe_browsing::SetSafeBrowsingState(
profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION);
}
void PerformActionBasedOnOmahaAttributes(const std::string& extension_id,
bool is_malware,
bool is_allowlisted) {
base::Value attributes(base::Value::Type::DICT);
if (is_malware)
attributes.SetBoolKey("_malware", true);
attributes.SetBoolKey("_esbAllowlist", is_allowlisted);
service()->PerformActionBasedOnOmahaAttributes(extension_id, attributes);
}
bool IsEnabled(const std::string& extension_id) {
return registry()->enabled_extensions().Contains(extension_id);
}
bool IsDisabled(const std::string& extension_id) {
return registry()->disabled_extensions().Contains(extension_id);
}
bool IsBlocklisted(const std::string& extension_id) {
return registry()->blocklisted_extensions().Contains(extension_id);
}
ExtensionAllowlist* allowlist() { return service()->allowlist(); }
ExtensionPrefs* extension_prefs() { return extension_prefs_; }
private:
raw_ptr<ExtensionPrefs> extension_prefs_;
};
class ExtensionAllowlistUnitTest : public ExtensionAllowlistUnitTestBase {
public:
ExtensionAllowlistUnitTest() {
feature_list_.InitWithFeatures(
{extensions_features::kSafeBrowsingCrxAllowlistShowWarnings,
extensions_features::kSafeBrowsingCrxAllowlistAutoDisable},
{});
}
};
TEST_F(ExtensionAllowlistUnitTest, AllowlistEnforcement) {
// Created with 3 installed extensions.
CreateExtensionService(/*enhanced_protection_enabled=*/true);
service()->Init();
// On the first startup, the allowlist state for existing extensions will be
// undefined.
EXPECT_EQ(ALLOWLIST_UNDEFINED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(disable_reason::DISABLE_NONE,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsEnabled(kExtensionId1));
// A first update check will set the allowlist state. In this case, an
// extension not in the allowlist will be disabled.
PerformActionBasedOnOmahaAttributes(kExtensionId1,
/*is_malware=*/false,
/*is_allowlisted=*/false);
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsDisabled(kExtensionId1));
// A future update check can change the allowlist state. Here the extension is
// now allowlisted and should be re-enabled.
PerformActionBasedOnOmahaAttributes(kExtensionId1,
/*is_malware=*/false,
/*is_allowlisted=*/true);
EXPECT_EQ(ALLOWLIST_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(disable_reason::DISABLE_NONE,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsEnabled(kExtensionId1));
// When an extension is disabled remotely for malware and is not allowlisted,
// it should have both disable reasons.
PerformActionBasedOnOmahaAttributes(kExtensionId1,
/*is_malware=*/true,
/*is_allowlisted=*/false);
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(BitMapBlocklistState::BLOCKLISTED_MALWARE,
blocklist_prefs::GetExtensionBlocklistState(kExtensionId1,
extension_prefs()));
EXPECT_TRUE(blocklist_prefs::HasOmahaBlocklistState(
kExtensionId1, BitMapBlocklistState::BLOCKLISTED_MALWARE,
extension_prefs()));
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsBlocklisted(kExtensionId1));
// A blocklisted item should not be allowlisted, but if the improbable
// happens, the item should still be blocklisted.
PerformActionBasedOnOmahaAttributes(kExtensionId1,
/*is_malware=*/true,
/*is_allowlisted=*/true);
EXPECT_EQ(ALLOWLIST_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(BitMapBlocklistState::BLOCKLISTED_MALWARE,
blocklist_prefs::GetExtensionBlocklistState(kExtensionId1,
extension_prefs()));
EXPECT_TRUE(blocklist_prefs::HasOmahaBlocklistState(
kExtensionId1, BitMapBlocklistState::BLOCKLISTED_MALWARE,
extension_prefs()));
EXPECT_EQ(disable_reason::DISABLE_NONE,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsBlocklisted(kExtensionId1));
// If the item is removed from the malware blocklist, it should stay disabled
// if it's not allowlisted.
PerformActionBasedOnOmahaAttributes(kExtensionId1,
/*is_malware=*/false,
/*is_allowlisted=*/false);
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(BitMapBlocklistState::NOT_BLOCKLISTED,
blocklist_prefs::GetSafeBrowsingExtensionBlocklistState(
kExtensionId1, extension_prefs()));
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsDisabled(kExtensionId1));
}
TEST_F(ExtensionAllowlistUnitTest, DisabledReasonResetWhenBlocklisted) {
// Created with 3 installed extensions.
CreateExtensionService(/*enhanced_protection_enabled=*/true);
service()->Init();
// The disabled reason should be set if an extension is not in the allowlist.
PerformActionBasedOnOmahaAttributes(kExtensionId1,
/*is_malware=*/false,
/*is_allowlisted=*/false);
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId1));
// The extension is added to the blocklist.
service()->BlocklistExtensionForTest(kExtensionId1);
// A blocklisted item should not be allowlisted, but if the improbable
// happens, the item should still be blocklisted.
PerformActionBasedOnOmahaAttributes(kExtensionId1,
/*is_malware=*/false,
/*is_allowlisted=*/true);
EXPECT_TRUE(IsBlocklisted(kExtensionId1));
// The disabled reason should be reset because the extension is in the
// allowlist.
EXPECT_EQ(disable_reason::DISABLE_NONE,
extension_prefs()->GetDisableReasons(kExtensionId1));
}
TEST_F(ExtensionAllowlistUnitTest, DisabledItemStaysDisabledWhenAllowlisted) {
// Created with 3 installed extensions.
CreateExtensionService(/*enhanced_protection_enabled=*/true);
service()->Init();
// Start with an extension disabled by user.
service()->DisableExtension(kExtensionId1,
disable_reason::DISABLE_USER_ACTION);
EXPECT_TRUE(IsDisabled(kExtensionId1));
// Disable the extension with allowlist enforcement.
PerformActionBasedOnOmahaAttributes(kExtensionId1,
/*is_malware=*/false,
/*is_allowlisted=*/false);
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(disable_reason::DISABLE_USER_ACTION |
disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsDisabled(kExtensionId1));
// The extension is allowlisted, but stays disabled by user action.
PerformActionBasedOnOmahaAttributes(kExtensionId1,
/*is_malware=*/false,
/*is_allowlisted=*/true);
EXPECT_EQ(ALLOWLIST_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(BitMapBlocklistState::NOT_BLOCKLISTED,
blocklist_prefs::GetSafeBrowsingExtensionBlocklistState(
kExtensionId1, extension_prefs()));
EXPECT_EQ(disable_reason::DISABLE_USER_ACTION,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsDisabled(kExtensionId1));
}
TEST_F(ExtensionAllowlistUnitTest, EnforcementOnInit) {
// Created with 3 installed extensions.
CreateExtensionService(/*enhanced_protection_enabled=*/true);
// Start an extension not allowlisted and in an unenforced state, this can
// happen if the 'EnforceSafeBrowsingExtensionAllowlist' feature was
// previously disabled for this profile.
allowlist()->SetExtensionAllowlistState(kExtensionId1,
ALLOWLIST_NOT_ALLOWLISTED);
// During initialization, the allowlist will be enforced for extensions not
// allowlisted.
service()->Init();
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsDisabled(kExtensionId1));
// The enforcement isn't done for extensions having an undefined allowlist
// state.
EXPECT_EQ(ALLOWLIST_UNDEFINED,
allowlist()->GetExtensionAllowlistState(kExtensionId2));
EXPECT_TRUE(IsEnabled(kExtensionId2));
}
TEST_F(ExtensionAllowlistUnitTest, EnhancedProtectionSettingChange) {
// Created with 3 installed extensions.
CreateExtensionService(/*enhanced_protection_enabled=*/false);
// Start with ESB off and one extension not allowlisted.
allowlist()->SetExtensionAllowlistState(kExtensionId1,
ALLOWLIST_NOT_ALLOWLISTED);
// Since ESB is off, no enforcement will be done for extensions not
// allowlisted.
service()->Init();
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_TRUE(IsEnabled(kExtensionId1));
EXPECT_EQ(ALLOWLIST_UNDEFINED,
allowlist()->GetExtensionAllowlistState(kExtensionId2));
// Even if the enforcement is off, the allowlist state is still tracked when
// receiving update check results.
PerformActionBasedOnOmahaAttributes(kExtensionId2,
/*is_malware=*/false,
/*is_allowlisted=*/false);
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId2));
EXPECT_TRUE(IsEnabled(kExtensionId2));
// When ESB is enabled, the extension service will enforce all extensions with
// `ALLOWLIST_NOT_ALLOWLISTED` state.
safe_browsing::SetSafeBrowsingState(
profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION);
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsDisabled(kExtensionId1));
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId2));
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId2));
EXPECT_TRUE(IsDisabled(kExtensionId2));
// If the ESB setting is turned off, the extensions are re-enabled.
safe_browsing::SetSafeBrowsingState(
profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::STANDARD_PROTECTION);
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_TRUE(IsEnabled(kExtensionId1));
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId2));
EXPECT_TRUE(IsEnabled(kExtensionId2));
}
TEST_F(ExtensionAllowlistUnitTest, ExtensionsNotAllowlistedThenBlocklisted) {
// Created with 3 installed extensions.
CreateExtensionService(/*enhanced_protection_enabled=*/true);
TestBlocklist test_blocklist;
test_blocklist.Attach(service()->blocklist_);
// Start with two not allowlisted extensions, the enforcement will be done
// during `Init`.
allowlist()->SetExtensionAllowlistState(kExtensionId1,
ALLOWLIST_NOT_ALLOWLISTED);
allowlist()->SetExtensionAllowlistState(kExtensionId2,
ALLOWLIST_NOT_ALLOWLISTED);
service()->Init();
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_TRUE(IsDisabled(kExtensionId1));
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId2));
EXPECT_TRUE(IsDisabled(kExtensionId2));
// Then blocklist and greylist the two extensions respectively.
test_blocklist.SetBlocklistState(kExtensionId1, BLOCKLISTED_MALWARE, true);
test_blocklist.SetBlocklistState(kExtensionId2,
BLOCKLISTED_POTENTIALLY_UNWANTED, true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(BitMapBlocklistState::BLOCKLISTED_MALWARE,
blocklist_prefs::GetSafeBrowsingExtensionBlocklistState(
kExtensionId1, extension_prefs()));
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsBlocklisted(kExtensionId1));
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId2));
EXPECT_EQ(BitMapBlocklistState::BLOCKLISTED_POTENTIALLY_UNWANTED,
blocklist_prefs::GetSafeBrowsingExtensionBlocklistState(
kExtensionId2, extension_prefs()));
EXPECT_EQ(disable_reason::DISABLE_GREYLIST |
disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId2));
EXPECT_TRUE(IsDisabled(kExtensionId2));
// When the extensions are unblocklisted, the allowlist enforcement will still
// be effective if the extensions are not allowlisted.
test_blocklist.SetBlocklistState(kExtensionId1, NOT_BLOCKLISTED, true);
test_blocklist.SetBlocklistState(kExtensionId2, NOT_BLOCKLISTED, true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(BitMapBlocklistState::NOT_BLOCKLISTED,
blocklist_prefs::GetSafeBrowsingExtensionBlocklistState(
kExtensionId1, extension_prefs()));
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsDisabled(kExtensionId1));
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId2));
EXPECT_EQ(BitMapBlocklistState::NOT_BLOCKLISTED,
blocklist_prefs::GetSafeBrowsingExtensionBlocklistState(
kExtensionId2, extension_prefs()));
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId2));
EXPECT_TRUE(IsDisabled(kExtensionId2));
}
TEST_F(ExtensionAllowlistUnitTest, ExtensionsBlocklistedThenNotAllowlisted) {
// Created with 3 installed extensions.
CreateExtensionService(/*enhanced_protection_enabled=*/true);
TestBlocklist test_blocklist;
test_blocklist.Attach(service()->blocklist_);
service()->Init();
// Blocklist and greylist the two extensions respectively.
test_blocklist.SetBlocklistState(kExtensionId1, BLOCKLISTED_MALWARE, true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(BitMapBlocklistState::BLOCKLISTED_MALWARE,
blocklist_prefs::GetSafeBrowsingExtensionBlocklistState(
kExtensionId1, extension_prefs()));
EXPECT_TRUE(IsBlocklisted(kExtensionId1));
// The extension is then also disabled from allowlist enforcement.
PerformActionBasedOnOmahaAttributes(kExtensionId1,
/*is_malware=*/false,
/*is_allowlisted=*/false);
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(BitMapBlocklistState::BLOCKLISTED_MALWARE,
blocklist_prefs::GetSafeBrowsingExtensionBlocklistState(
kExtensionId1, extension_prefs()));
// The disable reason is added even if the extension is already blocklisted.
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsBlocklisted(kExtensionId1));
// When the extensions is unblocklisted, the allowlist enforcement will still
// be effective if the extension is not allowlisted.
test_blocklist.SetBlocklistState(kExtensionId1, NOT_BLOCKLISTED, true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_EQ(BitMapBlocklistState::NOT_BLOCKLISTED,
blocklist_prefs::GetSafeBrowsingExtensionBlocklistState(
kExtensionId1, extension_prefs()));
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_TRUE(IsDisabled(kExtensionId1));
}
TEST_F(ExtensionAllowlistUnitTest, MissingAttributeAreIgnored) {
// Created with 3 installed extensions.
CreateExtensionService(/*enhanced_protection_enabled=*/true);
// Start with one extension allowlisted and another not allowlisted.
allowlist()->SetExtensionAllowlistState(kExtensionId1, ALLOWLIST_ALLOWLISTED);
allowlist()->SetExtensionAllowlistState(kExtensionId2,
ALLOWLIST_NOT_ALLOWLISTED);
// During initialization, the allowlist will be enforced for extensions not
// allowlisted.
service()->Init();
EXPECT_TRUE(IsEnabled(kExtensionId1));
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId2));
// Simulate an update check with no custom attribute defined.
base::Value attributes(base::Value::Type::DICT);
service()->PerformActionBasedOnOmahaAttributes(kExtensionId1, attributes);
service()->PerformActionBasedOnOmahaAttributes(kExtensionId2, attributes);
// The undefined allowlist attributes should be ignored and the state should
// remain unchanged.
EXPECT_EQ(ALLOWLIST_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
EXPECT_TRUE(IsEnabled(kExtensionId1));
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId2));
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED,
extension_prefs()->GetDisableReasons(kExtensionId2));
}
TEST_F(ExtensionAllowlistUnitTest, AcknowledgeNeededOnEnforcement) {
CreateExtensionService(/*enhanced_protection_enabled=*/true);
service()->Init();
EXPECT_TRUE(IsEnabled(kExtensionId1));
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_NONE,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId1));
// Make the extension not allowlisted.
PerformActionBasedOnOmahaAttributes(kExtensionId1,
/*is_malware=*/false,
/*is_allowlisted=*/false);
// Expect the acknowledge state to change appropriately.
EXPECT_TRUE(IsDisabled(kExtensionId1));
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_NEEDED,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId1));
}
TEST_F(ExtensionAllowlistUnitTest, AcknowledgeNotNeededIfAlreadyDisabled) {
CreateExtensionService(/*enhanced_protection_enabled=*/true);
service()->Init();
service()->DisableExtension(kExtensionId1,
disable_reason::DISABLE_USER_ACTION);
EXPECT_TRUE(IsDisabled(kExtensionId1));
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_NONE,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId1));
// Make the extension not allowlisted.
PerformActionBasedOnOmahaAttributes(kExtensionId1,
/*is_malware=*/false,
/*is_allowlisted=*/false);
// There is no need for acknowledge if the extension was already disabled.
EXPECT_TRUE(IsDisabled(kExtensionId1));
EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED |
disable_reason::DISABLE_USER_ACTION,
extension_prefs()->GetDisableReasons(kExtensionId1));
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_NONE,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId1));
}
TEST_F(ExtensionAllowlistUnitTest,
AcknowledgeStateIsSetWhenExtensionIsReenabled) {
CreateExtensionService(/*enhanced_protection_enabled=*/true);
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_NONE,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId1));
// Start with a not allowlisted extension.
allowlist()->SetExtensionAllowlistState(kExtensionId1,
ALLOWLIST_NOT_ALLOWLISTED);
// The enforcement on init should disable the extension.
service()->Init();
EXPECT_TRUE(IsDisabled(kExtensionId1));
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_NEEDED,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId1));
// Re-enable the extension.
service()->EnableExtension(kExtensionId1);
// The extensions should now be marked with
// `ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER'.
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId1));
EXPECT_TRUE(IsEnabled(kExtensionId1));
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId1));
}
TEST_F(ExtensionAllowlistUnitTest, ReenabledExtensionsAreNotReenforced) {
CreateExtensionService(/*enhanced_protection_enabled=*/true);
// Start with a not allowlisted extension that was re-enabled by user.
allowlist()->SetExtensionAllowlistState(kExtensionId1,
ALLOWLIST_NOT_ALLOWLISTED);
allowlist()->SetExtensionAllowlistAcknowledgeState(
kExtensionId1, ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER);
// And an extension that became allowlisted after it was re-enabled by user.
allowlist()->SetExtensionAllowlistState(kExtensionId2, ALLOWLIST_ALLOWLISTED);
allowlist()->SetExtensionAllowlistAcknowledgeState(
kExtensionId2, ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER);
service()->Init();
// Even though ExtensionId1 is not allowlisted, it should stay enabled because
// it was re-enabled by user.
EXPECT_TRUE(IsEnabled(kExtensionId1));
// Assert that ExtensionId2 is enabled before testing the allowlist state
// change.
EXPECT_TRUE(IsEnabled(kExtensionId2));
// If `kExtensionId2` becomes not allowlisted again, it should stay enabled
// because the user already chose to re-enable it in the past.
PerformActionBasedOnOmahaAttributes(kExtensionId2,
/*is_malware=*/false,
/*is_allowlisted=*/false);
EXPECT_TRUE(IsEnabled(kExtensionId2));
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId2));
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kExtensionId2));
}
TEST_F(ExtensionAllowlistUnitTest, TurnOffEnhancedProtection) {
CreateExtensionService(/*enhanced_protection_enabled=*/true);
// Start with 3 not allowlisted extensions.
allowlist()->SetExtensionAllowlistState(kExtensionId1,
ALLOWLIST_NOT_ALLOWLISTED);
allowlist()->SetExtensionAllowlistState(kExtensionId2,
ALLOWLIST_NOT_ALLOWLISTED);
allowlist()->SetExtensionAllowlistState(kExtensionId3,
ALLOWLIST_NOT_ALLOWLISTED);
allowlist()->SetExtensionAllowlistAcknowledgeState(
kExtensionId3, ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER);
// They should get disabled by allowlist enforcement and have their
// acknowledge state set (except the extension re-enabled by user).
service()->Init();
EXPECT_TRUE(IsDisabled(kExtensionId1));
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_NEEDED,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId1));
EXPECT_TRUE(IsDisabled(kExtensionId2));
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_NEEDED,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId2));
EXPECT_TRUE(IsEnabled(kExtensionId3));
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId3));
// Leave `kExtensionId1` with acknowledge needed and acknowledge
// `kExtensionId2`.
allowlist()->SetExtensionAllowlistAcknowledgeState(
kExtensionId2, ALLOWLIST_ACKNOWLEDGE_DONE);
// When turning off enhanced protection.
safe_browsing::SetSafeBrowsingState(
profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::STANDARD_PROTECTION);
// 'kExtensionId1' and 'kExtensionId2' should be re-enabled and have their
// acknowledge state reset.
EXPECT_TRUE(IsEnabled(kExtensionId1));
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_NONE,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId1));
EXPECT_TRUE(IsEnabled(kExtensionId2));
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_NONE,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId2));
// 'kExtensionId3' should remain enabled because it was already re-enabled by
// user.
EXPECT_TRUE(IsEnabled(kExtensionId3));
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER,
allowlist()->GetExtensionAllowlistAcknowledgeState(kExtensionId3));
}
TEST_F(ExtensionAllowlistUnitTest, BypassFrictionSetAckowledgeEnabledByUser) {
CreateExtensionService(/*enhanced_protection_enabled=*/true);
scoped_refptr<CrxInstaller> installer(CrxInstaller::CreateSilent(service()));
installer->set_allow_silent_install(true);
installer->set_bypassed_safebrowsing_friction_for_testing(true);
base::RunLoop run_loop;
installer->AddInstallerCallback(base::BindOnce(
[](base::OnceClosure quit_closure,
const absl::optional<CrxInstallError>& error) {
ASSERT_FALSE(error) << error->message();
std::move(quit_closure).Run();
},
run_loop.QuitWhenIdleClosure()));
installer->InstallCrx(data_dir().AppendASCII("good.crx"));
run_loop.Run();
EXPECT_TRUE(registry()->enabled_extensions().GetByID(kInstalledCrx));
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(kInstalledCrx));
EXPECT_EQ(ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER,
allowlist()->GetExtensionAllowlistAcknowledgeState(kInstalledCrx));
}
TEST_F(ExtensionAllowlistUnitTest, NoEnforcementOnPolicyForceInstall) {
CreateEmptyExtensionService();
service()->Init();
// Add a policy installed extension.
scoped_refptr<const Extension> extension =
ExtensionBuilder("policy_installed")
.SetPath(data_dir().AppendASCII("good.crx"))
.SetLocation(mojom::ManifestLocation::kExternalPolicyDownload)
.Build();
service()->AddExtension(extension.get());
{
ManagementPrefUpdater pref(profile_->GetTestingPrefService());
pref.SetIndividualExtensionAutoInstalled(
extension->id(), "http://example.com/update_url", true);
}
EXPECT_TRUE(IsEnabled(extension->id()));
// On next update check, the extension is now marked as not allowlisted.
PerformActionBasedOnOmahaAttributes(extension->id(),
/*is_malware=*/false,
/*is_allowlisted=*/false);
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(extension->id()));
// A policy installed extension is not disabled by allowlist enforcement.
EXPECT_TRUE(IsEnabled(extension->id()));
// No warnings are shown for policy installed extensions.
EXPECT_FALSE(allowlist()->ShouldDisplayWarning(extension->id()));
}
class ExtensionAllowlistWithFeatureDisabledUnitTest
: public ExtensionAllowlistUnitTestBase {
public:
ExtensionAllowlistWithFeatureDisabledUnitTest() {
// Test with warnings enabled but auto disable disabled.
feature_list_.InitWithFeatures(
{extensions_features::kSafeBrowsingCrxAllowlistShowWarnings},
{extensions_features::kSafeBrowsingCrxAllowlistAutoDisable});
}
};
TEST_F(ExtensionAllowlistWithFeatureDisabledUnitTest,
NoEnforcementWhenFeatureDisabled) {
// Created with 3 installed extensions.
CreateExtensionService(/*enhanced_protection_enabled=*/true);
allowlist()->SetExtensionAllowlistState(kExtensionId1,
ALLOWLIST_NOT_ALLOWLISTED);
service()->Init();
EXPECT_TRUE(IsEnabled(kExtensionId1));
PerformActionBasedOnOmahaAttributes(kExtensionId2,
/*is_malware=*/false,
/*is_allowlisted=*/false);
EXPECT_TRUE(IsEnabled(kExtensionId1));
}
// TODO(jeffcyr): Test with auto-disablement enabled when the enforcement is
// skipped for policy recommended and policy allowed extensions.
TEST_F(ExtensionAllowlistWithFeatureDisabledUnitTest,
NoEnforcementOnPolicyRecommendedInstall) {
CreateEmptyExtensionService();
service()->Init();
// Add a policy installed extension.
scoped_refptr<const Extension> extension =
ExtensionBuilder("policy_installed")
.SetPath(data_dir().AppendASCII("good.crx"))
.SetLocation(mojom::ManifestLocation::kExternalPrefDownload)
.Build();
service()->AddExtension(extension.get());
{
ManagementPrefUpdater pref(profile_->GetTestingPrefService());
pref.SetIndividualExtensionAutoInstalled(
extension->id(), "http://example.com/update_url", false);
}
EXPECT_TRUE(IsEnabled(extension->id()));
// On next update check, the extension is now marked as not allowlisted.
PerformActionBasedOnOmahaAttributes(extension->id(),
/*is_malware=*/false,
/*is_allowlisted=*/false);
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(extension->id()));
// A policy installed extension is not disabled by allowlist enforcement.
EXPECT_TRUE(IsEnabled(extension->id()));
// No warnings are shown for policy installed extensions.
EXPECT_FALSE(allowlist()->ShouldDisplayWarning(extension->id()));
}
TEST_F(ExtensionAllowlistWithFeatureDisabledUnitTest,
NoEnforcementOnPolicyAllowedInstall) {
CreateEmptyExtensionService();
service()->Init();
// Add a policy allowed extension.
scoped_refptr<const Extension> extension =
ExtensionBuilder("policy_allowed")
.SetPath(data_dir().AppendASCII("good.crx"))
.SetLocation(mojom::ManifestLocation::kInternal)
.Build();
service()->AddExtension(extension.get());
{
ManagementPrefUpdater pref(profile_->GetTestingPrefService());
pref.SetIndividualExtensionInstallationAllowed(extension->id(), true);
}
EXPECT_TRUE(IsEnabled(extension->id()));
// On next update check, the extension is now marked as not allowlisted.
PerformActionBasedOnOmahaAttributes(extension->id(),
/*is_malware=*/false,
/*is_allowlisted=*/false);
EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED,
allowlist()->GetExtensionAllowlistState(extension->id()));
// An extension allowed by policy is not disabled by allowlist enforcement.
EXPECT_TRUE(IsEnabled(extension->id()));
// No warnings are shown for policy allowed extensions.
EXPECT_FALSE(allowlist()->ShouldDisplayWarning(extension->id()));
}
// TODO(crbug.com/1194051): Add more ExtensionAllowlist::Observer coverage
} // namespace extensions