chrome_permission_message_provider.cc revision f2477e01787aa58f445919b809d89e252beef54f
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
16namespace extensions {
17
18ChromePermissionMessageProvider::ChromePermissionMessageProvider() {
19}
20
21ChromePermissionMessageProvider::~ChromePermissionMessageProvider() {
22}
23
24// static
25PermissionMessages 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
51std::vector<string16> ChromePermissionMessageProvider::GetWarningMessages(
52    const PermissionSet* permissions,
53    Manifest::Type extension_type) const {
54  std::vector<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
121std::vector<string16>
122ChromePermissionMessageProvider::GetWarningMessagesDetails(
123    const PermissionSet* permissions,
124    Manifest::Type extension_type) const {
125  std::vector<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
137bool 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
161std::set<PermissionMessage>
162ChromePermissionMessageProvider::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, string16()));
181  if (write_directory_message != messages.end()) {
182    messages.erase(
183        PermissionMessage(PermissionMessage::kFileSystemWrite, string16()));
184    messages.erase(
185        PermissionMessage(PermissionMessage::kFileSystemDirectory, string16()));
186  }
187
188  // A special hack: The warning message for declarativeWebRequest
189  // permissions speaks about blocking parts of pages, which is a
190  // subset of what the "<all_urls>" access allows. Therefore we
191  // display only the "<all_urls>" warning message if both permissions
192  // are required.
193  if (permissions->HasEffectiveAccessToAllHosts()) {
194    messages.erase(
195        PermissionMessage(
196            PermissionMessage::kDeclarativeWebRequest, string16()));
197  }
198
199  return messages;
200}
201
202std::set<PermissionMessage>
203ChromePermissionMessageProvider::GetManifestPermissionMessages(
204    const PermissionSet* permissions) const {
205  std::set<PermissionMessage> messages;
206  for (ManifestPermissionSet::const_iterator permission_it =
207           permissions->manifest_permissions().begin();
208      permission_it != permissions->manifest_permissions().end();
209      ++permission_it) {
210    if (permission_it->HasMessages()) {
211      PermissionMessages new_messages = permission_it->GetMessages();
212      messages.insert(new_messages.begin(), new_messages.end());
213    }
214  }
215  return messages;
216}
217
218std::set<PermissionMessage>
219ChromePermissionMessageProvider::GetHostPermissionMessages(
220    const PermissionSet* permissions,
221    Manifest::Type extension_type) const {
222  std::set<PermissionMessage> messages;
223  // Since platform apps always use isolated storage, they can't (silently)
224  // access user data on other domains, so there's no need to prompt.
225  // Note: this must remain consistent with IsHostPrivilegeIncrease.
226  // See crbug.com/255229.
227  if (extension_type == Manifest::TYPE_PLATFORM_APP)
228    return messages;
229
230  if (permissions->HasEffectiveAccessToAllHosts()) {
231    messages.insert(PermissionMessage(
232        PermissionMessage::kHostsAll,
233        l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS)));
234  } else {
235    URLPatternSet regular_hosts;
236    ExtensionsClient::Get()->FilterHostPermissions(
237        permissions->effective_hosts(), &regular_hosts, &messages);
238
239    std::set<std::string> hosts =
240        permission_message_util::GetDistinctHosts(regular_hosts, true, true);
241    if (!hosts.empty())
242      messages.insert(permission_message_util::CreateFromHostList(hosts));
243  }
244  return messages;
245}
246
247bool ChromePermissionMessageProvider::IsAPIPrivilegeIncrease(
248    const PermissionSet* old_permissions,
249    const PermissionSet* new_permissions) const {
250  if (new_permissions == NULL)
251    return false;
252
253  typedef std::set<PermissionMessage> PermissionMsgSet;
254  PermissionMsgSet old_warnings = GetAPIPermissionMessages(old_permissions);
255  PermissionMsgSet new_warnings = GetAPIPermissionMessages(new_permissions);
256  PermissionMsgSet delta_warnings =
257      base::STLSetDifference<PermissionMsgSet>(new_warnings, old_warnings);
258
259  // A special hack: kFileSystemWriteDirectory implies kFileSystemDirectory and
260  // kFileSystemWrite.
261  // TODO(sammc): Remove this. See http://crbug.com/284849.
262  if (old_warnings.find(PermissionMessage(
263          PermissionMessage::kFileSystemWriteDirectory, string16())) !=
264      old_warnings.end()) {
265    delta_warnings.erase(
266        PermissionMessage(PermissionMessage::kFileSystemDirectory, string16()));
267    delta_warnings.erase(
268        PermissionMessage(PermissionMessage::kFileSystemWrite, string16()));
269  }
270
271  // It is a privilege increase if there are additional warnings present.
272  return !delta_warnings.empty();
273}
274
275bool ChromePermissionMessageProvider::IsManifestPermissionPrivilegeIncrease(
276    const PermissionSet* old_permissions,
277    const PermissionSet* new_permissions) const {
278  if (new_permissions == NULL)
279    return false;
280
281  typedef std::set<PermissionMessage> PermissionMsgSet;
282  PermissionMsgSet old_warnings =
283      GetManifestPermissionMessages(old_permissions);
284  PermissionMsgSet new_warnings =
285      GetManifestPermissionMessages(new_permissions);
286  PermissionMsgSet delta_warnings =
287      base::STLSetDifference<PermissionMsgSet>(new_warnings, old_warnings);
288
289  // It is a privilege increase if there are additional warnings present.
290  return !delta_warnings.empty();
291}
292
293bool ChromePermissionMessageProvider::IsHostPrivilegeIncrease(
294    const PermissionSet* old_permissions,
295    const PermissionSet* new_permissions,
296    Manifest::Type extension_type) const {
297  // Platform apps host permission changes do not count as privilege increases.
298  // Note: this must remain consistent with GetHostPermissionMessages.
299  if (extension_type == Manifest::TYPE_PLATFORM_APP)
300    return false;
301
302  // If the old permission set can access any host, then it can't be elevated.
303  if (old_permissions->HasEffectiveAccessToAllHosts())
304    return false;
305
306  // Likewise, if the new permission set has full host access, then it must be
307  // a privilege increase.
308  if (new_permissions->HasEffectiveAccessToAllHosts())
309    return true;
310
311  const URLPatternSet& old_list = old_permissions->effective_hosts();
312  const URLPatternSet& new_list = new_permissions->effective_hosts();
313
314  // TODO(jstritar): This is overly conservative with respect to subdomains.
315  // For example, going from *.google.com to www.google.com will be
316  // considered an elevation, even though it is not (http://crbug.com/65337).
317  std::set<std::string> new_hosts_set(
318      permission_message_util::GetDistinctHosts(new_list, false, false));
319  std::set<std::string> old_hosts_set(
320      permission_message_util::GetDistinctHosts(old_list, false, false));
321  std::set<std::string> new_hosts_only =
322      base::STLSetDifference<std::set<std::string> >(new_hosts_set,
323                                                     old_hosts_set);
324
325  return !new_hosts_only.empty();
326}
327
328}  // namespace extensions
329