• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/common/extensions/permissions/chrome_permission_message_provider.h"
6 
7 #include "base/stl_util.h"
8 #include "chrome/common/extensions/permissions/permission_message_util.h"
9 #include "extensions/common/extensions_client.h"
10 #include "extensions/common/permissions/permission_message.h"
11 #include "extensions/common/permissions/permission_set.h"
12 #include "extensions/common/url_pattern_set.h"
13 #include "grit/generated_resources.h"
14 #include "ui/base/l10n/l10n_util.h"
15 
16 namespace extensions {
17 
ChromePermissionMessageProvider()18 ChromePermissionMessageProvider::ChromePermissionMessageProvider() {
19 }
20 
~ChromePermissionMessageProvider()21 ChromePermissionMessageProvider::~ChromePermissionMessageProvider() {
22 }
23 
24 // static
GetPermissionMessages(const PermissionSet * permissions,Manifest::Type extension_type) const25 PermissionMessages ChromePermissionMessageProvider::GetPermissionMessages(
26     const PermissionSet* permissions,
27     Manifest::Type extension_type) const {
28   PermissionMessages messages;
29 
30   if (permissions->HasEffectiveFullAccess()) {
31     messages.push_back(PermissionMessage(
32         PermissionMessage::kFullAccess,
33         l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS)));
34     return messages;
35   }
36 
37   std::set<PermissionMessage> host_msgs =
38       GetHostPermissionMessages(permissions, extension_type);
39   std::set<PermissionMessage> api_msgs = GetAPIPermissionMessages(permissions);
40   std::set<PermissionMessage> manifest_permission_msgs =
41       GetManifestPermissionMessages(permissions);
42   messages.insert(messages.end(), host_msgs.begin(), host_msgs.end());
43   messages.insert(messages.end(), api_msgs.begin(), api_msgs.end());
44   messages.insert(messages.end(), manifest_permission_msgs.begin(),
45                   manifest_permission_msgs.end());
46 
47   return messages;
48 }
49 
50 // static
GetWarningMessages(const PermissionSet * permissions,Manifest::Type extension_type) const51 std::vector<base::string16> ChromePermissionMessageProvider::GetWarningMessages(
52     const PermissionSet* permissions,
53     Manifest::Type extension_type) const {
54   std::vector<base::string16> message_strings;
55   PermissionMessages messages =
56       GetPermissionMessages(permissions, extension_type);
57 
58   bool audio_capture = false;
59   bool video_capture = false;
60   bool media_galleries_read = false;
61   bool media_galleries_copy_to = false;
62   bool media_galleries_delete = false;
63   for (PermissionMessages::const_iterator i = messages.begin();
64        i != messages.end(); ++i) {
65     switch (i->id()) {
66       case PermissionMessage::kAudioCapture:
67         audio_capture = true;
68         break;
69       case PermissionMessage::kVideoCapture:
70         video_capture = true;
71         break;
72       case PermissionMessage::kMediaGalleriesAllGalleriesRead:
73         media_galleries_read = true;
74         break;
75       case PermissionMessage::kMediaGalleriesAllGalleriesCopyTo:
76         media_galleries_copy_to = true;
77         break;
78       case PermissionMessage::kMediaGalleriesAllGalleriesDelete:
79         media_galleries_delete = true;
80         break;
81       default:
82         break;
83     }
84   }
85 
86   for (PermissionMessages::const_iterator i = messages.begin();
87        i != messages.end(); ++i) {
88     int id = i->id();
89     if (audio_capture && video_capture) {
90       if (id == PermissionMessage::kAudioCapture) {
91         message_strings.push_back(l10n_util::GetStringUTF16(
92             IDS_EXTENSION_PROMPT_WARNING_AUDIO_AND_VIDEO_CAPTURE));
93         continue;
94       } else if (id == PermissionMessage::kVideoCapture) {
95         // The combined message will be pushed above.
96         continue;
97       }
98     }
99     if (media_galleries_read &&
100         (media_galleries_copy_to || media_galleries_delete)) {
101       if (id == PermissionMessage::kMediaGalleriesAllGalleriesRead) {
102         int m_id = media_galleries_copy_to ?
103             IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_READ_WRITE :
104             IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_READ_DELETE;
105         message_strings.push_back(l10n_util::GetStringUTF16(m_id));
106         continue;
107       } else if (id == PermissionMessage::kMediaGalleriesAllGalleriesCopyTo ||
108                  id == PermissionMessage::kMediaGalleriesAllGalleriesDelete) {
109         // The combined message will be pushed above.
110         continue;
111       }
112     }
113 
114     message_strings.push_back(i->message());
115   }
116 
117   return message_strings;
118 }
119 
120 // static
121 std::vector<base::string16>
GetWarningMessagesDetails(const PermissionSet * permissions,Manifest::Type extension_type) const122 ChromePermissionMessageProvider::GetWarningMessagesDetails(
123     const PermissionSet* permissions,
124     Manifest::Type extension_type) const {
125   std::vector<base::string16> message_strings;
126   PermissionMessages messages =
127       GetPermissionMessages(permissions, extension_type);
128 
129   for (PermissionMessages::const_iterator i = messages.begin();
130        i != messages.end(); ++i)
131     message_strings.push_back(i->details());
132 
133   return message_strings;
134 }
135 
136 // static
IsPrivilegeIncrease(const PermissionSet * old_permissions,const PermissionSet * new_permissions,Manifest::Type extension_type) const137 bool ChromePermissionMessageProvider::IsPrivilegeIncrease(
138     const PermissionSet* old_permissions,
139     const PermissionSet* new_permissions,
140     Manifest::Type extension_type) const {
141   // Things can't get worse than native code access.
142   if (old_permissions->HasEffectiveFullAccess())
143     return false;
144 
145   // Otherwise, it's a privilege increase if the new one has full access.
146   if (new_permissions->HasEffectiveFullAccess())
147     return true;
148 
149   if (IsHostPrivilegeIncrease(old_permissions, new_permissions, extension_type))
150     return true;
151 
152   if (IsAPIPrivilegeIncrease(old_permissions, new_permissions))
153     return true;
154 
155   if (IsManifestPermissionPrivilegeIncrease(old_permissions, new_permissions))
156     return true;
157 
158   return false;
159 }
160 
161 std::set<PermissionMessage>
GetAPIPermissionMessages(const PermissionSet * permissions) const162 ChromePermissionMessageProvider::GetAPIPermissionMessages(
163     const PermissionSet* permissions) const {
164   std::set<PermissionMessage> messages;
165   for (APIPermissionSet::const_iterator permission_it =
166            permissions->apis().begin();
167        permission_it != permissions->apis().end(); ++permission_it) {
168     if (permission_it->HasMessages()) {
169       PermissionMessages new_messages = permission_it->GetMessages();
170       messages.insert(new_messages.begin(), new_messages.end());
171     }
172   }
173 
174   // A special hack: If kFileSystemWriteDirectory would be displayed, hide
175   // kFileSystemDirectory and and kFileSystemWrite as the write directory
176   // message implies the other two.
177   // TODO(sammc): Remove this. See http://crbug.com/284849.
178   std::set<PermissionMessage>::iterator write_directory_message =
179       messages.find(PermissionMessage(
180           PermissionMessage::kFileSystemWriteDirectory, base::string16()));
181   if (write_directory_message != messages.end()) {
182     messages.erase(
183         PermissionMessage(PermissionMessage::kFileSystemWrite,
184                           base::string16()));
185     messages.erase(
186         PermissionMessage(PermissionMessage::kFileSystemDirectory,
187                           base::string16()));
188   }
189 
190   // A special hack: The warning message for declarativeWebRequest
191   // permissions speaks about blocking parts of pages, which is a
192   // subset of what the "<all_urls>" access allows. Therefore we
193   // display only the "<all_urls>" warning message if both permissions
194   // are required.
195   if (permissions->HasEffectiveAccessToAllHosts()) {
196     messages.erase(
197         PermissionMessage(
198             PermissionMessage::kDeclarativeWebRequest, base::string16()));
199   }
200 
201   return messages;
202 }
203 
204 std::set<PermissionMessage>
GetManifestPermissionMessages(const PermissionSet * permissions) const205 ChromePermissionMessageProvider::GetManifestPermissionMessages(
206     const PermissionSet* permissions) const {
207   std::set<PermissionMessage> messages;
208   for (ManifestPermissionSet::const_iterator permission_it =
209            permissions->manifest_permissions().begin();
210       permission_it != permissions->manifest_permissions().end();
211       ++permission_it) {
212     if (permission_it->HasMessages()) {
213       PermissionMessages new_messages = permission_it->GetMessages();
214       messages.insert(new_messages.begin(), new_messages.end());
215     }
216   }
217   return messages;
218 }
219 
220 std::set<PermissionMessage>
GetHostPermissionMessages(const PermissionSet * permissions,Manifest::Type extension_type) const221 ChromePermissionMessageProvider::GetHostPermissionMessages(
222     const PermissionSet* permissions,
223     Manifest::Type extension_type) const {
224   std::set<PermissionMessage> messages;
225   // Since platform apps always use isolated storage, they can't (silently)
226   // access user data on other domains, so there's no need to prompt.
227   // Note: this must remain consistent with IsHostPrivilegeIncrease.
228   // See crbug.com/255229.
229   if (extension_type == Manifest::TYPE_PLATFORM_APP)
230     return messages;
231 
232   if (permissions->HasEffectiveAccessToAllHosts()) {
233     messages.insert(PermissionMessage(
234         PermissionMessage::kHostsAll,
235         l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS)));
236   } else {
237     URLPatternSet regular_hosts;
238     ExtensionsClient::Get()->FilterHostPermissions(
239         permissions->effective_hosts(), &regular_hosts, &messages);
240 
241     std::set<std::string> hosts =
242         permission_message_util::GetDistinctHosts(regular_hosts, true, true);
243     if (!hosts.empty())
244       messages.insert(permission_message_util::CreateFromHostList(hosts));
245   }
246   return messages;
247 }
248 
IsAPIPrivilegeIncrease(const PermissionSet * old_permissions,const PermissionSet * new_permissions) const249 bool ChromePermissionMessageProvider::IsAPIPrivilegeIncrease(
250     const PermissionSet* old_permissions,
251     const PermissionSet* new_permissions) const {
252   if (new_permissions == NULL)
253     return false;
254 
255   typedef std::set<PermissionMessage> PermissionMsgSet;
256   PermissionMsgSet old_warnings = GetAPIPermissionMessages(old_permissions);
257   PermissionMsgSet new_warnings = GetAPIPermissionMessages(new_permissions);
258   PermissionMsgSet delta_warnings =
259       base::STLSetDifference<PermissionMsgSet>(new_warnings, old_warnings);
260 
261   // A special hack: kFileSystemWriteDirectory implies kFileSystemDirectory and
262   // kFileSystemWrite.
263   // TODO(sammc): Remove this. See http://crbug.com/284849.
264   if (old_warnings.find(PermissionMessage(
265           PermissionMessage::kFileSystemWriteDirectory, base::string16())) !=
266       old_warnings.end()) {
267     delta_warnings.erase(
268         PermissionMessage(PermissionMessage::kFileSystemDirectory,
269                           base::string16()));
270     delta_warnings.erase(
271         PermissionMessage(PermissionMessage::kFileSystemWrite,
272                           base::string16()));
273   }
274 
275   // It is a privilege increase if there are additional warnings present.
276   return !delta_warnings.empty();
277 }
278 
IsManifestPermissionPrivilegeIncrease(const PermissionSet * old_permissions,const PermissionSet * new_permissions) const279 bool ChromePermissionMessageProvider::IsManifestPermissionPrivilegeIncrease(
280     const PermissionSet* old_permissions,
281     const PermissionSet* new_permissions) const {
282   if (new_permissions == NULL)
283     return false;
284 
285   typedef std::set<PermissionMessage> PermissionMsgSet;
286   PermissionMsgSet old_warnings =
287       GetManifestPermissionMessages(old_permissions);
288   PermissionMsgSet new_warnings =
289       GetManifestPermissionMessages(new_permissions);
290   PermissionMsgSet delta_warnings =
291       base::STLSetDifference<PermissionMsgSet>(new_warnings, old_warnings);
292 
293   // It is a privilege increase if there are additional warnings present.
294   return !delta_warnings.empty();
295 }
296 
IsHostPrivilegeIncrease(const PermissionSet * old_permissions,const PermissionSet * new_permissions,Manifest::Type extension_type) const297 bool ChromePermissionMessageProvider::IsHostPrivilegeIncrease(
298     const PermissionSet* old_permissions,
299     const PermissionSet* new_permissions,
300     Manifest::Type extension_type) const {
301   // Platform apps host permission changes do not count as privilege increases.
302   // Note: this must remain consistent with GetHostPermissionMessages.
303   if (extension_type == Manifest::TYPE_PLATFORM_APP)
304     return false;
305 
306   // If the old permission set can access any host, then it can't be elevated.
307   if (old_permissions->HasEffectiveAccessToAllHosts())
308     return false;
309 
310   // Likewise, if the new permission set has full host access, then it must be
311   // a privilege increase.
312   if (new_permissions->HasEffectiveAccessToAllHosts())
313     return true;
314 
315   const URLPatternSet& old_list = old_permissions->effective_hosts();
316   const URLPatternSet& new_list = new_permissions->effective_hosts();
317 
318   // TODO(jstritar): This is overly conservative with respect to subdomains.
319   // For example, going from *.google.com to www.google.com will be
320   // considered an elevation, even though it is not (http://crbug.com/65337).
321   std::set<std::string> new_hosts_set(
322       permission_message_util::GetDistinctHosts(new_list, false, false));
323   std::set<std::string> old_hosts_set(
324       permission_message_util::GetDistinctHosts(old_list, false, false));
325   std::set<std::string> new_hosts_only =
326       base::STLSetDifference<std::set<std::string> >(new_hosts_set,
327                                                      old_hosts_set);
328 
329   return !new_hosts_only.empty();
330 }
331 
332 }  // namespace extensions
333