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
16 namespace extensions {
17
ChromePermissionMessageProvider()18 ChromePermissionMessageProvider::ChromePermissionMessageProvider() {
19 }
20
~ChromePermissionMessageProvider()21 ChromePermissionMessageProvider::~ChromePermissionMessageProvider() {
22 }
23
24 // static
GetPermissionMessages(const PermissionSet * permissions,Manifest::Type extension_type) const25 PermissionMessages 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
GetWarningMessages(const PermissionSet * permissions,Manifest::Type extension_type) const51 std::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
121 std::vector<base::string16>
GetWarningMessagesDetails(const PermissionSet * permissions,Manifest::Type extension_type) const122 ChromePermissionMessageProvider::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
IsPrivilegeIncrease(const PermissionSet * old_permissions,const PermissionSet * new_permissions,Manifest::Type extension_type) const137 bool 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
161 std::set<PermissionMessage>
GetAPIPermissionMessages(const PermissionSet * permissions) const162 ChromePermissionMessageProvider::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
204 std::set<PermissionMessage>
GetManifestPermissionMessages(const PermissionSet * permissions) const205 ChromePermissionMessageProvider::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
220 std::set<PermissionMessage>
GetHostPermissionMessages(const PermissionSet * permissions,Manifest::Type extension_type) const221 ChromePermissionMessageProvider::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(), ®ular_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
IsAPIPrivilegeIncrease(const PermissionSet * old_permissions,const PermissionSet * new_permissions) const249 bool 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
IsManifestPermissionPrivilegeIncrease(const PermissionSet * old_permissions,const PermissionSet * new_permissions) const279 bool 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
IsHostPrivilegeIncrease(const PermissionSet * old_permissions,const PermissionSet * new_permissions,Manifest::Type extension_type) const297 bool 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