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 18 ChromePermissionMessageProvider::ChromePermissionMessageProvider() { 19 } 20 21 ChromePermissionMessageProvider::~ChromePermissionMessageProvider() { 22 } 23 24 // static 25 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 51 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> 122 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 137 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> 162 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> 205 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> 221 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 249 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 279 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 297 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