Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / permissions / chrome_permission_message_provider.cc
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 "base/strings/stringprintf.h"
9 #include "extensions/common/extensions_client.h"
10 #include "extensions/common/permissions/permission_message.h"
11 #include "extensions/common/permissions/permission_message_util.h"
12 #include "extensions/common/permissions/permission_set.h"
13 #include "extensions/common/url_pattern.h"
14 #include "extensions/common/url_pattern_set.h"
15 #include "grit/generated_resources.h"
16 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
17 #include "ui/base/l10n/l10n_util.h"
18 #include "url/gurl.h"
19
20 namespace extensions {
21
22 namespace {
23
24 typedef std::set<PermissionMessage> PermissionMsgSet;
25
26 bool ShouldWarnAllHosts(const PermissionSet* permissions) {
27   if (permissions->HasEffectiveAccessToAllHosts())
28     return true;
29
30   const URLPatternSet& effective_hosts = permissions->effective_hosts();
31   for (URLPatternSet::const_iterator iter = effective_hosts.begin();
32        iter != effective_hosts.end();
33        ++iter) {
34     // If this doesn't even match subdomains, it can't possibly imply all hosts.
35     if (!iter->match_subdomains())
36       continue;
37
38     // If iter->host() is a recognized TLD, this will be 0. We don't include
39     // private TLDs, so that, e.g., *.appspot.com does not imply all hosts.
40     size_t registry_length =
41         net::registry_controlled_domains::GetRegistryLength(
42             iter->host(),
43             net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
44             net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
45     // If there was more than just a TLD in the host (e.g., *.foobar.com), it
46     // doesn't imply all hosts.
47     if (registry_length > 0)
48       continue;
49
50     // At this point the host could either be just a TLD ("com") or some unknown
51     // TLD-like string ("notatld"). To disambiguate between them construct a
52     // fake URL, and check the registry. This returns 0 if the TLD is
53     // unrecognized, or the length of the recognized TLD.
54     registry_length = net::registry_controlled_domains::GetRegistryLength(
55         base::StringPrintf("foo.%s", iter->host().c_str()),
56         net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
57         net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
58     // If we recognized this TLD, then this is a pattern like *.com, and it
59     // should imply all hosts.
60     if (registry_length > 0)
61       return true;
62   }
63
64   return false;
65 }
66
67 template<typename T>
68 typename T::iterator FindMessageByID(T& messages, int id) {
69   for (typename T::iterator it = messages.begin();
70        it != messages.end(); ++it) {
71     if (it->id() == id)
72       return it;
73   }
74   return messages.end();
75 }
76
77 template<typename T>
78 void SuppressMessage(T& messages,
79                      int suppressing_message,
80                      int suppressed_message) {
81   typename T::iterator suppressed = FindMessageByID(messages,
82                                                     suppressed_message);
83   if (suppressed != messages.end() &&
84       FindMessageByID(messages, suppressing_message) != messages.end()) {
85     messages.erase(suppressed);
86   }
87 }
88
89 }  // namespace
90
91 ChromePermissionMessageProvider::ChromePermissionMessageProvider() {
92 }
93
94 ChromePermissionMessageProvider::~ChromePermissionMessageProvider() {
95 }
96
97 PermissionMessages ChromePermissionMessageProvider::GetPermissionMessages(
98     const PermissionSet* permissions,
99     Manifest::Type extension_type) const {
100   PermissionMessages messages;
101
102   if (permissions->HasEffectiveFullAccess()) {
103     messages.push_back(PermissionMessage(
104         PermissionMessage::kFullAccess,
105         l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS)));
106     return messages;
107   }
108
109   PermissionMsgSet host_msgs =
110       GetHostPermissionMessages(permissions, extension_type);
111   PermissionMsgSet api_msgs = GetAPIPermissionMessages(permissions);
112   PermissionMsgSet manifest_permission_msgs =
113       GetManifestPermissionMessages(permissions);
114   messages.insert(messages.end(), host_msgs.begin(), host_msgs.end());
115   messages.insert(messages.end(), api_msgs.begin(), api_msgs.end());
116   messages.insert(messages.end(), manifest_permission_msgs.begin(),
117                   manifest_permission_msgs.end());
118
119   // Some warnings are more generic and/or powerful and superseed other
120   // warnings. In that case, suppress the superseeded warning.
121   SuppressMessage(messages,
122                   PermissionMessage::kBookmarks,
123                   PermissionMessage::kOverrideBookmarksUI);
124   // Both tabs and history already allow reading favicons.
125   SuppressMessage(messages,
126                   PermissionMessage::kTabs,
127                   PermissionMessage::kFavicon);
128   SuppressMessage(messages,
129                   PermissionMessage::kBrowsingHistory,
130                   PermissionMessage::kFavicon);
131   // Warning for history permission already covers warning for tabs permission.
132   SuppressMessage(messages,
133                   PermissionMessage::kBrowsingHistory,
134                   PermissionMessage::kTabs);
135   return messages;
136 }
137
138 std::vector<base::string16> ChromePermissionMessageProvider::GetWarningMessages(
139     const PermissionSet* permissions,
140     Manifest::Type extension_type) const {
141   std::vector<base::string16> message_strings;
142   PermissionMessages messages =
143       GetPermissionMessages(permissions, extension_type);
144
145   bool audio_capture = false;
146   bool video_capture = false;
147   bool media_galleries_read = false;
148   bool media_galleries_copy_to = false;
149   bool media_galleries_delete = false;
150   for (PermissionMessages::const_iterator i = messages.begin();
151        i != messages.end(); ++i) {
152     switch (i->id()) {
153       case PermissionMessage::kAudioCapture:
154         audio_capture = true;
155         break;
156       case PermissionMessage::kVideoCapture:
157         video_capture = true;
158         break;
159       case PermissionMessage::kMediaGalleriesAllGalleriesRead:
160         media_galleries_read = true;
161         break;
162       case PermissionMessage::kMediaGalleriesAllGalleriesCopyTo:
163         media_galleries_copy_to = true;
164         break;
165       case PermissionMessage::kMediaGalleriesAllGalleriesDelete:
166         media_galleries_delete = true;
167         break;
168       default:
169         break;
170     }
171   }
172
173   for (PermissionMessages::const_iterator i = messages.begin();
174        i != messages.end(); ++i) {
175     int id = i->id();
176     if (audio_capture && video_capture) {
177       if (id == PermissionMessage::kAudioCapture) {
178         message_strings.push_back(l10n_util::GetStringUTF16(
179             IDS_EXTENSION_PROMPT_WARNING_AUDIO_AND_VIDEO_CAPTURE));
180         continue;
181       } else if (id == PermissionMessage::kVideoCapture) {
182         // The combined message will be pushed above.
183         continue;
184       }
185     }
186     if (media_galleries_read &&
187         (media_galleries_copy_to || media_galleries_delete)) {
188       if (id == PermissionMessage::kMediaGalleriesAllGalleriesRead) {
189         int m_id = media_galleries_copy_to ?
190             IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_READ_WRITE :
191             IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_READ_DELETE;
192         message_strings.push_back(l10n_util::GetStringUTF16(m_id));
193         continue;
194       } else if (id == PermissionMessage::kMediaGalleriesAllGalleriesCopyTo ||
195                  id == PermissionMessage::kMediaGalleriesAllGalleriesDelete) {
196         // The combined message will be pushed above.
197         continue;
198       }
199     }
200
201     message_strings.push_back(i->message());
202   }
203
204   return message_strings;
205 }
206
207 std::vector<base::string16>
208 ChromePermissionMessageProvider::GetWarningMessagesDetails(
209     const PermissionSet* permissions,
210     Manifest::Type extension_type) const {
211   std::vector<base::string16> message_strings;
212   PermissionMessages messages =
213       GetPermissionMessages(permissions, extension_type);
214
215   for (PermissionMessages::const_iterator i = messages.begin();
216        i != messages.end(); ++i)
217     message_strings.push_back(i->details());
218
219   return message_strings;
220 }
221
222 bool ChromePermissionMessageProvider::IsPrivilegeIncrease(
223     const PermissionSet* old_permissions,
224     const PermissionSet* new_permissions,
225     Manifest::Type extension_type) const {
226   // Things can't get worse than native code access.
227   if (old_permissions->HasEffectiveFullAccess())
228     return false;
229
230   // Otherwise, it's a privilege increase if the new one has full access.
231   if (new_permissions->HasEffectiveFullAccess())
232     return true;
233
234   if (IsHostPrivilegeIncrease(old_permissions, new_permissions, extension_type))
235     return true;
236
237   if (IsAPIPrivilegeIncrease(old_permissions, new_permissions))
238     return true;
239
240   if (IsManifestPermissionPrivilegeIncrease(old_permissions, new_permissions))
241     return true;
242
243   return false;
244 }
245
246 std::set<PermissionMessage>
247 ChromePermissionMessageProvider::GetAPIPermissionMessages(
248     const PermissionSet* permissions) const {
249   PermissionMsgSet messages;
250   for (APIPermissionSet::const_iterator permission_it =
251            permissions->apis().begin();
252        permission_it != permissions->apis().end(); ++permission_it) {
253     if (permission_it->HasMessages()) {
254       PermissionMessages new_messages = permission_it->GetMessages();
255       messages.insert(new_messages.begin(), new_messages.end());
256     }
257   }
258
259   // A special hack: If kFileSystemWriteDirectory would be displayed, hide
260   // kFileSystemDirectory as the write directory message implies it.
261   // TODO(sammc): Remove this. See http://crbug.com/284849.
262   SuppressMessage(messages,
263                   PermissionMessage::kFileSystemWriteDirectory,
264                   PermissionMessage::kFileSystemDirectory);
265   // A special hack: The warning message for declarativeWebRequest
266   // permissions speaks about blocking parts of pages, which is a
267   // subset of what the "<all_urls>" access allows. Therefore we
268   // display only the "<all_urls>" warning message if both permissions
269   // are required.
270   if (ShouldWarnAllHosts(permissions)) {
271     messages.erase(
272         PermissionMessage(
273             PermissionMessage::kDeclarativeWebRequest, base::string16()));
274   }
275   return messages;
276 }
277
278 std::set<PermissionMessage>
279 ChromePermissionMessageProvider::GetManifestPermissionMessages(
280     const PermissionSet* permissions) const {
281   PermissionMsgSet messages;
282   for (ManifestPermissionSet::const_iterator permission_it =
283            permissions->manifest_permissions().begin();
284       permission_it != permissions->manifest_permissions().end();
285       ++permission_it) {
286     if (permission_it->HasMessages()) {
287       PermissionMessages new_messages = permission_it->GetMessages();
288       messages.insert(new_messages.begin(), new_messages.end());
289     }
290   }
291   return messages;
292 }
293
294 std::set<PermissionMessage>
295 ChromePermissionMessageProvider::GetHostPermissionMessages(
296     const PermissionSet* permissions,
297     Manifest::Type extension_type) const {
298   PermissionMsgSet messages;
299   // Since platform apps always use isolated storage, they can't (silently)
300   // access user data on other domains, so there's no need to prompt.
301   // Note: this must remain consistent with IsHostPrivilegeIncrease.
302   // See crbug.com/255229.
303   if (extension_type == Manifest::TYPE_PLATFORM_APP)
304     return messages;
305
306   if (ShouldWarnAllHosts(permissions)) {
307     messages.insert(PermissionMessage(
308         PermissionMessage::kHostsAll,
309         l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS)));
310   } else {
311     URLPatternSet regular_hosts;
312     ExtensionsClient::Get()->FilterHostPermissions(
313         permissions->effective_hosts(), &regular_hosts, &messages);
314
315     std::set<std::string> hosts =
316         permission_message_util::GetDistinctHosts(regular_hosts, true, true);
317     if (!hosts.empty())
318       messages.insert(permission_message_util::CreateFromHostList(hosts));
319   }
320   return messages;
321 }
322
323 bool ChromePermissionMessageProvider::IsAPIPrivilegeIncrease(
324     const PermissionSet* old_permissions,
325     const PermissionSet* new_permissions) const {
326   if (new_permissions == NULL)
327     return false;
328
329   PermissionMsgSet old_warnings = GetAPIPermissionMessages(old_permissions);
330   PermissionMsgSet new_warnings = GetAPIPermissionMessages(new_permissions);
331   PermissionMsgSet delta_warnings =
332       base::STLSetDifference<PermissionMsgSet>(new_warnings, old_warnings);
333
334   // A special hack: kFileSystemWriteDirectory implies kFileSystemDirectory.
335   // TODO(sammc): Remove this. See http://crbug.com/284849.
336   if (old_warnings.find(PermissionMessage(
337           PermissionMessage::kFileSystemWriteDirectory, base::string16())) !=
338       old_warnings.end()) {
339     delta_warnings.erase(
340         PermissionMessage(PermissionMessage::kFileSystemDirectory,
341                           base::string16()));
342   }
343
344   // It is a privilege increase if there are additional warnings present.
345   return !delta_warnings.empty();
346 }
347
348 bool ChromePermissionMessageProvider::IsManifestPermissionPrivilegeIncrease(
349     const PermissionSet* old_permissions,
350     const PermissionSet* new_permissions) const {
351   if (new_permissions == NULL)
352     return false;
353
354   PermissionMsgSet old_warnings =
355       GetManifestPermissionMessages(old_permissions);
356   PermissionMsgSet new_warnings =
357       GetManifestPermissionMessages(new_permissions);
358   PermissionMsgSet delta_warnings =
359       base::STLSetDifference<PermissionMsgSet>(new_warnings, old_warnings);
360
361   // It is a privilege increase if there are additional warnings present.
362   return !delta_warnings.empty();
363 }
364
365 bool ChromePermissionMessageProvider::IsHostPrivilegeIncrease(
366     const PermissionSet* old_permissions,
367     const PermissionSet* new_permissions,
368     Manifest::Type extension_type) const {
369   // Platform apps host permission changes do not count as privilege increases.
370   // Note: this must remain consistent with GetHostPermissionMessages.
371   if (extension_type == Manifest::TYPE_PLATFORM_APP)
372     return false;
373
374   // If the old permission set can access any host, then it can't be elevated.
375   if (old_permissions->HasEffectiveAccessToAllHosts())
376     return false;
377
378   // Likewise, if the new permission set has full host access, then it must be
379   // a privilege increase.
380   if (new_permissions->HasEffectiveAccessToAllHosts())
381     return true;
382
383   const URLPatternSet& old_list = old_permissions->effective_hosts();
384   const URLPatternSet& new_list = new_permissions->effective_hosts();
385
386   // TODO(jstritar): This is overly conservative with respect to subdomains.
387   // For example, going from *.google.com to www.google.com will be
388   // considered an elevation, even though it is not (http://crbug.com/65337).
389   std::set<std::string> new_hosts_set(
390       permission_message_util::GetDistinctHosts(new_list, false, false));
391   std::set<std::string> old_hosts_set(
392       permission_message_util::GetDistinctHosts(old_list, false, false));
393   std::set<std::string> new_hosts_only =
394       base::STLSetDifference<std::set<std::string> >(new_hosts_set,
395                                                      old_hosts_set);
396
397   return !new_hosts_only.empty();
398 }
399
400 }  // namespace extensions