chrome_permission_message_provider.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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<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
121std::vector<base::string16>
122ChromePermissionMessageProvider::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
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, 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
204std::set<PermissionMessage>
205ChromePermissionMessageProvider::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
220std::set<PermissionMessage>
221ChromePermissionMessageProvider::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
249bool 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
279bool 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
297bool 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