| // 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/web_accessible_resources_info.h" |
| |
| #include <stddef.h> |
| #include <utility> |
| |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "components/crx_file/id_util.h" |
| #include "extensions/common/api/web_accessible_resources.h" |
| #include "extensions/common/api/web_accessible_resources_mv2.h" |
| #include "extensions/common/constants.h" |
| #include "extensions/common/error_utils.h" |
| #include "extensions/common/extension_id.h" |
| #include "extensions/common/manifest.h" |
| #include "extensions/common/manifest_constants.h" |
| |
| namespace extensions { |
| |
| namespace keys = manifest_keys; |
| namespace errors = manifest_errors; |
| |
| using WebAccessibleResourcesManifestKeys = |
| api::web_accessible_resources::ManifestKeys; |
| using WebAccessibleResourcesMv2ManifestKeys = |
| api::web_accessible_resources_mv2::ManifestKeys; |
| |
| namespace { |
| |
| const WebAccessibleResourcesInfo* GetResourcesInfo(const Extension* extension) { |
| return static_cast<WebAccessibleResourcesInfo*>(extension->GetManifestData( |
| WebAccessibleResourcesManifestKeys::kWebAccessibleResources)); |
| } // namespace |
| |
| URLPattern GetPattern(std::string relative_path, const Extension& extension) { |
| URLPattern pattern(URLPattern::SCHEME_EXTENSION); |
| URLPattern::ParseResult result = pattern.Parse(extension.url().spec()); |
| DCHECK_EQ(URLPattern::ParseResult::kSuccess, result); |
| while (relative_path[0] == '/') |
| relative_path = relative_path.substr(1, relative_path.length() - 1); |
| pattern.SetPath(pattern.path() + relative_path); |
| return pattern; |
| } |
| |
| std::unique_ptr<WebAccessibleResourcesInfo> ParseResourceStringList( |
| const Extension& extension, |
| std::u16string* error) { |
| WebAccessibleResourcesMv2ManifestKeys manifest_keys; |
| if (!WebAccessibleResourcesMv2ManifestKeys::ParseFromDictionary( |
| extension.manifest()->available_values(), &manifest_keys, error)) { |
| return nullptr; |
| } |
| |
| auto info = std::make_unique<WebAccessibleResourcesInfo>(); |
| URLPatternSet resource_set; |
| |
| for (std::string& web_accessible_resource : |
| manifest_keys.web_accessible_resources) { |
| resource_set.AddPattern( |
| GetPattern(std::move(web_accessible_resource), extension)); |
| } |
| |
| // In extensions where only a resource list is provided (as is the case in |
| // manifest_version 2), resources are embeddable by any site. To handle |
| // this, have |matches| match the specified schemes. |
| URLPatternSet matches; |
| matches.AddPattern( |
| URLPattern(URLPattern::SCHEME_ALL, URLPattern::kAllUrlsPattern)); |
| info->web_accessible_resources.emplace_back( |
| std::move(resource_set), std::move(matches), std::vector<ExtensionId>(), |
| false); |
| return info; |
| } |
| |
| std::unique_ptr<WebAccessibleResourcesInfo> ParseEntryList( |
| const Extension& extension, |
| std::u16string* error) { |
| auto info = std::make_unique<WebAccessibleResourcesInfo>(); |
| auto get_error = [](size_t i, base::StringPiece message) { |
| return ErrorUtils::FormatErrorMessageUTF16( |
| errors::kInvalidWebAccessibleResource, base::NumberToString(i), |
| message); |
| }; |
| |
| WebAccessibleResourcesManifestKeys manifest_keys; |
| if (!WebAccessibleResourcesManifestKeys::ParseFromDictionary( |
| extension.manifest()->available_values(), &manifest_keys, error)) { |
| return nullptr; |
| } |
| |
| size_t i = 0; |
| for (auto& web_accessible_resource : manifest_keys.web_accessible_resources) { |
| bool use_dynamic_url_bool = web_accessible_resource.use_dynamic_url && |
| *web_accessible_resource.use_dynamic_url; |
| |
| if (!web_accessible_resource.matches && |
| !web_accessible_resource.extension_ids && !use_dynamic_url_bool) { |
| *error = get_error( |
| i, "Entry must at least have resources, and one other valid key."); |
| return nullptr; |
| } |
| |
| // Prepare each key of the web accessible resources. |
| URLPatternSet resource_set; |
| for (std::string& resource : web_accessible_resource.resources) { |
| resource_set.AddPattern(GetPattern(std::move(resource), extension)); |
| } |
| URLPatternSet match_set; |
| if (web_accessible_resource.matches) { |
| for (const std::string& match : *web_accessible_resource.matches) { |
| URLPattern pattern(URLPattern::SCHEME_ALL); |
| if (pattern.Parse(match) != URLPattern::ParseResult::kSuccess || |
| pattern.path() != "/*") { |
| *error = get_error(i, "Invalid match pattern."); |
| return nullptr; |
| } |
| match_set.AddPattern(pattern); |
| } |
| } |
| std::vector<ExtensionId> extension_id_list; |
| if (web_accessible_resource.extension_ids) { |
| extension_id_list.reserve(web_accessible_resource.extension_ids->size()); |
| for (std::string& extension_id : *web_accessible_resource.extension_ids) { |
| if (!crx_file::id_util::IdIsValid(extension_id)) { |
| *error = get_error(i, "Invalid extension id."); |
| return nullptr; |
| } |
| extension_id_list.push_back(std::move(extension_id)); |
| } |
| } |
| |
| info->web_accessible_resources.emplace_back( |
| std::move(resource_set), std::move(match_set), |
| std::move(extension_id_list), use_dynamic_url_bool); |
| ++i; |
| } |
| return info; |
| } |
| |
| } // namespace |
| |
| WebAccessibleResourcesInfo::WebAccessibleResourcesInfo() = default; |
| |
| WebAccessibleResourcesInfo::~WebAccessibleResourcesInfo() = default; |
| |
| // static |
| bool WebAccessibleResourcesInfo::IsResourceWebAccessible( |
| const Extension* extension, |
| const std::string& relative_path, |
| const base::Optional<url::Origin>& initiator_origin) { |
| auto initiator_url = |
| initiator_origin.has_value() ? initiator_origin->GetURL() : GURL(); |
| const WebAccessibleResourcesInfo* info = GetResourcesInfo(extension); |
| if (!info) { // No web-accessible resources |
| return false; |
| } |
| for (const auto& entry : info->web_accessible_resources) { |
| if (extension->ResourceMatches(entry.resources, relative_path)) { |
| // Prior to MV3, web-accessible resources were accessible by any |
| // site. Preserve this behavior. |
| if (extension->manifest_version() < 3) |
| return true; |
| |
| if (entry.matches.MatchesURL(initiator_url)) |
| return true; |
| if (initiator_url.SchemeIs(extensions::kExtensionScheme) && |
| base::Contains(entry.extension_ids, initiator_url.host())) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| // static |
| bool WebAccessibleResourcesInfo::HasWebAccessibleResources( |
| const Extension* extension) { |
| const WebAccessibleResourcesInfo* info = GetResourcesInfo(extension); |
| return info && (info->web_accessible_resources.size() > 0); |
| } |
| |
| WebAccessibleResourcesInfo::Entry::Entry() = default; |
| |
| WebAccessibleResourcesInfo::Entry::Entry( |
| WebAccessibleResourcesInfo::Entry&& rhs) = default; |
| |
| WebAccessibleResourcesInfo::Entry::~Entry() = default; |
| |
| WebAccessibleResourcesInfo::Entry::Entry(URLPatternSet resources, |
| URLPatternSet matches, |
| std::vector<ExtensionId> extension_ids, |
| bool use_dynamic_url) |
| : resources(std::move(resources)), |
| matches(std::move(matches)), |
| extension_ids(std::move(extension_ids)), |
| use_dynamic_url(use_dynamic_url) {} |
| |
| WebAccessibleResourcesHandler::WebAccessibleResourcesHandler() = default; |
| |
| WebAccessibleResourcesHandler::~WebAccessibleResourcesHandler() { |
| } |
| |
| bool WebAccessibleResourcesHandler::Parse(Extension* extension, |
| std::u16string* error) { |
| auto info = extension->manifest_version() < 3 |
| ? ParseResourceStringList(*extension, error) |
| : ParseEntryList(*extension, error); |
| if (!info) { |
| return false; |
| } |
| extension->SetManifestData( |
| WebAccessibleResourcesManifestKeys::kWebAccessibleResources, |
| std::move(info)); |
| return true; |
| } |
| base::span<const char* const> WebAccessibleResourcesHandler::Keys() const { |
| static constexpr const char* kKeys[] = { |
| WebAccessibleResourcesManifestKeys::kWebAccessibleResources}; |
| return kKeys; |
| } |
| |
| } // namespace extensions |