| // Copyright 2017 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/api/declarative_net_request/dnr_manifest_handler.h" |
| |
| #include <set> |
| |
| #include "base/files/file_path.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "extensions/common/api/declarative_net_request.h" |
| #include "extensions/common/api/declarative_net_request/constants.h" |
| #include "extensions/common/api/declarative_net_request/dnr_manifest_data.h" |
| #include "extensions/common/error_utils.h" |
| #include "extensions/common/extension_resource.h" |
| #include "extensions/common/manifest_constants.h" |
| #include "extensions/common/manifest_handlers/permissions_parser.h" |
| #include "extensions/common/permissions/api_permission.h" |
| #include "tools/json_schema_compiler/util.h" |
| |
| namespace extensions { |
| |
| namespace errors = manifest_errors; |
| namespace dnr_api = api::declarative_net_request; |
| |
| namespace declarative_net_request { |
| |
| DNRManifestHandler::DNRManifestHandler() = default; |
| DNRManifestHandler::~DNRManifestHandler() = default; |
| |
| bool DNRManifestHandler::Parse(Extension* extension, std::u16string* error) { |
| DCHECK(extension->manifest()->HasKey( |
| dnr_api::ManifestKeys::kDeclarativeNetRequest)); |
| |
| if (!PermissionsParser::HasAPIPermission( |
| extension, APIPermission::kDeclarativeNetRequest)) { |
| *error = ErrorUtils::FormatErrorMessageUTF16( |
| errors::kDeclarativeNetRequestPermissionNeeded, kAPIPermission, |
| dnr_api::ManifestKeys::kDeclarativeNetRequest); |
| return false; |
| } |
| |
| dnr_api::ManifestKeys manifest_keys; |
| if (!dnr_api::ManifestKeys::ParseFromDictionary( |
| extension->manifest()->available_values(), &manifest_keys, error)) { |
| return false; |
| } |
| std::vector<dnr_api::Ruleset> rulesets = |
| std::move(manifest_keys.declarative_net_request.rule_resources); |
| |
| if (rulesets.size() > |
| static_cast<size_t>(dnr_api::MAX_NUMBER_OF_STATIC_RULESETS)) { |
| *error = ErrorUtils::FormatErrorMessageUTF16( |
| errors::kRulesetCountExceeded, |
| dnr_api::ManifestKeys::kDeclarativeNetRequest, |
| dnr_api::DNRInfo::kRuleResources, |
| base::NumberToString(dnr_api::MAX_NUMBER_OF_STATIC_RULESETS)); |
| return false; |
| } |
| |
| std::set<base::StringPiece> ruleset_ids; |
| |
| // Validates the ruleset at the given |index|. On success, returns true and |
| // populates |info|. On failure, returns false and populates |error|. |
| auto get_ruleset_info = [extension, error, &rulesets, &ruleset_ids]( |
| int index, DNRManifestData::RulesetInfo* info) { |
| // Path validation. |
| ExtensionResource resource = extension->GetResource(rulesets[index].path); |
| if (resource.empty() || resource.relative_path().ReferencesParent()) { |
| *error = ErrorUtils::FormatErrorMessageUTF16( |
| errors::kRulesFileIsInvalid, |
| dnr_api::ManifestKeys::kDeclarativeNetRequest, |
| dnr_api::DNRInfo::kRuleResources, rulesets[index].path); |
| return false; |
| } |
| |
| // ID validation. |
| const std::string& manifest_id = rulesets[index].id; |
| |
| // Sanity check that the dynamic and session ruleset IDs are reserved. |
| DCHECK_EQ(kReservedRulesetIDPrefix, dnr_api::DYNAMIC_RULESET_ID[0]); |
| DCHECK_EQ(kReservedRulesetIDPrefix, dnr_api::SESSION_RULESET_ID[0]); |
| |
| if (manifest_id.empty() || !ruleset_ids.insert(manifest_id).second || |
| manifest_id[0] == kReservedRulesetIDPrefix) { |
| *error = ErrorUtils::FormatErrorMessageUTF16( |
| errors::kInvalidRulesetID, |
| dnr_api::ManifestKeys::kDeclarativeNetRequest, |
| dnr_api::DNRInfo::kRuleResources, base::NumberToString(index)); |
| return false; |
| } |
| |
| info->relative_path = resource.relative_path().NormalizePathSeparators(); |
| |
| info->id = RulesetID(kMinValidStaticRulesetID.value() + index); |
| |
| info->enabled = rulesets[index].enabled; |
| info->manifest_id = manifest_id; |
| return true; |
| }; |
| |
| std::vector<DNRManifestData::RulesetInfo> rulesets_info; |
| rulesets_info.reserve(rulesets.size()); |
| |
| // Note: the static_cast<int> below is safe because we did already verify that |
| // |rulesets.size()| <= dnr_api::MAX_NUMBER_OF_STATIC_RULESETS, which is an |
| // integer. |
| for (int i = 0; i < static_cast<int>(rulesets.size()); ++i) { |
| DNRManifestData::RulesetInfo info; |
| if (!get_ruleset_info(i, &info)) |
| return false; |
| |
| rulesets_info.push_back(std::move(info)); |
| } |
| |
| extension->SetManifestData( |
| dnr_api::ManifestKeys::kDeclarativeNetRequest, |
| std::make_unique<DNRManifestData>(std::move(rulesets_info))); |
| return true; |
| } |
| |
| bool DNRManifestHandler::Validate(const Extension* extension, |
| std::string* error, |
| std::vector<InstallWarning>* warnings) const { |
| DNRManifestData* data = |
| static_cast<DNRManifestData*>(extension->GetManifestData( |
| dnr_api::ManifestKeys::kDeclarativeNetRequest)); |
| DCHECK(data); |
| |
| for (const DNRManifestData::RulesetInfo& info : data->rulesets) { |
| // Check file path validity. We don't use Extension::GetResource since it |
| // returns a failure if the relative path contains Windows path separators |
| // and we have already normalized the path separators. |
| if (ExtensionResource::GetFilePath( |
| extension->path(), info.relative_path, |
| ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT) |
| .empty()) { |
| *error = ErrorUtils::FormatErrorMessage( |
| errors::kRulesFileIsInvalid, |
| dnr_api::ManifestKeys::kDeclarativeNetRequest, |
| dnr_api::DNRInfo::kRuleResources, info.relative_path.AsUTF8Unsafe()); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| base::span<const char* const> DNRManifestHandler::Keys() const { |
| static constexpr const char* kKeys[] = { |
| dnr_api::ManifestKeys::kDeclarativeNetRequest}; |
| return kKeys; |
| } |
| |
| } // namespace declarative_net_request |
| } // namespace extensions |