chrome_permission_message_provider.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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 "chrome/common/extensions/permissions/permission_set.h"
10#include "extensions/common/extensions_client.h"
11#include "extensions/common/permissions/permission_message.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  messages.insert(messages.end(), host_msgs.begin(), host_msgs.end());
41  messages.insert(messages.end(), api_msgs.begin(), api_msgs.end());
42
43  return messages;
44}
45
46// static
47std::vector<string16> ChromePermissionMessageProvider::GetWarningMessages(
48    const PermissionSet* permissions,
49    Manifest::Type extension_type) const {
50  std::vector<string16> message_strings;
51  PermissionMessages messages =
52      GetPermissionMessages(permissions, extension_type);
53
54  bool audio_capture = false;
55  bool video_capture = false;
56  bool media_galleries_read = false;
57  bool media_galleries_copy_to = false;
58  for (PermissionMessages::const_iterator i = messages.begin();
59       i != messages.end(); ++i) {
60    switch (i->id()) {
61      case PermissionMessage::kAudioCapture:
62        audio_capture = true;
63        break;
64      case PermissionMessage::kVideoCapture:
65        video_capture = true;
66        break;
67      case PermissionMessage::kMediaGalleriesAllGalleriesRead:
68        media_galleries_read = true;
69        break;
70      case PermissionMessage::kMediaGalleriesAllGalleriesCopyTo:
71        media_galleries_copy_to = true;
72        break;
73      default:
74        break;
75    }
76  }
77
78  for (PermissionMessages::const_iterator i = messages.begin();
79       i != messages.end(); ++i) {
80    int id = i->id();
81    if (audio_capture && video_capture) {
82      if (id == PermissionMessage::kAudioCapture) {
83        message_strings.push_back(l10n_util::GetStringUTF16(
84            IDS_EXTENSION_PROMPT_WARNING_AUDIO_AND_VIDEO_CAPTURE));
85        continue;
86      } else if (id == PermissionMessage::kVideoCapture) {
87        // The combined message will be pushed above.
88        continue;
89      }
90    }
91    if (media_galleries_read && media_galleries_copy_to) {
92      if (id == PermissionMessage::kMediaGalleriesAllGalleriesRead) {
93        message_strings.push_back(l10n_util::GetStringUTF16(
94            IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_READ_WRITE));
95        continue;
96      } else if (id == PermissionMessage::kMediaGalleriesAllGalleriesCopyTo) {
97        // The combined message will be pushed above.
98        continue;
99      }
100    }
101
102    message_strings.push_back(i->message());
103  }
104
105  return message_strings;
106}
107
108// static
109std::vector<string16>
110ChromePermissionMessageProvider::GetWarningMessagesDetails(
111    const PermissionSet* permissions,
112    Manifest::Type extension_type) const {
113  std::vector<string16> message_strings;
114  PermissionMessages messages =
115      GetPermissionMessages(permissions, extension_type);
116
117  for (PermissionMessages::const_iterator i = messages.begin();
118       i != messages.end(); ++i)
119    message_strings.push_back(i->details());
120
121  return message_strings;
122}
123
124// static
125bool ChromePermissionMessageProvider::IsPrivilegeIncrease(
126    const PermissionSet* old_permissions,
127    const PermissionSet* new_permissions,
128    Manifest::Type extension_type) const {
129  // Things can't get worse than native code access.
130  if (old_permissions->HasEffectiveFullAccess())
131    return false;
132
133  // Otherwise, it's a privilege increase if the new one has full access.
134  if (new_permissions->HasEffectiveFullAccess())
135    return true;
136
137  if (IsHostPrivilegeIncrease(old_permissions, new_permissions, extension_type))
138    return true;
139
140  if (IsAPIPrivilegeIncrease(old_permissions, new_permissions))
141    return true;
142
143  return false;
144}
145
146std::set<PermissionMessage>
147ChromePermissionMessageProvider::GetAPIPermissionMessages(
148    const PermissionSet* permissions) const {
149  std::set<PermissionMessage> messages;
150  for (APIPermissionSet::const_iterator permission_it =
151           permissions->apis().begin();
152       permission_it != permissions->apis().end(); ++permission_it) {
153    if (permission_it->HasMessages()) {
154      PermissionMessages new_messages = permission_it->GetMessages();
155      messages.insert(new_messages.begin(), new_messages.end());
156    }
157  }
158
159  // A special hack: If kFileSystemWriteDirectory would be displayed, hide
160  // kFileSystemDirectory and and kFileSystemWrite as the write directory
161  // message implies the other two.
162  // TODO(sammc): Remove this. See http://crbug.com/284849.
163  std::set<PermissionMessage>::iterator write_directory_message =
164      messages.find(PermissionMessage(
165          PermissionMessage::kFileSystemWriteDirectory, string16()));
166  if (write_directory_message != messages.end()) {
167    messages.erase(
168        PermissionMessage(PermissionMessage::kFileSystemWrite, string16()));
169    messages.erase(
170        PermissionMessage(PermissionMessage::kFileSystemDirectory, string16()));
171  }
172
173  // A special hack: The warning message for declarativeWebRequest
174  // permissions speaks about blocking parts of pages, which is a
175  // subset of what the "<all_urls>" access allows. Therefore we
176  // display only the "<all_urls>" warning message if both permissions
177  // are required.
178  if (permissions->HasEffectiveAccessToAllHosts()) {
179    messages.erase(
180        PermissionMessage(
181            PermissionMessage::kDeclarativeWebRequest, string16()));
182  }
183
184  return messages;
185}
186
187std::set<PermissionMessage>
188ChromePermissionMessageProvider::GetHostPermissionMessages(
189    const PermissionSet* permissions,
190    Manifest::Type extension_type) const {
191  std::set<PermissionMessage> messages;
192  // Since platform apps always use isolated storage, they can't (silently)
193  // access user data on other domains, so there's no need to prompt.
194  // Note: this must remain consistent with IsHostPrivilegeIncrease.
195  // See crbug.com/255229.
196  if (extension_type == Manifest::TYPE_PLATFORM_APP)
197    return messages;
198
199  if (permissions->HasEffectiveAccessToAllHosts()) {
200    messages.insert(PermissionMessage(
201        PermissionMessage::kHostsAll,
202        l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS)));
203  } else {
204    URLPatternSet regular_hosts;
205    ExtensionsClient::Get()->FilterHostPermissions(
206        permissions->effective_hosts(), &regular_hosts, &messages);
207
208    std::set<std::string> hosts =
209        permission_message_util::GetDistinctHosts(regular_hosts, true, true);
210    if (!hosts.empty())
211      messages.insert(permission_message_util::CreateFromHostList(hosts));
212  }
213  return messages;
214}
215
216bool ChromePermissionMessageProvider::IsAPIPrivilegeIncrease(
217    const PermissionSet* old_permissions,
218    const PermissionSet* new_permissions) const {
219  if (new_permissions == NULL)
220    return false;
221
222  typedef std::set<PermissionMessage> PermissionMsgSet;
223  PermissionMsgSet old_warnings = GetAPIPermissionMessages(old_permissions);
224  PermissionMsgSet new_warnings = GetAPIPermissionMessages(new_permissions);
225  PermissionMsgSet delta_warnings =
226      base::STLSetDifference<PermissionMsgSet>(new_warnings, old_warnings);
227
228  // A special hack: kFileSystemWriteDirectory implies kFileSystemDirectory and
229  // kFileSystemWrite.
230  // TODO(sammc): Remove this. See http://crbug.com/284849.
231  if (old_warnings.find(PermissionMessage(
232          PermissionMessage::kFileSystemWriteDirectory, string16())) !=
233      old_warnings.end()) {
234    delta_warnings.erase(
235        PermissionMessage(PermissionMessage::kFileSystemDirectory, string16()));
236    delta_warnings.erase(
237        PermissionMessage(PermissionMessage::kFileSystemWrite, string16()));
238  }
239
240  // We have less privileges if there are additional warnings present.
241  return !delta_warnings.empty();
242}
243
244bool ChromePermissionMessageProvider::IsHostPrivilegeIncrease(
245    const PermissionSet* old_permissions,
246    const PermissionSet* new_permissions,
247    Manifest::Type extension_type) const {
248  // Platform apps host permission changes do not count as privilege increases.
249  // Note: this must remain consistent with GetHostPermissionMessages.
250  if (extension_type == Manifest::TYPE_PLATFORM_APP)
251    return false;
252
253  // If the old permission set can access any host, then it can't be elevated.
254  if (old_permissions->HasEffectiveAccessToAllHosts())
255    return false;
256
257  // Likewise, if the new permission set has full host access, then it must be
258  // a privilege increase.
259  if (new_permissions->HasEffectiveAccessToAllHosts())
260    return true;
261
262  const URLPatternSet& old_list = old_permissions->effective_hosts();
263  const URLPatternSet& new_list = new_permissions->effective_hosts();
264
265  // TODO(jstritar): This is overly conservative with respect to subdomains.
266  // For example, going from *.google.com to www.google.com will be
267  // considered an elevation, even though it is not (http://crbug.com/65337).
268  std::set<std::string> new_hosts_set(
269      permission_message_util::GetDistinctHosts(new_list, false, false));
270  std::set<std::string> old_hosts_set(
271      permission_message_util::GetDistinctHosts(old_list, false, false));
272  std::set<std::string> new_hosts_only =
273      base::STLSetDifference<std::set<std::string> >(new_hosts_set,
274                                                     old_hosts_set);
275
276  return !new_hosts_only.empty();
277}
278
279}  // namespace extensions
280