chrome_permission_message_provider.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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 "chrome/common/extensions/permissions/permission_set.h" 10#include "extensions/common/extensions_client.h" 11#include "extensions/common/permissions/permission_message.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 18ChromePermissionMessageProvider::ChromePermissionMessageProvider() { 19} 20 21ChromePermissionMessageProvider::~ChromePermissionMessageProvider() { 22} 23 24// static 25PermissionMessages 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 messages.insert(messages.end(), host_msgs.begin(), host_msgs.end()); 41 messages.insert(messages.end(), api_msgs.begin(), api_msgs.end()); 42 43 return messages; 44} 45 46// static 47std::vector<string16> ChromePermissionMessageProvider::GetWarningMessages( 48 const PermissionSet* permissions, 49 Manifest::Type extension_type) const { 50 std::vector<string16> message_strings; 51 PermissionMessages messages = 52 GetPermissionMessages(permissions, extension_type); 53 54 bool audio_capture = false; 55 bool video_capture = false; 56 bool media_galleries_read = false; 57 bool media_galleries_copy_to = false; 58 for (PermissionMessages::const_iterator i = messages.begin(); 59 i != messages.end(); ++i) { 60 switch (i->id()) { 61 case PermissionMessage::kAudioCapture: 62 audio_capture = true; 63 break; 64 case PermissionMessage::kVideoCapture: 65 video_capture = true; 66 break; 67 case PermissionMessage::kMediaGalleriesAllGalleriesRead: 68 media_galleries_read = true; 69 break; 70 case PermissionMessage::kMediaGalleriesAllGalleriesCopyTo: 71 media_galleries_copy_to = true; 72 break; 73 default: 74 break; 75 } 76 } 77 78 for (PermissionMessages::const_iterator i = messages.begin(); 79 i != messages.end(); ++i) { 80 int id = i->id(); 81 if (audio_capture && video_capture) { 82 if (id == PermissionMessage::kAudioCapture) { 83 message_strings.push_back(l10n_util::GetStringUTF16( 84 IDS_EXTENSION_PROMPT_WARNING_AUDIO_AND_VIDEO_CAPTURE)); 85 continue; 86 } else if (id == PermissionMessage::kVideoCapture) { 87 // The combined message will be pushed above. 88 continue; 89 } 90 } 91 if (media_galleries_read && media_galleries_copy_to) { 92 if (id == PermissionMessage::kMediaGalleriesAllGalleriesRead) { 93 message_strings.push_back(l10n_util::GetStringUTF16( 94 IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_READ_WRITE)); 95 continue; 96 } else if (id == PermissionMessage::kMediaGalleriesAllGalleriesCopyTo) { 97 // The combined message will be pushed above. 98 continue; 99 } 100 } 101 102 message_strings.push_back(i->message()); 103 } 104 105 return message_strings; 106} 107 108// static 109std::vector<string16> 110ChromePermissionMessageProvider::GetWarningMessagesDetails( 111 const PermissionSet* permissions, 112 Manifest::Type extension_type) const { 113 std::vector<string16> message_strings; 114 PermissionMessages messages = 115 GetPermissionMessages(permissions, extension_type); 116 117 for (PermissionMessages::const_iterator i = messages.begin(); 118 i != messages.end(); ++i) 119 message_strings.push_back(i->details()); 120 121 return message_strings; 122} 123 124// static 125bool ChromePermissionMessageProvider::IsPrivilegeIncrease( 126 const PermissionSet* old_permissions, 127 const PermissionSet* new_permissions, 128 Manifest::Type extension_type) const { 129 // Things can't get worse than native code access. 130 if (old_permissions->HasEffectiveFullAccess()) 131 return false; 132 133 // Otherwise, it's a privilege increase if the new one has full access. 134 if (new_permissions->HasEffectiveFullAccess()) 135 return true; 136 137 if (IsHostPrivilegeIncrease(old_permissions, new_permissions, extension_type)) 138 return true; 139 140 if (IsAPIPrivilegeIncrease(old_permissions, new_permissions)) 141 return true; 142 143 return false; 144} 145 146std::set<PermissionMessage> 147ChromePermissionMessageProvider::GetAPIPermissionMessages( 148 const PermissionSet* permissions) const { 149 std::set<PermissionMessage> messages; 150 for (APIPermissionSet::const_iterator permission_it = 151 permissions->apis().begin(); 152 permission_it != permissions->apis().end(); ++permission_it) { 153 if (permission_it->HasMessages()) { 154 PermissionMessages new_messages = permission_it->GetMessages(); 155 messages.insert(new_messages.begin(), new_messages.end()); 156 } 157 } 158 159 // A special hack: If kFileSystemWriteDirectory would be displayed, hide 160 // kFileSystemDirectory and and kFileSystemWrite as the write directory 161 // message implies the other two. 162 // TODO(sammc): Remove this. See http://crbug.com/284849. 163 std::set<PermissionMessage>::iterator write_directory_message = 164 messages.find(PermissionMessage( 165 PermissionMessage::kFileSystemWriteDirectory, string16())); 166 if (write_directory_message != messages.end()) { 167 messages.erase( 168 PermissionMessage(PermissionMessage::kFileSystemWrite, string16())); 169 messages.erase( 170 PermissionMessage(PermissionMessage::kFileSystemDirectory, string16())); 171 } 172 173 // A special hack: The warning message for declarativeWebRequest 174 // permissions speaks about blocking parts of pages, which is a 175 // subset of what the "<all_urls>" access allows. Therefore we 176 // display only the "<all_urls>" warning message if both permissions 177 // are required. 178 if (permissions->HasEffectiveAccessToAllHosts()) { 179 messages.erase( 180 PermissionMessage( 181 PermissionMessage::kDeclarativeWebRequest, string16())); 182 } 183 184 return messages; 185} 186 187std::set<PermissionMessage> 188ChromePermissionMessageProvider::GetHostPermissionMessages( 189 const PermissionSet* permissions, 190 Manifest::Type extension_type) const { 191 std::set<PermissionMessage> messages; 192 // Since platform apps always use isolated storage, they can't (silently) 193 // access user data on other domains, so there's no need to prompt. 194 // Note: this must remain consistent with IsHostPrivilegeIncrease. 195 // See crbug.com/255229. 196 if (extension_type == Manifest::TYPE_PLATFORM_APP) 197 return messages; 198 199 if (permissions->HasEffectiveAccessToAllHosts()) { 200 messages.insert(PermissionMessage( 201 PermissionMessage::kHostsAll, 202 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS))); 203 } else { 204 URLPatternSet regular_hosts; 205 ExtensionsClient::Get()->FilterHostPermissions( 206 permissions->effective_hosts(), ®ular_hosts, &messages); 207 208 std::set<std::string> hosts = 209 permission_message_util::GetDistinctHosts(regular_hosts, true, true); 210 if (!hosts.empty()) 211 messages.insert(permission_message_util::CreateFromHostList(hosts)); 212 } 213 return messages; 214} 215 216bool ChromePermissionMessageProvider::IsAPIPrivilegeIncrease( 217 const PermissionSet* old_permissions, 218 const PermissionSet* new_permissions) const { 219 if (new_permissions == NULL) 220 return false; 221 222 typedef std::set<PermissionMessage> PermissionMsgSet; 223 PermissionMsgSet old_warnings = GetAPIPermissionMessages(old_permissions); 224 PermissionMsgSet new_warnings = GetAPIPermissionMessages(new_permissions); 225 PermissionMsgSet delta_warnings = 226 base::STLSetDifference<PermissionMsgSet>(new_warnings, old_warnings); 227 228 // A special hack: kFileSystemWriteDirectory implies kFileSystemDirectory and 229 // kFileSystemWrite. 230 // TODO(sammc): Remove this. See http://crbug.com/284849. 231 if (old_warnings.find(PermissionMessage( 232 PermissionMessage::kFileSystemWriteDirectory, string16())) != 233 old_warnings.end()) { 234 delta_warnings.erase( 235 PermissionMessage(PermissionMessage::kFileSystemDirectory, string16())); 236 delta_warnings.erase( 237 PermissionMessage(PermissionMessage::kFileSystemWrite, string16())); 238 } 239 240 // We have less privileges if there are additional warnings present. 241 return !delta_warnings.empty(); 242} 243 244bool ChromePermissionMessageProvider::IsHostPrivilegeIncrease( 245 const PermissionSet* old_permissions, 246 const PermissionSet* new_permissions, 247 Manifest::Type extension_type) const { 248 // Platform apps host permission changes do not count as privilege increases. 249 // Note: this must remain consistent with GetHostPermissionMessages. 250 if (extension_type == Manifest::TYPE_PLATFORM_APP) 251 return false; 252 253 // If the old permission set can access any host, then it can't be elevated. 254 if (old_permissions->HasEffectiveAccessToAllHosts()) 255 return false; 256 257 // Likewise, if the new permission set has full host access, then it must be 258 // a privilege increase. 259 if (new_permissions->HasEffectiveAccessToAllHosts()) 260 return true; 261 262 const URLPatternSet& old_list = old_permissions->effective_hosts(); 263 const URLPatternSet& new_list = new_permissions->effective_hosts(); 264 265 // TODO(jstritar): This is overly conservative with respect to subdomains. 266 // For example, going from *.google.com to www.google.com will be 267 // considered an elevation, even though it is not (http://crbug.com/65337). 268 std::set<std::string> new_hosts_set( 269 permission_message_util::GetDistinctHosts(new_list, false, false)); 270 std::set<std::string> old_hosts_set( 271 permission_message_util::GetDistinctHosts(old_list, false, false)); 272 std::set<std::string> new_hosts_only = 273 base::STLSetDifference<std::set<std::string> >(new_hosts_set, 274 old_hosts_set); 275 276 return !new_hosts_only.empty(); 277} 278 279} // namespace extensions 280