Home | History | Annotate | Download | only in permissions
      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(), &regular_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