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 "browserAction", | |
48 "devtools", | |
49 "events", | |
50 "extension", | |
51 "i18n", | |
52 "omnibox", | |
53 "pageAction", | |
54 "pageActions", | |
55 "permissions", | |
56 "runtime", | |
57 "scriptBadge", | |
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 { kAppWindow, "appWindow" }, | |
292 { kAudioCapture, "audioCapture", kFlagNone, | |
293 IDS_EXTENSION_PROMPT_WARNING_AUDIO_CAPTURE, | |
294 ExtensionPermissionMessage::kAudioCapture }, | |
295 { kVideoCapture, "videoCapture", kFlagNone, | |
296 IDS_EXTENSION_PROMPT_WARNING_VIDEO_CAPTURE, | |
297 ExtensionPermissionMessage::kVideoCapture }, | |
298 }; | |
299 | |
300 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(PermissionsToRegister); ++i) { | |
301 const PermissionRegistration& pr = PermissionsToRegister[i]; | |
302 info->RegisterPermission( | |
303 pr.id, pr.name, pr.l10n_message_id, | |
304 pr.message_id ? pr.message_id : ExtensionPermissionMessage::kNone, | |
305 pr.flags); | |
306 } | |
307 | |
308 // Register aliases. | |
309 info->RegisterAlias("unlimitedStorage", kOldUnlimitedStoragePermission); | |
310 info->RegisterAlias("tabs", kWindowsPermission); | |
311 // TODO(mihaip): Should be removed for the M20 branch, see | |
312 // http://crbug.com/120447 for more details. | |
313 info->RegisterAlias("background", kTemporaryBackgroundAlias); | |
314 } | |
315 | |
316 // | |
317 // ExtensionPermissionsInfo | |
318 // | |
319 | |
320 // static | |
321 ExtensionPermissionsInfo* ExtensionPermissionsInfo::GetInstance() { | |
322 return Singleton<ExtensionPermissionsInfo>::get(); | |
323 } | |
324 | |
325 ExtensionAPIPermission* ExtensionPermissionsInfo::GetByID( | |
326 ExtensionAPIPermission::ID id) { | |
327 IDMap::iterator i = id_map_.find(id); | |
328 return (i == id_map_.end()) ? NULL : i->second; | |
329 } | |
330 | |
331 ExtensionAPIPermission* ExtensionPermissionsInfo::GetByName( | |
332 const std::string& name) { | |
333 NameMap::iterator i = name_map_.find(name); | |
334 return (i == name_map_.end()) ? NULL : i->second; | |
335 } | |
336 | |
337 ExtensionAPIPermissionSet ExtensionPermissionsInfo::GetAll() { | |
338 ExtensionAPIPermissionSet permissions; | |
339 for (IDMap::const_iterator i = id_map_.begin(); i != id_map_.end(); ++i) | |
340 permissions.insert(i->second->id()); | |
341 return permissions; | |
342 } | |
343 | |
344 ExtensionAPIPermissionSet ExtensionPermissionsInfo::GetAllByName( | |
345 const std::set<std::string>& permission_names) { | |
346 ExtensionAPIPermissionSet permissions; | |
347 for (std::set<std::string>::const_iterator i = permission_names.begin(); | |
348 i != permission_names.end(); ++i) { | |
349 ExtensionAPIPermission* permission = GetByName(*i); | |
350 if (permission) | |
351 permissions.insert(permission->id()); | |
352 } | |
353 return permissions; | |
354 } | |
355 | |
356 ExtensionPermissionsInfo::~ExtensionPermissionsInfo() { | |
357 for (IDMap::iterator i = id_map_.begin(); i != id_map_.end(); ++i) | |
358 delete i->second; | |
359 } | |
360 | |
361 ExtensionPermissionsInfo::ExtensionPermissionsInfo() | |
362 : hosted_app_permission_count_(0), | |
363 permission_count_(0) { | |
364 ExtensionAPIPermission::RegisterAllPermissions(this); | |
365 } | |
366 | |
367 void ExtensionPermissionsInfo::RegisterAlias( | |
368 const char* name, | |
369 const char* alias) { | |
370 DCHECK(name_map_.find(name) != name_map_.end()); | |
371 DCHECK(name_map_.find(alias) == name_map_.end()); | |
372 name_map_[alias] = name_map_[name]; | |
373 } | |
374 | |
375 ExtensionAPIPermission* ExtensionPermissionsInfo::RegisterPermission( | |
376 ExtensionAPIPermission::ID id, | |
377 const char* name, | |
378 int l10n_message_id, | |
379 ExtensionPermissionMessage::ID message_id, | |
380 int flags) { | |
381 DCHECK(id_map_.find(id) == id_map_.end()); | |
382 DCHECK(name_map_.find(name) == name_map_.end()); | |
383 | |
384 ExtensionAPIPermission* permission = new ExtensionAPIPermission( | |
385 id, name, l10n_message_id, message_id, flags); | |
386 | |
387 id_map_[id] = permission; | |
388 name_map_[name] = permission; | |
389 | |
390 permission_count_++; | |
391 | |
392 return permission; | |
393 } | |
394 | |
395 // | |
396 // ExtensionPermissionSet | |
397 // | |
398 | |
399 ExtensionPermissionSet::ExtensionPermissionSet() {} | |
400 | |
401 ExtensionPermissionSet::ExtensionPermissionSet( | |
402 const extensions::Extension* extension, | |
403 const ExtensionAPIPermissionSet& apis, | |
404 const URLPatternSet& explicit_hosts, | |
405 const ExtensionOAuth2Scopes& scopes) | |
406 : apis_(apis), | |
407 scopes_(scopes) { | |
408 DCHECK(extension); | |
409 AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_); | |
410 InitImplicitExtensionPermissions(extension); | |
411 InitEffectiveHosts(); | |
412 } | |
413 | |
414 ExtensionPermissionSet::ExtensionPermissionSet( | |
415 const ExtensionAPIPermissionSet& apis, | |
416 const URLPatternSet& explicit_hosts, | |
417 const URLPatternSet& scriptable_hosts) | |
418 : apis_(apis), | |
419 scriptable_hosts_(scriptable_hosts) { | |
420 AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_); | |
421 InitEffectiveHosts(); | |
422 } | |
423 | |
424 ExtensionPermissionSet::ExtensionPermissionSet( | |
425 const ExtensionAPIPermissionSet& apis, | |
426 const URLPatternSet& explicit_hosts, | |
427 const URLPatternSet& scriptable_hosts, | |
428 const ExtensionOAuth2Scopes& scopes) | |
429 : apis_(apis), | |
430 scriptable_hosts_(scriptable_hosts), | |
431 scopes_(scopes) { | |
432 AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_); | |
433 InitEffectiveHosts(); | |
434 } | |
435 | |
436 ExtensionPermissionSet::ExtensionPermissionSet( | |
437 const ExtensionOAuth2Scopes& scopes) | |
438 : scopes_(scopes) { | |
439 InitEffectiveHosts(); | |
440 } | |
441 | |
442 // static | |
443 ExtensionPermissionSet* ExtensionPermissionSet::CreateDifference( | |
444 const ExtensionPermissionSet* set1, | |
445 const ExtensionPermissionSet* set2) { | |
446 scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet(); | |
447 const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? empty : set1; | |
448 const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? empty : set2; | |
449 | |
450 ExtensionAPIPermissionSet apis; | |
451 std::set_difference(set1_safe->apis().begin(), set1_safe->apis().end(), | |
452 set2_safe->apis().begin(), set2_safe->apis().end(), | |
453 std::insert_iterator<ExtensionAPIPermissionSet>( | |
454 apis, apis.begin())); | |
455 | |
456 URLPatternSet explicit_hosts; | |
457 URLPatternSet::CreateDifference(set1_safe->explicit_hosts(), | |
458 set2_safe->explicit_hosts(), | |
459 &explicit_hosts); | |
460 | |
461 URLPatternSet scriptable_hosts; | |
462 URLPatternSet::CreateDifference(set1_safe->scriptable_hosts(), | |
463 set2_safe->scriptable_hosts(), | |
464 &scriptable_hosts); | |
465 | |
466 ExtensionOAuth2Scopes scopes; | |
467 std::set_difference(set1_safe->scopes().begin(), set1_safe->scopes().end(), | |
468 set2_safe->scopes().begin(), set2_safe->scopes().end(), | |
469 std::insert_iterator<ExtensionOAuth2Scopes>( | |
470 scopes, scopes.begin())); | |
471 | |
472 return new ExtensionPermissionSet( | |
473 apis, explicit_hosts, scriptable_hosts, scopes); | |
474 } | |
475 | |
476 // static | |
477 ExtensionPermissionSet* ExtensionPermissionSet::CreateIntersection( | |
478 const ExtensionPermissionSet* set1, | |
479 const ExtensionPermissionSet* set2) { | |
480 scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet(); | |
481 const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? empty : set1; | |
482 const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? empty : set2; | |
483 | |
484 ExtensionAPIPermissionSet apis; | |
485 std::set_intersection(set1_safe->apis().begin(), set1_safe->apis().end(), | |
486 set2_safe->apis().begin(), set2_safe->apis().end(), | |
487 std::insert_iterator<ExtensionAPIPermissionSet>( | |
488 apis, apis.begin())); | |
489 URLPatternSet explicit_hosts; | |
490 URLPatternSet::CreateIntersection(set1_safe->explicit_hosts(), | |
491 set2_safe->explicit_hosts(), | |
492 &explicit_hosts); | |
493 | |
494 URLPatternSet scriptable_hosts; | |
495 URLPatternSet::CreateIntersection(set1_safe->scriptable_hosts(), | |
496 set2_safe->scriptable_hosts(), | |
497 &scriptable_hosts); | |
498 | |
499 ExtensionOAuth2Scopes scopes; | |
500 std::set_intersection(set1_safe->scopes().begin(), set1_safe->scopes().end(), | |
501 set2_safe->scopes().begin(), set2_safe->scopes().end(), | |
502 std::insert_iterator<ExtensionOAuth2Scopes>( | |
503 scopes, scopes.begin())); | |
504 | |
505 return new ExtensionPermissionSet( | |
506 apis, explicit_hosts, scriptable_hosts, scopes); | |
507 } | |
508 | |
509 // static | |
510 ExtensionPermissionSet* ExtensionPermissionSet::CreateUnion( | |
511 const ExtensionPermissionSet* set1, | |
512 const ExtensionPermissionSet* set2) { | |
513 scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet(); | |
514 const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? empty : set1; | |
515 const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? empty : set2; | |
516 | |
517 ExtensionAPIPermissionSet apis; | |
518 std::set_union(set1_safe->apis().begin(), set1_safe->apis().end(), | |
519 set2_safe->apis().begin(), set2_safe->apis().end(), | |
520 std::insert_iterator<ExtensionAPIPermissionSet>( | |
521 apis, apis.begin())); | |
522 | |
523 URLPatternSet explicit_hosts; | |
524 URLPatternSet::CreateUnion(set1_safe->explicit_hosts(), | |
525 set2_safe->explicit_hosts(), | |
526 &explicit_hosts); | |
527 | |
528 URLPatternSet scriptable_hosts; | |
529 URLPatternSet::CreateUnion(set1_safe->scriptable_hosts(), | |
530 set2_safe->scriptable_hosts(), | |
531 &scriptable_hosts); | |
532 | |
533 ExtensionOAuth2Scopes scopes; | |
534 std::set_union(set1_safe->scopes().begin(), set1_safe->scopes().end(), | |
535 set2_safe->scopes().begin(), set2_safe->scopes().end(), | |
536 std::insert_iterator<ExtensionOAuth2Scopes>( | |
537 scopes, scopes.begin())); | |
538 | |
539 return new ExtensionPermissionSet( | |
540 apis, explicit_hosts, scriptable_hosts, scopes); | |
541 } | |
542 | |
543 bool ExtensionPermissionSet::operator==( | |
544 const ExtensionPermissionSet& rhs) const { | |
545 return apis_ == rhs.apis_ && | |
546 scriptable_hosts_ == rhs.scriptable_hosts_ && | |
547 explicit_hosts_ == rhs.explicit_hosts_ && | |
548 scopes_ == rhs.scopes_; | |
549 } | |
550 | |
551 bool ExtensionPermissionSet::Contains(const ExtensionPermissionSet& set) const { | |
552 // Every set includes the empty set. | |
553 if (set.IsEmpty()) | |
554 return true; | |
555 | |
556 if (!std::includes(apis_.begin(), apis_.end(), | |
557 set.apis().begin(), set.apis().end())) | |
558 return false; | |
559 | |
560 if (!explicit_hosts().Contains(set.explicit_hosts())) | |
561 return false; | |
562 | |
563 if (!scriptable_hosts().Contains(set.scriptable_hosts())) | |
564 return false; | |
565 | |
566 if (!std::includes(scopes_.begin(), scopes_.end(), | |
567 set.scopes().begin(), set.scopes().end())) | |
568 return false; | |
569 | |
570 return true; | |
571 } | |
572 | |
573 std::set<std::string> ExtensionPermissionSet::GetAPIsAsStrings() const { | |
574 ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); | |
575 std::set<std::string> apis_str; | |
576 for (ExtensionAPIPermissionSet::const_iterator i = apis_.begin(); | |
577 i != apis_.end(); ++i) { | |
578 ExtensionAPIPermission* permission = info->GetByID(*i); | |
579 if (permission) | |
580 apis_str.insert(permission->name()); | |
581 } | |
582 return apis_str; | |
583 } | |
584 | |
585 std::set<std::string> ExtensionPermissionSet:: | |
586 GetAPIsWithAnyAccessAsStrings() const { | |
587 std::set<std::string> result = GetAPIsAsStrings(); | |
588 for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i) | |
589 result.insert(kNonPermissionModuleNames[i]); | |
590 for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) | |
591 result.insert(GetPermissionName(kNonPermissionFunctionNames[i])); | |
592 return result; | |
593 } | |
594 | |
595 bool ExtensionPermissionSet::HasAnyAccessToAPI( | |
596 const std::string& api_name) const { | |
597 if (HasAccessToFunction(api_name)) | |
598 return true; | |
599 | |
600 for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) { | |
601 if (api_name == GetPermissionName(kNonPermissionFunctionNames[i])) | |
602 return true; | |
603 } | |
604 | |
605 return false; | |
606 } | |
607 | |
608 std::set<std::string> | |
609 ExtensionPermissionSet::GetDistinctHostsForDisplay() const { | |
610 return GetDistinctHosts(effective_hosts_, true, true); | |
611 } | |
612 | |
613 ExtensionPermissionMessages | |
614 ExtensionPermissionSet::GetPermissionMessages() const { | |
615 ExtensionPermissionMessages messages; | |
616 | |
617 if (HasEffectiveFullAccess()) { | |
618 messages.push_back(ExtensionPermissionMessage( | |
619 ExtensionPermissionMessage::kFullAccess, | |
620 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS))); | |
621 return messages; | |
622 } | |
623 | |
624 if (HasEffectiveAccessToAllHosts()) { | |
625 messages.push_back(ExtensionPermissionMessage( | |
626 ExtensionPermissionMessage::kHostsAll, | |
627 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS))); | |
628 } else { | |
629 std::set<std::string> hosts = GetDistinctHostsForDisplay(); | |
630 if (!hosts.empty()) | |
631 messages.push_back(ExtensionPermissionMessage::CreateFromHostList(hosts)); | |
632 } | |
633 | |
634 std::set<ExtensionPermissionMessage> simple_msgs = | |
635 GetSimplePermissionMessages(); | |
636 messages.insert(messages.end(), simple_msgs.begin(), simple_msgs.end()); | |
637 | |
638 return messages; | |
639 } | |
640 | |
641 std::vector<string16> ExtensionPermissionSet::GetWarningMessages() const { | |
642 std::vector<string16> messages; | |
643 ExtensionPermissionMessages permissions = GetPermissionMessages(); | |
644 | |
645 bool audio_capture = false; | |
646 bool video_capture = false; | |
647 for (ExtensionPermissionMessages::const_iterator i = permissions.begin(); | |
648 i != permissions.end(); ++i) { | |
649 if (i->id() == ExtensionPermissionMessage::kAudioCapture) | |
650 audio_capture = true; | |
651 if (i->id() == ExtensionPermissionMessage::kVideoCapture) | |
652 video_capture = true; | |
653 } | |
654 | |
655 for (ExtensionPermissionMessages::const_iterator i = permissions.begin(); | |
656 i != permissions.end(); ++i) { | |
657 if (audio_capture && video_capture) { | |
658 if (i->id() == ExtensionPermissionMessage::kAudioCapture) { | |
659 messages.push_back(l10n_util::GetStringUTF16( | |
660 IDS_EXTENSION_PROMPT_WARNING_AUDIO_AND_VIDEO_CAPTURE)); | |
661 continue; | |
662 } else if (i->id() == ExtensionPermissionMessage::kVideoCapture) { | |
663 // The combined message will be pushed above. | |
664 continue; | |
665 } | |
666 } | |
667 | |
668 messages.push_back(i->message()); | |
669 } | |
670 | |
671 return messages; | |
672 } | |
673 | |
674 bool ExtensionPermissionSet::IsEmpty() const { | |
675 // Not default if any host permissions are present. | |
676 if (!(explicit_hosts().is_empty() && scriptable_hosts().is_empty())) | |
677 return false; | |
678 | |
679 // Or if it has no api permissions. | |
680 return apis().empty(); | |
681 } | |
682 | |
683 bool ExtensionPermissionSet::HasAPIPermission( | |
684 ExtensionAPIPermission::ID permission) const { | |
685 return apis().find(permission) != apis().end(); | |
686 } | |
687 | |
688 bool ExtensionPermissionSet::HasAccessToFunction( | |
689 const std::string& function_name) const { | |
690 // TODO(jstritar): Embed this information in each permission and add a method | |
691 // like GrantsAccess(function_name) to ExtensionAPIPermission. A "default" | |
692 // permission can then handle the modules and functions that everyone can | |
693 // access. | |
694 for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) { | |
695 if (function_name == kNonPermissionFunctionNames[i]) | |
696 return true; | |
697 } | |
698 | |
699 std::string permission_name = GetPermissionName(function_name); | |
700 ExtensionAPIPermission* permission = | |
701 ExtensionPermissionsInfo::GetInstance()->GetByName(permission_name); | |
702 if (permission && apis_.count(permission->id())) | |
703 return true; | |
704 | |
705 for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i) { | |
706 if (permission_name == kNonPermissionModuleNames[i]) { | |
707 return true; | |
708 } | |
709 } | |
710 | |
711 return false; | |
712 } | |
713 | |
714 bool ExtensionPermissionSet::HasExplicitAccessToOrigin( | |
715 const GURL& origin) const { | |
716 return explicit_hosts().MatchesURL(origin); | |
717 } | |
718 | |
719 bool ExtensionPermissionSet::HasScriptableAccessToURL( | |
720 const GURL& origin) const { | |
721 // We only need to check our host list to verify access. The host list should | |
722 // already reflect any special rules (such as chrome://favicon, all hosts | |
723 // access, etc.). | |
724 return scriptable_hosts().MatchesURL(origin); | |
725 } | |
726 | |
727 bool ExtensionPermissionSet::HasEffectiveAccessToAllHosts() const { | |
728 // There are two ways this set can have effective access to all hosts: | |
729 // 1) it has an <all_urls> URL pattern. | |
730 // 2) it has a named permission with implied full URL access. | |
731 for (URLPatternSet::const_iterator host = effective_hosts().begin(); | |
732 host != effective_hosts().end(); ++host) { | |
733 if (host->match_all_urls() || | |
734 (host->match_subdomains() && host->host().empty())) | |
735 return true; | |
736 } | |
737 | |
738 ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); | |
739 for (ExtensionAPIPermissionSet::const_iterator i = apis().begin(); | |
740 i != apis().end(); ++i) { | |
741 ExtensionAPIPermission* permission = info->GetByID(*i); | |
742 if (permission->implies_full_url_access()) | |
743 return true; | |
744 } | |
745 return false; | |
746 } | |
747 | |
748 bool ExtensionPermissionSet::HasEffectiveAccessToURL( | |
749 const GURL& url) const { | |
750 return effective_hosts().MatchesURL(url); | |
751 } | |
752 | |
753 bool ExtensionPermissionSet::HasEffectiveFullAccess() const { | |
754 ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); | |
755 for (ExtensionAPIPermissionSet::const_iterator i = apis().begin(); | |
756 i != apis().end(); ++i) { | |
757 ExtensionAPIPermission* permission = info->GetByID(*i); | |
758 if (permission->implies_full_access()) | |
759 return true; | |
760 } | |
761 return false; | |
762 } | |
763 | |
764 bool ExtensionPermissionSet::HasLessPrivilegesThan( | |
765 const ExtensionPermissionSet* permissions) const { | |
766 // Things can't get worse than native code access. | |
767 if (HasEffectiveFullAccess()) | |
768 return false; | |
769 | |
770 // Otherwise, it's a privilege increase if the new one has full access. | |
771 if (permissions->HasEffectiveFullAccess()) | |
772 return true; | |
773 | |
774 if (HasLessHostPrivilegesThan(permissions)) | |
775 return true; | |
776 | |
777 if (HasLessAPIPrivilegesThan(permissions)) | |
778 return true; | |
779 | |
780 if (HasLessScopesThan(permissions)) | |
781 return true; | |
782 | |
783 return false; | |
784 } | |
785 | |
786 ExtensionPermissionSet::~ExtensionPermissionSet() {} | |
787 | |
788 // static | |
789 std::set<std::string> ExtensionPermissionSet::GetDistinctHosts( | |
790 const URLPatternSet& host_patterns, | |
791 bool include_rcd, | |
792 bool exclude_file_scheme) { | |
793 // Use a vector to preserve order (also faster than a map on small sets). | |
794 // Each item is a host split into two parts: host without RCDs and | |
795 // current best RCD. | |
796 typedef std::vector<std::pair<std::string, std::string> > HostVector; | |
797 HostVector hosts_best_rcd; | |
798 for (URLPatternSet::const_iterator i = host_patterns.begin(); | |
799 i != host_patterns.end(); ++i) { | |
800 if (exclude_file_scheme && i->scheme() == chrome::kFileScheme) | |
801 continue; | |
802 | |
803 std::string host = i->host(); | |
804 | |
805 // Add the subdomain wildcard back to the host, if necessary. | |
806 if (i->match_subdomains()) | |
807 host = "*." + host; | |
808 | |
809 // If the host has an RCD, split it off so we can detect duplicates. | |
810 std::string rcd; | |
811 size_t reg_len = net::RegistryControlledDomainService::GetRegistryLength( | |
812 host, false); | |
813 if (reg_len && reg_len != std::string::npos) { | |
814 if (include_rcd) // else leave rcd empty | |
815 rcd = host.substr(host.size() - reg_len); | |
816 host = host.substr(0, host.size() - reg_len); | |
817 } | |
818 | |
819 // Check if we've already seen this host. | |
820 HostVector::iterator it = hosts_best_rcd.begin(); | |
821 for (; it != hosts_best_rcd.end(); ++it) { | |
822 if (it->first == host) | |
823 break; | |
824 } | |
825 // If this host was found, replace the RCD if this one is better. | |
826 if (it != hosts_best_rcd.end()) { | |
827 if (include_rcd && RcdBetterThan(rcd, it->second)) | |
828 it->second = rcd; | |
829 } else { // Previously unseen host, append it. | |
830 hosts_best_rcd.push_back(std::make_pair(host, rcd)); | |
831 } | |
832 } | |
833 | |
834 // Build up the final vector by concatenating hosts and RCDs. | |
835 std::set<std::string> distinct_hosts; | |
836 for (HostVector::iterator it = hosts_best_rcd.begin(); | |
837 it != hosts_best_rcd.end(); ++it) | |
838 distinct_hosts.insert(it->first + it->second); | |
839 return distinct_hosts; | |
840 } | |
841 | |
842 void ExtensionPermissionSet::InitImplicitExtensionPermissions( | |
843 const extensions::Extension* extension) { | |
844 // Add the implied permissions. | |
845 if (!extension->plugins().empty()) | |
846 apis_.insert(ExtensionAPIPermission::kPlugin); | |
847 | |
848 if (!extension->devtools_url().is_empty()) | |
849 apis_.insert(ExtensionAPIPermission::kDevtools); | |
850 | |
851 // The webRequest permission implies the internal version as well. | |
852 if (apis_.find(ExtensionAPIPermission::kWebRequest) != apis_.end()) | |
853 apis_.insert(ExtensionAPIPermission::kWebRequestInternal); | |
854 | |
855 // The fileBrowserHandler permission implies the internal version as well. | |
856 if (apis_.find(ExtensionAPIPermission::kFileBrowserHandler) != apis_.end()) | |
857 apis_.insert(ExtensionAPIPermission::kFileBrowserHandlerInternal); | |
858 | |
859 // Add the scriptable hosts. | |
860 for (UserScriptList::const_iterator content_script = | |
861 extension->content_scripts().begin(); | |
862 content_script != extension->content_scripts().end(); ++content_script) { | |
863 URLPatternSet::const_iterator pattern = | |
864 content_script->url_patterns().begin(); | |
865 for (; pattern != content_script->url_patterns().end(); ++pattern) | |
866 scriptable_hosts_.AddPattern(*pattern); | |
867 } | |
868 } | |
869 | |
870 void ExtensionPermissionSet::InitEffectiveHosts() { | |
871 effective_hosts_.ClearPatterns(); | |
872 | |
873 URLPatternSet::CreateUnion( | |
874 explicit_hosts(), scriptable_hosts(), &effective_hosts_); | |
875 } | |
876 | |
877 std::set<ExtensionPermissionMessage> | |
878 ExtensionPermissionSet::GetSimplePermissionMessages() const { | |
879 std::set<ExtensionPermissionMessage> messages; | |
880 ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); | |
881 for (ExtensionAPIPermissionSet::const_iterator i = apis_.begin(); | |
882 i != apis_.end(); ++i) { | |
883 DCHECK_GT(ExtensionPermissionMessage::kNone, | |
884 ExtensionPermissionMessage::kUnknown); | |
885 ExtensionAPIPermission* perm = info->GetByID(*i); | |
886 if (perm && perm->message_id() > ExtensionPermissionMessage::kNone) | |
887 messages.insert(perm->GetMessage()); | |
888 } | |
889 return messages; | |
890 } | |
891 | |
892 bool ExtensionPermissionSet::HasLessAPIPrivilegesThan( | |
893 const ExtensionPermissionSet* permissions) const { | |
894 if (permissions == NULL) | |
895 return false; | |
896 | |
897 std::set<ExtensionPermissionMessage> current_warnings = | |
898 GetSimplePermissionMessages(); | |
899 std::set<ExtensionPermissionMessage> new_warnings = | |
900 permissions->GetSimplePermissionMessages(); | |
901 std::set<ExtensionPermissionMessage> delta_warnings; | |
902 std::set_difference(new_warnings.begin(), new_warnings.end(), | |
903 current_warnings.begin(), current_warnings.end(), | |
904 std::inserter(delta_warnings, delta_warnings.begin())); | |
905 | |
906 // We have less privileges if there are additional warnings present. | |
907 return !delta_warnings.empty(); | |
908 } | |
909 | |
910 bool ExtensionPermissionSet::HasLessHostPrivilegesThan( | |
911 const ExtensionPermissionSet* permissions) const { | |
912 // If this permission set can access any host, then it can't be elevated. | |
913 if (HasEffectiveAccessToAllHosts()) | |
914 return false; | |
915 | |
916 // Likewise, if the other permission set has full host access, then it must be | |
917 // a privilege increase. | |
918 if (permissions->HasEffectiveAccessToAllHosts()) | |
919 return true; | |
920 | |
921 const URLPatternSet& old_list = effective_hosts(); | |
922 const URLPatternSet& new_list = permissions->effective_hosts(); | |
923 | |
924 // TODO(jstritar): This is overly conservative with respect to subdomains. | |
925 // For example, going from *.google.com to www.google.com will be | |
926 // considered an elevation, even though it is not (http://crbug.com/65337). | |
927 std::set<std::string> new_hosts_set(GetDistinctHosts(new_list, false, false)); | |
928 std::set<std::string> old_hosts_set(GetDistinctHosts(old_list, false, false)); | |
929 std::set<std::string> new_hosts_only; | |
930 | |
931 std::set_difference(new_hosts_set.begin(), new_hosts_set.end(), | |
932 old_hosts_set.begin(), old_hosts_set.end(), | |
933 std::inserter(new_hosts_only, new_hosts_only.begin())); | |
934 | |
935 return !new_hosts_only.empty(); | |
936 } | |
937 | |
938 bool ExtensionPermissionSet::HasLessScopesThan( | |
939 const ExtensionPermissionSet* permissions) const { | |
940 if (permissions == NULL) | |
941 return false; | |
942 | |
943 ExtensionOAuth2Scopes current_scopes = scopes(); | |
944 ExtensionOAuth2Scopes new_scopes = permissions->scopes(); | |
945 ExtensionOAuth2Scopes delta_scopes; | |
946 std::set_difference(new_scopes.begin(), new_scopes.end(), | |
947 current_scopes.begin(), current_scopes.end(), | |
948 std::inserter(delta_scopes, delta_scopes.begin())); | |
949 | |
950 // We have less privileges if there are additional scopes present. | |
951 return !delta_scopes.empty(); | |
952 } | |
OLD | NEW |