OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/extension_permission_set.h" | |
6 | |
7 #include <algorithm> | |
8 #include <string> | |
9 | |
10 #include "base/basictypes.h" | |
11 #include "base/command_line.h" | |
12 #include "base/memory/singleton.h" | |
13 #include "base/values.h" | |
14 #include "base/string_number_conversions.h" | |
15 #include "base/utf_string_conversions.h" | |
16 #include "chrome/common/chrome_switches.h" | |
17 #include "chrome/common/extensions/extension.h" | |
18 #include "chrome/common/extensions/extension_constants.h" | |
19 #include "chrome/common/extensions/extension_l10n_util.h" | |
20 #include "chrome/common/extensions/url_pattern.h" | |
21 #include "chrome/common/extensions/url_pattern_set.h" | |
22 #include "content/public/common/url_constants.h" | |
23 #include "grit/generated_resources.h" | |
24 #include "net/base/registry_controlled_domain.h" | |
25 #include "ui/base/l10n/l10n_util.h" | |
26 | |
27 namespace ids = extension_misc; | |
28 namespace { | |
29 | |
30 // Helper for GetDistinctHosts(): com > net > org > everything else. | |
31 bool RcdBetterThan(std::string a, std::string b) { | |
32 if (a == b) | |
33 return false; | |
34 if (a == "com") | |
35 return true; | |
36 if (a == "net") | |
37 return b != "com"; | |
38 if (a == "org") | |
39 return b != "com" && b != "net"; | |
40 return false; | |
41 } | |
42 | |
43 // Names of API modules that can be used without listing it in the | |
44 // permissions section of the manifest. | |
45 const char* kNonPermissionModuleNames[] = { | |
46 "app", | |
47 "appWindow", | |
48 "browserAction", | |
49 "devtools", | |
50 "events", | |
51 "extension", | |
52 "i18n", | |
53 "omnibox", | |
54 "pageAction", | |
55 "pageActions", | |
56 "permissions", | |
57 "runtime", | |
58 "test", | |
59 "types" | |
60 }; | |
61 const size_t kNumNonPermissionModuleNames = | |
62 arraysize(kNonPermissionModuleNames); | |
63 | |
64 // Names of functions (within modules requiring permissions) that can be used | |
65 // without asking for the module permission. In other words, functions you can | |
66 // use with no permissions specified. | |
67 const char* kNonPermissionFunctionNames[] = { | |
68 "management.getPermissionWarningsByManifest", | |
69 "tabs.create", | |
70 "tabs.onRemoved", | |
71 "tabs.remove", | |
72 "tabs.update", | |
73 }; | |
74 const size_t kNumNonPermissionFunctionNames = | |
75 arraysize(kNonPermissionFunctionNames); | |
76 | |
77 const char kOldUnlimitedStoragePermission[] = "unlimited_storage"; | |
78 const char kWindowsPermission[] = "windows"; | |
79 const char kTemporaryBackgroundAlias[] = "background_alias_do_not_use"; | |
80 | |
81 void AddPatternsAndRemovePaths(const URLPatternSet& set, URLPatternSet* out) { | |
82 DCHECK(out); | |
83 for (URLPatternSet::const_iterator i = set.begin(); i != set.end(); ++i) { | |
84 URLPattern p = *i; | |
85 p.SetPath("/*"); | |
86 out->AddPattern(p); | |
87 } | |
88 } | |
89 | |
90 // Strips out the API name from a function or event name. | |
91 // Functions will be of the form api_name.function | |
92 // Events will be of the form api_name/id or api_name.optional.stuff | |
93 std::string GetPermissionName(const std::string& function_name) { | |
94 size_t separator = function_name.find_first_of("./"); | |
95 if (separator != std::string::npos) | |
96 return function_name.substr(0, separator); | |
97 else | |
98 return function_name; | |
99 } | |
100 | |
101 } // namespace | |
102 | |
103 // | |
104 // PermissionMessage | |
105 // | |
106 | |
107 // static | |
108 ExtensionPermissionMessage ExtensionPermissionMessage::CreateFromHostList( | |
109 const std::set<std::string>& hosts) { | |
110 std::vector<std::string> host_list(hosts.begin(), hosts.end()); | |
111 DCHECK_GT(host_list.size(), 0UL); | |
112 ID message_id; | |
113 string16 message; | |
114 | |
115 switch (host_list.size()) { | |
116 case 1: | |
117 message_id = kHosts1; | |
118 message = l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_1_HOST, | |
119 UTF8ToUTF16(host_list[0])); | |
120 break; | |
121 case 2: | |
122 message_id = kHosts2; | |
123 message = l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_2_HOSTS, | |
124 UTF8ToUTF16(host_list[0]), | |
125 UTF8ToUTF16(host_list[1])); | |
126 break; | |
127 case 3: | |
128 message_id = kHosts3; | |
129 message = l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_3_HOSTS, | |
130 UTF8ToUTF16(host_list[0]), | |
131 UTF8ToUTF16(host_list[1]), | |
132 UTF8ToUTF16(host_list[2])); | |
133 break; | |
134 default: | |
135 message_id = kHosts4OrMore; | |
136 message = l10n_util::GetStringFUTF16( | |
137 IDS_EXTENSION_PROMPT_WARNING_4_OR_MORE_HOSTS, | |
138 UTF8ToUTF16(host_list[0]), | |
139 UTF8ToUTF16(host_list[1]), | |
140 base::IntToString16(hosts.size() - 2)); | |
141 break; | |
142 } | |
143 | |
144 return ExtensionPermissionMessage(message_id, message); | |
145 } | |
146 | |
147 ExtensionPermissionMessage::ExtensionPermissionMessage( | |
148 ExtensionPermissionMessage::ID id, const string16& message) | |
149 : id_(id), message_(message) { | |
150 } | |
151 | |
152 ExtensionPermissionMessage::~ExtensionPermissionMessage() {} | |
153 | |
154 // | |
155 // ExtensionPermission | |
156 // | |
157 | |
158 ExtensionAPIPermission::~ExtensionAPIPermission() {} | |
159 | |
160 ExtensionPermissionMessage ExtensionAPIPermission::GetMessage() const { | |
161 return ExtensionPermissionMessage( | |
162 message_id_, l10n_util::GetStringUTF16(l10n_message_id_)); | |
163 } | |
164 | |
165 ExtensionAPIPermission::ExtensionAPIPermission( | |
166 ID id, | |
167 const char* name, | |
168 int l10n_message_id, | |
169 ExtensionPermissionMessage::ID message_id, | |
170 int flags) | |
171 : id_(id), | |
172 name_(name), | |
173 flags_(flags), | |
174 l10n_message_id_(l10n_message_id), | |
175 message_id_(message_id) {} | |
176 | |
177 // static | |
178 void ExtensionAPIPermission::RegisterAllPermissions( | |
179 ExtensionPermissionsInfo* info) { | |
180 | |
181 struct PermissionRegistration { | |
182 ExtensionAPIPermission::ID id; | |
183 const char* name; | |
184 int flags; | |
185 int l10n_message_id; | |
186 ExtensionPermissionMessage::ID message_id; | |
187 } PermissionsToRegister[] = { | |
188 // Register permissions for all extension types. | |
189 { kBackground, "background" }, | |
190 { kClipboardRead, "clipboardRead", kFlagNone, | |
191 IDS_EXTENSION_PROMPT_WARNING_CLIPBOARD, | |
192 ExtensionPermissionMessage::kClipboard }, | |
193 { kClipboardWrite, "clipboardWrite" }, | |
194 { kDeclarative, "declarative" }, | |
195 { kDeclarativeWebRequest, "declarativeWebRequest" }, | |
196 { kDownloads, "downloads", kFlagNone, | |
197 IDS_EXTENSION_PROMPT_WARNING_DOWNLOADS, | |
198 ExtensionPermissionMessage::kDownloads }, | |
199 { kExperimental, "experimental", kFlagCannotBeOptional }, | |
200 { kGeolocation, "geolocation", kFlagCannotBeOptional, | |
201 IDS_EXTENSION_PROMPT_WARNING_GEOLOCATION, | |
202 ExtensionPermissionMessage::kGeolocation }, | |
203 { kNotification, "notifications" }, | |
204 { kUnlimitedStorage, "unlimitedStorage", kFlagCannotBeOptional }, | |
205 | |
206 // Register hosted and packaged app permissions. | |
207 { kAppNotifications, "appNotifications" }, | |
208 | |
209 // Register extension permissions. | |
210 { kActiveTab, "activeTab" }, | |
211 { kAlarms, "alarms" }, | |
212 { kBookmark, "bookmarks", kFlagNone, | |
213 IDS_EXTENSION_PROMPT_WARNING_BOOKMARKS, | |
214 ExtensionPermissionMessage::kBookmarks }, | |
215 { kBrowsingData, "browsingData" }, | |
216 { kContentSettings, "contentSettings", kFlagNone, | |
217 IDS_EXTENSION_PROMPT_WARNING_CONTENT_SETTINGS, | |
218 ExtensionPermissionMessage::kContentSettings }, | |
219 { kContextMenus, "contextMenus" }, | |
220 { kCookie, "cookies" }, | |
221 { kFileBrowserHandler, "fileBrowserHandler", kFlagCannotBeOptional }, | |
222 { kFileSystem, "fileSystem" }, | |
223 { kHistory, "history", kFlagNone, | |
224 IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY, | |
225 ExtensionPermissionMessage::kBrowsingHistory }, | |
226 { kKeybinding, "keybinding" }, | |
227 { kIdle, "idle" }, | |
228 { kInput, "input", kFlagNone, | |
229 IDS_EXTENSION_PROMPT_WARNING_INPUT, | |
230 ExtensionPermissionMessage::kInput }, | |
231 { kManagement, "management", kFlagNone, | |
232 IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT, | |
233 ExtensionPermissionMessage::kManagement }, | |
234 { kPageCapture, "pageCapture", kFlagNone, | |
235 IDS_EXTENSION_PROMPT_WARNING_ALL_PAGES_CONTENT, | |
236 ExtensionPermissionMessage::kAllPageContent }, | |
237 { kPrivacy, "privacy", kFlagNone, | |
238 IDS_EXTENSION_PROMPT_WARNING_PRIVACY, | |
239 ExtensionPermissionMessage::kPrivacy }, | |
240 { kStorage, "storage" }, | |
241 { kTab, "tabs", kFlagNone, | |
242 IDS_EXTENSION_PROMPT_WARNING_TABS, | |
243 ExtensionPermissionMessage::kTabs }, | |
244 { kTopSites, "topSites", kFlagNone, | |
245 IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY, | |
246 ExtensionPermissionMessage::kBrowsingHistory }, | |
247 { kTts, "tts", 0, kFlagCannotBeOptional }, | |
248 { kTtsEngine, "ttsEngine", kFlagCannotBeOptional, | |
249 IDS_EXTENSION_PROMPT_WARNING_TTS_ENGINE, | |
250 ExtensionPermissionMessage::kTtsEngine }, | |
251 { kUsb, "usb", kFlagNone, | |
252 IDS_EXTENSION_PROMPT_WARNING_USB, | |
253 ExtensionPermissionMessage::kNone }, | |
254 { kWebNavigation, "webNavigation", kFlagNone, | |
255 IDS_EXTENSION_PROMPT_WARNING_TABS, ExtensionPermissionMessage::kTabs }, | |
256 { kWebRequest, "webRequest" }, | |
257 { kWebRequestBlocking, "webRequestBlocking" }, | |
258 | |
259 // Register private permissions. | |
260 { kChromeosInfoPrivate, "chromeosInfoPrivate", kFlagCannotBeOptional }, | |
261 { kFileBrowserHandlerInternal, "fileBrowserHandlerInternal", | |
262 kFlagCannotBeOptional }, | |
263 { kFileBrowserPrivate, "fileBrowserPrivate", kFlagCannotBeOptional }, | |
264 { kManagedModePrivate, "managedModePrivate", kFlagCannotBeOptional }, | |
265 { kMediaPlayerPrivate, "mediaPlayerPrivate", kFlagCannotBeOptional }, | |
266 { kMetricsPrivate, "metricsPrivate", kFlagCannotBeOptional }, | |
267 { kSystemPrivate, "systemPrivate", kFlagCannotBeOptional }, | |
268 { kChromeAuthPrivate, "chromeAuthPrivate", kFlagCannotBeOptional }, | |
269 { kInputMethodPrivate, "inputMethodPrivate", kFlagCannotBeOptional }, | |
270 { kEchoPrivate, "echoPrivate", kFlagCannotBeOptional }, | |
271 { kTerminalPrivate, "terminalPrivate", kFlagCannotBeOptional }, | |
272 { kWebRequestInternal, "webRequestInternal", kFlagCannotBeOptional }, | |
273 { kWebSocketProxyPrivate, "webSocketProxyPrivate", kFlagCannotBeOptional }, | |
274 { kWebstorePrivate, "webstorePrivate", kFlagCannotBeOptional }, | |
275 | |
276 // Full url access permissions. | |
277 { kProxy, "proxy", kFlagImpliesFullURLAccess | kFlagCannotBeOptional }, | |
278 { kDebugger, "debugger", kFlagImpliesFullURLAccess | kFlagCannotBeOptional, | |
279 IDS_EXTENSION_PROMPT_WARNING_DEBUGGER, | |
280 ExtensionPermissionMessage::kDebugger }, | |
281 { kDevtools, "devtools", | |
282 kFlagImpliesFullURLAccess | kFlagCannotBeOptional }, | |
283 { kPlugin, "plugin", | |
284 kFlagImpliesFullURLAccess | kFlagImpliesFullAccess | | |
285 kFlagCannotBeOptional, | |
286 IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS, | |
287 ExtensionPermissionMessage::kFullAccess }, | |
288 | |
289 // Platform-app permissions. | |
290 { kSocket, "socket", kFlagCannotBeOptional }, | |
291 { kAudioCapture, "audioCapture", kFlagNone, | |
292 IDS_EXTENSION_PROMPT_WARNING_AUDIO_CAPTURE, | |
293 ExtensionPermissionMessage::kAudioCapture }, | |
294 { kVideoCapture, "videoCapture", kFlagNone, | |
295 IDS_EXTENSION_PROMPT_WARNING_VIDEO_CAPTURE, | |
296 ExtensionPermissionMessage::kVideoCapture }, | |
297 }; | |
298 | |
299 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(PermissionsToRegister); ++i) { | |
300 const PermissionRegistration& pr = PermissionsToRegister[i]; | |
301 info->RegisterPermission( | |
302 pr.id, pr.name, pr.l10n_message_id, | |
303 pr.message_id ? pr.message_id : ExtensionPermissionMessage::kNone, | |
304 pr.flags); | |
305 } | |
306 | |
307 // Register aliases. | |
308 info->RegisterAlias("unlimitedStorage", kOldUnlimitedStoragePermission); | |
309 info->RegisterAlias("tabs", kWindowsPermission); | |
310 // TODO(mihaip): Should be removed for the M20 branch, see | |
311 // http://crbug.com/120447 for more details. | |
312 info->RegisterAlias("background", kTemporaryBackgroundAlias); | |
313 } | |
314 | |
315 // | |
316 // ExtensionPermissionsInfo | |
317 // | |
318 | |
319 // static | |
320 ExtensionPermissionsInfo* ExtensionPermissionsInfo::GetInstance() { | |
321 return Singleton<ExtensionPermissionsInfo>::get(); | |
322 } | |
323 | |
324 ExtensionAPIPermission* ExtensionPermissionsInfo::GetByID( | |
325 ExtensionAPIPermission::ID id) { | |
326 IDMap::iterator i = id_map_.find(id); | |
327 return (i == id_map_.end()) ? NULL : i->second; | |
328 } | |
329 | |
330 ExtensionAPIPermission* ExtensionPermissionsInfo::GetByName( | |
331 const std::string& name) { | |
332 NameMap::iterator i = name_map_.find(name); | |
333 return (i == name_map_.end()) ? NULL : i->second; | |
334 } | |
335 | |
336 ExtensionAPIPermissionSet ExtensionPermissionsInfo::GetAll() { | |
337 ExtensionAPIPermissionSet permissions; | |
338 for (IDMap::const_iterator i = id_map_.begin(); i != id_map_.end(); ++i) | |
339 permissions.insert(i->second->id()); | |
340 return permissions; | |
341 } | |
342 | |
343 ExtensionAPIPermissionSet ExtensionPermissionsInfo::GetAllByName( | |
344 const std::set<std::string>& permission_names) { | |
345 ExtensionAPIPermissionSet permissions; | |
346 for (std::set<std::string>::const_iterator i = permission_names.begin(); | |
347 i != permission_names.end(); ++i) { | |
348 ExtensionAPIPermission* permission = GetByName(*i); | |
349 if (permission) | |
350 permissions.insert(permission->id()); | |
351 } | |
352 return permissions; | |
353 } | |
354 | |
355 ExtensionPermissionsInfo::~ExtensionPermissionsInfo() { | |
356 for (IDMap::iterator i = id_map_.begin(); i != id_map_.end(); ++i) | |
357 delete i->second; | |
358 } | |
359 | |
360 ExtensionPermissionsInfo::ExtensionPermissionsInfo() | |
361 : hosted_app_permission_count_(0), | |
362 permission_count_(0) { | |
363 ExtensionAPIPermission::RegisterAllPermissions(this); | |
364 } | |
365 | |
366 void ExtensionPermissionsInfo::RegisterAlias( | |
367 const char* name, | |
368 const char* alias) { | |
369 DCHECK(name_map_.find(name) != name_map_.end()); | |
370 DCHECK(name_map_.find(alias) == name_map_.end()); | |
371 name_map_[alias] = name_map_[name]; | |
372 } | |
373 | |
374 ExtensionAPIPermission* ExtensionPermissionsInfo::RegisterPermission( | |
375 ExtensionAPIPermission::ID id, | |
376 const char* name, | |
377 int l10n_message_id, | |
378 ExtensionPermissionMessage::ID message_id, | |
379 int flags) { | |
380 DCHECK(id_map_.find(id) == id_map_.end()); | |
381 DCHECK(name_map_.find(name) == name_map_.end()); | |
382 | |
383 ExtensionAPIPermission* permission = new ExtensionAPIPermission( | |
384 id, name, l10n_message_id, message_id, flags); | |
385 | |
386 id_map_[id] = permission; | |
387 name_map_[name] = permission; | |
388 | |
389 permission_count_++; | |
390 | |
391 return permission; | |
392 } | |
393 | |
394 // | |
395 // ExtensionPermissionSet | |
396 // | |
397 | |
398 ExtensionPermissionSet::ExtensionPermissionSet() {} | |
399 | |
400 ExtensionPermissionSet::ExtensionPermissionSet( | |
401 const extensions::Extension* extension, | |
402 const ExtensionAPIPermissionSet& apis, | |
403 const URLPatternSet& explicit_hosts, | |
404 const ExtensionOAuth2Scopes& scopes) | |
405 : apis_(apis), | |
406 scopes_(scopes) { | |
407 DCHECK(extension); | |
408 AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_); | |
409 InitImplicitExtensionPermissions(extension); | |
410 InitEffectiveHosts(); | |
411 } | |
412 | |
413 ExtensionPermissionSet::ExtensionPermissionSet( | |
414 const ExtensionAPIPermissionSet& apis, | |
415 const URLPatternSet& explicit_hosts, | |
416 const URLPatternSet& scriptable_hosts) | |
417 : apis_(apis), | |
418 scriptable_hosts_(scriptable_hosts) { | |
419 AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_); | |
420 InitEffectiveHosts(); | |
421 } | |
422 | |
423 ExtensionPermissionSet::ExtensionPermissionSet( | |
424 const ExtensionAPIPermissionSet& apis, | |
425 const URLPatternSet& explicit_hosts, | |
426 const URLPatternSet& scriptable_hosts, | |
427 const ExtensionOAuth2Scopes& scopes) | |
428 : apis_(apis), | |
429 scriptable_hosts_(scriptable_hosts), | |
430 scopes_(scopes) { | |
431 AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_); | |
432 InitEffectiveHosts(); | |
433 } | |
434 | |
435 ExtensionPermissionSet::ExtensionPermissionSet( | |
436 const ExtensionOAuth2Scopes& scopes) | |
437 : scopes_(scopes) { | |
438 InitEffectiveHosts(); | |
439 } | |
440 | |
441 // static | |
442 ExtensionPermissionSet* ExtensionPermissionSet::CreateDifference( | |
443 const ExtensionPermissionSet* set1, | |
444 const ExtensionPermissionSet* set2) { | |
445 scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet(); | |
446 const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? empty : set1; | |
447 const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? empty : set2; | |
448 | |
449 ExtensionAPIPermissionSet apis; | |
450 std::set_difference(set1_safe->apis().begin(), set1_safe->apis().end(), | |
451 set2_safe->apis().begin(), set2_safe->apis().end(), | |
452 std::insert_iterator<ExtensionAPIPermissionSet>( | |
453 apis, apis.begin())); | |
454 | |
455 URLPatternSet explicit_hosts; | |
456 URLPatternSet::CreateDifference(set1_safe->explicit_hosts(), | |
457 set2_safe->explicit_hosts(), | |
458 &explicit_hosts); | |
459 | |
460 URLPatternSet scriptable_hosts; | |
461 URLPatternSet::CreateDifference(set1_safe->scriptable_hosts(), | |
462 set2_safe->scriptable_hosts(), | |
463 &scriptable_hosts); | |
464 | |
465 ExtensionOAuth2Scopes scopes; | |
466 std::set_difference(set1_safe->scopes().begin(), set1_safe->scopes().end(), | |
467 set2_safe->scopes().begin(), set2_safe->scopes().end(), | |
468 std::insert_iterator<ExtensionOAuth2Scopes>( | |
469 scopes, scopes.begin())); | |
470 | |
471 return new ExtensionPermissionSet( | |
472 apis, explicit_hosts, scriptable_hosts, scopes); | |
473 } | |
474 | |
475 // static | |
476 ExtensionPermissionSet* ExtensionPermissionSet::CreateIntersection( | |
477 const ExtensionPermissionSet* set1, | |
478 const ExtensionPermissionSet* set2) { | |
479 scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet(); | |
480 const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? empty : set1; | |
481 const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? empty : set2; | |
482 | |
483 ExtensionAPIPermissionSet apis; | |
484 std::set_intersection(set1_safe->apis().begin(), set1_safe->apis().end(), | |
485 set2_safe->apis().begin(), set2_safe->apis().end(), | |
486 std::insert_iterator<ExtensionAPIPermissionSet>( | |
487 apis, apis.begin())); | |
488 URLPatternSet explicit_hosts; | |
489 URLPatternSet::CreateIntersection(set1_safe->explicit_hosts(), | |
490 set2_safe->explicit_hosts(), | |
491 &explicit_hosts); | |
492 | |
493 URLPatternSet scriptable_hosts; | |
494 URLPatternSet::CreateIntersection(set1_safe->scriptable_hosts(), | |
495 set2_safe->scriptable_hosts(), | |
496 &scriptable_hosts); | |
497 | |
498 ExtensionOAuth2Scopes scopes; | |
499 std::set_intersection(set1_safe->scopes().begin(), set1_safe->scopes().end(), | |
500 set2_safe->scopes().begin(), set2_safe->scopes().end(), | |
501 std::insert_iterator<ExtensionOAuth2Scopes>( | |
502 scopes, scopes.begin())); | |
503 | |
504 return new ExtensionPermissionSet( | |
505 apis, explicit_hosts, scriptable_hosts, scopes); | |
506 } | |
507 | |
508 // static | |
509 ExtensionPermissionSet* ExtensionPermissionSet::CreateUnion( | |
510 const ExtensionPermissionSet* set1, | |
511 const ExtensionPermissionSet* set2) { | |
512 scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet(); | |
513 const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? empty : set1; | |
514 const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? empty : set2; | |
515 | |
516 ExtensionAPIPermissionSet apis; | |
517 std::set_union(set1_safe->apis().begin(), set1_safe->apis().end(), | |
518 set2_safe->apis().begin(), set2_safe->apis().end(), | |
519 std::insert_iterator<ExtensionAPIPermissionSet>( | |
520 apis, apis.begin())); | |
521 | |
522 URLPatternSet explicit_hosts; | |
523 URLPatternSet::CreateUnion(set1_safe->explicit_hosts(), | |
524 set2_safe->explicit_hosts(), | |
525 &explicit_hosts); | |
526 | |
527 URLPatternSet scriptable_hosts; | |
528 URLPatternSet::CreateUnion(set1_safe->scriptable_hosts(), | |
529 set2_safe->scriptable_hosts(), | |
530 &scriptable_hosts); | |
531 | |
532 ExtensionOAuth2Scopes scopes; | |
533 std::set_union(set1_safe->scopes().begin(), set1_safe->scopes().end(), | |
534 set2_safe->scopes().begin(), set2_safe->scopes().end(), | |
535 std::insert_iterator<ExtensionOAuth2Scopes>( | |
536 scopes, scopes.begin())); | |
537 | |
538 return new ExtensionPermissionSet( | |
539 apis, explicit_hosts, scriptable_hosts, scopes); | |
540 } | |
541 | |
542 bool ExtensionPermissionSet::operator==( | |
543 const ExtensionPermissionSet& rhs) const { | |
544 return apis_ == rhs.apis_ && | |
545 scriptable_hosts_ == rhs.scriptable_hosts_ && | |
546 explicit_hosts_ == rhs.explicit_hosts_ && | |
547 scopes_ == rhs.scopes_; | |
548 } | |
549 | |
550 bool ExtensionPermissionSet::Contains(const ExtensionPermissionSet& set) const { | |
551 // Every set includes the empty set. | |
552 if (set.IsEmpty()) | |
553 return true; | |
554 | |
555 if (!std::includes(apis_.begin(), apis_.end(), | |
556 set.apis().begin(), set.apis().end())) | |
557 return false; | |
558 | |
559 if (!explicit_hosts().Contains(set.explicit_hosts())) | |
560 return false; | |
561 | |
562 if (!scriptable_hosts().Contains(set.scriptable_hosts())) | |
563 return false; | |
564 | |
565 if (!std::includes(scopes_.begin(), scopes_.end(), | |
566 set.scopes().begin(), set.scopes().end())) | |
567 return false; | |
568 | |
569 return true; | |
570 } | |
571 | |
572 std::set<std::string> ExtensionPermissionSet::GetAPIsAsStrings() const { | |
573 ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); | |
574 std::set<std::string> apis_str; | |
575 for (ExtensionAPIPermissionSet::const_iterator i = apis_.begin(); | |
576 i != apis_.end(); ++i) { | |
577 ExtensionAPIPermission* permission = info->GetByID(*i); | |
578 if (permission) | |
579 apis_str.insert(permission->name()); | |
580 } | |
581 return apis_str; | |
582 } | |
583 | |
584 std::set<std::string> ExtensionPermissionSet:: | |
585 GetAPIsWithAnyAccessAsStrings() const { | |
586 std::set<std::string> result = GetAPIsAsStrings(); | |
587 for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i) | |
588 result.insert(kNonPermissionModuleNames[i]); | |
589 for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) | |
590 result.insert(GetPermissionName(kNonPermissionFunctionNames[i])); | |
591 return result; | |
592 } | |
593 | |
594 bool ExtensionPermissionSet::HasAnyAccessToAPI( | |
595 const std::string& api_name) const { | |
596 if (HasAccessToFunction(api_name)) | |
597 return true; | |
598 | |
599 for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) { | |
600 if (api_name == GetPermissionName(kNonPermissionFunctionNames[i])) | |
601 return true; | |
602 } | |
603 | |
604 return false; | |
605 } | |
606 | |
607 std::set<std::string> | |
608 ExtensionPermissionSet::GetDistinctHostsForDisplay() const { | |
609 return GetDistinctHosts(effective_hosts_, true, true); | |
610 } | |
611 | |
612 ExtensionPermissionMessages | |
613 ExtensionPermissionSet::GetPermissionMessages() const { | |
614 ExtensionPermissionMessages messages; | |
615 | |
616 if (HasEffectiveFullAccess()) { | |
617 messages.push_back(ExtensionPermissionMessage( | |
618 ExtensionPermissionMessage::kFullAccess, | |
619 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS))); | |
620 return messages; | |
621 } | |
622 | |
623 if (HasEffectiveAccessToAllHosts()) { | |
624 messages.push_back(ExtensionPermissionMessage( | |
625 ExtensionPermissionMessage::kHostsAll, | |
626 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS))); | |
627 } else { | |
628 std::set<std::string> hosts = GetDistinctHostsForDisplay(); | |
629 if (!hosts.empty()) | |
630 messages.push_back(ExtensionPermissionMessage::CreateFromHostList(hosts)); | |
631 } | |
632 | |
633 std::set<ExtensionPermissionMessage> simple_msgs = | |
634 GetSimplePermissionMessages(); | |
635 messages.insert(messages.end(), simple_msgs.begin(), simple_msgs.end()); | |
636 | |
637 return messages; | |
638 } | |
639 | |
640 std::vector<string16> ExtensionPermissionSet::GetWarningMessages() const { | |
641 std::vector<string16> messages; | |
642 ExtensionPermissionMessages permissions = GetPermissionMessages(); | |
643 | |
644 bool audio_capture = false; | |
645 bool video_capture = false; | |
646 for (ExtensionPermissionMessages::const_iterator i = permissions.begin(); | |
647 i != permissions.end(); ++i) { | |
648 if (i->id() == ExtensionPermissionMessage::kAudioCapture) | |
649 audio_capture = true; | |
650 if (i->id() == ExtensionPermissionMessage::kVideoCapture) | |
651 video_capture = true; | |
652 } | |
653 | |
654 for (ExtensionPermissionMessages::const_iterator i = permissions.begin(); | |
655 i != permissions.end(); ++i) { | |
656 if (audio_capture && video_capture) { | |
657 if (i->id() == ExtensionPermissionMessage::kAudioCapture) { | |
658 messages.push_back(l10n_util::GetStringUTF16( | |
659 IDS_EXTENSION_PROMPT_WARNING_AUDIO_AND_VIDEO_CAPTURE)); | |
660 continue; | |
661 } else if (i->id() == ExtensionPermissionMessage::kVideoCapture) { | |
662 // The combined message will be pushed above. | |
663 continue; | |
664 } | |
665 } | |
666 | |
667 messages.push_back(i->message()); | |
668 } | |
669 | |
670 return messages; | |
671 } | |
672 | |
673 bool ExtensionPermissionSet::IsEmpty() const { | |
674 // Not default if any host permissions are present. | |
675 if (!(explicit_hosts().is_empty() && scriptable_hosts().is_empty())) | |
676 return false; | |
677 | |
678 // Or if it has no api permissions. | |
679 return apis().empty(); | |
680 } | |
681 | |
682 bool ExtensionPermissionSet::HasAPIPermission( | |
683 ExtensionAPIPermission::ID permission) const { | |
684 return apis().find(permission) != apis().end(); | |
685 } | |
686 | |
687 bool ExtensionPermissionSet::HasAccessToFunction( | |
688 const std::string& function_name) const { | |
689 // TODO(jstritar): Embed this information in each permission and add a method | |
690 // like GrantsAccess(function_name) to ExtensionAPIPermission. A "default" | |
691 // permission can then handle the modules and functions that everyone can | |
692 // access. | |
693 for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) { | |
694 if (function_name == kNonPermissionFunctionNames[i]) | |
695 return true; | |
696 } | |
697 | |
698 std::string permission_name = GetPermissionName(function_name); | |
699 ExtensionAPIPermission* permission = | |
700 ExtensionPermissionsInfo::GetInstance()->GetByName(permission_name); | |
701 if (permission && apis_.count(permission->id())) | |
702 return true; | |
703 | |
704 for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i) { | |
705 if (permission_name == kNonPermissionModuleNames[i]) { | |
706 return true; | |
707 } | |
708 } | |
709 | |
710 return false; | |
711 } | |
712 | |
713 bool ExtensionPermissionSet::HasExplicitAccessToOrigin( | |
714 const GURL& origin) const { | |
715 return explicit_hosts().MatchesURL(origin); | |
716 } | |
717 | |
718 bool ExtensionPermissionSet::HasScriptableAccessToURL( | |
719 const GURL& origin) const { | |
720 // We only need to check our host list to verify access. The host list should | |
721 // already reflect any special rules (such as chrome://favicon, all hosts | |
722 // access, etc.). | |
723 return scriptable_hosts().MatchesURL(origin); | |
724 } | |
725 | |
726 bool ExtensionPermissionSet::HasEffectiveAccessToAllHosts() const { | |
727 // There are two ways this set can have effective access to all hosts: | |
728 // 1) it has an <all_urls> URL pattern. | |
729 // 2) it has a named permission with implied full URL access. | |
730 for (URLPatternSet::const_iterator host = effective_hosts().begin(); | |
731 host != effective_hosts().end(); ++host) { | |
732 if (host->match_all_urls() || | |
733 (host->match_subdomains() && host->host().empty())) | |
734 return true; | |
735 } | |
736 | |
737 ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); | |
738 for (ExtensionAPIPermissionSet::const_iterator i = apis().begin(); | |
739 i != apis().end(); ++i) { | |
740 ExtensionAPIPermission* permission = info->GetByID(*i); | |
741 if (permission->implies_full_url_access()) | |
742 return true; | |
743 } | |
744 return false; | |
745 } | |
746 | |
747 bool ExtensionPermissionSet::HasEffectiveAccessToURL( | |
748 const GURL& url) const { | |
749 return effective_hosts().MatchesURL(url); | |
750 } | |
751 | |
752 bool ExtensionPermissionSet::HasEffectiveFullAccess() const { | |
753 ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); | |
754 for (ExtensionAPIPermissionSet::const_iterator i = apis().begin(); | |
755 i != apis().end(); ++i) { | |
756 ExtensionAPIPermission* permission = info->GetByID(*i); | |
757 if (permission->implies_full_access()) | |
758 return true; | |
759 } | |
760 return false; | |
761 } | |
762 | |
763 bool ExtensionPermissionSet::HasLessPrivilegesThan( | |
764 const ExtensionPermissionSet* permissions) const { | |
765 // Things can't get worse than native code access. | |
766 if (HasEffectiveFullAccess()) | |
767 return false; | |
768 | |
769 // Otherwise, it's a privilege increase if the new one has full access. | |
770 if (permissions->HasEffectiveFullAccess()) | |
771 return true; | |
772 | |
773 if (HasLessHostPrivilegesThan(permissions)) | |
774 return true; | |
775 | |
776 if (HasLessAPIPrivilegesThan(permissions)) | |
777 return true; | |
778 | |
779 if (HasLessScopesThan(permissions)) | |
780 return true; | |
781 | |
782 return false; | |
783 } | |
784 | |
785 ExtensionPermissionSet::~ExtensionPermissionSet() {} | |
786 | |
787 // static | |
788 std::set<std::string> ExtensionPermissionSet::GetDistinctHosts( | |
789 const URLPatternSet& host_patterns, | |
790 bool include_rcd, | |
791 bool exclude_file_scheme) { | |
792 // Use a vector to preserve order (also faster than a map on small sets). | |
793 // Each item is a host split into two parts: host without RCDs and | |
794 // current best RCD. | |
795 typedef std::vector<std::pair<std::string, std::string> > HostVector; | |
796 HostVector hosts_best_rcd; | |
797 for (URLPatternSet::const_iterator i = host_patterns.begin(); | |
798 i != host_patterns.end(); ++i) { | |
799 if (exclude_file_scheme && i->scheme() == chrome::kFileScheme) | |
800 continue; | |
801 | |
802 std::string host = i->host(); | |
803 | |
804 // Add the subdomain wildcard back to the host, if necessary. | |
805 if (i->match_subdomains()) | |
806 host = "*." + host; | |
807 | |
808 // If the host has an RCD, split it off so we can detect duplicates. | |
809 std::string rcd; | |
810 size_t reg_len = net::RegistryControlledDomainService::GetRegistryLength( | |
811 host, false); | |
812 if (reg_len && reg_len != std::string::npos) { | |
813 if (include_rcd) // else leave rcd empty | |
814 rcd = host.substr(host.size() - reg_len); | |
815 host = host.substr(0, host.size() - reg_len); | |
816 } | |
817 | |
818 // Check if we've already seen this host. | |
819 HostVector::iterator it = hosts_best_rcd.begin(); | |
820 for (; it != hosts_best_rcd.end(); ++it) { | |
821 if (it->first == host) | |
822 break; | |
823 } | |
824 // If this host was found, replace the RCD if this one is better. | |
825 if (it != hosts_best_rcd.end()) { | |
826 if (include_rcd && RcdBetterThan(rcd, it->second)) | |
827 it->second = rcd; | |
828 } else { // Previously unseen host, append it. | |
829 hosts_best_rcd.push_back(std::make_pair(host, rcd)); | |
830 } | |
831 } | |
832 | |
833 // Build up the final vector by concatenating hosts and RCDs. | |
834 std::set<std::string> distinct_hosts; | |
835 for (HostVector::iterator it = hosts_best_rcd.begin(); | |
836 it != hosts_best_rcd.end(); ++it) | |
837 distinct_hosts.insert(it->first + it->second); | |
838 return distinct_hosts; | |
839 } | |
840 | |
841 void ExtensionPermissionSet::InitImplicitExtensionPermissions( | |
842 const extensions::Extension* extension) { | |
843 // Add the implied permissions. | |
844 if (!extension->plugins().empty()) | |
845 apis_.insert(ExtensionAPIPermission::kPlugin); | |
846 | |
847 if (!extension->devtools_url().is_empty()) | |
848 apis_.insert(ExtensionAPIPermission::kDevtools); | |
849 | |
850 // The webRequest permission implies the internal version as well. | |
851 if (apis_.find(ExtensionAPIPermission::kWebRequest) != apis_.end()) | |
852 apis_.insert(ExtensionAPIPermission::kWebRequestInternal); | |
853 | |
854 // The fileBrowserHandler permission implies the internal version as well. | |
855 if (apis_.find(ExtensionAPIPermission::kFileBrowserHandler) != apis_.end()) | |
856 apis_.insert(ExtensionAPIPermission::kFileBrowserHandlerInternal); | |
857 | |
858 // Add the scriptable hosts. | |
859 for (UserScriptList::const_iterator content_script = | |
860 extension->content_scripts().begin(); | |
861 content_script != extension->content_scripts().end(); ++content_script) { | |
862 URLPatternSet::const_iterator pattern = | |
863 content_script->url_patterns().begin(); | |
864 for (; pattern != content_script->url_patterns().end(); ++pattern) | |
865 scriptable_hosts_.AddPattern(*pattern); | |
866 } | |
867 } | |
868 | |
869 void ExtensionPermissionSet::InitEffectiveHosts() { | |
870 effective_hosts_.ClearPatterns(); | |
871 | |
872 URLPatternSet::CreateUnion( | |
873 explicit_hosts(), scriptable_hosts(), &effective_hosts_); | |
874 } | |
875 | |
876 std::set<ExtensionPermissionMessage> | |
877 ExtensionPermissionSet::GetSimplePermissionMessages() const { | |
878 std::set<ExtensionPermissionMessage> messages; | |
879 ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); | |
880 for (ExtensionAPIPermissionSet::const_iterator i = apis_.begin(); | |
881 i != apis_.end(); ++i) { | |
882 DCHECK_GT(ExtensionPermissionMessage::kNone, | |
883 ExtensionPermissionMessage::kUnknown); | |
884 ExtensionAPIPermission* perm = info->GetByID(*i); | |
885 if (perm && perm->message_id() > ExtensionPermissionMessage::kNone) | |
886 messages.insert(perm->GetMessage()); | |
887 } | |
888 return messages; | |
889 } | |
890 | |
891 bool ExtensionPermissionSet::HasLessAPIPrivilegesThan( | |
892 const ExtensionPermissionSet* permissions) const { | |
893 if (permissions == NULL) | |
894 return false; | |
895 | |
896 std::set<ExtensionPermissionMessage> current_warnings = | |
897 GetSimplePermissionMessages(); | |
898 std::set<ExtensionPermissionMessage> new_warnings = | |
899 permissions->GetSimplePermissionMessages(); | |
900 std::set<ExtensionPermissionMessage> delta_warnings; | |
901 std::set_difference(new_warnings.begin(), new_warnings.end(), | |
902 current_warnings.begin(), current_warnings.end(), | |
903 std::inserter(delta_warnings, delta_warnings.begin())); | |
904 | |
905 // We have less privileges if there are additional warnings present. | |
906 return !delta_warnings.empty(); | |
907 } | |
908 | |
909 bool ExtensionPermissionSet::HasLessHostPrivilegesThan( | |
910 const ExtensionPermissionSet* permissions) const { | |
911 // If this permission set can access any host, then it can't be elevated. | |
912 if (HasEffectiveAccessToAllHosts()) | |
913 return false; | |
914 | |
915 // Likewise, if the other permission set has full host access, then it must be | |
916 // a privilege increase. | |
917 if (permissions->HasEffectiveAccessToAllHosts()) | |
918 return true; | |
919 | |
920 const URLPatternSet& old_list = effective_hosts(); | |
921 const URLPatternSet& new_list = permissions->effective_hosts(); | |
922 | |
923 // TODO(jstritar): This is overly conservative with respect to subdomains. | |
924 // For example, going from *.google.com to www.google.com will be | |
925 // considered an elevation, even though it is not (http://crbug.com/65337). | |
926 std::set<std::string> new_hosts_set(GetDistinctHosts(new_list, false, false)); | |
927 std::set<std::string> old_hosts_set(GetDistinctHosts(old_list, false, false)); | |
928 std::set<std::string> new_hosts_only; | |
929 | |
930 std::set_difference(new_hosts_set.begin(), new_hosts_set.end(), | |
931 old_hosts_set.begin(), old_hosts_set.end(), | |
932 std::inserter(new_hosts_only, new_hosts_only.begin())); | |
933 | |
934 return !new_hosts_only.empty(); | |
935 } | |
936 | |
937 bool ExtensionPermissionSet::HasLessScopesThan( | |
938 const ExtensionPermissionSet* permissions) const { | |
939 if (permissions == NULL) | |
940 return false; | |
941 | |
942 ExtensionOAuth2Scopes current_scopes = scopes(); | |
943 ExtensionOAuth2Scopes new_scopes = permissions->scopes(); | |
944 ExtensionOAuth2Scopes delta_scopes; | |
945 std::set_difference(new_scopes.begin(), new_scopes.end(), | |
946 current_scopes.begin(), current_scopes.end(), | |
947 std::inserter(delta_scopes, delta_scopes.begin())); | |
948 | |
949 // We have less privileges if there are additional scopes present. | |
950 return !delta_scopes.empty(); | |
951 } | |
OLD | NEW |