// Copyright 2021 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/ash_extension_keeplist_manager.h"
#include "ash/constants/ash_features.h"
#include "base/check.h"
#include "base/feature_list.h"
#include "chrome/browser/ash/crosapi/browser_util.h"
#include "chrome/browser/ash/crosapi/hosted_app_util.h"
#include "chrome/browser/extensions/extension_keeplist_chromeos.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "components/app_constants/constants.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_id.h"
namespace extensions {
AshExtensionKeeplistManager::AshExtensionKeeplistManager(
Profile* profile,
ExtensionPrefs* extension_prefs,
ExtensionService* extension_service)
: extension_prefs_(extension_prefs),
extension_service_(extension_service),
registry_(ExtensionRegistry::Get(profile)),
should_enforce_keeplist_(
crosapi::browser_util::ShouldEnforceAshExtensionKeepList()) {
if (should_enforce_keeplist_)
registry_observation_.Observe(registry_);
}
AshExtensionKeeplistManager::~AshExtensionKeeplistManager() = default;
void AshExtensionKeeplistManager::Init() {
if (should_enforce_keeplist_)
ActivateKeeplistEnforcement();
else
DeactivateKeeplistEnforcement();
}
void AshExtensionKeeplistManager::ActivateKeeplistEnforcement() {
DCHECK(should_enforce_keeplist_);
const ExtensionSet all_extensions =
registry_->GenerateInstalledExtensionsSet();
for (const auto& extension : all_extensions) {
if (ShouldDisable(extension.get()))
Disable(extension->id());
}
}
bool AshExtensionKeeplistManager::ShouldDisable(
const Extension* extension) const {
if (extension->is_extension() && !ExtensionRunsInOS(extension->id()))
return true;
if (extension->is_platform_app() &&
crosapi::browser_util::IsLacrosChromeAppsEnabled() &&
!ExtensionAppRunsInOS(extension->id())) {
return true;
}
if (extension->is_hosted_app() &&
extension->id() != app_constants::kChromeAppId &&
crosapi::IsStandaloneBrowserHostedAppsEnabled()) {
return true;
}
return false;
}
void AshExtensionKeeplistManager::Disable(const ExtensionId& extension_id) {
DCHECK(should_enforce_keeplist_);
extension_service_->DisableExtension(
extension_id, disable_reason::DISABLE_NOT_ASH_KEEPLISTED);
// An extension is not allowed to be disabled by user due to different reasons
// (shared module, installed as a component extension or installed by policy,
// etc.). We would log a message here to track the extensions that can't be
// disabled and analyze to see if we have missed any extensions in the keep
// list during the audit.
if (registry_->enabled_extensions().Contains(extension_id)) {
LOG(WARNING) << "Can not enforce disabling extension id:" << extension_id;
}
}
void AshExtensionKeeplistManager::DeactivateKeeplistEnforcement() {
DCHECK(!should_enforce_keeplist_);
const ExtensionSet all_extensions =
registry_->GenerateInstalledExtensionsSet();
// Find all extensions disabled by keeplist enforcement, remove the disable
// reason.
for (const auto& extension : all_extensions) {
if (extension_prefs_->HasDisableReason(
extension->id(), disable_reason::DISABLE_NOT_ASH_KEEPLISTED)) {
extension_service_->RemoveDisableReasonAndMaybeEnable(
extension->id(), disable_reason::DISABLE_NOT_ASH_KEEPLISTED);
}
}
}
void AshExtensionKeeplistManager::OnExtensionReady(
content::BrowserContext* browser_context,
const Extension* extension) {
if (!should_enforce_keeplist_)
return;
if (ShouldDisable(extension))
Disable(extension->id());
}
} // namespace extensions