chromium/chrome/browser/extensions/ash_extension_keeplist_manager.cc

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