diff options
author | rockot <rockot@chromium.org> | 2014-12-08 19:09:18 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-12-09 03:09:44 +0000 |
commit | a980ec6fb4d0dffeb35cca31e36512717e765463 (patch) | |
tree | 97ab3a91bafe3971a7cdd0b8ed8e6264b92877c4 /extensions/common/manifest_handlers | |
parent | 4989e6046a5c34d02356dd84c70f18f1e9a16188 (diff) | |
download | chromium_src-a980ec6fb4d0dffeb35cca31e36512717e765463.zip chromium_src-a980ec6fb4d0dffeb35cca31e36512717e765463.tar.gz chromium_src-a980ec6fb4d0dffeb35cca31e36512717e765463.tar.bz2 |
Add content_capabilities manifest feature
This adds basic manifest data parsing support for the
content_capabilities feature, which is - for now -
constrained to a whitelist.
BUG=409269
Review URL: https://codereview.chromium.org/780703003
Cr-Commit-Position: refs/heads/master@{#307407}
Diffstat (limited to 'extensions/common/manifest_handlers')
3 files changed, 249 insertions, 0 deletions
diff --git a/extensions/common/manifest_handlers/content_capabilities_handler.cc b/extensions/common/manifest_handlers/content_capabilities_handler.cc new file mode 100644 index 0000000..a55bb82 --- /dev/null +++ b/extensions/common/manifest_handlers/content_capabilities_handler.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/manifest_handlers/content_capabilities_handler.h" + +#include "base/lazy_instance.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "extensions/common/api/extensions_manifest_types.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/install_warning.h" +#include "extensions/common/manifest_constants.h" +#include "extensions/common/permissions/permissions_info.h" +#include "extensions/common/url_pattern.h" + +namespace extensions { + +namespace keys = manifest_keys; +namespace errors = manifest_errors; + +using core_api::extensions_manifest_types::ContentCapabilities; + +ContentCapabilitiesInfo::ContentCapabilitiesInfo() { +} + +ContentCapabilitiesInfo::~ContentCapabilitiesInfo() { +} + +static base::LazyInstance<ContentCapabilitiesInfo> +g_empty_content_capabilities_info = LAZY_INSTANCE_INITIALIZER; + +// static +const ContentCapabilitiesInfo& ContentCapabilitiesInfo::Get( + const Extension* extension) { + ContentCapabilitiesInfo* info = static_cast<ContentCapabilitiesInfo*>( + extension->GetManifestData(keys::kContentCapabilities)); + return info ? *info : g_empty_content_capabilities_info.Get(); +} + +ContentCapabilitiesHandler::ContentCapabilitiesHandler() { +} + +ContentCapabilitiesHandler::~ContentCapabilitiesHandler() { +} + +bool ContentCapabilitiesHandler::Parse(Extension* extension, + base::string16* error) { + scoped_ptr<ContentCapabilitiesInfo> info(new ContentCapabilitiesInfo); + + const base::Value* value = NULL; + if (!extension->manifest()->Get(keys::kContentCapabilities, &value)) { + *error = base::ASCIIToUTF16(errors::kInvalidContentCapabilities); + return false; + } + + scoped_ptr<ContentCapabilities> capabilities(ContentCapabilities::FromValue( + *value, error)); + if (!capabilities) + return false; + + std::string url_error; + URLPatternSet potential_url_patterns; + if (!potential_url_patterns.Populate(capabilities->matches, + URLPattern::SCHEME_HTTPS, false /* allow_file_access */, + &url_error)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidContentCapabilitiesMatch, url_error); + return false; + } + + // Filter wildcard URL patterns and emit warnings for them. + std::set<URLPattern> valid_url_patterns; + for (const URLPattern& pattern : potential_url_patterns) { + if (pattern.match_subdomains() || pattern.ImpliesAllHosts()) { + extension->AddInstallWarning(InstallWarning( + errors::kInvalidContentCapabilitiesMatchOrigin)); + } else { + valid_url_patterns.insert(pattern); + } + } + info->url_patterns = URLPatternSet(valid_url_patterns); + + // Filter invalid permissions and emit warnings for them. + for (const std::string& permission_name : capabilities->permissions) { + const APIPermissionInfo* permission_info = PermissionsInfo::GetInstance() + ->GetByName(permission_name); + if (!permission_info || !permission_info->supports_content_capabilities()) { + extension->AddInstallWarning(InstallWarning( + errors::kInvalidContentCapabilitiesPermission, + keys::kContentCapabilities, + permission_name)); + } else { + info->permissions.insert(permission_info->CreateAPIPermission()); + } + } + + extension->SetManifestData(keys::kContentCapabilities, info.release()); + return true; +} + +const std::vector<std::string> ContentCapabilitiesHandler::Keys() + const { + return SingleKey(keys::kContentCapabilities); +} + +} // namespace extensions diff --git a/extensions/common/manifest_handlers/content_capabilities_handler.h b/extensions/common/manifest_handlers/content_capabilities_handler.h new file mode 100644 index 0000000..ef1bd11 --- /dev/null +++ b/extensions/common/manifest_handlers/content_capabilities_handler.h @@ -0,0 +1,50 @@ +// 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_MANIFEST_HANDLERS_CONTENT_CAPABILITIES_HANDLER_H_ +#define EXTENSIONS_COMMON_MANIFEST_HANDLERS_CONTENT_CAPABILITIES_HANDLER_H_ + +#include <set> +#include <string> +#include <vector> + +#include "extensions/common/extension.h" +#include "extensions/common/manifest_handler.h" +#include "extensions/common/permissions/permission_set.h" +#include "extensions/common/url_pattern_set.h" + +namespace extensions { + +// Manifest data describing an extension's set of granted content capabilities. +struct ContentCapabilitiesInfo : public Extension::ManifestData { + // The set of API permissions to be granted to web content. + APIPermissionSet permissions; + + // The URL pattern set which should be used to decide which content is granted + // these capabilities. + URLPatternSet url_patterns; + + ContentCapabilitiesInfo(); + ~ContentCapabilitiesInfo() override; + + static const ContentCapabilitiesInfo& Get(const Extension* extension); +}; + +// Parses the "content_capabilities" manifest key. +class ContentCapabilitiesHandler : public ManifestHandler { + public: + ContentCapabilitiesHandler(); + ~ContentCapabilitiesHandler() override; + + bool Parse(Extension* extension, base::string16* error) override; + + private: + const std::vector<std::string> Keys() const override; + + DISALLOW_COPY_AND_ASSIGN(ContentCapabilitiesHandler); +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_MANIFEST_HANDLERS_CONTENT_CAPABILITIES_HANDLER_H_ diff --git a/extensions/common/manifest_handlers/content_capabilities_manifest_unittest.cc b/extensions/common/manifest_handlers/content_capabilities_manifest_unittest.cc new file mode 100644 index 0000000..e9f78c3 --- /dev/null +++ b/extensions/common/manifest_handlers/content_capabilities_manifest_unittest.cc @@ -0,0 +1,90 @@ +// 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 "base/command_line.h" +#include "extensions/common/manifest_constants.h" +#include "extensions/common/manifest_handlers/content_capabilities_handler.h" +#include "extensions/common/manifest_test.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/extensions_api_permissions.h" +#include "extensions/common/permissions/permissions_info.h" +#include "extensions/common/switches.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace extensions { + +using extensions::APIPermission; + +class ContentCapabilitiesManifestTest : public ManifestTest { + std::string GetTestExtensionID() const override { + return std::string("apdfllckaahabafndbhieahigkjlhalf"); + } +}; + +TEST_F(ContentCapabilitiesManifestTest, RejectDomainWildcards) { + scoped_refptr<Extension> extension(LoadAndExpectWarning( + "content_capabilities_domain_wildcard.json", + manifest_errors::kInvalidContentCapabilitiesMatchOrigin)); + const ContentCapabilitiesInfo& info = ContentCapabilitiesInfo::Get( + extension.get()); + // Make sure the wildcard is not included in the pattern set. + EXPECT_FALSE(info.url_patterns.MatchesURL(GURL("https://bar.example.com/"))); + EXPECT_TRUE(info.url_patterns.MatchesURL(GURL("https://foo.example.com/"))); +} + +TEST_F(ContentCapabilitiesManifestTest, RejectedAllHosts) { + scoped_refptr<Extension> extension(LoadAndExpectWarning( + "content_capabilities_all_hosts.json", + manifest_errors::kInvalidContentCapabilitiesMatchOrigin)); + const ContentCapabilitiesInfo& info = ContentCapabilitiesInfo::Get( + extension.get()); + // Make sure the wildcard is not included in the pattern set. + EXPECT_FALSE(info.url_patterns.MatchesURL(GURL("https://nonspecific.com/"))); + EXPECT_TRUE(info.url_patterns.MatchesURL(GURL("https://example.com/"))); +} + +TEST_F(ContentCapabilitiesManifestTest, InvalidPermission) { + scoped_refptr<Extension> extension(LoadAndExpectWarning( + "content_capabilities_invalid_permission.json", + manifest_errors::kInvalidContentCapabilitiesPermission)); + const ContentCapabilitiesInfo& info = ContentCapabilitiesInfo::Get( + extension.get()); + // Make sure the invalid permission is not included in the permission set. + EXPECT_EQ(3u, info.permissions.size()); + EXPECT_EQ(1u, info.permissions.count(APIPermission::kClipboardRead)); + EXPECT_EQ(1u, info.permissions.count(APIPermission::kClipboardWrite)); + EXPECT_EQ(1u, info.permissions.count(APIPermission::kUnlimitedStorage)); + EXPECT_EQ(0u, info.permissions.count(APIPermission::kUsb)); +} + +TEST_F(ContentCapabilitiesManifestTest, InvalidValue) { + LoadAndExpectError("content_capabilities_invalid_value.json", + "expected dictionary, got list"); +} + +TEST_F(ContentCapabilitiesManifestTest, RejectNonHttpsUrlPatterns) { + LoadAndExpectError("content_capabilities_non_https_matches.json", + manifest_errors::kInvalidContentCapabilitiesMatch); +} + +TEST_F(ContentCapabilitiesManifestTest, Valid) { + scoped_refptr<Extension> extension( + LoadAndExpectSuccess("content_capabilities_valid.json")); + const ContentCapabilitiesInfo& info = ContentCapabilitiesInfo::Get( + extension.get()); + EXPECT_EQ(1u, info.url_patterns.size()); + EXPECT_FALSE(info.url_patterns.MatchesURL(GURL("http://valid.example.com/"))); + EXPECT_FALSE(info.url_patterns.MatchesURL(GURL("https://foo.example.com/"))); + EXPECT_FALSE(info.url_patterns.MatchesURL(GURL("https://example.com/"))); + EXPECT_TRUE(info.url_patterns.MatchesURL(GURL("https://valid.example.com/"))); + EXPECT_EQ(3u, info.permissions.size()); + EXPECT_EQ(1u, info.permissions.count(APIPermission::kClipboardRead)); + EXPECT_EQ(1u, info.permissions.count(APIPermission::kClipboardWrite)); + EXPECT_EQ(1u, info.permissions.count(APIPermission::kUnlimitedStorage)); + EXPECT_EQ(0u, info.permissions.count(APIPermission::kUsb)); +} + + +} // namespace extensions |