diff options
25 files changed, 674 insertions, 38 deletions
diff --git a/chrome/browser/extensions/permission_message_combinations_unittest.cc b/chrome/browser/extensions/permission_message_combinations_unittest.cc index 03c2cd6..42e2a20 100644 --- a/chrome/browser/extensions/permission_message_combinations_unittest.cc +++ b/chrome/browser/extensions/permission_message_combinations_unittest.cc @@ -589,6 +589,173 @@ TEST_F(PermissionMessageCombinationsUnittest, HostsPermissionMessages) { ASSERT_TRUE(CheckManifestProducesHostPermissions()); } +// Check that permission messages are generated correctly for the sockets +// permission, which has host-like permission messages. +TEST_F(PermissionMessageCombinationsUnittest, SocketsPermissionMessages) { + CreateAndInstall( + "{" + " 'app': {" + " 'background': {" + " 'scripts': ['background.js']" + " }" + " }," + " 'sockets': {" + " 'udp': {'send': '*'}," + " }" + "}"); + ASSERT_TRUE(CheckManifestProducesPermissions( + "Exchange data with any computer on the local network or internet")); + ASSERT_TRUE(CheckManifestProducesHostPermissions()); + + CreateAndInstall( + "{" + " 'app': {" + " 'background': {" + " 'scripts': ['background.js']" + " }" + " }," + " 'sockets': {" + " 'udp': {'send': ':99'}," + " }" + "}"); + ASSERT_TRUE(CheckManifestProducesPermissions( + "Exchange data with any computer on the local network or internet")); + ASSERT_TRUE(CheckManifestProducesHostPermissions()); + + CreateAndInstall( + "{" + " 'app': {" + " 'background': {" + " 'scripts': ['background.js']" + " }" + " }," + " 'sockets': {" + " 'tcp': {'connect': '127.0.0.1:80'}," + " }" + "}"); + ASSERT_TRUE(CheckManifestProducesPermissions( + "Exchange data with the computer named 127.0.0.1")); + ASSERT_TRUE(CheckManifestProducesHostPermissions()); + + CreateAndInstall( + "{" + " 'app': {" + " 'background': {" + " 'scripts': ['background.js']" + " }" + " }," + " 'sockets': {" + " 'tcp': {'connect': 'www.example.com:23'}," + " }" + "}"); + ASSERT_TRUE(CheckManifestProducesPermissions( + "Exchange data with the computer named www.example.com")); + ASSERT_TRUE(CheckManifestProducesHostPermissions()); + + CreateAndInstall( + "{" + " 'app': {" + " 'background': {" + " 'scripts': ['background.js']" + " }" + " }," + " 'sockets': {" + " 'tcpServer': {'listen': '127.0.0.1:80'}" + " }" + "}"); + ASSERT_TRUE(CheckManifestProducesPermissions( + "Exchange data with the computer named 127.0.0.1")); + ASSERT_TRUE(CheckManifestProducesHostPermissions()); + + CreateAndInstall( + "{" + " 'app': {" + " 'background': {" + " 'scripts': ['background.js']" + " }" + " }," + " 'sockets': {" + " 'tcpServer': {'listen': ':8080'}" + " }" + "}"); + ASSERT_TRUE(CheckManifestProducesPermissions( + "Exchange data with any computer on the local network or internet")); + ASSERT_TRUE(CheckManifestProducesHostPermissions()); + + CreateAndInstall( + "{" + " 'app': {" + " 'background': {" + " 'scripts': ['background.js']" + " }" + " }," + " 'sockets': {" + " 'tcpServer': {" + " 'listen': [" + " '127.0.0.1:80'," + " 'www.google.com'," + " 'www.example.com:*'," + " 'www.foo.com:200'," + " 'www.bar.com:200'" + " ]" + " }" + " }" + "}"); + ASSERT_TRUE(CheckManifestProducesPermissions( + "Exchange data with the computers named: 127.0.0.1 www.bar.com " + "www.example.com www.foo.com www.google.com")); + ASSERT_TRUE(CheckManifestProducesHostPermissions()); + + CreateAndInstall( + "{" + " 'app': {" + " 'background': {" + " 'scripts': ['background.js']" + " }" + " }," + " 'sockets': {" + " 'tcp': {" + " 'connect': [" + " 'www.abc.com:*'," + " 'www.mywebsite.com:320'," + " 'www.freestuff.com'," + " 'www.foo.com:34'," + " 'www.test.com'" + " ]" + " }," + " 'tcpServer': {" + " 'listen': [" + " '127.0.0.1:80'," + " 'www.google.com'," + " 'www.example.com:*'," + " 'www.foo.com:200'," + " ]" + " }" + " }" + "}"); + ASSERT_TRUE(CheckManifestProducesPermissions( + "Exchange data with the computers named: 127.0.0.1 www.abc.com " + "www.example.com www.foo.com www.freestuff.com www.google.com " + "www.mywebsite.com www.test.com")); + ASSERT_TRUE(CheckManifestProducesHostPermissions()); + + CreateAndInstall( + "{" + " 'app': {" + " 'background': {" + " 'scripts': ['background.js']" + " }" + " }," + " 'sockets': {" + " 'tcp': {'send': '*:*'}," + " 'tcpServer': {'listen': '*:*'}," + " }" + "}"); + ASSERT_TRUE(CheckManifestProducesPermissions( + "Exchange data with any computer on the local network or internet")); + ASSERT_TRUE(CheckManifestProducesHostPermissions()); +} + // Test that hosted apps are not given any messages for host permissions. TEST_F(PermissionMessageCombinationsUnittest, PackagedAppsHaveNoHostPermissions) { diff --git a/chrome/common/extensions/chrome_extensions_client.cc b/chrome/common/extensions/chrome_extensions_client.cc index 4f2fbc2..d6c4bff 100644 --- a/chrome/common/extensions/chrome_extensions_client.cc +++ b/chrome/common/extensions/chrome_extensions_client.cc @@ -175,6 +175,9 @@ void ChromeExtensionsClient::FilterHostPermissions( const URLPatternSet& hosts, URLPatternSet* new_hosts, std::set<PermissionMessage>* messages) const { + // When editing this function, be sure to add the same functionality to + // FilterHostPermissions() below. + // TODO(sashab): Deprecate and remove this function. for (URLPatternSet::const_iterator i = hosts.begin(); i != hosts.end(); ++i) { // Filters out every URL pattern that matches chrome:// scheme. @@ -193,6 +196,30 @@ void ChromeExtensionsClient::FilterHostPermissions( } } +void ChromeExtensionsClient::FilterHostPermissions( + const URLPatternSet& hosts, + URLPatternSet* new_hosts, + PermissionIDSet* permissions) const { + // When editing this function, be sure to add the same functionality to + // FilterHostPermissions() above. + for (URLPatternSet::const_iterator i = hosts.begin(); i != hosts.end(); ++i) { + // Filters out every URL pattern that matches chrome:// scheme. + if (i->scheme() == content::kChromeUIScheme) { + // chrome://favicon is the only URL for chrome:// scheme that we + // want to support. We want to deprecate the "chrome" scheme. + // We should not add any additional "host" here. + if (GURL(chrome::kChromeUIFaviconURL).host() != i->host()) + continue; + // TODO(sashab): Add the rule + // kFavicon -> IDS_EXTENSION_PROMPT_WARNING_FAVICON + // to ChromePermissionMessageProvider. + permissions->insert(APIPermission::kFavicon); + } else { + new_hosts->AddPattern(*i); + } + } +} + void ChromeExtensionsClient::SetScriptingWhitelist( const ExtensionsClient::ScriptingWhitelist& whitelist) { scripting_whitelist_ = whitelist; diff --git a/chrome/common/extensions/chrome_extensions_client.h b/chrome/common/extensions/chrome_extensions_client.h index bdcf13a..2796c7e 100644 --- a/chrome/common/extensions/chrome_extensions_client.h +++ b/chrome/common/extensions/chrome_extensions_client.h @@ -35,6 +35,9 @@ class ChromeExtensionsClient : public ExtensionsClient { const URLPatternSet& hosts, URLPatternSet* new_hosts, std::set<PermissionMessage>* messages) const override; + void FilterHostPermissions(const URLPatternSet& hosts, + URLPatternSet* new_hosts, + PermissionIDSet* permissions) const override; void SetScriptingWhitelist(const ScriptingWhitelist& whitelist) override; const ScriptingWhitelist& GetScriptingWhitelist() const override; URLPatternSet GetPermittedChromeSchemeHosts( diff --git a/chrome/common/extensions/manifest_handlers/automation.cc b/chrome/common/extensions/manifest_handlers/automation.cc index 10287fb..6f4aae5 100644 --- a/chrome/common/extensions/manifest_handlers/automation.cc +++ b/chrome/common/extensions/manifest_handlers/automation.cc @@ -48,6 +48,8 @@ class AutomationManifestPermission : public ManifestPermission { std::string id() const override; + PermissionIDSet GetPermissions() const override; + bool HasMessages() const override; PermissionMessages GetMessages() const override; @@ -78,7 +80,46 @@ bool AutomationManifestPermission::HasMessages() const { return GetMessages().size() > 0; } +PermissionIDSet AutomationManifestPermission::GetPermissions() const { + // Meant to mimic the behavior of GetMessages(). + PermissionIDSet permissions; + if (automation_info_->desktop) { + // TODO(sashab): Add the rule + // kFullAccess -> IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS + // to ChromePermissionMessageProvider, when it exists. + permissions.insert(APIPermission::kFullAccess); + } else if (automation_info_->matches.MatchesAllURLs()) { + if (automation_info_->interact) { + permissions.insert(APIPermission::kHostsAll); + // TODO(sashab): Add the rule + // kHostsAll -> IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS + // to ChromePermissionMessageProvider, when it exists. + } else { + permissions.insert(APIPermission::kHostsAllReadOnly); + // TODO(sashab): Add the rule + // kHostsAllReadOnly -> IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS_READ_ONLY + // to ChromePermissionMessageProvider, when it exists. + } + } else { + // Check if we get any additional permissions from FilterHostPermissions. + URLPatternSet regular_hosts; + ExtensionsClient::Get()->FilterHostPermissions( + automation_info_->matches, ®ular_hosts, &permissions); + std::set<std::string> hosts = + permission_message_util::GetDistinctHosts(regular_hosts, true, true); + if (!hosts.empty()) { + permission_message_util::AddHostPermissions( + &permissions, hosts, automation_info_->interact + ? permission_message_util::kReadWrite + : permission_message_util::kReadOnly); + } + } + return permissions; +} + PermissionMessages AutomationManifestPermission::GetMessages() const { + // When modifying this function, be careful to modify the functionality in + // GetPermissions() above as well. PermissionMessages messages; if (automation_info_->desktop) { messages.push_back(PermissionMessage( diff --git a/chrome/common/extensions/manifest_handlers/ui_overrides_handler.cc b/chrome/common/extensions/manifest_handlers/ui_overrides_handler.cc index adf85f7..84120a2 100644 --- a/chrome/common/extensions/manifest_handlers/ui_overrides_handler.cc +++ b/chrome/common/extensions/manifest_handlers/ui_overrides_handler.cc @@ -34,11 +34,24 @@ class UIOverridesHandler::ManifestPermissionImpl : public ManifestPermission { std::string id() const override { return name(); } + PermissionIDSet GetPermissions() const override { + PermissionIDSet permissions; + if (override_bookmarks_ui_permission_) { + // TODO(sashab): Add rule to ChromePermissionMessageProvider: + // kOverrideBookmarksUI -> + // IDS_EXTENSION_PROMPT_WARNING_OVERRIDE_BOOKMARKS_UI + permissions.insert(APIPermission::kOverrideBookmarksUI); + } + return permissions; + } + bool HasMessages() const override { return override_bookmarks_ui_permission_; } PermissionMessages GetMessages() const override { + // When making changes to this function, be careful to modify + // GetPermissions() above to have the same behaviour. PermissionMessages result; if (override_bookmarks_ui_permission_) { result.push_back(PermissionMessage( diff --git a/extensions/common/api/bluetooth/bluetooth_manifest_permission.cc b/extensions/common/api/bluetooth/bluetooth_manifest_permission.cc index d56d2c1..9a10ccb 100644 --- a/extensions/common/api/bluetooth/bluetooth_manifest_permission.cc +++ b/extensions/common/api/bluetooth/bluetooth_manifest_permission.cc @@ -117,9 +117,24 @@ std::string BluetoothManifestPermission::name() const { std::string BluetoothManifestPermission::id() const { return name(); } +PermissionIDSet BluetoothManifestPermission::GetPermissions() const { + PermissionIDSet permissions; + // TODO(sashab): Add a rule to ChromePermissionMessageProvider: + // kBluetooth -> IDS_EXTENSION_PROMPT_WARNING_BLUETOOTH + permissions.insert(APIPermission::kBluetooth); + if (!uuids_.empty()) { + // TODO(sashab): Add a rule to ChromePermissionMessageProvider: + // kBluetoothDevices -> IDS_EXTENSION_PROMPT_WARNING_BLUETOOTH_DEVICES + permissions.insert(APIPermission::kBluetoothDevices); + } + return permissions; +} + bool BluetoothManifestPermission::HasMessages() const { return true; } PermissionMessages BluetoothManifestPermission::GetMessages() const { + // When modifying this function, be careful to also modify GetPermissions() + // above to have the same functionality. DCHECK(HasMessages()); PermissionMessages result; diff --git a/extensions/common/api/bluetooth/bluetooth_manifest_permission.h b/extensions/common/api/bluetooth/bluetooth_manifest_permission.h index eb71fa4b..c77eee5 100644 --- a/extensions/common/api/bluetooth/bluetooth_manifest_permission.h +++ b/extensions/common/api/bluetooth/bluetooth_manifest_permission.h @@ -43,6 +43,7 @@ class BluetoothManifestPermission : public ManifestPermission { // extensions::ManifestPermission overrides. std::string name() const override; std::string id() const override; + PermissionIDSet GetPermissions() const override; bool HasMessages() const override; PermissionMessages GetMessages() const override; bool FromValue(const base::Value* value) override; diff --git a/extensions/common/api/sockets/sockets_manifest_permission.cc b/extensions/common/api/sockets/sockets_manifest_permission.cc index f695494..d248d28 100644 --- a/extensions/common/api/sockets/sockets_manifest_permission.cc +++ b/extensions/common/api/sockets/sockets_manifest_permission.cc @@ -6,6 +6,7 @@ #include "base/memory/scoped_ptr.h" #include "base/stl_util.h" +#include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "extensions/common/api/extensions_manifest_types.h" @@ -158,21 +159,23 @@ std::string SocketsManifestPermission::name() const { std::string SocketsManifestPermission::id() const { return name(); } +PermissionIDSet SocketsManifestPermission::GetPermissions() const { + PermissionMessages messages; + PermissionIDSet ids; + AddAllHostMessages(messages, ids); + return ids; +} + bool SocketsManifestPermission::HasMessages() const { bool is_empty = permissions_.empty(); return !is_empty; } PermissionMessages SocketsManifestPermission::GetMessages() const { - // TODO(rpaquay): This function and callees is (almost) a copy/paste - // from extensions::SocketPermissiona. - PermissionMessages result; - if (!AddAnyHostMessage(result)) { - AddSpecificHostMessage(result); - AddSubdomainHostMessage(result); - } - AddNetworkListMessage(result); - return result; + PermissionMessages messages; + PermissionIDSet ids; + AddAllHostMessages(messages, ids); + return messages; } bool SocketsManifestPermission::FromValue(const base::Value* value) { @@ -260,13 +263,27 @@ void SocketsManifestPermission::AddPermission( permissions_.insert(entry); } -bool SocketsManifestPermission::AddAnyHostMessage( - PermissionMessages& messages) const { +void SocketsManifestPermission::AddAllHostMessages(PermissionMessages& messages, + PermissionIDSet& ids) const { + // TODO(rpaquay): This function and callees is (almost) a copy/paste from + // extensions::SocketPermission. + if (!AddAnyHostMessage(messages, ids)) { + AddSpecificHostMessage(messages, ids); + AddSubdomainHostMessage(messages, ids); + } + AddNetworkListMessage(messages, ids); +} + +bool SocketsManifestPermission::AddAnyHostMessage(PermissionMessages& messages, + PermissionIDSet& ids) const { for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); it != permissions_.end(); ++it) { if (it->IsAddressBoundType() && it->GetHostType() == SocketPermissionEntry::ANY_HOST) { + // TODO(sashab): Add a rule to ChromePermissionMessageProvider: + // kSocketAnyHost -> IDS_EXTENSION_PROMPT_WARNING_SOCKET_ANY_HOST + ids.insert(APIPermission::kSocketAnyHost); messages.push_back( PermissionMessage(PermissionMessage::kSocketAnyHost, l10n_util::GetStringUTF16( @@ -278,7 +295,8 @@ bool SocketsManifestPermission::AddAnyHostMessage( } void SocketsManifestPermission::AddSubdomainHostMessage( - PermissionMessages& messages) const { + PermissionMessages& messages, + PermissionIDSet& ids) const { std::set<base::string16> domains; for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); it != permissions_.end(); @@ -287,6 +305,8 @@ void SocketsManifestPermission::AddSubdomainHostMessage( domains.insert(base::UTF8ToUTF16(it->pattern().host)); } if (!domains.empty()) { + // TODO(sashab): This is not correct for all languages - add proper + // internationalization of this string for all plural states. int id = (domains.size() == 1) ? IDS_EXTENSION_PROMPT_WARNING_SOCKET_HOSTS_IN_DOMAIN : IDS_EXTENSION_PROMPT_WARNING_SOCKET_HOSTS_IN_DOMAINS; @@ -297,11 +317,22 @@ void SocketsManifestPermission::AddSubdomainHostMessage( JoinString( std::vector<base::string16>(domains.begin(), domains.end()), ' ')))); + // TODO(sashab): Add rules to ChromePermissionMessageProvider: + // kSocketDomainHostsSingular -> + // IDS_EXTENSION_PROMPT_WARNING_SOCKET_HOSTS_IN_DOMAIN + // kSocketDomainHostsPlural -> + // IDS_EXTENSION_PROMPT_WARNING_SOCKET_HOSTS_IN_DOMAINS + APIPermission::ID pid = (domains.size() == 1) + ? APIPermission::kSocketDomainHostsSingular + : APIPermission::kSocketDomainHostsPlural; + for (const auto& domain : domains) + ids.insert(pid, domain); } } void SocketsManifestPermission::AddSpecificHostMessage( - PermissionMessages& messages) const { + PermissionMessages& messages, + PermissionIDSet& ids) const { std::set<base::string16> hostnames; for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); it != permissions_.end(); @@ -310,6 +341,8 @@ void SocketsManifestPermission::AddSpecificHostMessage( hostnames.insert(base::UTF8ToUTF16(it->pattern().host)); } if (!hostnames.empty()) { + // TODO(sashab): This is not correct for all languages - add proper + // internationalization of this string for all plural states. int id = (hostnames.size() == 1) ? IDS_EXTENSION_PROMPT_WARNING_SOCKET_SPECIFIC_HOST : IDS_EXTENSION_PROMPT_WARNING_SOCKET_SPECIFIC_HOSTS; @@ -320,15 +353,29 @@ void SocketsManifestPermission::AddSpecificHostMessage( JoinString( std::vector<base::string16>(hostnames.begin(), hostnames.end()), ' ')))); + // TODO(sashab): Add rules to ChromePermissionMessageProvider: + // kSocketSpecificHostsSingular -> + // IDS_EXTENSION_PROMPT_WARNING_SOCKET_SPECIFIC_HOST + // kSocketSpecificHostsPlural -> + // IDS_EXTENSION_PROMPT_WARNING_SOCKET_SPECIFIC_HOSTS + APIPermission::ID pid = (hostnames.size() == 1) + ? APIPermission::kSocketSpecificHostsSingular + : APIPermission::kSocketSpecificHostsPlural; + for (const auto& hostname : hostnames) + ids.insert(pid, hostname); } } void SocketsManifestPermission::AddNetworkListMessage( - PermissionMessages& messages) const { + PermissionMessages& messages, + PermissionIDSet& ids) const { for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); it != permissions_.end(); ++it) { if (it->pattern().type == SocketPermissionRequest::NETWORK_STATE) { + // TODO(sashab): Add a rule to ChromePermissionMessageProvider: + // kNetworkState -> IDS_EXTENSION_PROMPT_WARNING_NETWORK_STATE + ids.insert(APIPermission::kNetworkState); messages.push_back( PermissionMessage(PermissionMessage::kNetworkState, l10n_util::GetStringUTF16( diff --git a/extensions/common/api/sockets/sockets_manifest_permission.h b/extensions/common/api/sockets/sockets_manifest_permission.h index d636f22..bf844da 100644 --- a/extensions/common/api/sockets/sockets_manifest_permission.h +++ b/extensions/common/api/sockets/sockets_manifest_permission.h @@ -42,6 +42,7 @@ class SocketsManifestPermission : public ManifestPermission { // extensions::ManifestPermission overrides. std::string name() const override; std::string id() const override; + PermissionIDSet GetPermissions() const override; bool HasMessages() const override; PermissionMessages GetMessages() const override; bool FromValue(const base::Value* value) override; @@ -53,10 +54,20 @@ class SocketsManifestPermission : public ManifestPermission { const SocketPermissionEntrySet& entries() const { return permissions_; } private: - bool AddAnyHostMessage(PermissionMessages& messages) const; - void AddSubdomainHostMessage(PermissionMessages& messages) const; - void AddSpecificHostMessage(PermissionMessages& messages) const; - void AddNetworkListMessage(PermissionMessages& messages) const; + // Add all host messages for this manifest permission into the given lists. + // TODO(sashab): Remove the |messages| argument from these methods, and remove + // the AddAllHostMessages() function (move all the logic into GetPermissions() + // above). + void AddAllHostMessages(PermissionMessages& messages, + PermissionIDSet& ids) const; + bool AddAnyHostMessage(PermissionMessages& messages, + PermissionIDSet& ids) const; + void AddSubdomainHostMessage(PermissionMessages& messages, + PermissionIDSet& ids) const; + void AddSpecificHostMessage(PermissionMessages& messages, + PermissionIDSet& ids) const; + void AddNetworkListMessage(PermissionMessages& messages, + PermissionIDSet& ids) const; SocketPermissionEntrySet permissions_; }; diff --git a/extensions/common/extensions_client.h b/extensions/common/extensions_client.h index f2292b1..bfe67ca 100644 --- a/extensions/common/extensions_client.h +++ b/extensions/common/extensions_client.h @@ -11,6 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/string_piece.h" +#include "extensions/common/permissions/api_permission_set.h" class GURL; @@ -66,11 +67,21 @@ class ExtensionsClient { // Takes the list of all hosts and filters out those with special // permission strings. Adds the regular hosts to |new_hosts|, // and adds the special permission messages to |messages|. + // TODO(sashab): Deprecate this in favour of FilterHostPermissions() below. virtual void FilterHostPermissions( const URLPatternSet& hosts, URLPatternSet* new_hosts, std::set<PermissionMessage>* messages) const = 0; + // Takes the list of all hosts and filters out those with special + // permission strings. Adds the regular hosts to |new_hosts|, + // and adds any additional permissions to |permissions|. + // TODO(sashab): Split this function in two: One to filter out ignored host + // permissions, and one to get permissions for the given hosts. + virtual void FilterHostPermissions(const URLPatternSet& hosts, + URLPatternSet* new_hosts, + PermissionIDSet* permissions) const = 0; + // Replaces the scripting whitelist with |whitelist|. Used in the renderer; // only used for testing in the browser process. virtual void SetScriptingWhitelist(const ScriptingWhitelist& whitelist) = 0; diff --git a/extensions/common/permissions/api_permission.h b/extensions/common/permissions/api_permission.h index 8d663f0..924412b 100644 --- a/extensions/common/permissions/api_permission.h +++ b/extensions/common/permissions/api_permission.h @@ -29,6 +29,13 @@ class ChromeAPIPermissions; // There is one instance per permission per loaded extension. class APIPermission { public: + // The IDs of all permissions available to apps. Add as many permissions here + // as needed to generate meaningful permission messages. Add the rules for the + // messages to ChromePermissionMessageProvider. + // Remove permissions from this list if they have no longer have a + // corresponding API permission and no permission message. + // TODO(sashab): Move this to a more central location, and rename it to + // PermissionID. enum ID { // Error codes. kInvalid = -2, @@ -195,6 +202,27 @@ class APIPermission { kSystemNetwork, kSystemInfoCpu, kSystemInfoMemory, + + // Permission message IDs that are not currently valid permissions on their + // own, but are needed by various manifest permissions to represent their + // permission message rule combinations. + // TODO(sashab): Move these in-line with the other permission IDs. + kBluetooth, + kBluetoothDevices, + kFavicon, + kFullAccess, + kHostReadOnly, + kHostReadWrite, + kHostsAll, + kHostsAllReadOnly, + kOverrideBookmarksUI, + kSocketAnyHost, + kSocketDomainHostsSingular, + kSocketDomainHostsPlural, + kSocketSpecificHostsSingular, + kSocketSpecificHostsPlural, + kNetworkState, + kEnumBoundary }; diff --git a/extensions/common/permissions/api_permission_set.cc b/extensions/common/permissions/api_permission_set.cc index d21ab43..38fb80e 100644 --- a/extensions/common/permissions/api_permission_set.cc +++ b/extensions/common/permissions/api_permission_set.cc @@ -183,4 +183,47 @@ void APIPermissionSet::AddImpliedPermissions() { } } +PermissionIDSet::PermissionIDSet() : permissions() { +} + +PermissionIDSet::~PermissionIDSet() { +} + +PermissionIDSet::PermissionIDSet(APIPermission::ID permission_one) { + insert(permission_one); +} + +PermissionIDSet::PermissionIDSet(APIPermission::ID permission_one, + APIPermission::ID permission_two) { + insert(permission_one); + insert(permission_two); +} + +PermissionIDSet::PermissionIDSet(APIPermission::ID permission_one, + APIPermission::ID permission_two, + APIPermission::ID permission_three) { + insert(permission_one); + insert(permission_two); + insert(permission_three); +} + +PermissionIDSet::PermissionIDSet(APIPermission::ID permission_one, + APIPermission::ID permission_two, + APIPermission::ID permission_three, + APIPermission::ID permission_four) { + insert(permission_one); + insert(permission_two); + insert(permission_three); + insert(permission_four); +} + +void PermissionIDSet::insert(APIPermission::ID permission) { + permissions.insert(PermissionID(permission, base::string16())); +} + +void PermissionIDSet::insert(APIPermission::ID permission, + base::string16 permission_detail) { + permissions.insert(PermissionID(permission, permission_detail)); +} + } // namespace extensions diff --git a/extensions/common/permissions/api_permission_set.h b/extensions/common/permissions/api_permission_set.h index 4db19f6..64eb7be 100644 --- a/extensions/common/permissions/api_permission_set.h +++ b/extensions/common/permissions/api_permission_set.h @@ -60,6 +60,55 @@ class APIPermissionSet : public BaseSetOperators<APIPermissionSet> { void AddImpliedPermissions(); }; +// A set of permissions for an app or extension. Used for passing around groups +// of permissions, such as required or optional permissions. Has convenience +// constructors so that it can be constructed inline. +// +// Each permission can also store a string, such as a hostname or device number, +// as a parameter that helps identify the permission. This parameter can then +// be used when the permission message is generated. For example, the permission +// kHostReadOnly might have the parameter "google.com", which means that the app +// or extension has the permission to read the host google.com. This parameter +// may then be included in the permission message when it is generated later. +// +// Example: +// // Create a PermissionIDSet. +// PermissionIDSet p(APIPermission::kBluetooth, APIPermission::kFavicon); +// // Add a permission to the set. +// p.insertPermission(APIPermission::kNetworkState); +// // Add a permission with a detail to the set. +// p.insertPermission(APIPermission::kHostReadOnly, +// base::ASCIIToUTF16("http://www.google.com")); +// +// TODO(sashab): Move this to its own file and rename it to PermissionSet after +// APIPermission is removed, the current PermissionSet is no longer used, and +// APIPermission::ID is the only type of Permission ID. +typedef std::pair<APIPermission::ID, base::string16> PermissionID; +class PermissionIDSet { + public: + PermissionIDSet(); + virtual ~PermissionIDSet(); + + // Convenience constructors for inline initialization. + PermissionIDSet(APIPermission::ID permission_one); + PermissionIDSet(APIPermission::ID permission_one, + APIPermission::ID permission_two); + PermissionIDSet(APIPermission::ID permission_one, + APIPermission::ID permission_two, + APIPermission::ID permission_three); + PermissionIDSet(APIPermission::ID permission_one, + APIPermission::ID permission_two, + APIPermission::ID permission_three, + APIPermission::ID permission_four); + + // Adds the given permission, and an optional permission detail, to the set. + void insert(APIPermission::ID permission); + void insert(APIPermission::ID permission, base::string16 permission_detail); + + private: + std::set<PermissionID> permissions; +}; + } // namespace extensions #endif // EXTENSIONS_COMMON_PERMISSIONS_API_PERMISSION_SET_H_ diff --git a/extensions/common/permissions/coalesced_permission_message.cc b/extensions/common/permissions/coalesced_permission_message.cc new file mode 100644 index 0000000..8239ebe --- /dev/null +++ b/extensions/common/permissions/coalesced_permission_message.cc @@ -0,0 +1,25 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/common/permissions/coalesced_permission_message.h" + +namespace extensions { + +CoalescedPermissionMessage::CoalescedPermissionMessage( + const base::string16& message, + const PermissionIDSet& permissions) + : message_(message), permissions_(permissions) { +} + +CoalescedPermissionMessage::CoalescedPermissionMessage( + const base::string16& message, + const PermissionIDSet& permissions, + const std::vector<base::string16>& submessages) + : message_(message), permissions_(permissions), submessages_(submessages) { +} + +CoalescedPermissionMessage::~CoalescedPermissionMessage() { +} + +} // namespace extensions diff --git a/extensions/common/permissions/coalesced_permission_message.h b/extensions/common/permissions/coalesced_permission_message.h new file mode 100644 index 0000000..7ff24c0 --- /dev/null +++ b/extensions/common/permissions/coalesced_permission_message.h @@ -0,0 +1,70 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_COMMON_PERMISSIONS_COALESCED_PERMISSION_MESSAGE_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_COALESCED_PERMISSION_MESSAGE_H_ + +#include <set> +#include <string> + +#include "base/memory/scoped_vector.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/api_permission_set.h" + +namespace extensions { + +// The new kind of Chrome app/extension permission messages. +// +// A CoalescedPermissionMessage is an immutable object that represents a single +// bullet in the list of an app or extension's permissions. It contains the +// localized permission message to display, as well as the set of permissions +// that contributed to that message (and should be revoked if this permission is +// revoked). It can also optionally contain a list of sub-messages which should +// appear as nested bullet points below the main one. +// +// |permissions| contains the permissions that are 'represented' by this +// message and should be revoked if this permission message is revoked. Note +// that other permissions could have contributed to the message, but these are +// the ones 'contained' in this message - if this set is taken for all +// CoalescedPermissionMessages, each permission will only be in at most one +// CoalescedPermissionMessage. +// +// Some permissions may contain nested messages, stored in |submessages|. These +// are appropriate to show as nested bullet points below the permission, +// collapsed if needed. For example, host permission messages may list all the +// sites the app has access to in |submessages|, with a summary message in +// |message|. +// +// TODO(sashab): Add a custom revoke action for each permission and nested +// permission message, registerable as a callback. +// TODO(sashab): Once the existing PermissionMessage is no longer used, rename +// this to PermissionMessage. +class CoalescedPermissionMessage { + public: + CoalescedPermissionMessage(const base::string16& message, + const PermissionIDSet& permissions); + CoalescedPermissionMessage(const base::string16& message, + const PermissionIDSet& permissions, + const std::vector<base::string16>& submessages); + virtual ~CoalescedPermissionMessage(); + + const base::string16& message() const { return message_; } + const PermissionIDSet& permissions() const { return permissions_; } + const std::vector<base::string16>& submessages() const { + return submessages_; + } + + private: + const base::string16 message_; + const PermissionIDSet permissions_; + const std::vector<base::string16> submessages_; + + DISALLOW_COPY_AND_ASSIGN(CoalescedPermissionMessage); +}; + +typedef std::vector<CoalescedPermissionMessage> CoalescedPermissionMessages; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_COALESCED_PERMISSION_MESSAGE_H_ diff --git a/extensions/common/permissions/manifest_permission.h b/extensions/common/permissions/manifest_permission.h index fdfb3f4..5c405b4 100644 --- a/extensions/common/permissions/manifest_permission.h +++ b/extensions/common/permissions/manifest_permission.h @@ -9,6 +9,8 @@ #include "base/memory/scoped_ptr.h" #include "base/pickle.h" +#include "extensions/common/permissions/api_permission_set.h" +#include "extensions/common/permissions/coalesced_permission_message.h" #include "extensions/common/permissions/permission_message.h" class PickleIterator; @@ -37,10 +39,20 @@ class ManifestPermission { // Same as name(), needed for compatibility with APIPermission. virtual std::string id() const = 0; + // The set of permissions this manifest entry has. These permissions are used + // by PermissionMessageProvider to generate meaningful permission messages + // for the app. + // TODO(sashab): Use this in PermissionMessageProvider. + virtual PermissionIDSet GetPermissions() const = 0; + // Returns true if this permission has any PermissionMessages. + // TODO(sashab): Deprecate this, using GetPermissions() above and adding + // message rules to ChromePermissionMessageProvider. virtual bool HasMessages() const = 0; // Returns the localized permission messages of this permission. + // TODO(sashab): Deprecate this, using GetPermissions() above and adding + // message rules to ChromePermissionMessageProvider. virtual PermissionMessages GetMessages() const = 0; // Parses the ManifestPermission from |value|. Returns false if error happens. diff --git a/extensions/common/permissions/manifest_permission_set_unittest.cc b/extensions/common/permissions/manifest_permission_set_unittest.cc index 7716c9a..595663b 100644 --- a/extensions/common/permissions/manifest_permission_set_unittest.cc +++ b/extensions/common/permissions/manifest_permission_set_unittest.cc @@ -21,6 +21,8 @@ class MockManifestPermission : public ManifestPermission { std::string id() const override { return name(); } + PermissionIDSet GetPermissions() const override { return PermissionIDSet(); } + bool HasMessages() const override { return false; } PermissionMessages GetMessages() const override { diff --git a/extensions/common/permissions/permission_message.h b/extensions/common/permissions/permission_message.h index 2515de0..fa65ab2 100644 --- a/extensions/common/permissions/permission_message.h +++ b/extensions/common/permissions/permission_message.h @@ -21,6 +21,8 @@ class PermissionMessage { public: // Do not reorder this enumeration. If you need to add a new enum, add it just // prior to kEnumBoundary. + // TODO(sashab): Deprecate these IDs - use whatever APIPermission::ID becomes + // instead. enum ID { kUnknown, kNone, diff --git a/extensions/common/permissions/permission_message_util.cc b/extensions/common/permissions/permission_message_util.cc index e4c1d09..ed487f8 100644 --- a/extensions/common/permissions/permission_message_util.cc +++ b/extensions/common/permissions/permission_message_util.cc @@ -8,6 +8,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" +#include "extensions/common/permissions/coalesced_permission_message.h" #include "extensions/common/permissions/permission_message.h" #include "extensions/common/permissions/permission_set.h" #include "extensions/common/url_pattern_set.h" @@ -15,10 +16,8 @@ #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "ui/base/l10n/l10n_util.h" #include "url/url_constants.h" -#include "base/strings/string_split.h" using extensions::PermissionMessage; -using extensions::PermissionSet; using extensions::URLPatternSet; namespace { @@ -40,6 +39,32 @@ bool RcdBetterThan(const std::string& a, const std::string& b) { namespace permission_message_util { +// The number of host messages supported. The first N - 1 of these messages are +// specific for the number of hosts; the last one is a catch-all for N or more +// hosts. +static const int kNumMessages = 4; + +std::vector<base::string16> GetHostListFromHosts( + const std::set<std::string>& hosts, + PermissionMessageProperties properties) { + int host_msg_id = hosts.size() < kNumMessages + ? IDS_EXTENSION_PROMPT_WARNING_HOST_AND_SUBDOMAIN + : IDS_EXTENSION_PROMPT_WARNING_HOST_AND_SUBDOMAIN_LIST; + std::vector<base::string16> host_list; + for (std::set<std::string>::const_iterator it = hosts.begin(); + it != hosts.end(); + ++it) { + std::string host = *it; + host_list.push_back( + host[0] == '*' && host[1] == '.' + ? l10n_util::GetStringFUTF16(host_msg_id, + base::UTF8ToUTF16(host.erase(0, 2))) + : base::UTF8ToUTF16(host)); + } + DCHECK(host_list.size()); + return host_list; +} + PermissionMessage CreateFromHostList(const std::set<std::string>& hosts, PermissionMessageProperties properties) { typedef std::pair<PermissionMessage::ID, int> MsgPair; @@ -64,25 +89,13 @@ PermissionMessage CreateFromHostList(const std::set<std::string>& hosts, COMPILE_ASSERT( arraysize(kReadWriteMessagesList) == arraysize(kReadOnlyMessagesList), message_lists_different_size); - static const int kNumMessages = arraysize(kReadWriteMessagesList); + COMPILE_ASSERT(kNumMessages == arraysize(kReadWriteMessagesList), + messages_array_different_size); + const MsgPair(&messages_list)[kNumMessages] = properties == kReadOnly ? kReadOnlyMessagesList : kReadWriteMessagesList; - - int host_msg_id = hosts.size() < kNumMessages - ? IDS_EXTENSION_PROMPT_WARNING_HOST_AND_SUBDOMAIN - : IDS_EXTENSION_PROMPT_WARNING_HOST_AND_SUBDOMAIN_LIST; - std::vector<base::string16> host_list; - for (std::set<std::string>::const_iterator it = hosts.begin(); - it != hosts.end(); - ++it) { - std::string host = *it; - host_list.push_back( - host[0] == '*' && host[1] == '.' - ? l10n_util::GetStringFUTF16(host_msg_id, - base::UTF8ToUTF16(host.erase(0, 2))) - : base::UTF8ToUTF16(host)); - } - DCHECK(host_list.size()); + std::vector<base::string16> host_list = + GetHostListFromHosts(hosts, properties); if (host_list.size() < kNumMessages) { return PermissionMessage( @@ -104,6 +117,24 @@ PermissionMessage CreateFromHostList(const std::set<std::string>& hosts, details); } +void AddHostPermissions(extensions::PermissionIDSet* permissions, + const std::set<std::string>& hosts, + PermissionMessageProperties properties) { + std::vector<base::string16> host_list = + GetHostListFromHosts(hosts, properties); + + // Create a separate permission for each host, and add it to the permissions + // list. + // TODO(sashab): Add coalescing rules for kHostReadOnly and kHostReadWrite + // to mimic the current behavior of CreateFromHostList() above. + for (const auto& host : host_list) { + permissions->insert(properties == kReadOnly + ? extensions::APIPermission::kHostReadOnly + : extensions::APIPermission::kHostReadWrite, + host); + } +} + std::set<std::string> GetDistinctHosts(const URLPatternSet& host_patterns, bool include_rcd, bool exclude_file_scheme) { diff --git a/extensions/common/permissions/permission_message_util.h b/extensions/common/permissions/permission_message_util.h index 1046bfb..995d226 100644 --- a/extensions/common/permissions/permission_message_util.h +++ b/extensions/common/permissions/permission_message_util.h @@ -7,10 +7,13 @@ #include <set> #include <string> +#include <vector> + +#include "base/strings/string16.h" namespace extensions { +class PermissionIDSet; class PermissionMessage; -class PermissionSet; class URLPatternSet; } @@ -18,13 +21,27 @@ namespace permission_message_util { enum PermissionMessageProperties { kReadOnly, kReadWrite }; +// Get a list of hosts to display in a permission message from the given list of +// hosts from the manifest. +// TODO(sashab): Merge this into AddHostPermissions() once CreateFromHostList() +// is deprecated. +std::vector<base::string16> GetHostListFromHosts( + const std::set<std::string>& hosts, + PermissionMessageProperties properties); + // Creates the corresponding permission message for a list of hosts. // The messages change depending on how many hosts are present, and whether // |read_only| is true. +// TODO(sashab): Deprecate this, prefer AddHostPermissions() instead. extensions::PermissionMessage CreateFromHostList( const std::set<std::string>& hosts, PermissionMessageProperties); +// Adds the appropriate permissions from given hosts to |permissions|. +void AddHostPermissions(extensions::PermissionIDSet* permissions, + const std::set<std::string>& hosts, + PermissionMessageProperties properties); + std::set<std::string> GetDistinctHosts( const extensions::URLPatternSet& host_patterns, bool include_rcd, diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index 21b17e0..e7c350c 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -207,6 +207,8 @@ 'common/permissions/api_permission_set.cc', 'common/permissions/api_permission_set.h', 'common/permissions/base_set_operators.h', + 'common/permissions/coalesced_permission_message.cc', + 'common/permissions/coalesced_permission_message.h', 'common/permissions/extensions_api_permissions.cc', 'common/permissions/extensions_api_permissions.h', 'common/permissions/manifest_permission.cc', diff --git a/extensions/shell/common/shell_extensions_client.cc b/extensions/shell/common/shell_extensions_client.cc index a598ded..3f7bc48 100644 --- a/extensions/shell/common/shell_extensions_client.cc +++ b/extensions/shell/common/shell_extensions_client.cc @@ -147,6 +147,13 @@ void ShellExtensionsClient::FilterHostPermissions( NOTIMPLEMENTED(); } +void ShellExtensionsClient::FilterHostPermissions( + const URLPatternSet& hosts, + URLPatternSet* new_hosts, + PermissionIDSet* permissions) const { + NOTIMPLEMENTED(); +} + void ShellExtensionsClient::SetScriptingWhitelist( const ScriptingWhitelist& whitelist) { scripting_whitelist_ = whitelist; diff --git a/extensions/shell/common/shell_extensions_client.h b/extensions/shell/common/shell_extensions_client.h index cbb72e1..3e5e9bb 100644 --- a/extensions/shell/common/shell_extensions_client.h +++ b/extensions/shell/common/shell_extensions_client.h @@ -31,6 +31,9 @@ class ShellExtensionsClient : public ExtensionsClient { const URLPatternSet& hosts, URLPatternSet* new_hosts, std::set<PermissionMessage>* messages) const override; + void FilterHostPermissions(const URLPatternSet& hosts, + URLPatternSet* new_hosts, + PermissionIDSet* permissions) const override; void SetScriptingWhitelist(const ScriptingWhitelist& whitelist) override; const ScriptingWhitelist& GetScriptingWhitelist() const override; URLPatternSet GetPermittedChromeSchemeHosts( diff --git a/extensions/test/test_extensions_client.cc b/extensions/test/test_extensions_client.cc index 1f44750..a2989e8 100644 --- a/extensions/test/test_extensions_client.cc +++ b/extensions/test/test_extensions_client.cc @@ -104,6 +104,12 @@ void TestExtensionsClient::FilterHostPermissions( std::set<PermissionMessage>* messages) const { } +void TestExtensionsClient::FilterHostPermissions( + const URLPatternSet& hosts, + URLPatternSet* new_hosts, + PermissionIDSet* permissions) const { +} + void TestExtensionsClient::SetScriptingWhitelist( const ExtensionsClient::ScriptingWhitelist& whitelist) { scripting_whitelist_ = whitelist; diff --git a/extensions/test/test_extensions_client.h b/extensions/test/test_extensions_client.h index 2c49577..05ebb7f 100644 --- a/extensions/test/test_extensions_client.h +++ b/extensions/test/test_extensions_client.h @@ -28,6 +28,9 @@ class TestExtensionsClient : public ExtensionsClient { const URLPatternSet& hosts, URLPatternSet* new_hosts, std::set<PermissionMessage>* messages) const override; + void FilterHostPermissions(const URLPatternSet& hosts, + URLPatternSet* new_hosts, + PermissionIDSet* permissions) const override; void SetScriptingWhitelist(const ScriptingWhitelist& whitelist) override; const ScriptingWhitelist& GetScriptingWhitelist() const override; URLPatternSet GetPermittedChromeSchemeHosts( |