Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / common / extensions / permissions / chrome_permission_message_provider.cc
blob26a5ae4693564a2b7aa4e46377cd583a2b66e826
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.
5 #include "chrome/common/extensions/permissions/chrome_permission_message_provider.h"
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"
20 namespace extensions {
22 namespace {
24 typedef std::set<PermissionMessage> PermissionMsgSet;
26 bool ShouldWarnAllHosts(const PermissionSet* permissions) {
27 if (permissions->HasEffectiveAccessToAllHosts())
28 return true;
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;
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;
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;
64 return false;
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;
74 return messages.end();
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);
89 } // namespace
91 ChromePermissionMessageProvider::ChromePermissionMessageProvider() {
94 ChromePermissionMessageProvider::~ChromePermissionMessageProvider() {
97 PermissionMessages ChromePermissionMessageProvider::GetPermissionMessages(
98 const PermissionSet* permissions,
99 Manifest::Type extension_type) const {
100 PermissionMessages messages;
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;
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());
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;
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);
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;
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;
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;
201 message_strings.push_back(i->message());
204 return message_strings;
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);
215 for (PermissionMessages::const_iterator i = messages.begin();
216 i != messages.end(); ++i)
217 message_strings.push_back(i->details());
219 return message_strings;
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;
230 // Otherwise, it's a privilege increase if the new one has full access.
231 if (new_permissions->HasEffectiveFullAccess())
232 return true;
234 if (IsHostPrivilegeIncrease(old_permissions, new_permissions, extension_type))
235 return true;
237 if (IsAPIPrivilegeIncrease(old_permissions, new_permissions))
238 return true;
240 if (IsManifestPermissionPrivilegeIncrease(old_permissions, new_permissions))
241 return true;
243 return false;
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());
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()));
275 return messages;
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());
291 return messages;
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;
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);
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));
320 return messages;
323 bool ChromePermissionMessageProvider::IsAPIPrivilegeIncrease(
324 const PermissionSet* old_permissions,
325 const PermissionSet* new_permissions) const {
326 if (new_permissions == NULL)
327 return false;
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);
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()));
344 // It is a privilege increase if there are additional warnings present.
345 return !delta_warnings.empty();
348 bool ChromePermissionMessageProvider::IsManifestPermissionPrivilegeIncrease(
349 const PermissionSet* old_permissions,
350 const PermissionSet* new_permissions) const {
351 if (new_permissions == NULL)
352 return false;
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);
361 // It is a privilege increase if there are additional warnings present.
362 return !delta_warnings.empty();
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;
374 // If the old permission set can access any host, then it can't be elevated.
375 if (old_permissions->HasEffectiveAccessToAllHosts())
376 return false;
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;
383 const URLPatternSet& old_list = old_permissions->effective_hosts();
384 const URLPatternSet& new_list = new_permissions->effective_hosts();
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);
397 return !new_hosts_only.empty();
400 } // namespace extensions