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 "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"
16 namespace extensions
{
20 PermissionMessages::iterator
FindMessageByID(PermissionMessages
& messages
,
22 for (PermissionMessages::iterator it
= messages
.begin();
23 it
!= messages
.end(); ++it
) {
28 return messages
.end();
33 ChromePermissionMessageProvider::ChromePermissionMessageProvider() {
36 ChromePermissionMessageProvider::~ChromePermissionMessageProvider() {
40 PermissionMessages
ChromePermissionMessageProvider::GetPermissionMessages(
41 const PermissionSet
* permissions
,
42 Manifest::Type extension_type
) const {
43 PermissionMessages messages
;
45 if (permissions
->HasEffectiveFullAccess()) {
46 messages
.push_back(PermissionMessage(
47 PermissionMessage::kFullAccess
,
48 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS
)));
52 std::set
<PermissionMessage
> host_msgs
=
53 GetHostPermissionMessages(permissions
, extension_type
);
54 std::set
<PermissionMessage
> api_msgs
= GetAPIPermissionMessages(permissions
);
55 std::set
<PermissionMessage
> manifest_permission_msgs
=
56 GetManifestPermissionMessages(permissions
);
57 messages
.insert(messages
.end(), host_msgs
.begin(), host_msgs
.end());
58 messages
.insert(messages
.end(), api_msgs
.begin(), api_msgs
.end());
59 messages
.insert(messages
.end(), manifest_permission_msgs
.begin(),
60 manifest_permission_msgs
.end());
62 // Special hack: bookmarks permission message supersedes override bookmarks UI
63 // permission message if both permissions are specified.
64 PermissionMessages::iterator override_bookmarks_ui
=
65 FindMessageByID(messages
, PermissionMessage::kOverrideBookmarksUI
);
66 if (override_bookmarks_ui
!= messages
.end() &&
67 FindMessageByID(messages
, PermissionMessage::kBookmarks
) !=
69 messages
.erase(override_bookmarks_ui
);
76 std::vector
<base::string16
> ChromePermissionMessageProvider::GetWarningMessages(
77 const PermissionSet
* permissions
,
78 Manifest::Type extension_type
) const {
79 std::vector
<base::string16
> message_strings
;
80 PermissionMessages messages
=
81 GetPermissionMessages(permissions
, extension_type
);
83 bool audio_capture
= false;
84 bool video_capture
= false;
85 bool media_galleries_read
= false;
86 bool media_galleries_copy_to
= false;
87 bool media_galleries_delete
= false;
88 for (PermissionMessages::const_iterator i
= messages
.begin();
89 i
!= messages
.end(); ++i
) {
91 case PermissionMessage::kAudioCapture
:
94 case PermissionMessage::kVideoCapture
:
97 case PermissionMessage::kMediaGalleriesAllGalleriesRead
:
98 media_galleries_read
= true;
100 case PermissionMessage::kMediaGalleriesAllGalleriesCopyTo
:
101 media_galleries_copy_to
= true;
103 case PermissionMessage::kMediaGalleriesAllGalleriesDelete
:
104 media_galleries_delete
= true;
111 for (PermissionMessages::const_iterator i
= messages
.begin();
112 i
!= messages
.end(); ++i
) {
114 if (audio_capture
&& video_capture
) {
115 if (id
== PermissionMessage::kAudioCapture
) {
116 message_strings
.push_back(l10n_util::GetStringUTF16(
117 IDS_EXTENSION_PROMPT_WARNING_AUDIO_AND_VIDEO_CAPTURE
));
119 } else if (id
== PermissionMessage::kVideoCapture
) {
120 // The combined message will be pushed above.
124 if (media_galleries_read
&&
125 (media_galleries_copy_to
|| media_galleries_delete
)) {
126 if (id
== PermissionMessage::kMediaGalleriesAllGalleriesRead
) {
127 int m_id
= media_galleries_copy_to
?
128 IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_READ_WRITE
:
129 IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_READ_DELETE
;
130 message_strings
.push_back(l10n_util::GetStringUTF16(m_id
));
132 } else if (id
== PermissionMessage::kMediaGalleriesAllGalleriesCopyTo
||
133 id
== PermissionMessage::kMediaGalleriesAllGalleriesDelete
) {
134 // The combined message will be pushed above.
139 message_strings
.push_back(i
->message());
142 return message_strings
;
146 std::vector
<base::string16
>
147 ChromePermissionMessageProvider::GetWarningMessagesDetails(
148 const PermissionSet
* permissions
,
149 Manifest::Type extension_type
) const {
150 std::vector
<base::string16
> message_strings
;
151 PermissionMessages messages
=
152 GetPermissionMessages(permissions
, extension_type
);
154 for (PermissionMessages::const_iterator i
= messages
.begin();
155 i
!= messages
.end(); ++i
)
156 message_strings
.push_back(i
->details());
158 return message_strings
;
162 bool ChromePermissionMessageProvider::IsPrivilegeIncrease(
163 const PermissionSet
* old_permissions
,
164 const PermissionSet
* new_permissions
,
165 Manifest::Type extension_type
) const {
166 // Things can't get worse than native code access.
167 if (old_permissions
->HasEffectiveFullAccess())
170 // Otherwise, it's a privilege increase if the new one has full access.
171 if (new_permissions
->HasEffectiveFullAccess())
174 if (IsHostPrivilegeIncrease(old_permissions
, new_permissions
, extension_type
))
177 if (IsAPIPrivilegeIncrease(old_permissions
, new_permissions
))
180 if (IsManifestPermissionPrivilegeIncrease(old_permissions
, new_permissions
))
186 std::set
<PermissionMessage
>
187 ChromePermissionMessageProvider::GetAPIPermissionMessages(
188 const PermissionSet
* permissions
) const {
189 std::set
<PermissionMessage
> messages
;
190 for (APIPermissionSet::const_iterator permission_it
=
191 permissions
->apis().begin();
192 permission_it
!= permissions
->apis().end(); ++permission_it
) {
193 if (permission_it
->HasMessages()) {
194 PermissionMessages new_messages
= permission_it
->GetMessages();
195 messages
.insert(new_messages
.begin(), new_messages
.end());
199 // A special hack: If kFileSystemWriteDirectory would be displayed, hide
200 // kFileSystemDirectory and and kFileSystemWrite as the write directory
201 // message implies the other two.
202 // TODO(sammc): Remove this. See http://crbug.com/284849.
203 std::set
<PermissionMessage
>::iterator write_directory_message
=
204 messages
.find(PermissionMessage(
205 PermissionMessage::kFileSystemWriteDirectory
, base::string16()));
206 if (write_directory_message
!= messages
.end()) {
208 PermissionMessage(PermissionMessage::kFileSystemWrite
,
211 PermissionMessage(PermissionMessage::kFileSystemDirectory
,
215 // A special hack: The warning message for declarativeWebRequest
216 // permissions speaks about blocking parts of pages, which is a
217 // subset of what the "<all_urls>" access allows. Therefore we
218 // display only the "<all_urls>" warning message if both permissions
220 if (permissions
->HasEffectiveAccessToAllHosts()) {
223 PermissionMessage::kDeclarativeWebRequest
, base::string16()));
229 std::set
<PermissionMessage
>
230 ChromePermissionMessageProvider::GetManifestPermissionMessages(
231 const PermissionSet
* permissions
) const {
232 std::set
<PermissionMessage
> messages
;
233 for (ManifestPermissionSet::const_iterator permission_it
=
234 permissions
->manifest_permissions().begin();
235 permission_it
!= permissions
->manifest_permissions().end();
237 if (permission_it
->HasMessages()) {
238 PermissionMessages new_messages
= permission_it
->GetMessages();
239 messages
.insert(new_messages
.begin(), new_messages
.end());
245 std::set
<PermissionMessage
>
246 ChromePermissionMessageProvider::GetHostPermissionMessages(
247 const PermissionSet
* permissions
,
248 Manifest::Type extension_type
) const {
249 std::set
<PermissionMessage
> messages
;
250 // Since platform apps always use isolated storage, they can't (silently)
251 // access user data on other domains, so there's no need to prompt.
252 // Note: this must remain consistent with IsHostPrivilegeIncrease.
253 // See crbug.com/255229.
254 if (extension_type
== Manifest::TYPE_PLATFORM_APP
)
257 if (permissions
->HasEffectiveAccessToAllHosts()) {
258 messages
.insert(PermissionMessage(
259 PermissionMessage::kHostsAll
,
260 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS
)));
262 URLPatternSet regular_hosts
;
263 ExtensionsClient::Get()->FilterHostPermissions(
264 permissions
->effective_hosts(), ®ular_hosts
, &messages
);
266 std::set
<std::string
> hosts
=
267 permission_message_util::GetDistinctHosts(regular_hosts
, true, true);
269 messages
.insert(permission_message_util::CreateFromHostList(hosts
));
274 bool ChromePermissionMessageProvider::IsAPIPrivilegeIncrease(
275 const PermissionSet
* old_permissions
,
276 const PermissionSet
* new_permissions
) const {
277 if (new_permissions
== NULL
)
280 typedef std::set
<PermissionMessage
> PermissionMsgSet
;
281 PermissionMsgSet old_warnings
= GetAPIPermissionMessages(old_permissions
);
282 PermissionMsgSet new_warnings
= GetAPIPermissionMessages(new_permissions
);
283 PermissionMsgSet delta_warnings
=
284 base::STLSetDifference
<PermissionMsgSet
>(new_warnings
, old_warnings
);
286 // A special hack: kFileSystemWriteDirectory implies kFileSystemDirectory and
288 // TODO(sammc): Remove this. See http://crbug.com/284849.
289 if (old_warnings
.find(PermissionMessage(
290 PermissionMessage::kFileSystemWriteDirectory
, base::string16())) !=
291 old_warnings
.end()) {
292 delta_warnings
.erase(
293 PermissionMessage(PermissionMessage::kFileSystemDirectory
,
295 delta_warnings
.erase(
296 PermissionMessage(PermissionMessage::kFileSystemWrite
,
300 // It is a privilege increase if there are additional warnings present.
301 return !delta_warnings
.empty();
304 bool ChromePermissionMessageProvider::IsManifestPermissionPrivilegeIncrease(
305 const PermissionSet
* old_permissions
,
306 const PermissionSet
* new_permissions
) const {
307 if (new_permissions
== NULL
)
310 typedef std::set
<PermissionMessage
> PermissionMsgSet
;
311 PermissionMsgSet old_warnings
=
312 GetManifestPermissionMessages(old_permissions
);
313 PermissionMsgSet new_warnings
=
314 GetManifestPermissionMessages(new_permissions
);
315 PermissionMsgSet delta_warnings
=
316 base::STLSetDifference
<PermissionMsgSet
>(new_warnings
, old_warnings
);
318 // It is a privilege increase if there are additional warnings present.
319 return !delta_warnings
.empty();
322 bool ChromePermissionMessageProvider::IsHostPrivilegeIncrease(
323 const PermissionSet
* old_permissions
,
324 const PermissionSet
* new_permissions
,
325 Manifest::Type extension_type
) const {
326 // Platform apps host permission changes do not count as privilege increases.
327 // Note: this must remain consistent with GetHostPermissionMessages.
328 if (extension_type
== Manifest::TYPE_PLATFORM_APP
)
331 // If the old permission set can access any host, then it can't be elevated.
332 if (old_permissions
->HasEffectiveAccessToAllHosts())
335 // Likewise, if the new permission set has full host access, then it must be
336 // a privilege increase.
337 if (new_permissions
->HasEffectiveAccessToAllHosts())
340 const URLPatternSet
& old_list
= old_permissions
->effective_hosts();
341 const URLPatternSet
& new_list
= new_permissions
->effective_hosts();
343 // TODO(jstritar): This is overly conservative with respect to subdomains.
344 // For example, going from *.google.com to www.google.com will be
345 // considered an elevation, even though it is not (http://crbug.com/65337).
346 std::set
<std::string
> new_hosts_set(
347 permission_message_util::GetDistinctHosts(new_list
, false, false));
348 std::set
<std::string
> old_hosts_set(
349 permission_message_util::GetDistinctHosts(old_list
, false, false));
350 std::set
<std::string
> new_hosts_only
=
351 base::STLSetDifference
<std::set
<std::string
> >(new_hosts_set
,
354 return !new_hosts_only
.empty();
357 } // namespace extensions