diff options
Diffstat (limited to 'extensions')
27 files changed, 1880 insertions, 10 deletions
diff --git a/extensions/browser/DEPS b/extensions/browser/DEPS index 57b1dfc..0060709 100644 --- a/extensions/browser/DEPS +++ b/extensions/browser/DEPS @@ -20,7 +20,6 @@ include_rules = [ "+chrome/browser/renderer_host/chrome_render_message_filter.h", "+chrome/common/extensions/api/sockets/sockets_manifest_data.h", "+chrome/common/extensions/features/feature_channel.h", - "+chrome/common/extensions/permissions", "+grit/generated_resources.h", ] diff --git a/extensions/browser/api/socket/socket_api.cc b/extensions/browser/api/socket/socket_api.cc index 34a5517..dbe7a87 100644 --- a/extensions/browser/api/socket/socket_api.cc +++ b/extensions/browser/api/socket/socket_api.cc @@ -9,7 +9,6 @@ #include "base/bind.h" #include "base/containers/hash_tables.h" #include "chrome/browser/extensions/api/dns/host_resolver_wrapper.h" -#include "chrome/common/extensions/permissions/socket_permission.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/resource_context.h" #include "extensions/browser/api/socket/socket.h" @@ -18,6 +17,7 @@ #include "extensions/browser/extension_system.h" #include "extensions/common/extension.h" #include "extensions/common/permissions/permissions_data.h" +#include "extensions/common/permissions/socket_permission.h" #include "net/base/host_port_pair.h" #include "net/base/io_buffer.h" #include "net/base/ip_endpoint.h" diff --git a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc index 6adc0fc..4a0c8e6e 100644 --- a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc +++ b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc @@ -5,11 +5,11 @@ #include "extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h" #include "chrome/common/extensions/api/sockets/sockets_manifest_data.h" -#include "chrome/common/extensions/permissions/socket_permission.h" #include "content/public/common/socket_permission_request.h" #include "extensions/browser/api/socket/tcp_socket.h" #include "extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h" #include "extensions/common/permissions/permissions_data.h" +#include "extensions/common/permissions/socket_permission.h" #include "net/base/net_errors.h" using content::SocketPermissionRequest; diff --git a/extensions/common/DEPS b/extensions/common/DEPS index c75b833..12baab5 100644 --- a/extensions/common/DEPS +++ b/extensions/common/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+device/usb", "+net", "+third_party/re2", @@ -9,8 +10,6 @@ include_rules = [ # # TODO(benwells): Remove these. http://crbug.com/162530 "+chrome/common/extensions/api/generated_schemas.h", - "+chrome/common/extensions/api/messaging/message.h", - "+chrome/common/extensions/permissions", "+grit/chromium_strings.h", "+grit/common_resources.h", "+grit/extensions_api_resources.h", diff --git a/extensions/common/api/messaging/message.h b/extensions/common/api/messaging/message.h new file mode 100644 index 0000000..64ecb82 --- /dev/null +++ b/extensions/common/api/messaging/message.h @@ -0,0 +1,22 @@ +// Copyright 2014 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_API_MESSAGING_MESSAGE_H_ +#define EXTENSIONS_COMMON_API_MESSAGING_MESSAGE_H_ + +namespace extensions { + +// A message consists of both the data itself as well as a user gesture state. +struct Message { + std::string data; + bool user_gesture; + + Message() : data(), user_gesture(false) {} + Message(const std::string& data, bool user_gesture) + : data(data), user_gesture(user_gesture) {} +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_API_MESSAGING_MESSAGE_H_ diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h index f5c2817..b7ce195 100644 --- a/extensions/common/extension_messages.h +++ b/extensions/common/extension_messages.h @@ -10,16 +10,16 @@ #include "base/memory/shared_memory.h" #include "base/values.h" -#include "chrome/common/extensions/api/messaging/message.h" -#include "chrome/common/extensions/permissions/media_galleries_permission_data.h" -#include "chrome/common/extensions/permissions/socket_permission_data.h" -#include "chrome/common/extensions/permissions/usb_device_permission_data.h" #include "content/public/common/common_param_traits.h" #include "content/public/common/socket_permission_request.h" +#include "extensions/common/api/messaging/message.h" #include "extensions/common/draggable_region.h" #include "extensions/common/extension.h" #include "extensions/common/extensions_client.h" +#include "extensions/common/permissions/media_galleries_permission_data.h" #include "extensions/common/permissions/permission_set.h" +#include "extensions/common/permissions/socket_permission_data.h" +#include "extensions/common/permissions/usb_device_permission_data.h" #include "extensions/common/url_pattern.h" #include "extensions/common/url_pattern_set.h" #include "extensions/common/view_type.h" diff --git a/extensions/common/permissions/media_galleries_permission.cc b/extensions/common/permissions/media_galleries_permission.cc new file mode 100644 index 0000000..baf7162 --- /dev/null +++ b/extensions/common/permissions/media_galleries_permission.cc @@ -0,0 +1,157 @@ +// Copyright 2014 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/media_galleries_permission.h" + +#include <set> +#include <string> + +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "extensions/common/permissions/permissions_info.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +namespace { + +// copyTo permission requires delete permission as a prerequisite. +// delete permission requires read permission as a prerequisite. +bool IsValidPermissionSet(bool has_read, bool has_copy_to, bool has_delete, + std::string* error) { + if (has_copy_to) { + if (has_read && has_delete) + return true; + if (error) + *error = "copyTo permission requires read and delete permissions"; + return false; + } + if (has_delete) { + if (has_read) + return true; + if (error) + *error = "delete permission requires read permission"; + return false; + } + return true; +} + +} // namespace + +namespace extensions { + +const char MediaGalleriesPermission::kAllAutoDetectedPermission[] = + "allAutoDetected"; +const char MediaGalleriesPermission::kScanPermission[] = "scan"; +const char MediaGalleriesPermission::kReadPermission[] = "read"; +const char MediaGalleriesPermission::kCopyToPermission[] = "copyTo"; +const char MediaGalleriesPermission::kDeletePermission[] = "delete"; + +MediaGalleriesPermission::MediaGalleriesPermission( + const APIPermissionInfo* info) + : SetDisjunctionPermission<MediaGalleriesPermissionData, + MediaGalleriesPermission>(info) { +} + +MediaGalleriesPermission::~MediaGalleriesPermission() { +} + +bool MediaGalleriesPermission::FromValue(const base::Value* value, + std::string* error) { + if (!SetDisjunctionPermission<MediaGalleriesPermissionData, + MediaGalleriesPermission>::FromValue(value, + error)) { + return false; + } + + bool has_read = false; + bool has_copy_to = false; + bool has_delete = false; + for (std::set<MediaGalleriesPermissionData>::const_iterator it = + data_set_.begin(); it != data_set_.end(); ++it) { + if (it->permission() == kAllAutoDetectedPermission || + it->permission() == kScanPermission) { + continue; + } + if (it->permission() == kReadPermission) { + has_read = true; + continue; + } + if (it->permission() == kCopyToPermission) { + has_copy_to = true; + continue; + } + if (it->permission() == kDeletePermission) { + has_delete = true; + continue; + } + + // No other permissions, so reaching this means + // MediaGalleriesPermissionData is probably out of sync in some way. + // Fail so developers notice this. + NOTREACHED(); + return false; + } + + return IsValidPermissionSet(has_read, has_copy_to, has_delete, error); +} + +PermissionMessages MediaGalleriesPermission::GetMessages() const { + DCHECK(HasMessages()); + PermissionMessages result; + + bool has_all_auto_detected = false; + bool has_read = false; + bool has_copy_to = false; + bool has_delete = false; + + for (std::set<MediaGalleriesPermissionData>::const_iterator it = + data_set_.begin(); it != data_set_.end(); ++it) { + if (it->permission() == kAllAutoDetectedPermission) + has_all_auto_detected = true; + else if (it->permission() == kReadPermission) + has_read = true; + else if (it->permission() == kCopyToPermission) + has_copy_to = true; + else if (it->permission() == kDeletePermission) + has_delete = true; + } + + if (!IsValidPermissionSet(has_read, has_copy_to, has_delete, NULL)) { + NOTREACHED(); + return result; + } + + // If |has_all_auto_detected| is false, then Chrome will prompt the user at + // runtime when the extension call the getMediaGalleries API. + if (!has_all_auto_detected) + return result; + // No access permission case. + if (!has_read) + return result; + + // Separate PermissionMessage IDs for read, copyTo, and delete. Otherwise an + // extension can silently gain new access capabilities. + result.push_back(PermissionMessage( + PermissionMessage::kMediaGalleriesAllGalleriesRead, + l10n_util::GetStringUTF16( + IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_READ))); + + // For copyTo and delete, the proper combined permission message will be + // derived in ChromePermissionMessageProvider::GetWarningMessages(), such + // that the user get 1 entry for all media galleries access permissions, + // rather than several separate entries. + if (has_copy_to) { + result.push_back(PermissionMessage( + PermissionMessage::kMediaGalleriesAllGalleriesCopyTo, + base::string16())); + } + if (has_delete) { + result.push_back(PermissionMessage( + PermissionMessage::kMediaGalleriesAllGalleriesDelete, + base::string16())); + } + return result; +} + +} // namespace extensions diff --git a/extensions/common/permissions/media_galleries_permission.h b/extensions/common/permissions/media_galleries_permission.h new file mode 100644 index 0000000..36b6dba --- /dev/null +++ b/extensions/common/permissions/media_galleries_permission.h @@ -0,0 +1,56 @@ +// Copyright 2014 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_MEDIA_GALLERIES_PERMISSION_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_MEDIA_GALLERIES_PERMISSION_H_ + +#include "base/basictypes.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/media_galleries_permission_data.h" +#include "extensions/common/permissions/set_disjunction_permission.h" + +namespace extensions { + +// Media Galleries permissions are as follows: +// <media-galleries-permission-pattern> +// := <access> | <access> 'allAutoDetected' | 'allAutoDetected' | +// <access> 'scan' | 'scan' +// <access> := 'read' | 'read' <access> | 'read' <secondary-access> +// <secondary-access> +// := 'delete' | 'delete' <secondary-access> | +// 'delete' <tertiary-access> +// <tertiary-access> +// := 'copyTo' | 'copyTo' <tertiary-access> +class MediaGalleriesPermission + : public SetDisjunctionPermission<MediaGalleriesPermissionData, + MediaGalleriesPermission> { + public: + struct CheckParam : public APIPermission::CheckParam { + explicit CheckParam(const std::string& permission) + : permission(permission) {} + const std::string permission; + }; + + explicit MediaGalleriesPermission(const APIPermissionInfo* info); + virtual ~MediaGalleriesPermission(); + + // SetDisjunctionPermission overrides. + // MediaGalleriesPermission does additional checks to make sure the + // permissions do not contain unknown values. + virtual bool FromValue(const base::Value* value, std::string* error) OVERRIDE; + + // APIPermission overrides. + virtual PermissionMessages GetMessages() const OVERRIDE; + + // Permission strings. + static const char kAllAutoDetectedPermission[]; + static const char kScanPermission[]; + static const char kReadPermission[]; + static const char kCopyToPermission[]; + static const char kDeletePermission[]; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_MEDIA_GALLERIES_PERMISSION_H_ diff --git a/extensions/common/permissions/media_galleries_permission_data.cc b/extensions/common/permissions/media_galleries_permission_data.cc new file mode 100644 index 0000000..d7e7937 --- /dev/null +++ b/extensions/common/permissions/media_galleries_permission_data.cc @@ -0,0 +1,62 @@ +// Copyright 2014 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/media_galleries_permission_data.h" + +#include "base/strings/string_util.h" +#include "base/values.h" +#include "extensions/common/permissions/media_galleries_permission.h" + +namespace extensions { + +MediaGalleriesPermissionData::MediaGalleriesPermissionData() { +} + +bool MediaGalleriesPermissionData::Check( + const APIPermission::CheckParam* param) const { + if (!param) + return false; + + const MediaGalleriesPermission::CheckParam& specific_param = + *static_cast<const MediaGalleriesPermission::CheckParam*>(param); + return permission_ == specific_param.permission; +} + +scoped_ptr<base::Value> MediaGalleriesPermissionData::ToValue() const { + return scoped_ptr<base::Value>(new base::StringValue(permission_)); +} + +bool MediaGalleriesPermissionData::FromValue(const base::Value* value) { + if (!value) + return false; + + std::string raw_permission; + if (!value->GetAsString(&raw_permission)) + return false; + + std::string permission; + base::TrimWhitespaceASCII(raw_permission, base::TRIM_ALL, &permission); + + if (permission == MediaGalleriesPermission::kAllAutoDetectedPermission || + permission == MediaGalleriesPermission::kScanPermission || + permission == MediaGalleriesPermission::kReadPermission || + permission == MediaGalleriesPermission::kCopyToPermission || + permission == MediaGalleriesPermission::kDeletePermission) { + permission_ = permission; + return true; + } + return false; +} + +bool MediaGalleriesPermissionData::operator<( + const MediaGalleriesPermissionData& rhs) const { + return permission_ < rhs.permission_; +} + +bool MediaGalleriesPermissionData::operator==( + const MediaGalleriesPermissionData& rhs) const { + return permission_ == rhs.permission_; +} + +} // namespace extensions diff --git a/extensions/common/permissions/media_galleries_permission_data.h b/extensions/common/permissions/media_galleries_permission_data.h new file mode 100644 index 0000000..920a905 --- /dev/null +++ b/extensions/common/permissions/media_galleries_permission_data.h @@ -0,0 +1,51 @@ +// Copyright 2014 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_MEDIA_GALLERIES_PERMISSION_DATA_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_MEDIA_GALLERIES_PERMISSION_DATA_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "extensions/common/permissions/api_permission.h" + +namespace base { +class Value; +} + +namespace extensions { + +// A MediaGalleriesPermissionData instance represents a single part of the +// MediaGalleriesPermission. e.g. "read" or "allAutoDetected". +class MediaGalleriesPermissionData { + public: + MediaGalleriesPermissionData(); + + // Check if |param| (which must be a MediaGalleriesPermission::CheckParam) + // matches the encapsulated attribute. + bool Check(const APIPermission::CheckParam* param) const; + + // Convert |this| into a base::Value. + scoped_ptr<base::Value> ToValue() const; + + // Populate |this| from a base::Value. + bool FromValue(const base::Value* value); + + bool operator<(const MediaGalleriesPermissionData& rhs) const; + bool operator==(const MediaGalleriesPermissionData& rhs) const; + + std::string permission() const { return permission_; } + + // This accessor is provided for IPC_STRUCT_TRAITS_MEMBER. Please think + // twice before using it for anything else. + std::string& permission() { return permission_; } + + private: + std::string permission_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_MEDIA_GALLERIES_PERMISSION_DATA_H_ diff --git a/extensions/common/permissions/permission_message_util.cc b/extensions/common/permissions/permission_message_util.cc new file mode 100644 index 0000000..76d1c61 --- /dev/null +++ b/extensions/common/permissions/permission_message_util.cc @@ -0,0 +1,150 @@ +// Copyright 2014 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/permission_message_util.h" + +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/common/url_constants.h" +#include "extensions/common/permissions/permission_message.h" +#include "extensions/common/permissions/permission_set.h" +#include "extensions/common/url_pattern_set.h" +#include "grit/generated_resources.h" +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#include "ui/base/l10n/l10n_util.h" + +using extensions::PermissionMessage; +using extensions::PermissionSet; +using extensions::URLPatternSet; + +namespace { + +// Helper for GetDistinctHosts(): com > net > org > everything else. +bool RcdBetterThan(const std::string& a, const std::string& b) { + if (a == b) + return false; + if (a == "com") + return true; + if (a == "net") + return b != "com"; + if (a == "org") + return b != "com" && b != "net"; + return false; +} + +} // namespace + +namespace permission_message_util { + +PermissionMessage CreateFromHostList(const std::set<std::string>& hosts) { + std::vector<std::string> host_list(hosts.begin(), hosts.end()); + DCHECK(host_list.size()); + PermissionMessage::ID message_id; + base::string16 message; + base::string16 details; + + switch (host_list.size()) { + case 1: + message_id = PermissionMessage::kHosts1; + message = l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_1_HOST, + base::UTF8ToUTF16(host_list[0])); + break; + case 2: + message_id = PermissionMessage::kHosts2; + message = l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_2_HOSTS, + base::UTF8ToUTF16(host_list[0]), + base::UTF8ToUTF16(host_list[1])); + break; + case 3: + message_id = PermissionMessage::kHosts3; + message = l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_3_HOSTS, + base::UTF8ToUTF16(host_list[0]), + base::UTF8ToUTF16(host_list[1]), + base::UTF8ToUTF16(host_list[2])); + break; + default: + message_id = PermissionMessage::kHosts4OrMore; + + const int kRetainedFilesMessageIDs[6] = { + IDS_EXTENSION_PROMPT_WARNING_HOSTS_DEFAULT, + IDS_EXTENSION_PROMPT_WARNING_HOST_SINGULAR, + IDS_EXTENSION_PROMPT_WARNING_HOSTS_ZERO, + IDS_EXTENSION_PROMPT_WARNING_HOSTS_TWO, + IDS_EXTENSION_PROMPT_WARNING_HOSTS_FEW, + IDS_EXTENSION_PROMPT_WARNING_HOSTS_MANY, }; + std::vector<int> message_ids; + for (size_t i = 0; i < arraysize(kRetainedFilesMessageIDs); i++) { + message_ids.push_back(kRetainedFilesMessageIDs[i]); + } + message = l10n_util::GetPluralStringFUTF16(message_ids, host_list.size()); + + for (size_t i = 0; i < host_list.size(); ++i) { + if (i > 0) + details += base::ASCIIToUTF16("\n"); + details += l10n_util::GetStringFUTF16( + IDS_EXTENSION_PROMPT_WARNING_HOST_LIST_ENTRY, + base::UTF8ToUTF16(host_list[i])); + } + } + + return PermissionMessage(message_id, message, details); +} + +std::set<std::string> GetDistinctHosts(const URLPatternSet& host_patterns, + bool include_rcd, + bool exclude_file_scheme) { + // Use a vector to preserve order (also faster than a map on small sets). + // Each item is a host split into two parts: host without RCDs and + // current best RCD. + typedef std::vector<std::pair<std::string, std::string> > HostVector; + HostVector hosts_best_rcd; + for (URLPatternSet::const_iterator i = host_patterns.begin(); + i != host_patterns.end(); + ++i) { + if (exclude_file_scheme && i->scheme() == content::kFileScheme) + continue; + + std::string host = i->host(); + + // Add the subdomain wildcard back to the host, if necessary. + if (i->match_subdomains()) + host = "*." + host; + + // If the host has an RCD, split it off so we can detect duplicates. + std::string rcd; + size_t reg_len = net::registry_controlled_domains::GetRegistryLength( + host, + net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, + net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); + if (reg_len && reg_len != std::string::npos) { + if (include_rcd) // else leave rcd empty + rcd = host.substr(host.size() - reg_len); + host = host.substr(0, host.size() - reg_len); + } + + // Check if we've already seen this host. + HostVector::iterator it = hosts_best_rcd.begin(); + for (; it != hosts_best_rcd.end(); ++it) { + if (it->first == host) + break; + } + // If this host was found, replace the RCD if this one is better. + if (it != hosts_best_rcd.end()) { + if (include_rcd && RcdBetterThan(rcd, it->second)) + it->second = rcd; + } else { // Previously unseen host, append it. + hosts_best_rcd.push_back(std::make_pair(host, rcd)); + } + } + + // Build up the final vector by concatenating hosts and RCDs. + std::set<std::string> distinct_hosts; + for (HostVector::iterator it = hosts_best_rcd.begin(); + it != hosts_best_rcd.end(); + ++it) + distinct_hosts.insert(it->first + it->second); + return distinct_hosts; +} + +} // namespace permission_message_util diff --git a/extensions/common/permissions/permission_message_util.h b/extensions/common/permissions/permission_message_util.h new file mode 100644 index 0000000..410e1a2 --- /dev/null +++ b/extensions/common/permissions/permission_message_util.h @@ -0,0 +1,31 @@ +// Copyright 2014 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_PERMISSION_MESSAGE_UTIL_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_PERMISSION_MESSAGE_UTIL_H_ + +#include <set> +#include <string> + +namespace extensions { +class PermissionMessage; +class PermissionSet; +class URLPatternSet; +} + +namespace permission_message_util { + +// Creates the corresponding permission message for a list of hosts. +// The messages change depending on what hosts are present. +extensions::PermissionMessage CreateFromHostList( + const std::set<std::string>& hosts); + +std::set<std::string> GetDistinctHosts( + const extensions::URLPatternSet& host_patterns, + bool include_rcd, + bool exclude_file_scheme); + +} // namespace permission_message_util + +#endif // EXTENSIONS_COMMON_PERMISSIONS_PERMISSION_MESSAGE_UTIL_H_ diff --git a/extensions/common/permissions/permissions_data_unittest.cc b/extensions/common/permissions/permissions_data_unittest.cc index fda57f9..2ebc9ba 100644 --- a/extensions/common/permissions/permissions_data_unittest.cc +++ b/extensions/common/permissions/permissions_data_unittest.cc @@ -11,7 +11,6 @@ #include "chrome/common/chrome_version_info.h" #include "chrome/common/extensions/extension_test_util.h" #include "chrome/common/extensions/features/feature_channel.h" -#include "chrome/common/extensions/permissions/socket_permission.h" #include "content/public/common/socket_permission_request.h" #include "extensions/common/error_utils.h" #include "extensions/common/extension.h" @@ -20,6 +19,7 @@ #include "extensions/common/permissions/api_permission.h" #include "extensions/common/permissions/permission_set.h" #include "extensions/common/permissions/permissions_data.h" +#include "extensions/common/permissions/socket_permission.h" #include "extensions/common/switches.h" #include "extensions/common/url_pattern_set.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/extensions/common/permissions/set_disjunction_permission.h b/extensions/common/permissions/set_disjunction_permission.h new file mode 100644 index 0000000..3a3e9c5 --- /dev/null +++ b/extensions/common/permissions/set_disjunction_permission.h @@ -0,0 +1,172 @@ +// Copyright 2014 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_SET_DISJUNCTION_PERMISSION_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_SET_DISJUNCTION_PERMISSION_H_ + +#include <algorithm> +#include <set> +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "extensions/common/extension_messages.h" +#include "extensions/common/permissions/api_permission.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_utils.h" + +namespace extensions { + +// An abstract base class for permissions that are represented by the +// disjunction of a set of conditions. Each condition is represented by a +// |PermissionDataType| (e.g. SocketPermissionData). If an +// APIPermission::CheckParam matches any of the conditions in the set, the +// permission is granted. +// +// For an example of how to use this class, see SocketPermission. +template <class PermissionDataType, class DerivedType> +class SetDisjunctionPermission : public APIPermission { + public: + explicit SetDisjunctionPermission(const APIPermissionInfo* info) + : APIPermission(info) {} + + ~SetDisjunctionPermission() {} + + // APIPermission overrides + virtual bool HasMessages() const OVERRIDE { return !data_set_.empty(); } + + virtual bool Check(const APIPermission::CheckParam* param) const OVERRIDE { + for (typename std::set<PermissionDataType>::const_iterator i = + data_set_.begin(); + i != data_set_.end(); + ++i) { + if (i->Check(param)) + return true; + } + return false; + } + + virtual bool Contains(const APIPermission* rhs) const OVERRIDE { + CHECK(rhs->info() == info()); + const SetDisjunctionPermission* perm = + static_cast<const SetDisjunctionPermission*>(rhs); + return std::includes(data_set_.begin(), + data_set_.end(), + perm->data_set_.begin(), + perm->data_set_.end()); + } + + virtual bool Equal(const APIPermission* rhs) const OVERRIDE { + CHECK(rhs->info() == info()); + const SetDisjunctionPermission* perm = + static_cast<const SetDisjunctionPermission*>(rhs); + return data_set_ == perm->data_set_; + } + + virtual APIPermission* Clone() const OVERRIDE { + SetDisjunctionPermission* result = new DerivedType(info()); + result->data_set_ = data_set_; + return result; + } + + virtual APIPermission* Diff(const APIPermission* rhs) const OVERRIDE { + CHECK(rhs->info() == info()); + const SetDisjunctionPermission* perm = + static_cast<const SetDisjunctionPermission*>(rhs); + scoped_ptr<SetDisjunctionPermission> result(new DerivedType(info())); + std::set_difference(data_set_.begin(), + data_set_.end(), + perm->data_set_.begin(), + perm->data_set_.end(), + std::inserter<std::set<PermissionDataType> >( + result->data_set_, result->data_set_.begin())); + return result->data_set_.empty() ? NULL : result.release(); + } + + virtual APIPermission* Union(const APIPermission* rhs) const OVERRIDE { + CHECK(rhs->info() == info()); + const SetDisjunctionPermission* perm = + static_cast<const SetDisjunctionPermission*>(rhs); + scoped_ptr<SetDisjunctionPermission> result(new DerivedType(info())); + std::set_union(data_set_.begin(), + data_set_.end(), + perm->data_set_.begin(), + perm->data_set_.end(), + std::inserter<std::set<PermissionDataType> >( + result->data_set_, result->data_set_.begin())); + return result.release(); + } + + virtual APIPermission* Intersect(const APIPermission* rhs) const OVERRIDE { + CHECK(rhs->info() == info()); + const SetDisjunctionPermission* perm = + static_cast<const SetDisjunctionPermission*>(rhs); + scoped_ptr<SetDisjunctionPermission> result(new DerivedType(info())); + std::set_intersection(data_set_.begin(), + data_set_.end(), + perm->data_set_.begin(), + perm->data_set_.end(), + std::inserter<std::set<PermissionDataType> >( + result->data_set_, result->data_set_.begin())); + return result->data_set_.empty() ? NULL : result.release(); + } + + virtual bool FromValue(const base::Value* value, + std::string* error) OVERRIDE { + data_set_.clear(); + const base::ListValue* list = NULL; + + if (!value || !value->GetAsList(&list) || list->GetSize() == 0) { + if (error) + *error = "NULL or empty permission list"; + return false; + } + + for (size_t i = 0; i < list->GetSize(); ++i) { + const base::Value* item_value = NULL; + bool got_item = list->Get(i, &item_value); + DCHECK(got_item); + DCHECK(item_value); + + PermissionDataType data; + if (!data.FromValue(item_value)) { + if (error) + *error = "Cannot parse an item from the permission list"; + return false; + } + + data_set_.insert(data); + } + return true; + } + + virtual scoped_ptr<base::Value> ToValue() const OVERRIDE { + base::ListValue* list = new base::ListValue(); + typename std::set<PermissionDataType>::const_iterator i; + for (i = data_set_.begin(); i != data_set_.end(); ++i) { + scoped_ptr<base::Value> item_value(i->ToValue()); + list->Append(item_value.release()); + } + return scoped_ptr<base::Value>(list); + } + + virtual void Write(IPC::Message* m) const OVERRIDE { + IPC::WriteParam(m, data_set_); + } + + virtual bool Read(const IPC::Message* m, PickleIterator* iter) OVERRIDE { + return IPC::ReadParam(m, iter, &data_set_); + } + + virtual void Log(std::string* log) const OVERRIDE { + IPC::LogParam(data_set_, log); + } + + protected: + std::set<PermissionDataType> data_set_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_SET_DISJUNCTION_PERMISSION_H_ diff --git a/extensions/common/permissions/settings_override_permission.cc b/extensions/common/permissions/settings_override_permission.cc new file mode 100644 index 0000000..b58d937 --- /dev/null +++ b/extensions/common/permissions/settings_override_permission.cc @@ -0,0 +1,107 @@ +// Copyright 2014 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/settings_override_permission.h" + +#include "base/strings/utf_string_conversions.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +namespace extensions { + +SettingsOverrideAPIPermission::SettingsOverrideAPIPermission( + const APIPermissionInfo* permission, + const std::string& setting_value) + : APIPermission(permission), setting_value_(setting_value) {} + +SettingsOverrideAPIPermission::~SettingsOverrideAPIPermission() {} + +bool SettingsOverrideAPIPermission::HasMessages() const { + return info()->message_id() > PermissionMessage::kNone; +} + +PermissionMessages SettingsOverrideAPIPermission::GetMessages() const { + DCHECK(HasMessages()); + int string_id = -1; + switch (id()) { + case kHomepage: { + string_id = IDS_EXTENSION_PROMPT_WARNING_HOME_PAGE_SETTING_OVERRIDE; + break; + } + case kStartupPages: { + string_id = IDS_EXTENSION_PROMPT_WARNING_START_PAGE_SETTING_OVERRIDE; + break; + } + case kSearchProvider: { + string_id = IDS_EXTENSION_PROMPT_WARNING_SEARCH_SETTINGS_OVERRIDE; + break; + } + default: + NOTREACHED(); + } + PermissionMessages result; + result.push_back( + PermissionMessage(info()->message_id(), + l10n_util::GetStringFUTF16( + string_id, base::UTF8ToUTF16(setting_value_)))); + return result; +} + +bool SettingsOverrideAPIPermission::Check( + const APIPermission::CheckParam* param) const { + return (param == NULL); +} + +bool SettingsOverrideAPIPermission::Contains(const APIPermission* rhs) const { + CHECK_EQ(info(), rhs->info()); + return true; +} + +bool SettingsOverrideAPIPermission::Equal(const APIPermission* rhs) const { + if (this != rhs) + CHECK_EQ(info(), rhs->info()); + return true; +} + +bool SettingsOverrideAPIPermission::FromValue(const base::Value* value, + std::string* /*error*/) { + return (value == NULL); +} + +scoped_ptr<base::Value> SettingsOverrideAPIPermission::ToValue() const { + return scoped_ptr<base::Value>(); +} + +APIPermission* SettingsOverrideAPIPermission::Clone() const { + return new SettingsOverrideAPIPermission(info(), setting_value_); +} + +APIPermission* SettingsOverrideAPIPermission::Diff( + const APIPermission* rhs) const { + CHECK_EQ(info(), rhs->info()); + return NULL; +} + +APIPermission* SettingsOverrideAPIPermission::Union( + const APIPermission* rhs) const { + CHECK_EQ(info(), rhs->info()); + return new SettingsOverrideAPIPermission(info(), setting_value_); +} + +APIPermission* SettingsOverrideAPIPermission::Intersect( + const APIPermission* rhs) const { + CHECK_EQ(info(), rhs->info()); + return new SettingsOverrideAPIPermission(info(), setting_value_); +} + +void SettingsOverrideAPIPermission::Write(IPC::Message* m) const {} + +bool SettingsOverrideAPIPermission::Read(const IPC::Message* m, + PickleIterator* iter) { + return true; +} + +void SettingsOverrideAPIPermission::Log(std::string* log) const {} + +} // namespace extensions diff --git a/extensions/common/permissions/settings_override_permission.h b/extensions/common/permissions/settings_override_permission.h new file mode 100644 index 0000000..48d3d71 --- /dev/null +++ b/extensions/common/permissions/settings_override_permission.h @@ -0,0 +1,44 @@ +// Copyright 2014 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_SETTINGS_OVERRIDE_PERMISSION_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_SETTINGS_OVERRIDE_PERMISSION_H_ + +#include <string> + +#include "extensions/common/permissions/api_permission.h" + +namespace extensions { + +// Takes care of creating custom permission messages for extensions that +// override settings. +class SettingsOverrideAPIPermission : public APIPermission { + public: + SettingsOverrideAPIPermission(const APIPermissionInfo* permission, + const std::string& setting_value); + virtual ~SettingsOverrideAPIPermission(); + + // APIPermission overrides. + virtual bool HasMessages() const OVERRIDE; + virtual PermissionMessages GetMessages() const OVERRIDE; + virtual bool Check(const APIPermission::CheckParam* param) const OVERRIDE; + virtual bool Contains(const APIPermission* rhs) const OVERRIDE; + virtual bool Equal(const APIPermission* rhs) const OVERRIDE; + virtual bool FromValue(const base::Value* value, std::string* error) OVERRIDE; + virtual scoped_ptr<base::Value> ToValue() const OVERRIDE; + virtual APIPermission* Clone() const OVERRIDE; + virtual APIPermission* Diff(const APIPermission* rhs) const OVERRIDE; + virtual APIPermission* Union(const APIPermission* rhs) const OVERRIDE; + virtual APIPermission* Intersect(const APIPermission* rhs) const OVERRIDE; + virtual void Write(IPC::Message* m) const OVERRIDE; + virtual bool Read(const IPC::Message* m, PickleIterator* iter) OVERRIDE; + virtual void Log(std::string* log) const OVERRIDE; + + private: + std::string setting_value_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_SETTINGS_OVERRIDE_PERMISSION_H_ diff --git a/extensions/common/permissions/socket_permission.cc b/extensions/common/permissions/socket_permission.cc new file mode 100644 index 0000000..bc7d0c9 --- /dev/null +++ b/extensions/common/permissions/socket_permission.cc @@ -0,0 +1,108 @@ +// Copyright 2014 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/socket_permission.h" + +#include <algorithm> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/utf_string_conversions.h" +#include "extensions/common/permissions/permissions_info.h" +#include "extensions/common/permissions/set_disjunction_permission.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +namespace extensions { + +SocketPermission::SocketPermission(const APIPermissionInfo* info) + : SetDisjunctionPermission<SocketPermissionData, SocketPermission>(info) {} + +SocketPermission::~SocketPermission() {} + +PermissionMessages SocketPermission::GetMessages() const { + DCHECK(HasMessages()); + PermissionMessages result; + if (!AddAnyHostMessage(result)) { + AddSpecificHostMessage(result); + AddSubdomainHostMessage(result); + } + AddNetworkListMessage(result); + return result; +} + +bool SocketPermission::AddAnyHostMessage(PermissionMessages& messages) const { + std::set<SocketPermissionData>::const_iterator i; + for (i = data_set_.begin(); i != data_set_.end(); ++i) { + if (i->entry().IsAddressBoundType() && + i->entry().GetHostType() == SocketPermissionEntry::ANY_HOST) { + messages.push_back( + PermissionMessage(PermissionMessage::kSocketAnyHost, + l10n_util::GetStringUTF16( + IDS_EXTENSION_PROMPT_WARNING_SOCKET_ANY_HOST))); + return true; + } + } + return false; +} + +void SocketPermission::AddSubdomainHostMessage( + PermissionMessages& messages) const { + std::set<base::string16> domains; + std::set<SocketPermissionData>::const_iterator i; + for (i = data_set_.begin(); i != data_set_.end(); ++i) { + if (i->entry().GetHostType() == SocketPermissionEntry::HOSTS_IN_DOMAINS) + domains.insert(base::UTF8ToUTF16(i->entry().pattern().host)); + } + if (!domains.empty()) { + int id = (domains.size() == 1) + ? IDS_EXTENSION_PROMPT_WARNING_SOCKET_HOSTS_IN_DOMAIN + : IDS_EXTENSION_PROMPT_WARNING_SOCKET_HOSTS_IN_DOMAINS; + messages.push_back(PermissionMessage( + PermissionMessage::kSocketDomainHosts, + l10n_util::GetStringFUTF16( + id, + JoinString( + std::vector<base::string16>(domains.begin(), domains.end()), + ' ')))); + } +} + +void SocketPermission::AddSpecificHostMessage( + PermissionMessages& messages) const { + std::set<base::string16> hostnames; + std::set<SocketPermissionData>::const_iterator i; + for (i = data_set_.begin(); i != data_set_.end(); ++i) { + if (i->entry().GetHostType() == SocketPermissionEntry::SPECIFIC_HOSTS) + hostnames.insert(base::UTF8ToUTF16(i->entry().pattern().host)); + } + if (!hostnames.empty()) { + int id = (hostnames.size() == 1) + ? IDS_EXTENSION_PROMPT_WARNING_SOCKET_SPECIFIC_HOST + : IDS_EXTENSION_PROMPT_WARNING_SOCKET_SPECIFIC_HOSTS; + messages.push_back(PermissionMessage( + PermissionMessage::kSocketSpecificHosts, + l10n_util::GetStringFUTF16( + id, + JoinString( + std::vector<base::string16>(hostnames.begin(), hostnames.end()), + ' ')))); + } +} + +void SocketPermission::AddNetworkListMessage( + PermissionMessages& messages) const { + std::set<SocketPermissionData>::const_iterator i; + for (i = data_set_.begin(); i != data_set_.end(); ++i) { + if (i->entry().pattern().type == + content::SocketPermissionRequest::NETWORK_STATE) { + messages.push_back( + PermissionMessage(PermissionMessage::kNetworkState, + l10n_util::GetStringUTF16( + IDS_EXTENSION_PROMPT_WARNING_NETWORK_STATE))); + } + } +} + +} // namespace extensions diff --git a/extensions/common/permissions/socket_permission.h b/extensions/common/permissions/socket_permission.h new file mode 100644 index 0000000..2bfea7c --- /dev/null +++ b/extensions/common/permissions/socket_permission.h @@ -0,0 +1,43 @@ +// Copyright 2014 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_SOCKET_PERMISSION_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_SOCKET_PERMISSION_H_ + +#include <string> + +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/set_disjunction_permission.h" +#include "extensions/common/permissions/socket_permission_data.h" + +namespace extensions { + +class SocketPermission + : public SetDisjunctionPermission<SocketPermissionData, SocketPermission> { + public: + struct CheckParam : APIPermission::CheckParam { + CheckParam(content::SocketPermissionRequest::OperationType type, + const std::string& host, + int port) + : request(type, host, port) {} + content::SocketPermissionRequest request; + }; + + explicit SocketPermission(const APIPermissionInfo* info); + + virtual ~SocketPermission(); + + // Returns the localized permission messages of this permission. + virtual PermissionMessages GetMessages() const OVERRIDE; + + private: + bool AddAnyHostMessage(PermissionMessages& messages) const; + void AddSubdomainHostMessage(PermissionMessages& messages) const; + void AddSpecificHostMessage(PermissionMessages& messages) const; + void AddNetworkListMessage(PermissionMessages& messages) const; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_SOCKET_PERMISSION_H_ diff --git a/extensions/common/permissions/socket_permission_data.cc b/extensions/common/permissions/socket_permission_data.cc new file mode 100644 index 0000000..b679268 --- /dev/null +++ b/extensions/common/permissions/socket_permission_data.cc @@ -0,0 +1,158 @@ +// Copyright 2014 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/socket_permission_data.h" + +#include <cstdlib> +#include <sstream> +#include <vector> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/socket_permission.h" +#include "url/url_canon.h" + +namespace { + +using content::SocketPermissionRequest; +using extensions::SocketPermissionData; + +const char kColon = ':'; +const char kInvalid[] = "invalid"; +const char kTCPConnect[] = "tcp-connect"; +const char kTCPListen[] = "tcp-listen"; +const char kUDPBind[] = "udp-bind"; +const char kUDPSendTo[] = "udp-send-to"; +const char kUDPMulticastMembership[] = "udp-multicast-membership"; +const char kResolveHost[] = "resolve-host"; +const char kResolveProxy[] = "resolve-proxy"; +const char kNetworkState[] = "network-state"; + +SocketPermissionRequest::OperationType StringToType(const std::string& s) { + if (s == kTCPConnect) + return SocketPermissionRequest::TCP_CONNECT; + if (s == kTCPListen) + return SocketPermissionRequest::TCP_LISTEN; + if (s == kUDPBind) + return SocketPermissionRequest::UDP_BIND; + if (s == kUDPSendTo) + return SocketPermissionRequest::UDP_SEND_TO; + if (s == kUDPMulticastMembership) + return SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP; + if (s == kResolveHost) + return SocketPermissionRequest::RESOLVE_HOST; + if (s == kResolveProxy) + return SocketPermissionRequest::RESOLVE_PROXY; + if (s == kNetworkState) + return SocketPermissionRequest::NETWORK_STATE; + return SocketPermissionRequest::NONE; +} + +const char* TypeToString(SocketPermissionRequest::OperationType type) { + switch (type) { + case SocketPermissionRequest::TCP_CONNECT: + return kTCPConnect; + case SocketPermissionRequest::TCP_LISTEN: + return kTCPListen; + case SocketPermissionRequest::UDP_BIND: + return kUDPBind; + case SocketPermissionRequest::UDP_SEND_TO: + return kUDPSendTo; + case SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP: + return kUDPMulticastMembership; + case SocketPermissionRequest::RESOLVE_HOST: + return kResolveHost; + case SocketPermissionRequest::RESOLVE_PROXY: + return kResolveProxy; + case SocketPermissionRequest::NETWORK_STATE: + return kNetworkState; + default: + return kInvalid; + } +} + +} // namespace + +namespace extensions { + +SocketPermissionData::SocketPermissionData() {} + +SocketPermissionData::~SocketPermissionData() {} + +bool SocketPermissionData::operator<(const SocketPermissionData& rhs) const { + return entry_ < rhs.entry_; +} + +bool SocketPermissionData::operator==(const SocketPermissionData& rhs) const { + return entry_ == rhs.entry_; +} + +bool SocketPermissionData::Check(const APIPermission::CheckParam* param) const { + if (!param) + return false; + const SocketPermission::CheckParam& specific_param = + *static_cast<const SocketPermission::CheckParam*>(param); + const SocketPermissionRequest& request = specific_param.request; + + return entry_.Check(request); +} + +scoped_ptr<base::Value> SocketPermissionData::ToValue() const { + return scoped_ptr<base::Value>(new base::StringValue(GetAsString())); +} + +bool SocketPermissionData::FromValue(const base::Value* value) { + std::string spec; + if (!value->GetAsString(&spec)) + return false; + + return Parse(spec); +} + +SocketPermissionEntry& SocketPermissionData::entry() { + // Clear the spec because the caller could mutate |this|. + spec_.clear(); + return entry_; +} + +// TODO(ikarienator): Rewrite this method to support IPv6. +bool SocketPermissionData::Parse(const std::string& permission) { + Reset(); + + std::vector<std::string> tokens; + base::SplitStringDontTrim(permission, kColon, &tokens); + if (tokens.empty()) + return false; + + SocketPermissionRequest::OperationType type = StringToType(tokens[0]); + if (type == SocketPermissionRequest::NONE) + return false; + + tokens.erase(tokens.begin()); + return SocketPermissionEntry::ParseHostPattern(type, tokens, &entry_); +} + +const std::string& SocketPermissionData::GetAsString() const { + if (!spec_.empty()) + return spec_; + + spec_.reserve(64); + spec_.append(TypeToString(entry_.pattern().type)); + std::string pattern = entry_.GetHostPatternAsString(); + if (!pattern.empty()) { + spec_.append(1, kColon).append(pattern); + } + return spec_; +} + +void SocketPermissionData::Reset() { + entry_ = SocketPermissionEntry(); + spec_.clear(); +} + +} // namespace extensions diff --git a/extensions/common/permissions/socket_permission_data.h b/extensions/common/permissions/socket_permission_data.h new file mode 100644 index 0000000..f227f8d --- /dev/null +++ b/extensions/common/permissions/socket_permission_data.h @@ -0,0 +1,89 @@ +// Copyright 2014 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_SOCKET_PERMISSION_DATA_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_SOCKET_PERMISSION_DATA_H_ + +#include <string> + +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/socket_permission_entry.h" +#include "ipc/ipc_param_traits.h" + +namespace ipc_fuzzer { +template <class T> +struct FuzzTraits; +template <class T> +struct GenerateTraits; +} // namespace ipc_fuzzer + +namespace extensions { + +// A pattern that can be used to match socket permission. +// <socket-permission-pattern> +// := <op> | +// <op> ':' <host> | +// <op> ':' ':' <port> | +// <op> ':' <host> ':' <port> | +// 'udp-multicast-membership' +// <op> := 'tcp-connect' | +// 'tcp-listen' | +// 'udp-bind' | +// 'udp-send-to' | +// 'udp-multicast-membership' | +// 'resolve-host' | +// 'resolve-proxy' | +// 'network-state' +// <host> := '*' | +// '*.' <anychar except '/' and '*'>+ | +// <anychar except '/' and '*'>+ +// <port> := '*' | +// <port number between 0 and 65535>) +// The multicast membership permission implies a permission to any address. +class SocketPermissionData { + public: + SocketPermissionData(); + ~SocketPermissionData(); + + // operators <, == are needed by container std::set and algorithms + // std::set_includes and std::set_differences. + bool operator<(const SocketPermissionData& rhs) const; + bool operator==(const SocketPermissionData& rhs) const; + + // Check if |param| (which must be a SocketPermissionData::CheckParam) + // matches the spec of |this|. + bool Check(const APIPermission::CheckParam* param) const; + + // Convert |this| into a base::Value. + scoped_ptr<base::Value> ToValue() const; + + // Populate |this| from a base::Value. + bool FromValue(const base::Value* value); + + // TODO(bryeung): SocketPermissionData should be encoded as a base::Value + // instead of a string. Until that is done, expose these methods for + // testing. + bool ParseForTest(const std::string& permission) { return Parse(permission); } + const std::string& GetAsStringForTest() const { return GetAsString(); } + + const SocketPermissionEntry& entry() const { return entry_; } + + private: + // Friend so ParamTraits can serialize us. + friend struct IPC::ParamTraits<SocketPermissionData>; + friend struct ipc_fuzzer::FuzzTraits<SocketPermissionData>; + friend struct ipc_fuzzer::GenerateTraits<SocketPermissionData>; + + SocketPermissionEntry& entry(); + + bool Parse(const std::string& permission); + const std::string& GetAsString() const; + void Reset(); + + SocketPermissionEntry entry_; + mutable std::string spec_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_SOCKET_PERMISSION_DATA_H_ diff --git a/extensions/common/permissions/socket_permission_entry.cc b/extensions/common/permissions/socket_permission_entry.cc new file mode 100644 index 0000000..8c21edc --- /dev/null +++ b/extensions/common/permissions/socket_permission_entry.cc @@ -0,0 +1,226 @@ +// Copyright 2014 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/socket_permission_entry.h" + +#include <cstdlib> +#include <sstream> +#include <vector> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/socket_permission.h" +#include "url/url_canon.h" + +namespace { + +using content::SocketPermissionRequest; + +const char kColon = ':'; +const char kDot = '.'; +const char kWildcard[] = "*"; +const int kWildcardPortNumber = 0; +const int kInvalidPort = -1; + +bool StartsOrEndsWithWhitespace(const std::string& str) { + if (str.find_first_not_of(base::kWhitespaceASCII) != 0) + return true; + if (str.find_last_not_of(base::kWhitespaceASCII) != str.length() - 1) + return true; + return false; +} + +} // namespace + +namespace extensions { + +SocketPermissionEntry::SocketPermissionEntry() + : pattern_(SocketPermissionRequest::NONE, std::string(), kInvalidPort), + match_subdomains_(false) {} + +SocketPermissionEntry::~SocketPermissionEntry() {} + +bool SocketPermissionEntry::operator<(const SocketPermissionEntry& rhs) const { + if (pattern_.type < rhs.pattern_.type) + return true; + if (pattern_.type > rhs.pattern_.type) + return false; + + if (pattern_.host < rhs.pattern_.host) + return true; + if (pattern_.host > rhs.pattern_.host) + return false; + + if (match_subdomains_ < rhs.match_subdomains_) + return true; + if (match_subdomains_ > rhs.match_subdomains_) + return false; + + if (pattern_.port < rhs.pattern_.port) + return true; + return false; +} + +bool SocketPermissionEntry::operator==(const SocketPermissionEntry& rhs) const { + return (pattern_.type == rhs.pattern_.type) && + (pattern_.host == rhs.pattern_.host) && + (match_subdomains_ == rhs.match_subdomains_) && + (pattern_.port == rhs.pattern_.port); +} + +bool SocketPermissionEntry::Check( + const content::SocketPermissionRequest& request) const { + if (pattern_.type != request.type) + return false; + + std::string lhost = StringToLowerASCII(request.host); + if (pattern_.host != lhost) { + if (!match_subdomains_) + return false; + + if (!pattern_.host.empty()) { + // Do not wildcard part of IP address. + url_parse::Component component(0, lhost.length()); + url_canon::RawCanonOutputT<char, 128> ignored_output; + url_canon::CanonHostInfo host_info; + url_canon::CanonicalizeIPAddress( + lhost.c_str(), component, &ignored_output, &host_info); + if (host_info.IsIPAddress()) + return false; + + // host should equal one or more chars + "." + host_. + int i = lhost.length() - pattern_.host.length(); + if (i < 2) + return false; + + if (lhost.compare(i, pattern_.host.length(), pattern_.host) != 0) + return false; + + if (lhost[i - 1] != kDot) + return false; + } + } + + if (pattern_.port != request.port && pattern_.port != kWildcardPortNumber) + return false; + + return true; +} + +SocketPermissionEntry::HostType SocketPermissionEntry::GetHostType() const { + return pattern_.host.empty() + ? SocketPermissionEntry::ANY_HOST + : match_subdomains_ ? SocketPermissionEntry::HOSTS_IN_DOMAINS + : SocketPermissionEntry::SPECIFIC_HOSTS; +} + +bool SocketPermissionEntry::IsAddressBoundType() const { + return pattern_.type == SocketPermissionRequest::TCP_CONNECT || + pattern_.type == SocketPermissionRequest::TCP_LISTEN || + pattern_.type == SocketPermissionRequest::UDP_BIND || + pattern_.type == SocketPermissionRequest::UDP_SEND_TO; +} + +// static +bool SocketPermissionEntry::ParseHostPattern( + SocketPermissionRequest::OperationType type, + const std::string& pattern, + SocketPermissionEntry* entry) { + std::vector<std::string> tokens; + base::SplitStringDontTrim(pattern, kColon, &tokens); + return ParseHostPattern(type, tokens, entry); +} + +// static +bool SocketPermissionEntry::ParseHostPattern( + SocketPermissionRequest::OperationType type, + const std::vector<std::string>& pattern_tokens, + SocketPermissionEntry* entry) { + + SocketPermissionEntry result; + + if (type == SocketPermissionRequest::NONE) + return false; + + if (pattern_tokens.size() > 2) + return false; + + result.pattern_.type = type; + result.pattern_.port = kWildcardPortNumber; + result.match_subdomains_ = true; + + if (pattern_tokens.size() == 0) { + *entry = result; + return true; + } + + // Return an error if address is specified for permissions that don't + // need it (such as 'resolve-host'). + if (!result.IsAddressBoundType()) + return false; + + result.pattern_.host = pattern_tokens[0]; + if (!result.pattern_.host.empty()) { + if (StartsOrEndsWithWhitespace(result.pattern_.host)) + return false; + result.pattern_.host = StringToLowerASCII(result.pattern_.host); + + // The first component can optionally be '*' to match all subdomains. + std::vector<std::string> host_components; + base::SplitString(result.pattern_.host, kDot, &host_components); + DCHECK(!host_components.empty()); + + if (host_components[0] == kWildcard || host_components[0].empty()) { + host_components.erase(host_components.begin(), + host_components.begin() + 1); + } else { + result.match_subdomains_ = false; + } + result.pattern_.host = JoinString(host_components, kDot); + } + + if (pattern_tokens.size() == 1 || pattern_tokens[1].empty() || + pattern_tokens[1] == kWildcard) { + *entry = result; + return true; + } + + if (StartsOrEndsWithWhitespace(pattern_tokens[1])) + return false; + + if (!base::StringToInt(pattern_tokens[1], &result.pattern_.port) || + result.pattern_.port < 1 || result.pattern_.port > 65535) + return false; + + *entry = result; + return true; +} + +std::string SocketPermissionEntry::GetHostPatternAsString() const { + std::string result; + + if (!IsAddressBoundType()) + return result; + + if (match_subdomains()) { + result.append(kWildcard); + if (!pattern_.host.empty()) + result.append(1, kDot).append(pattern_.host); + } else { + result.append(pattern_.host); + } + + if (pattern_.port == kWildcardPortNumber) + result.append(1, kColon).append(kWildcard); + else + result.append(1, kColon).append(base::IntToString(pattern_.port)); + + return result; +} + +} // namespace extensions diff --git a/extensions/common/permissions/socket_permission_entry.h b/extensions/common/permissions/socket_permission_entry.h new file mode 100644 index 0000000..ee4e212 --- /dev/null +++ b/extensions/common/permissions/socket_permission_entry.h @@ -0,0 +1,84 @@ +// Copyright 2014 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_SOCKET_PERMISSION_ENTRY_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_SOCKET_PERMISSION_ENTRY_H_ + +#include <string> +#include <vector> + +#include "content/public/common/socket_permission_request.h" +#include "ipc/ipc_param_traits.h" + +namespace ipc_fuzzer { +template <class T> +struct FuzzTraits; +template <class T> +struct GenerateTraits; +} // namespace ipc_fuzzer + +namespace extensions { + +// Internal representation of a socket permission for a specific operation, such +// as UDP "bind", host 127.0.0.1, port *. +class SocketPermissionEntry { + public: + enum HostType { ANY_HOST, HOSTS_IN_DOMAINS, SPECIFIC_HOSTS, }; + + SocketPermissionEntry(); + ~SocketPermissionEntry(); + + // operators <, == are needed by container std::set and algorithms + // std::set_includes and std::set_differences. + bool operator<(const SocketPermissionEntry& rhs) const; + bool operator==(const SocketPermissionEntry& rhs) const; + + bool Check(const content::SocketPermissionRequest& request) const; + + // Parse a host:port pattern for a given operation type. + // <pattern> := '' | + // <host> | + // ':' <port> | + // <host> ':' <port> | + // + // <host> := '*' | + // '*.' <anychar except '/' and '*'>+ | + // <anychar except '/' and '*'>+ + // + // <port> := '*' | + // <port number between 0 and 65535>) + static bool ParseHostPattern( + content::SocketPermissionRequest::OperationType type, + const std::string& pattern, + SocketPermissionEntry* entry); + + static bool ParseHostPattern( + content::SocketPermissionRequest::OperationType type, + const std::vector<std::string>& pattern_tokens, + SocketPermissionEntry* entry); + + // Returns true if the permission type can be bound to a host or port. + bool IsAddressBoundType() const; + + std::string GetHostPatternAsString() const; + HostType GetHostType() const; + + const content::SocketPermissionRequest& pattern() const { return pattern_; } + bool match_subdomains() const { return match_subdomains_; } + + private: + // Friend so ParamTraits can serialize us. + friend struct IPC::ParamTraits<SocketPermissionEntry>; + friend struct ipc_fuzzer::FuzzTraits<SocketPermissionEntry>; + friend struct ipc_fuzzer::GenerateTraits<SocketPermissionEntry>; + + // The permission type, host and port. + content::SocketPermissionRequest pattern_; + + // True if there was a wildcard in the host name. + bool match_subdomains_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_SOCKET_PERMISSION_ENTRY_H_ diff --git a/extensions/common/permissions/usb_device_permission.cc b/extensions/common/permissions/usb_device_permission.cc new file mode 100644 index 0000000..c381b89a --- /dev/null +++ b/extensions/common/permissions/usb_device_permission.cc @@ -0,0 +1,74 @@ +// Copyright 2014 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/usb_device_permission.h" + +#include <set> +#include <string> + +#include "base/logging.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "extensions/common/permissions/permissions_info.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +#if defined(ENABLE_EXTENSIONS) +#include "device/usb/usb_ids.h" +#endif + +namespace extensions { + +UsbDevicePermission::UsbDevicePermission( + const APIPermissionInfo* info) + : SetDisjunctionPermission<UsbDevicePermissionData, + UsbDevicePermission>(info) { +} + +UsbDevicePermission::~UsbDevicePermission() { +} + +PermissionMessages UsbDevicePermission::GetMessages() const { + DCHECK(HasMessages()); + PermissionMessages result; + +#if defined(ENABLE_EXTENSIONS) + // //device/usb/usb.gyp:device_usb is not available when extensions are + // disabled. + for (std::set<UsbDevicePermissionData>::const_iterator i = + data_set_.begin(); i != data_set_.end(); ++i) { + const char* vendor = device::UsbIds::GetVendorName(i->vendor_id()); + + if (vendor) { + const char* product = + device::UsbIds::GetProductName(i->vendor_id(), i->product_id()); + if (product) { + result.push_back(PermissionMessage( + PermissionMessage::kUsbDevice, + l10n_util::GetStringFUTF16( + IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE, + base::ASCIIToUTF16(product), + base::ASCIIToUTF16(vendor)))); + } else { + result.push_back(PermissionMessage( + PermissionMessage::kUsbDevice, + l10n_util::GetStringFUTF16( + IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_MISSING_PRODUCT, + base::ASCIIToUTF16(vendor)))); + } + } else { + result.push_back(PermissionMessage( + PermissionMessage::kUsbDevice, + l10n_util::GetStringUTF16( + IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_MISSING_VENDOR))); + } + } +#else + NOTREACHED(); +#endif // defined(ENABLE_EXTENSIONS) + + return result; +} + +} // namespace extensions diff --git a/extensions/common/permissions/usb_device_permission.h b/extensions/common/permissions/usb_device_permission.h new file mode 100644 index 0000000..7f6b52a --- /dev/null +++ b/extensions/common/permissions/usb_device_permission.h @@ -0,0 +1,38 @@ +// Copyright 2014 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_USB_DEVICE_PERMISSION_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_USB_DEVICE_PERMISSION_H_ + +#include "base/basictypes.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/set_disjunction_permission.h" +#include "extensions/common/permissions/usb_device_permission_data.h" + +namespace extensions { + +class UsbDevicePermission + : public SetDisjunctionPermission<UsbDevicePermissionData, + UsbDevicePermission> { + public: + struct CheckParam : public APIPermission::CheckParam { + CheckParam(uint16 vendor_id, uint16 product_id, int interface_id) + : vendor_id(vendor_id), + product_id(product_id), + interface_id(interface_id) {} + const uint16 vendor_id; + const uint16 product_id; + const int interface_id; + }; + + explicit UsbDevicePermission(const APIPermissionInfo* info); + virtual ~UsbDevicePermission(); + + // APIPermission overrides + virtual PermissionMessages GetMessages() const OVERRIDE; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_USB_DEVICE_PERMISSION_H_ diff --git a/extensions/common/permissions/usb_device_permission_data.cc b/extensions/common/permissions/usb_device_permission_data.cc new file mode 100644 index 0000000..532886d --- /dev/null +++ b/extensions/common/permissions/usb_device_permission_data.cc @@ -0,0 +1,109 @@ +// Copyright 2014 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/usb_device_permission_data.h" + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/values.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/usb_device_permission.h" + +namespace { + +const char* kProductIdKey = "productId"; +const char* kVendorIdKey = "vendorId"; +const char* kInterfaceIdKey = "interfaceId"; + +} // namespace + +namespace extensions { + +UsbDevicePermissionData::UsbDevicePermissionData() + : vendor_id_(0), product_id_(0), interface_id_(ANY_INTERFACE) { +} + +UsbDevicePermissionData::UsbDevicePermissionData(uint16 vendor_id, + uint16 product_id, + int interface_id) + : vendor_id_(vendor_id), + product_id_(product_id), + interface_id_(interface_id) { +} + +bool UsbDevicePermissionData::Check( + const APIPermission::CheckParam* param) const { + if (!param) + return false; + const UsbDevicePermission::CheckParam& specific_param = + *static_cast<const UsbDevicePermission::CheckParam*>(param); + return vendor_id_ == specific_param.vendor_id && + product_id_ == specific_param.product_id && + (specific_param.interface_id == UNSPECIFIED_INTERFACE || + interface_id_ == specific_param.interface_id); +} + +scoped_ptr<base::Value> UsbDevicePermissionData::ToValue() const { + base::DictionaryValue* result = new base::DictionaryValue(); + result->SetInteger(kVendorIdKey, vendor_id_); + result->SetInteger(kProductIdKey, product_id_); + result->SetInteger(kInterfaceIdKey, interface_id_); + return scoped_ptr<base::Value>(result); +} + +bool UsbDevicePermissionData::FromValue(const base::Value* value) { + if (!value) + return false; + + const base::DictionaryValue* dict_value; + if (!value->GetAsDictionary(&dict_value)) + return false; + + int temp; + if (!dict_value->GetInteger(kVendorIdKey, &temp)) + return false; + if (temp < 0 || temp > kuint16max) + return false; + vendor_id_ = temp; + + if (!dict_value->GetInteger(kProductIdKey, &temp)) + return false; + if (temp < 0 || temp > kuint16max) + return false; + product_id_ = temp; + + if (!dict_value->GetInteger(kInterfaceIdKey, &temp)) + interface_id_ = ANY_INTERFACE; + else if (temp < ANY_INTERFACE || temp > kuint8max) + return false; + else + interface_id_ = temp; + + return true; +} + +bool UsbDevicePermissionData::operator<( + const UsbDevicePermissionData& rhs) const { + if (vendor_id_ == rhs.vendor_id_) { + if (product_id_ == rhs.product_id_) + return interface_id_ < rhs.interface_id_; + + return product_id_ < rhs.product_id_; + } + return vendor_id_ < rhs.vendor_id_; +} + +bool UsbDevicePermissionData::operator==( + const UsbDevicePermissionData& rhs) const { + return vendor_id_ == rhs.vendor_id_ && + product_id_ == rhs.product_id_ && + interface_id_ == rhs.interface_id_; +} + +} // namespace extensions diff --git a/extensions/common/permissions/usb_device_permission_data.h b/extensions/common/permissions/usb_device_permission_data.h new file mode 100644 index 0000000..001b12c --- /dev/null +++ b/extensions/common/permissions/usb_device_permission_data.h @@ -0,0 +1,71 @@ +// Copyright 2014 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_USB_DEVICE_PERMISSION_DATA_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_USB_DEVICE_PERMISSION_DATA_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "extensions/common/permissions/api_permission.h" + +namespace base { + +class Value; + +} // namespace base + +namespace extensions { + +// A pattern that can be used to match a USB device permission. +// Should be of the format: vendorId:productId, where both vendorId and +// productId are decimal strings representing uint16 values. +class UsbDevicePermissionData { + public: + enum SpecialInterfaces { + // A special interface id for stating permissions for an entire USB device, + // no specific interface. This value must match value of Rule::ANY_INTERFACE + // from ChromeOS permission_broker project. + ANY_INTERFACE = -1, + + // A special interface id for |Check| to indicate that interface field is + // not to be checked. Not used in manifest file. + UNSPECIFIED_INTERFACE = -2 + }; + + UsbDevicePermissionData(); + UsbDevicePermissionData(uint16 vendor_id, + uint16 product_id, + int interface_id); + + // Check if |param| (which must be a UsbDevicePermissionData::CheckParam) + // matches the vendor and product IDs associated with |this|. + bool Check(const APIPermission::CheckParam* param) const; + + // Convert |this| into a base::Value. + scoped_ptr<base::Value> ToValue() const; + + // Populate |this| from a base::Value. + bool FromValue(const base::Value* value); + + bool operator<(const UsbDevicePermissionData& rhs) const; + bool operator==(const UsbDevicePermissionData& rhs) const; + + const uint16& vendor_id() const { return vendor_id_; } + const uint16& product_id() const { return product_id_; } + + // These accessors are provided for IPC_STRUCT_TRAITS_MEMBER. Please + // think twice before using them for anything else. + uint16& vendor_id() { return vendor_id_; } + uint16& product_id() { return product_id_; } + + private: + uint16 vendor_id_; + uint16 product_id_; + int interface_id_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_USB_DEVICE_PERMISSION_DATA_H_ diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index f64f675..2da4dc4 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -28,6 +28,7 @@ '<(INTERMEDIATE_DIR)', ], 'sources': [ + 'common/api/messaging/message.h', 'common/common_manifest_handlers.cc', 'common/common_manifest_handlers.h', 'common/crx_file.cc', @@ -112,10 +113,16 @@ 'common/permissions/manifest_permission.h', 'common/permissions/manifest_permission_set.cc', 'common/permissions/manifest_permission_set.h', + 'common/permissions/media_galleries_permission.cc', + 'common/permissions/media_galleries_permission.h', + 'common/permissions/media_galleries_permission_data.cc', + 'common/permissions/media_galleries_permission_data.h', 'common/permissions/permission_message.cc', 'common/permissions/permission_message.h', 'common/permissions/permission_message_provider.cc', 'common/permissions/permission_message_provider.h', + 'common/permissions/permission_message_util.cc', + 'common/permissions/permission_message_util.h', 'common/permissions/permission_set.cc', 'common/permissions/permission_set.h', 'common/permissions/permissions_data.cc', @@ -123,6 +130,19 @@ 'common/permissions/permissions_info.cc', 'common/permissions/permissions_info.h', 'common/permissions/permissions_provider.h', + 'common/permissions/set_disjunction_permission.h', + 'common/permissions/settings_override_permission.cc', + 'common/permissions/settings_override_permission.h', + 'common/permissions/socket_permission.cc', + 'common/permissions/socket_permission.h', + 'common/permissions/socket_permission_data.cc', + 'common/permissions/socket_permission_data.h', + 'common/permissions/socket_permission_entry.cc', + 'common/permissions/socket_permission_entry.h', + 'common/permissions/usb_device_permission.cc', + 'common/permissions/usb_device_permission.h', + 'common/permissions/usb_device_permission_data.cc', + 'common/permissions/usb_device_permission_data.h', 'common/stack_frame.cc', 'common/stack_frame.h', 'common/switches.cc', |