chrome_permission_message_provider.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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 16namespace extensions { 17 18namespace { 19 20PermissionMessages::iterator FindMessageByID(PermissionMessages& messages, 21 int id) { 22 for (PermissionMessages::iterator it = messages.begin(); 23 it != messages.end(); ++it) { 24 if (it->id() == id) 25 return it; 26 } 27 28 return messages.end(); 29} 30 31} // namespace 32 33ChromePermissionMessageProvider::ChromePermissionMessageProvider() { 34} 35 36ChromePermissionMessageProvider::~ChromePermissionMessageProvider() { 37} 38 39// static 40PermissionMessages ChromePermissionMessageProvider::GetPermissionMessages( 41 const PermissionSet* permissions, 42 Manifest::Type extension_type) const { 43 PermissionMessages messages; 44 45 if (permissions->HasEffectiveFullAccess()) { 46 messages.push_back(PermissionMessage( 47 PermissionMessage::kFullAccess, 48 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS))); 49 return messages; 50 } 51 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()); 61 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) != 68 messages.end()) { 69 messages.erase(override_bookmarks_ui); 70 } 71 72 return messages; 73} 74 75// static 76std::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); 82 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) { 90 switch (i->id()) { 91 case PermissionMessage::kAudioCapture: 92 audio_capture = true; 93 break; 94 case PermissionMessage::kVideoCapture: 95 video_capture = true; 96 break; 97 case PermissionMessage::kMediaGalleriesAllGalleriesRead: 98 media_galleries_read = true; 99 break; 100 case PermissionMessage::kMediaGalleriesAllGalleriesCopyTo: 101 media_galleries_copy_to = true; 102 break; 103 case PermissionMessage::kMediaGalleriesAllGalleriesDelete: 104 media_galleries_delete = true; 105 break; 106 default: 107 break; 108 } 109 } 110 111 for (PermissionMessages::const_iterator i = messages.begin(); 112 i != messages.end(); ++i) { 113 int id = i->id(); 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)); 118 continue; 119 } else if (id == PermissionMessage::kVideoCapture) { 120 // The combined message will be pushed above. 121 continue; 122 } 123 } 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)); 131 continue; 132 } else if (id == PermissionMessage::kMediaGalleriesAllGalleriesCopyTo || 133 id == PermissionMessage::kMediaGalleriesAllGalleriesDelete) { 134 // The combined message will be pushed above. 135 continue; 136 } 137 } 138 139 message_strings.push_back(i->message()); 140 } 141 142 return message_strings; 143} 144 145// static 146std::vector<base::string16> 147ChromePermissionMessageProvider::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); 153 154 for (PermissionMessages::const_iterator i = messages.begin(); 155 i != messages.end(); ++i) 156 message_strings.push_back(i->details()); 157 158 return message_strings; 159} 160 161// static 162bool 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()) 168 return false; 169 170 // Otherwise, it's a privilege increase if the new one has full access. 171 if (new_permissions->HasEffectiveFullAccess()) 172 return true; 173 174 if (IsHostPrivilegeIncrease(old_permissions, new_permissions, extension_type)) 175 return true; 176 177 if (IsAPIPrivilegeIncrease(old_permissions, new_permissions)) 178 return true; 179 180 if (IsManifestPermissionPrivilegeIncrease(old_permissions, new_permissions)) 181 return true; 182 183 return false; 184} 185 186std::set<PermissionMessage> 187ChromePermissionMessageProvider::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()); 196 } 197 } 198 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()) { 207 messages.erase( 208 PermissionMessage(PermissionMessage::kFileSystemWrite, 209 base::string16())); 210 messages.erase( 211 PermissionMessage(PermissionMessage::kFileSystemDirectory, 212 base::string16())); 213 } 214 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 219 // are required. 220 if (permissions->HasEffectiveAccessToAllHosts()) { 221 messages.erase( 222 PermissionMessage( 223 PermissionMessage::kDeclarativeWebRequest, base::string16())); 224 } 225 226 return messages; 227} 228 229std::set<PermissionMessage> 230ChromePermissionMessageProvider::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(); 236 ++permission_it) { 237 if (permission_it->HasMessages()) { 238 PermissionMessages new_messages = permission_it->GetMessages(); 239 messages.insert(new_messages.begin(), new_messages.end()); 240 } 241 } 242 return messages; 243} 244 245std::set<PermissionMessage> 246ChromePermissionMessageProvider::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) 255 return messages; 256 257 if (permissions->HasEffectiveAccessToAllHosts()) { 258 messages.insert(PermissionMessage( 259 PermissionMessage::kHostsAll, 260 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS))); 261 } else { 262 URLPatternSet regular_hosts; 263 ExtensionsClient::Get()->FilterHostPermissions( 264 permissions->effective_hosts(), ®ular_hosts, &messages); 265 266 std::set<std::string> hosts = 267 permission_message_util::GetDistinctHosts(regular_hosts, true, true); 268 if (!hosts.empty()) 269 messages.insert(permission_message_util::CreateFromHostList(hosts)); 270 } 271 return messages; 272} 273 274bool ChromePermissionMessageProvider::IsAPIPrivilegeIncrease( 275 const PermissionSet* old_permissions, 276 const PermissionSet* new_permissions) const { 277 if (new_permissions == NULL) 278 return false; 279 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); 285 286 // A special hack: kFileSystemWriteDirectory implies kFileSystemDirectory and 287 // kFileSystemWrite. 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, 294 base::string16())); 295 delta_warnings.erase( 296 PermissionMessage(PermissionMessage::kFileSystemWrite, 297 base::string16())); 298 } 299 300 // It is a privilege increase if there are additional warnings present. 301 return !delta_warnings.empty(); 302} 303 304bool ChromePermissionMessageProvider::IsManifestPermissionPrivilegeIncrease( 305 const PermissionSet* old_permissions, 306 const PermissionSet* new_permissions) const { 307 if (new_permissions == NULL) 308 return false; 309 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); 317 318 // It is a privilege increase if there are additional warnings present. 319 return !delta_warnings.empty(); 320} 321 322bool 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) 329 return false; 330 331 // If the old permission set can access any host, then it can't be elevated. 332 if (old_permissions->HasEffectiveAccessToAllHosts()) 333 return false; 334 335 // Likewise, if the new permission set has full host access, then it must be 336 // a privilege increase. 337 if (new_permissions->HasEffectiveAccessToAllHosts()) 338 return true; 339 340 const URLPatternSet& old_list = old_permissions->effective_hosts(); 341 const URLPatternSet& new_list = new_permissions->effective_hosts(); 342 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, 352 old_hosts_set); 353 354 return !new_hosts_only.empty(); 355} 356 357} // namespace extensions 358