Skip to content

Commit

Permalink
Add ExtensionManifestV2Availability policy into ExtensionManagement
Browse files Browse the repository at this point in the history
Make the class load the policy value, store it as GlobalSettings and
return it via two new APIs.

Plus:
Refactor for extensions::internal::GlobalSettings. Using absl::optional
instead of an additional boolean to indicate whether a configuration is
set or not.

Bug: 1347794
Change-Id: I3e04abb6396c18e5d769b7c8040b3ae0055612f2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4030873
Reviewed-by: David Bertoni <dbertoni@chromium.org>
Commit-Queue: Owen Min <zmin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1073543}
  • Loading branch information
Owen Min authored and Chromium LUCI CQ committed Nov 18, 2022
1 parent e26d89a commit 33fe101
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 53 deletions.
58 changes: 47 additions & 11 deletions chrome/browser/extensions/extension_management.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ ExtensionManagement::ExtensionManagement(Profile* profile)
pref_change_registrar_.Add(enterprise_reporting::kCloudReportingEnabled,
pref_change_callback);
#endif
pref_change_registrar_.Add(pref_names::kManifestV2Availability,
pref_change_callback);
// Note that both |global_settings_| and |default_settings_| will be null
// before first call to Refresh(), so in order to resolve this, Refresh() must
// be called in the initialization of ExtensionManagement.
Expand All @@ -101,8 +103,7 @@ ExtensionManagement::ExtensionManagement(Profile* profile)
std::make_unique<PermissionsBasedManagementPolicyProvider>(this));
}

ExtensionManagement::~ExtensionManagement() {
}
ExtensionManagement::~ExtensionManagement() = default;

void ExtensionManagement::Shutdown() {
pref_change_registrar_.RemoveAll();
Expand Down Expand Up @@ -249,10 +250,10 @@ bool ExtensionManagement::IsOffstoreInstallAllowed(
const GURL& url,
const GURL& referrer_url) const {
// No allowed install sites specified, disallow by default.
if (!global_settings_->has_restricted_install_sources)
if (!global_settings_->install_sources.has_value())
return false;

const URLPatternSet& url_patterns = global_settings_->install_sources;
const URLPatternSet& url_patterns = *global_settings_->install_sources;

if (!url_patterns.MatchesURL(url))
return false;
Expand All @@ -271,13 +272,38 @@ bool ExtensionManagement::IsAllowedManifestType(
ThemeServiceFactory::GetForProfile(profile_)->UsingPolicyTheme())
return false;

if (!global_settings_->has_restricted_allowed_types)
if (!global_settings_->allowed_types.has_value())
return true;
const std::vector<Manifest::Type>& allowed_types =
global_settings_->allowed_types;
*global_settings_->allowed_types;
return base::Contains(allowed_types, manifest_type);
}

bool ExtensionManagement::IsAllowedManifestVersion(
int manifest_version,
const std::string& extension_id) {
switch (global_settings_->manifest_v2_setting) {
case internal::GlobalSettings::ManifestV2Setting::kDefault:
// TODO(crbug.com/1347794): Get actual manifest v2 feature.
return true;
case internal::GlobalSettings::ManifestV2Setting::kDisabled:
return manifest_version >= 3;
case internal::GlobalSettings::ManifestV2Setting::kEnabled:
return true;
case internal::GlobalSettings::ManifestV2Setting::kEnabledForForceInstalled:
auto installation_mode =
GetInstallationMode(extension_id, /*update_url=*/std::string());
return manifest_version >= 3 ||
installation_mode == InstallationMode::INSTALLATION_FORCED ||
installation_mode == INSTALLATION_RECOMMENDED;
}
}

bool ExtensionManagement::IsAllowedManifestVersion(const Extension* extension) {
return IsAllowedManifestVersion(extension->manifest_version(),
extension->id());
}

APIPermissionSet ExtensionManagement::GetBlockedAPIPermissions(
const Extension* extension) {
const std::string* update_url =
Expand Down Expand Up @@ -439,6 +465,10 @@ void ExtensionManagement::Refresh() {
const base::Value* extension_request_pref = LoadPreference(
prefs::kCloudExtensionRequestEnabled, false, base::Value::Type::BOOLEAN);

const base::Value* manifest_v2_pref =
LoadPreference(pref_names::kManifestV2Availability,
/*force_managed=*/true, base::Value::Type::INTEGER);

// Reset all settings.
global_settings_ = std::make_unique<internal::GlobalSettings>();
settings_by_id_.clear();
Expand Down Expand Up @@ -488,13 +518,13 @@ void ExtensionManagement::Refresh() {
UpdateForcedExtensions(forced_list_pref);

if (install_sources_pref) {
global_settings_->has_restricted_install_sources = true;
global_settings_->install_sources = URLPatternSet();
for (const auto& entry : install_sources_pref->GetList()) {
if (entry.is_string()) {
std::string url_pattern = entry.GetString();
URLPattern pattern(URLPattern::SCHEME_ALL);
if (pattern.Parse(url_pattern) == URLPattern::ParseResult::kSuccess) {
global_settings_->install_sources.AddPattern(pattern);
global_settings_->install_sources->AddPattern(pattern);
} else {
LOG(WARNING) << "Invalid URL pattern in for preference "
<< pref_names::kAllowedInstallSites << ": "
Expand All @@ -505,21 +535,27 @@ void ExtensionManagement::Refresh() {
}

if (allowed_types_pref) {
global_settings_->has_restricted_allowed_types = true;
global_settings_->allowed_types.emplace();
for (const auto& entry : allowed_types_pref->GetList()) {
if (entry.is_int() && entry.GetInt() >= 0 &&
entry.GetInt() < Manifest::Type::NUM_LOAD_TYPES) {
global_settings_->allowed_types.push_back(
global_settings_->allowed_types->push_back(
static_cast<Manifest::Type>(entry.GetInt()));
} else if (entry.is_string()) {
Manifest::Type manifest_type =
schema_constants::GetManifestType(entry.GetString());
if (manifest_type != Manifest::TYPE_UNKNOWN)
global_settings_->allowed_types.push_back(manifest_type);
global_settings_->allowed_types->push_back(manifest_type);
}
}
}

if (manifest_v2_pref) {
global_settings_->manifest_v2_setting =
static_cast<internal::GlobalSettings::ManifestV2Setting>(
manifest_v2_pref->GetInt());
}

if (dict_pref) {
// Parse new extension management preference.

Expand Down
4 changes: 4 additions & 0 deletions chrome/browser/extensions/extension_management.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ class ExtensionManagement : public KeyedService {
bool IsAllowedManifestType(Manifest::Type manifest_type,
const std::string& extension_id) const;

bool IsAllowedManifestVersion(int manifest_version,
const std::string& extension_id);
bool IsAllowedManifestVersion(const Extension* extension);

// Returns the list of blocked API permissions for |extension|.
APIPermissionSet GetBlockedAPIPermissions(const Extension* extension);

Expand Down
14 changes: 5 additions & 9 deletions chrome/browser/extensions/extension_management_internal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -245,18 +245,14 @@ void IndividualSettings::Reset() {
blocked_install_message.clear();
}

GlobalSettings::GlobalSettings() {
Reset();
}
GlobalSettings::GlobalSettings() = default;

GlobalSettings::~GlobalSettings() {
}
GlobalSettings::~GlobalSettings() = default;

void GlobalSettings::Reset() {
has_restricted_install_sources = false;
install_sources.ClearPatterns();
has_restricted_allowed_types = false;
allowed_types.clear();
install_sources.reset();
allowed_types.reset();
manifest_v2_setting = ManifestV2Setting::kDefault;
}

} // namespace internal
Expand Down
16 changes: 12 additions & 4 deletions chrome/browser/extensions/extension_management_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "chrome/browser/extensions/extension_management.h"
#include "extensions/common/manifest.h"
#include "extensions/common/permissions/api_permission_set.h"
#include "third_party/abseil-cpp/absl/types/optional.h"

namespace base {
class DictionaryValue;
Expand Down Expand Up @@ -151,6 +152,12 @@ struct IndividualSettings {

// Global extension management settings, applicable to all extensions.
struct GlobalSettings {
enum class ManifestV2Setting {
kDefault = 0,
kDisabled,
kEnabled,
kEnabledForForceInstalled,
};
GlobalSettings();

GlobalSettings(const GlobalSettings&) = delete;
Expand All @@ -162,13 +169,14 @@ struct GlobalSettings {

// Settings specifying which URLs are allowed to install extensions, will be
// enforced only if |has_restricted_install_sources| is set to true.
URLPatternSet install_sources;
bool has_restricted_install_sources;
absl::optional<URLPatternSet> install_sources;

// Settings specifying all allowed app/extension types, will be enforced
// only of |has_restricted_allowed_types| is set to true.
std::vector<Manifest::Type> allowed_types;
bool has_restricted_allowed_types;
absl::optional<std::vector<Manifest::Type>> allowed_types;

// An enum setting indicates if manifest v2 is allowed.
ManifestV2Setting manifest_v2_setting = ManifestV2Setting::kDefault;
};

} // namespace internal
Expand Down
97 changes: 75 additions & 22 deletions chrome/browser/extensions/extension_management_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -427,8 +427,8 @@ TEST_F(ExtensionManagementServiceTest, LegacyInstallSources) {
allowed_sites_pref.Append("https://corp.mycompany.com/*");
SetPref(true, pref_names::kAllowedInstallSites,
base::Value(std::move(allowed_sites_pref)));
const URLPatternSet& allowed_sites = ReadGlobalSettings()->install_sources;
ASSERT_TRUE(ReadGlobalSettings()->has_restricted_install_sources);
ASSERT_TRUE(ReadGlobalSettings()->install_sources);
const URLPatternSet& allowed_sites = *ReadGlobalSettings()->install_sources;
EXPECT_FALSE(allowed_sites.is_empty());
EXPECT_TRUE(allowed_sites.MatchesURL(GURL("https://www.example.com/foo")));
EXPECT_FALSE(allowed_sites.MatchesURL(GURL("https://www.example.com/bar")));
Expand All @@ -447,9 +447,9 @@ TEST_F(ExtensionManagementServiceTest, LegacyAllowedTypes) {

SetPref(true, pref_names::kAllowedTypes,
base::Value(std::move(allowed_types_pref)));
ASSERT_TRUE(ReadGlobalSettings()->allowed_types);
const std::vector<Manifest::Type>& allowed_types =
ReadGlobalSettings()->allowed_types;
ASSERT_TRUE(ReadGlobalSettings()->has_restricted_allowed_types);
*ReadGlobalSettings()->allowed_types;
EXPECT_EQ(allowed_types.size(), 2u);
EXPECT_FALSE(base::Contains(allowed_types, Manifest::TYPE_EXTENSION));
EXPECT_TRUE(base::Contains(allowed_types, Manifest::TYPE_THEME));
Expand Down Expand Up @@ -688,17 +688,17 @@ TEST_F(ExtensionManagementServiceTest, PreferenceParsing) {
ExtensionManagement::INSTALLATION_ALLOWED);

// Verifies global settings.
EXPECT_TRUE(ReadGlobalSettings()->has_restricted_install_sources);
const URLPatternSet& allowed_sites = ReadGlobalSettings()->install_sources;
ASSERT_TRUE(ReadGlobalSettings()->install_sources);
const URLPatternSet& allowed_sites = *ReadGlobalSettings()->install_sources;
EXPECT_EQ(allowed_sites.size(), 1u);
EXPECT_TRUE(allowed_sites.MatchesURL(GURL("http://foo.com/entry")));
EXPECT_FALSE(allowed_sites.MatchesURL(GURL("http://bar.com/entry")));
EXPECT_TRUE(GetPolicyBlockedHosts(kNonExistingExtension)
.MatchesURL(GURL("http://example.com/default")));

EXPECT_TRUE(ReadGlobalSettings()->has_restricted_allowed_types);
ASSERT_TRUE(ReadGlobalSettings()->allowed_types);
const std::vector<Manifest::Type>& allowed_types =
ReadGlobalSettings()->allowed_types;
*ReadGlobalSettings()->allowed_types;
EXPECT_EQ(allowed_types.size(), 2u);
EXPECT_TRUE(base::Contains(allowed_types, Manifest::TYPE_THEME));
EXPECT_TRUE(base::Contains(allowed_types, Manifest::TYPE_USER_SCRIPT));
Expand Down Expand Up @@ -871,8 +871,8 @@ TEST_F(ExtensionManagementServiceTest, NewInstallSources) {
allowed_sites_pref.Append("https://www.example.com/foo");
SetPref(true, pref_names::kAllowedInstallSites,
base::Value(std::move(allowed_sites_pref)));
EXPECT_TRUE(ReadGlobalSettings()->has_restricted_install_sources);
EXPECT_TRUE(ReadGlobalSettings()->install_sources.MatchesURL(
ASSERT_TRUE(ReadGlobalSettings()->install_sources);
EXPECT_TRUE(ReadGlobalSettings()->install_sources->MatchesURL(
GURL("https://www.example.com/foo")));

// Set the new dictionary preference.
Expand All @@ -881,17 +881,17 @@ TEST_F(ExtensionManagementServiceTest, NewInstallSources) {
updater.ClearInstallSources();
}
// Verifies that the new one overrides the legacy ones.
EXPECT_TRUE(ReadGlobalSettings()->has_restricted_install_sources);
EXPECT_FALSE(ReadGlobalSettings()->install_sources.MatchesURL(
ASSERT_TRUE(ReadGlobalSettings()->install_sources);
EXPECT_FALSE(ReadGlobalSettings()->install_sources->MatchesURL(
GURL("https://www.example.com/foo")));

// Updates the new dictionary preference.
{
PrefUpdater updater(pref_service_.get());
updater.AddInstallSource("https://corp.mycompany.com/*");
}
EXPECT_TRUE(ReadGlobalSettings()->has_restricted_install_sources);
EXPECT_TRUE(ReadGlobalSettings()->install_sources.MatchesURL(
ASSERT_TRUE(ReadGlobalSettings()->install_sources);
EXPECT_TRUE(ReadGlobalSettings()->install_sources->MatchesURL(
GURL("https://corp.mycompany.com/entry")));
}

Expand All @@ -903,27 +903,29 @@ TEST_F(ExtensionManagementServiceTest, NewAllowedTypes) {
allowed_types_pref.Append(Manifest::TYPE_USER_SCRIPT);
SetPref(true, pref_names::kAllowedTypes,
base::Value(allowed_types_pref.Clone()));
EXPECT_TRUE(ReadGlobalSettings()->has_restricted_allowed_types);
EXPECT_EQ(ReadGlobalSettings()->allowed_types.size(), 1u);
EXPECT_EQ(ReadGlobalSettings()->allowed_types[0], Manifest::TYPE_USER_SCRIPT);
ASSERT_TRUE(ReadGlobalSettings()->allowed_types);
EXPECT_EQ(ReadGlobalSettings()->allowed_types->size(), 1u);
EXPECT_EQ(ReadGlobalSettings()->allowed_types.value()[0],
Manifest::TYPE_USER_SCRIPT);

// Set the new dictionary preference.
{
PrefUpdater updater(pref_service_.get());
updater.ClearAllowedTypes();
}
// Verifies that the new one overrides the legacy ones.
EXPECT_TRUE(ReadGlobalSettings()->has_restricted_allowed_types);
EXPECT_EQ(ReadGlobalSettings()->allowed_types.size(), 0u);
ASSERT_TRUE(ReadGlobalSettings()->allowed_types);
EXPECT_EQ(ReadGlobalSettings()->allowed_types->size(), 0u);

// Updates the new dictionary preference.
{
PrefUpdater updater(pref_service_.get());
updater.AddAllowedType("theme");
}
EXPECT_TRUE(ReadGlobalSettings()->has_restricted_allowed_types);
EXPECT_EQ(ReadGlobalSettings()->allowed_types.size(), 1u);
EXPECT_EQ(ReadGlobalSettings()->allowed_types[0], Manifest::TYPE_THEME);
ASSERT_TRUE(ReadGlobalSettings()->allowed_types);
EXPECT_EQ(ReadGlobalSettings()->allowed_types->size(), 1u);
EXPECT_EQ(ReadGlobalSettings()->allowed_types.value()[0],
Manifest::TYPE_THEME);
}

// Tests functionality of new preference as to deprecate legacy
Expand Down Expand Up @@ -1116,6 +1118,57 @@ TEST_F(ExtensionManagementServiceTest,
GetInstallationModeById(kTargetExtension));
}

TEST_F(ExtensionManagementServiceTest, ManifestV2Default) {
SetPref(true, pref_names::kManifestV2Availability,
base::Value(static_cast<int>(
internal::GlobalSettings::ManifestV2Setting::kDefault)));
EXPECT_TRUE(
extension_management_->IsAllowedManifestVersion(2, kTargetExtension));
EXPECT_TRUE(
extension_management_->IsAllowedManifestVersion(3, kTargetExtension));
}

TEST_F(ExtensionManagementServiceTest, ManifestV2Disabled) {
SetPref(true, pref_names::kManifestV2Availability,
base::Value(static_cast<int>(
internal::GlobalSettings::ManifestV2Setting::kDisabled)));
EXPECT_FALSE(
extension_management_->IsAllowedManifestVersion(2, kTargetExtension));
EXPECT_TRUE(
extension_management_->IsAllowedManifestVersion(3, kTargetExtension));
}

TEST_F(ExtensionManagementServiceTest, ManifestV2Enabled) {
SetPref(true, pref_names::kManifestV2Availability,
base::Value(static_cast<int>(
internal::GlobalSettings::ManifestV2Setting::kEnabled)));
EXPECT_TRUE(
extension_management_->IsAllowedManifestVersion(2, kTargetExtension));
EXPECT_TRUE(
extension_management_->IsAllowedManifestVersion(3, kTargetExtension));
}

TEST_F(ExtensionManagementServiceTest, ManifestV2EnabledForForceInstalled) {
SetPref(
true, pref_names::kManifestV2Availability,
base::Value(static_cast<int>(internal::GlobalSettings::ManifestV2Setting::
kEnabledForForceInstalled)));
EXPECT_FALSE(
extension_management_->IsAllowedManifestVersion(2, kTargetExtension));
EXPECT_TRUE(
extension_management_->IsAllowedManifestVersion(3, kTargetExtension));

base::Value::Dict forced_list_pref;
ExternalPolicyLoader::AddExtension(forced_list_pref, kTargetExtension,
kExampleUpdateUrl);
SetPref(true, pref_names::kInstallForceList, forced_list_pref.Clone());

EXPECT_TRUE(
extension_management_->IsAllowedManifestVersion(2, kTargetExtension));
EXPECT_TRUE(
extension_management_->IsAllowedManifestVersion(3, kTargetExtension));
}

// Tests the flag value indicating that extensions are blocklisted by default.
TEST_F(ExtensionAdminPolicyTest, BlocklistedByDefault) {
EXPECT_FALSE(BlocklistedByDefault(nullptr));
Expand Down

0 comments on commit 33fe101

Please sign in to comment.