Update CrOS OOBE throbber to MD throbber; delete old asset
[chromium-blink-merge.git] / chrome / common / extensions / permissions / chrome_permission_message_provider.cc
blob2522a66d87d026e35eb92778e5ab26a6161b18f5
1 // Copyright 2013 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.
5 #include "chrome/common/extensions/permissions/chrome_permission_message_provider.h"
7 #include <vector>
9 #include "base/memory/scoped_vector.h"
10 #include "base/metrics/field_trial.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "chrome/common/extensions/permissions/chrome_permission_message_rules.h"
15 #include "chrome/grit/generated_resources.h"
16 #include "extensions/common/extensions_client.h"
17 #include "extensions/common/permissions/permission_message_util.h"
18 #include "extensions/common/permissions/permission_set.h"
19 #include "extensions/common/url_pattern.h"
20 #include "extensions/common/url_pattern_set.h"
21 #include "grit/extensions_strings.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "url/gurl.h"
25 namespace extensions {
27 namespace {
29 // Copyable wrapper to make CoalescedPermissionMessages comparable.
30 class ComparablePermission {
31 public:
32 explicit ComparablePermission(const CoalescedPermissionMessage& msg)
33 : msg_(&msg) {}
35 bool operator<(const ComparablePermission& rhs) const {
36 if (msg_->message() < rhs.msg_->message())
37 return true;
38 if (msg_->message() > rhs.msg_->message())
39 return false;
40 return msg_->submessages() < rhs.msg_->submessages();
43 private:
44 const CoalescedPermissionMessage* msg_;
46 using ComparablePermissions = std::vector<ComparablePermission>;
48 } // namespace
50 typedef std::set<PermissionMessage> PermissionMsgSet;
52 ChromePermissionMessageProvider::ChromePermissionMessageProvider() {
55 ChromePermissionMessageProvider::~ChromePermissionMessageProvider() {
58 CoalescedPermissionMessages
59 ChromePermissionMessageProvider::GetPermissionMessages(
60 const PermissionIDSet& permissions) const {
61 std::vector<ChromePermissionMessageRule> rules =
62 ChromePermissionMessageRule::GetAllRules();
64 // Apply each of the rules, in order, to generate the messages for the given
65 // permissions. Once a permission is used in a rule, remove it from the set
66 // of available permissions so it cannot be applied to subsequent rules.
67 PermissionIDSet remaining_permissions = permissions;
68 CoalescedPermissionMessages messages;
69 for (const auto& rule : rules) {
70 // Only apply the rule if we have all the required permission IDs. If the
71 // rule has no required IDs, then apply it only if any of the optional ones
72 // are there.
73 bool has_required_permissions = !rule.required_permissions().empty();
74 bool use_rule =
75 has_required_permissions
76 ? remaining_permissions.ContainsAllIDs(rule.required_permissions())
77 : remaining_permissions.ContainsAnyID(rule.optional_permissions());
78 if (use_rule) {
79 // We can apply the rule. Add all the required permissions, and as many
80 // optional permissions as we can, to the new message.
81 PermissionIDSet used_permissions =
82 remaining_permissions.GetAllPermissionsWithIDs(
83 rule.all_permissions());
84 messages.push_back(rule.GetPermissionMessage(used_permissions));
86 remaining_permissions =
87 PermissionIDSet::Difference(remaining_permissions, used_permissions);
91 return messages;
94 bool ChromePermissionMessageProvider::IsPrivilegeIncrease(
95 const PermissionSet* old_permissions,
96 const PermissionSet* new_permissions,
97 Manifest::Type extension_type) const {
98 // Things can't get worse than native code access.
99 if (old_permissions->HasEffectiveFullAccess())
100 return false;
102 // Otherwise, it's a privilege increase if the new one has full access.
103 if (new_permissions->HasEffectiveFullAccess())
104 return true;
106 if (IsHostPrivilegeIncrease(old_permissions, new_permissions, extension_type))
107 return true;
109 if (IsAPIPrivilegeIncrease(old_permissions, new_permissions))
110 return true;
112 if (IsManifestPermissionPrivilegeIncrease(old_permissions, new_permissions))
113 return true;
115 return false;
118 PermissionIDSet ChromePermissionMessageProvider::GetAllPermissionIDs(
119 const PermissionSet* permissions,
120 Manifest::Type extension_type) const {
121 PermissionIDSet permission_ids;
122 AddAPIPermissions(permissions, &permission_ids);
123 AddManifestPermissions(permissions, &permission_ids);
124 AddHostPermissions(permissions, &permission_ids, extension_type);
125 return permission_ids;
128 void ChromePermissionMessageProvider::AddAPIPermissions(
129 const PermissionSet* permissions,
130 PermissionIDSet* permission_ids) const {
131 for (const APIPermission* permission : permissions->apis())
132 permission_ids->InsertAll(permission->GetPermissions());
134 // A special hack: The warning message for declarativeWebRequest
135 // permissions speaks about blocking parts of pages, which is a
136 // subset of what the "<all_urls>" access allows. Therefore we
137 // display only the "<all_urls>" warning message if both permissions
138 // are required.
139 // TODO(treib): The same should apply to other permissions that are implied by
140 // "<all_urls>" (aka APIPermission::kHostsAll), such as kTab. This would
141 // happen automatically if we didn't differentiate between API/Manifest/Host
142 // permissions here.
143 if (permissions->ShouldWarnAllHosts())
144 permission_ids->erase(APIPermission::kDeclarativeWebRequest);
147 void ChromePermissionMessageProvider::AddManifestPermissions(
148 const PermissionSet* permissions,
149 PermissionIDSet* permission_ids) const {
150 for (const ManifestPermission* p : permissions->manifest_permissions())
151 permission_ids->InsertAll(p->GetPermissions());
154 void ChromePermissionMessageProvider::AddHostPermissions(
155 const PermissionSet* permissions,
156 PermissionIDSet* permission_ids,
157 Manifest::Type extension_type) const {
158 // Since platform apps always use isolated storage, they can't (silently)
159 // access user data on other domains, so there's no need to prompt.
160 // Note: this must remain consistent with IsHostPrivilegeIncrease.
161 // See crbug.com/255229.
162 if (extension_type == Manifest::TYPE_PLATFORM_APP)
163 return;
165 if (permissions->ShouldWarnAllHosts()) {
166 permission_ids->insert(APIPermission::kHostsAll);
167 } else {
168 URLPatternSet regular_hosts;
169 ExtensionsClient::Get()->FilterHostPermissions(
170 permissions->effective_hosts(), &regular_hosts, permission_ids);
172 std::set<std::string> hosts =
173 permission_message_util::GetDistinctHosts(regular_hosts, true, true);
174 if (!hosts.empty()) {
175 permission_message_util::AddHostPermissions(
176 permission_ids, hosts, permission_message_util::kReadWrite);
181 bool ChromePermissionMessageProvider::IsAPIPrivilegeIncrease(
182 const PermissionSet* old_permissions,
183 const PermissionSet* new_permissions) const {
184 PermissionIDSet old_ids;
185 AddAPIPermissions(old_permissions, &old_ids);
186 PermissionIDSet new_ids;
187 AddAPIPermissions(new_permissions, &new_ids);
189 // A special hack: kFileSystemWriteDirectory implies kFileSystemDirectory.
190 // TODO(sammc): Remove this. See http://crbug.com/284849.
191 if (old_ids.ContainsID(APIPermission::kFileSystemWriteDirectory))
192 old_ids.insert(APIPermission::kFileSystemDirectory);
194 return IsAPIOrManifestPrivilegeIncrease(old_ids, new_ids);
197 bool ChromePermissionMessageProvider::IsManifestPermissionPrivilegeIncrease(
198 const PermissionSet* old_permissions,
199 const PermissionSet* new_permissions) const {
200 PermissionIDSet old_ids;
201 AddManifestPermissions(old_permissions, &old_ids);
202 PermissionIDSet new_ids;
203 AddManifestPermissions(new_permissions, &new_ids);
205 return IsAPIOrManifestPrivilegeIncrease(old_ids, new_ids);
208 bool ChromePermissionMessageProvider::IsAPIOrManifestPrivilegeIncrease(
209 const PermissionIDSet& old_ids,
210 const PermissionIDSet& new_ids) const {
211 // If all the IDs were already there, it's not a privilege increase.
212 if (old_ids.Includes(new_ids))
213 return false;
215 // Otherwise, check the actual messages - not all IDs result in a message,
216 // and some messages can suppress others.
217 CoalescedPermissionMessages old_messages = GetPermissionMessages(old_ids);
218 CoalescedPermissionMessages new_messages = GetPermissionMessages(new_ids);
220 ComparablePermissions old_strings(old_messages.begin(), old_messages.end());
221 ComparablePermissions new_strings(new_messages.begin(), new_messages.end());
223 std::sort(old_strings.begin(), old_strings.end());
224 std::sort(new_strings.begin(), new_strings.end());
226 return !base::STLIncludes(old_strings, new_strings);
229 bool ChromePermissionMessageProvider::IsHostPrivilegeIncrease(
230 const PermissionSet* old_permissions,
231 const PermissionSet* new_permissions,
232 Manifest::Type extension_type) const {
233 // Platform apps host permission changes do not count as privilege increases.
234 // Note: this must remain consistent with AddHostPermissions.
235 if (extension_type == Manifest::TYPE_PLATFORM_APP)
236 return false;
238 // If the old permission set can access any host, then it can't be elevated.
239 if (old_permissions->HasEffectiveAccessToAllHosts())
240 return false;
242 // Likewise, if the new permission set has full host access, then it must be
243 // a privilege increase.
244 if (new_permissions->HasEffectiveAccessToAllHosts())
245 return true;
247 const URLPatternSet& old_list = old_permissions->effective_hosts();
248 const URLPatternSet& new_list = new_permissions->effective_hosts();
250 // TODO(jstritar): This is overly conservative with respect to subdomains.
251 // For example, going from *.google.com to www.google.com will be
252 // considered an elevation, even though it is not (http://crbug.com/65337).
253 std::set<std::string> new_hosts_set(
254 permission_message_util::GetDistinctHosts(new_list, false, false));
255 std::set<std::string> old_hosts_set(
256 permission_message_util::GetDistinctHosts(old_list, false, false));
257 std::set<std::string> new_hosts_only =
258 base::STLSetDifference<std::set<std::string> >(new_hosts_set,
259 old_hosts_set);
261 return !new_hosts_only.empty();
264 } // namespace extensions