binjin | 81d7c55 | 2014-10-02 11:47:12 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "chrome/browser/extensions/extension_management_internal.h" |
| 6 | |
dcheng | 1fc00f1 | 2015-12-26 22:18:03 | [diff] [blame] | 7 | #include <utility> |
| 8 | |
binjin | 81d7c55 | 2014-10-02 11:47:12 | [diff] [blame] | 9 | #include "base/logging.h" |
| 10 | #include "base/values.h" |
binjin | 8e3d018 | 2014-12-04 16:44:28 | [diff] [blame] | 11 | #include "base/version.h" |
binjin | 81d7c55 | 2014-10-02 11:47:12 | [diff] [blame] | 12 | #include "chrome/browser/extensions/extension_management_constants.h" |
| 13 | #include "extensions/common/url_pattern_set.h" |
| 14 | #include "url/gurl.h" |
| 15 | |
| 16 | namespace extensions { |
| 17 | |
| 18 | namespace internal { |
| 19 | |
| 20 | namespace { |
| 21 | const char kMalformedPreferenceWarning[] = |
| 22 | "Malformed extension management preference."; |
nrpeter | 2362e7e | 2017-05-10 17:21:26 | [diff] [blame] | 23 | |
| 24 | // Maximum number of characters for a 'blocked_install_message' value. |
| 25 | const int kBlockedInstallMessageMaxLength = 1000; |
binjin | 81d7c55 | 2014-10-02 11:47:12 | [diff] [blame] | 26 | } // namespace |
| 27 | |
| 28 | IndividualSettings::IndividualSettings() { |
| 29 | Reset(); |
| 30 | } |
| 31 | |
binjin | 8e3d018 | 2014-12-04 16:44:28 | [diff] [blame] | 32 | // Initializes from default settings. |
| 33 | IndividualSettings::IndividualSettings( |
| 34 | const IndividualSettings* default_settings) { |
| 35 | installation_mode = default_settings->installation_mode; |
Sergey Poromov | 7efa5c26 | 2019-05-21 14:47:56 | [diff] [blame] | 36 | update_url = default_settings->update_url; |
Devlin Cronin | 32708b0 | 2018-12-05 17:58:04 | [diff] [blame] | 37 | blocked_permissions = default_settings->blocked_permissions.Clone(); |
binjin | 8e3d018 | 2014-12-04 16:44:28 | [diff] [blame] | 38 | // We are not initializing |minimum_version_required| from |default_settings| |
| 39 | // here since it's not applicable to default settings. |
| 40 | } |
| 41 | |
binjin | 81d7c55 | 2014-10-02 11:47:12 | [diff] [blame] | 42 | IndividualSettings::~IndividualSettings() { |
| 43 | } |
| 44 | |
| 45 | bool IndividualSettings::Parse(const base::DictionaryValue* dict, |
| 46 | ParsingScope scope) { |
| 47 | std::string installation_mode_str; |
| 48 | if (dict->GetStringWithoutPathExpansion(schema_constants::kInstallationMode, |
| 49 | &installation_mode_str)) { |
| 50 | if (installation_mode_str == schema_constants::kAllowed) { |
| 51 | installation_mode = ExtensionManagement::INSTALLATION_ALLOWED; |
| 52 | } else if (installation_mode_str == schema_constants::kBlocked) { |
| 53 | installation_mode = ExtensionManagement::INSTALLATION_BLOCKED; |
| 54 | } else if (installation_mode_str == schema_constants::kForceInstalled) { |
| 55 | installation_mode = ExtensionManagement::INSTALLATION_FORCED; |
| 56 | } else if (installation_mode_str == schema_constants::kNormalInstalled) { |
| 57 | installation_mode = ExtensionManagement::INSTALLATION_RECOMMENDED; |
Kyle Spiers | bb4b9f5f5 | 2019-05-02 17:17:15 | [diff] [blame] | 58 | } else if (installation_mode_str == schema_constants::kRemoved) { |
| 59 | installation_mode = ExtensionManagement::INSTALLATION_REMOVED; |
binjin | 81d7c55 | 2014-10-02 11:47:12 | [diff] [blame] | 60 | } else { |
| 61 | // Invalid value for 'installation_mode'. |
| 62 | LOG(WARNING) << kMalformedPreferenceWarning; |
| 63 | return false; |
| 64 | } |
| 65 | |
| 66 | // Only proceed to fetch update url if force or recommended install mode |
| 67 | // is set. |
| 68 | if (installation_mode == ExtensionManagement::INSTALLATION_FORCED || |
| 69 | installation_mode == ExtensionManagement::INSTALLATION_RECOMMENDED) { |
| 70 | if (scope != SCOPE_INDIVIDUAL) { |
| 71 | // Only individual extensions are allowed to be automatically installed. |
| 72 | LOG(WARNING) << kMalformedPreferenceWarning; |
| 73 | return false; |
| 74 | } |
| 75 | std::string update_url_str; |
| 76 | if (dict->GetStringWithoutPathExpansion(schema_constants::kUpdateUrl, |
| 77 | &update_url_str) && |
| 78 | GURL(update_url_str).is_valid()) { |
| 79 | update_url = update_url_str; |
| 80 | } else { |
| 81 | // No valid update URL for extension. |
| 82 | LOG(WARNING) << kMalformedPreferenceWarning; |
| 83 | return false; |
| 84 | } |
| 85 | } |
| 86 | } |
| 87 | |
binjin | e6b58b5 | 2014-10-31 01:55:57 | [diff] [blame] | 88 | // Parses the blocked permission settings. |
| 89 | const base::ListValue* list_value = nullptr; |
| 90 | base::string16 error; |
| 91 | |
nrpeter | 40e1638 | 2017-04-13 17:34:58 | [diff] [blame] | 92 | // Set default blocked permissions, or replace with extension specific blocks. |
| 93 | APIPermissionSet parsed_blocked_permissions; |
| 94 | APIPermissionSet explicitly_allowed_permissions; |
| 95 | if (dict->GetListWithoutPathExpansion(schema_constants::kAllowedPermissions, |
binjin | e6b58b5 | 2014-10-31 01:55:57 | [diff] [blame] | 96 | &list_value)) { |
binjin | e6b58b5 | 2014-10-31 01:55:57 | [diff] [blame] | 97 | if (!APIPermissionSet::ParseFromJSON( |
nrpeter | 40e1638 | 2017-04-13 17:34:58 | [diff] [blame] | 98 | list_value, APIPermissionSet::kDisallowInternalPermissions, |
| 99 | &explicitly_allowed_permissions, &error, nullptr)) { |
binjin | e6b58b5 | 2014-10-31 01:55:57 | [diff] [blame] | 100 | LOG(WARNING) << error; |
| 101 | } |
binjin | e6b58b5 | 2014-10-31 01:55:57 | [diff] [blame] | 102 | } |
binjin | e6b58b5 | 2014-10-31 01:55:57 | [diff] [blame] | 103 | if (dict->GetListWithoutPathExpansion(schema_constants::kBlockedPermissions, |
| 104 | &list_value)) { |
binjin | e6b58b5 | 2014-10-31 01:55:57 | [diff] [blame] | 105 | if (!APIPermissionSet::ParseFromJSON( |
nrpeter | 40e1638 | 2017-04-13 17:34:58 | [diff] [blame] | 106 | list_value, APIPermissionSet::kDisallowInternalPermissions, |
| 107 | &parsed_blocked_permissions, &error, nullptr)) { |
binjin | e6b58b5 | 2014-10-31 01:55:57 | [diff] [blame] | 108 | LOG(WARNING) << error; |
| 109 | } |
binjin | e6b58b5 | 2014-10-31 01:55:57 | [diff] [blame] | 110 | } |
nrpeter | 40e1638 | 2017-04-13 17:34:58 | [diff] [blame] | 111 | APIPermissionSet::Difference(parsed_blocked_permissions, |
| 112 | explicitly_allowed_permissions, |
| 113 | &blocked_permissions); |
| 114 | |
| 115 | // Parses list of Match Patterns into a URLPatternSet. |
| 116 | auto parse_url_pattern_set = [](const base::DictionaryValue* dict, |
| 117 | const char key[], URLPatternSet* out_value) { |
| 118 | const base::ListValue* host_list_value = nullptr; |
| 119 | |
| 120 | // Get the list of URLPatterns. |
| 121 | if (dict->GetListWithoutPathExpansion(key, |
| 122 | &host_list_value)) { |
nrpeter | e33d2a5b | 2017-04-25 00:12:31 | [diff] [blame] | 123 | if (host_list_value->GetSize() > |
| 124 | schema_constants::kMaxItemsURLPatternSet) { |
| 125 | LOG(WARNING) << "Exceeded maximum number of URL match patterns (" |
| 126 | << schema_constants::kMaxItemsURLPatternSet |
| 127 | << ") for attribute '" << key << "'"; |
nrpeter | e33d2a5b | 2017-04-25 00:12:31 | [diff] [blame] | 128 | } |
| 129 | |
nrpeter | 40e1638 | 2017-04-13 17:34:58 | [diff] [blame] | 130 | out_value->ClearPatterns(); |
| 131 | const int extension_scheme_mask = |
| 132 | URLPattern::GetValidSchemeMaskForExtensions(); |
Yann Dago | ee29190 | 2019-08-19 15:49:06 | [diff] [blame] | 133 | auto numItems = std::min(host_list_value->GetSize(), |
| 134 | schema_constants::kMaxItemsURLPatternSet); |
| 135 | for (size_t i = 0; i < numItems; ++i) { |
nrpeter | 40e1638 | 2017-04-13 17:34:58 | [diff] [blame] | 136 | std::string unparsed_str; |
| 137 | host_list_value->GetString(i, &unparsed_str); |
Nick Peterson | 6bdf582 | 2017-06-01 20:42:45 | [diff] [blame] | 138 | URLPattern pattern(extension_scheme_mask); |
| 139 | if (unparsed_str != URLPattern::kAllUrlsPattern) |
| 140 | unparsed_str.append("/*"); |
Devlin Cronin | 35f8e37 | 2019-08-16 19:15:38 | [diff] [blame] | 141 | URLPattern::ParseResult parse_result = pattern.Parse(unparsed_str); |
Devlin Cronin | bd7f2b5fa | 2018-09-05 17:41:18 | [diff] [blame] | 142 | if (parse_result != URLPattern::ParseResult::kSuccess) { |
nrpeter | 40e1638 | 2017-04-13 17:34:58 | [diff] [blame] | 143 | LOG(WARNING) << kMalformedPreferenceWarning; |
| 144 | LOG(WARNING) << "Invalid URL pattern '" + unparsed_str + |
| 145 | "' for attribute " + key; |
| 146 | return false; |
| 147 | } |
| 148 | out_value->AddPattern(pattern); |
| 149 | } |
| 150 | } |
| 151 | return true; |
| 152 | }; |
| 153 | |
Devlin Cronin | 7e0f41ff | 2018-05-16 17:19:36 | [diff] [blame] | 154 | if (!parse_url_pattern_set(dict, schema_constants::kPolicyBlockedHosts, |
| 155 | &policy_blocked_hosts)) |
nrpeter | 40e1638 | 2017-04-13 17:34:58 | [diff] [blame] | 156 | return false; |
| 157 | |
Devlin Cronin | 7e0f41ff | 2018-05-16 17:19:36 | [diff] [blame] | 158 | if (!parse_url_pattern_set(dict, schema_constants::kPolicyAllowedHosts, |
| 159 | &policy_allowed_hosts)) |
nrpeter | 40e1638 | 2017-04-13 17:34:58 | [diff] [blame] | 160 | return false; |
binjin | e6b58b5 | 2014-10-31 01:55:57 | [diff] [blame] | 161 | |
binjin | 8e3d018 | 2014-12-04 16:44:28 | [diff] [blame] | 162 | // Parses the minimum version settings. |
| 163 | std::string minimum_version_required_str; |
| 164 | if (scope == SCOPE_INDIVIDUAL && |
| 165 | dict->GetStringWithoutPathExpansion( |
| 166 | schema_constants::kMinimumVersionRequired, |
| 167 | &minimum_version_required_str)) { |
dcheng | c963c714 | 2016-04-08 03:55:22 | [diff] [blame] | 168 | std::unique_ptr<base::Version> version( |
pwnall | cbd7319 | 2016-08-22 18:59:17 | [diff] [blame] | 169 | new base::Version(minimum_version_required_str)); |
binjin | 8e3d018 | 2014-12-04 16:44:28 | [diff] [blame] | 170 | // We accept a general version string here. Note that count of components in |
| 171 | // version string of extensions is limited to 4. |
| 172 | if (!version->IsValid()) |
| 173 | LOG(WARNING) << kMalformedPreferenceWarning; |
| 174 | else |
dcheng | 1fc00f1 | 2015-12-26 22:18:03 | [diff] [blame] | 175 | minimum_version_required = std::move(version); |
binjin | 8e3d018 | 2014-12-04 16:44:28 | [diff] [blame] | 176 | } |
| 177 | |
nrpeter | 2362e7e | 2017-05-10 17:21:26 | [diff] [blame] | 178 | if (dict->GetStringWithoutPathExpansion( |
| 179 | schema_constants::kBlockedInstallMessage, &blocked_install_message)) { |
| 180 | if (blocked_install_message.length() > kBlockedInstallMessageMaxLength) { |
| 181 | LOG(WARNING) << "Truncated blocked install message to 1000 characters"; |
| 182 | blocked_install_message.erase(kBlockedInstallMessageMaxLength, |
| 183 | std::string::npos); |
| 184 | } |
| 185 | } |
| 186 | |
binjin | 81d7c55 | 2014-10-02 11:47:12 | [diff] [blame] | 187 | return true; |
| 188 | } |
| 189 | |
| 190 | void IndividualSettings::Reset() { |
| 191 | installation_mode = ExtensionManagement::INSTALLATION_ALLOWED; |
| 192 | update_url.clear(); |
binjin | e6b58b5 | 2014-10-31 01:55:57 | [diff] [blame] | 193 | blocked_permissions.clear(); |
Devlin Cronin | 7e0f41ff | 2018-05-16 17:19:36 | [diff] [blame] | 194 | policy_blocked_hosts.ClearPatterns(); |
| 195 | policy_allowed_hosts.ClearPatterns(); |
nrpeter | 2362e7e | 2017-05-10 17:21:26 | [diff] [blame] | 196 | blocked_install_message.clear(); |
binjin | 81d7c55 | 2014-10-02 11:47:12 | [diff] [blame] | 197 | } |
| 198 | |
| 199 | GlobalSettings::GlobalSettings() { |
| 200 | Reset(); |
| 201 | } |
| 202 | |
| 203 | GlobalSettings::~GlobalSettings() { |
| 204 | } |
| 205 | |
| 206 | void GlobalSettings::Reset() { |
| 207 | has_restricted_install_sources = false; |
| 208 | install_sources.ClearPatterns(); |
| 209 | has_restricted_allowed_types = false; |
| 210 | allowed_types.clear(); |
| 211 | } |
| 212 | |
| 213 | } // namespace internal |
| 214 | |
| 215 | } // namespace extensions |