| // 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/omaha_attributes_handler.h" | 
 |  | 
 | #include <optional> | 
 |  | 
 | #include "base/metrics/histogram_functions.h" | 
 | #include "base/values.h" | 
 | #include "content/public/browser/browser_thread.h" | 
 | #include "extensions/browser/blocklist_extension_prefs.h" | 
 | #include "extensions/browser/blocklist_state.h" | 
 | #include "extensions/browser/extension_registrar.h" | 
 | #include "extensions/browser/extension_registry.h" | 
 | #include "extensions/buildflags/buildflags.h" | 
 |  | 
 | static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE)); | 
 |  | 
 | namespace extensions { | 
 |  | 
 | namespace { | 
 |  | 
 | // Logs UMA metrics when an extension is disabled remotely. | 
 | void ReportExtensionDisabledRemotely(bool should_be_remotely_disabled, | 
 |                                      ExtensionUpdateCheckDataKey reason) { | 
 |   // Report that the extension is newly disabled due to Omaha attributes. | 
 |   if (should_be_remotely_disabled) { | 
 |     base::UmaHistogramEnumeration("Extensions.ExtensionDisabledRemotely2", | 
 |                                   reason); | 
 |   } | 
 |  | 
 |   // Report that the extension has added a new disable reason. | 
 |   base::UmaHistogramEnumeration( | 
 |       "Extensions.ExtensionAddDisabledRemotelyReason2", reason); | 
 | } | 
 |  | 
 | // Logs UMA metrics when the key is not found in Omaha attributes. | 
 | void ReportNoUpdateCheckKeys() { | 
 |   base::UmaHistogramEnumeration("Extensions.ExtensionDisabledRemotely2", | 
 |                                 ExtensionUpdateCheckDataKey::kNoKey); | 
 | } | 
 |  | 
 | // Logs UMA metrics when a remotely disabled extension is re-enabled. | 
 | void ReportReenableExtension(ExtensionUpdateCheckDataKey reason) { | 
 |   const char* histogram = nullptr; | 
 |   switch (reason) { | 
 |     case ExtensionUpdateCheckDataKey::kMalware: | 
 |       histogram = "Extensions.ExtensionReenabledRemotely"; | 
 |       break; | 
 |     case ExtensionUpdateCheckDataKey::kPotentiallyUWS: | 
 |       histogram = "Extensions.ExtensionReenabledRemotelyForPotentiallyUWS"; | 
 |       break; | 
 |     case ExtensionUpdateCheckDataKey::kPolicyViolation: | 
 |       histogram = "Extensions.ExtensionReenabledRemotelyForPolicyViolation"; | 
 |       break; | 
 |     case ExtensionUpdateCheckDataKey::kNoKey: | 
 |       NOTREACHED(); | 
 |   } | 
 |   base::UmaHistogramCounts100(histogram, 1); | 
 | } | 
 |  | 
 | // Checks whether the `state` is in the `attributes`. | 
 | bool HasOmahaBlocklistStateInAttributes(const base::Value::Dict& attributes, | 
 |                                         BitMapBlocklistState state) { | 
 |   std::optional<bool> state_value; | 
 |   switch (state) { | 
 |     case BitMapBlocklistState::BLOCKLISTED_MALWARE: | 
 |       state_value = attributes.FindBool("_malware"); | 
 |       break; | 
 |     case BitMapBlocklistState::BLOCKLISTED_CWS_POLICY_VIOLATION: | 
 |       state_value = attributes.FindBool("_policy_violation"); | 
 |       break; | 
 |     case BitMapBlocklistState::BLOCKLISTED_POTENTIALLY_UNWANTED: | 
 |       state_value = attributes.FindBool("_potentially_uws"); | 
 |       break; | 
 |     case BitMapBlocklistState::NOT_BLOCKLISTED: | 
 |     case BitMapBlocklistState::BLOCKLISTED_SECURITY_VULNERABILITY: | 
 |       NOTREACHED() | 
 |           << "The other states are not applicable in Omaha attributes."; | 
 |   } | 
 |   return state_value.value_or(false); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | OmahaAttributesHandler::OmahaAttributesHandler( | 
 |     ExtensionPrefs* extension_prefs, | 
 |     ExtensionRegistry* registry, | 
 |     ExtensionRegistrar* registrar) | 
 |     : extension_prefs_(extension_prefs), | 
 |       registry_(registry), | 
 |       registrar_(registrar) {} | 
 |  | 
 | void OmahaAttributesHandler::PerformActionBasedOnOmahaAttributes( | 
 |     const ExtensionId& extension_id, | 
 |     const base::Value::Dict& attributes) { | 
 |   DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
 |   // It is possible that an extension is uninstalled when the omaha attributes | 
 |   // are notified by the update client asynchronously. In this case, we should | 
 |   // ignore this extension. | 
 |   if (!registry_->GetInstalledExtension(extension_id)) { | 
 |     return; | 
 |   } | 
 |   HandleMalwareOmahaAttribute(extension_id, attributes); | 
 |   HandleGreylistOmahaAttribute( | 
 |       extension_id, attributes, | 
 |       BitMapBlocklistState::BLOCKLISTED_CWS_POLICY_VIOLATION, | 
 |       ExtensionUpdateCheckDataKey::kPolicyViolation); | 
 |   HandleGreylistOmahaAttribute( | 
 |       extension_id, attributes, | 
 |       BitMapBlocklistState::BLOCKLISTED_POTENTIALLY_UNWANTED, | 
 |       ExtensionUpdateCheckDataKey::kPotentiallyUWS); | 
 | } | 
 |  | 
 | void OmahaAttributesHandler::HandleMalwareOmahaAttribute( | 
 |     const ExtensionId& extension_id, | 
 |     const base::Value::Dict& attributes) { | 
 |   bool has_malware_value = HasOmahaBlocklistStateInAttributes( | 
 |       attributes, BitMapBlocklistState::BLOCKLISTED_MALWARE); | 
 |   if (!has_malware_value) { | 
 |     ReportNoUpdateCheckKeys(); | 
 |     if (!blocklist_prefs::HasOmahaBlocklistState( | 
 |             extension_id, BitMapBlocklistState::BLOCKLISTED_MALWARE, | 
 |             extension_prefs_)) { | 
 |       return; | 
 |     } | 
 |     // The extension was previously blocklisted by Omaha, but is no longer. | 
 |     // Clear the old omaha state. | 
 |     ReportReenableExtension(ExtensionUpdateCheckDataKey::kMalware); | 
 |     blocklist_prefs::RemoveOmahaBlocklistState( | 
 |         extension_id, BitMapBlocklistState::BLOCKLISTED_MALWARE, | 
 |         extension_prefs_); | 
 |     registrar_->OnBlocklistStateRemoved(extension_id); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (blocklist_prefs::HasOmahaBlocklistState( | 
 |           extension_id, BitMapBlocklistState::BLOCKLISTED_MALWARE, | 
 |           extension_prefs_)) { | 
 |     // The extension is already disabled. No work needs to be done. | 
 |     return; | 
 |   } | 
 |  | 
 |   ReportExtensionDisabledRemotely(registrar_->IsExtensionEnabled(extension_id), | 
 |                                   ExtensionUpdateCheckDataKey::kMalware); | 
 |  | 
 |   blocklist_prefs::AddOmahaBlocklistState( | 
 |       extension_id, BitMapBlocklistState::BLOCKLISTED_MALWARE, | 
 |       extension_prefs_); | 
 |   registrar_->OnBlocklistStateAdded(extension_id); | 
 | } | 
 |  | 
 | void OmahaAttributesHandler::HandleGreylistOmahaAttribute( | 
 |     const ExtensionId& extension_id, | 
 |     const base::Value::Dict& attributes, | 
 |     BitMapBlocklistState greylist_state, | 
 |     ExtensionUpdateCheckDataKey reason) { | 
 |   bool has_attribute_value = | 
 |       HasOmahaBlocklistStateInAttributes(attributes, greylist_state); | 
 |   bool has_omaha_blocklist_state = blocklist_prefs::HasOmahaBlocklistState( | 
 |       extension_id, greylist_state, extension_prefs_); | 
 |   if (!has_attribute_value) { | 
 |     if (has_omaha_blocklist_state) { | 
 |       blocklist_prefs::RemoveOmahaBlocklistState(extension_id, greylist_state, | 
 |                                                  extension_prefs_); | 
 |       ReportReenableExtension(reason); | 
 |     } | 
 |     registrar_->OnGreylistStateRemoved(extension_id); | 
 |     return; | 
 |   } | 
 |  | 
 |   ReportExtensionDisabledRemotely( | 
 |       /*should_be_remotely_disabled=*/!has_omaha_blocklist_state, reason); | 
 |   blocklist_prefs::AddOmahaBlocklistState(extension_id, greylist_state, | 
 |                                           extension_prefs_); | 
 |   registrar_->OnGreylistStateAdded(extension_id, greylist_state); | 
 | } | 
 |  | 
 | }  // namespace extensions |