[go: nahoru, domu]

blob: 2e4dec312d1ce88b7d0288fc5c670daf05fc4b2b [file] [log] [blame]
// 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/browser/api/declarative_net_request/flat_ruleset_indexer.h"
#include <stdint.h>
#include <map>
#include <string>
#include "base/format_macros.h"
#include "base/json/json_reader.h"
#include "base/strings/stringprintf.h"
#include "components/url_pattern_index/flat/url_pattern_index_generated.h"
#include "extensions/browser/api/declarative_net_request/constants.h"
#include "extensions/browser/api/declarative_net_request/flat/extension_ruleset_generated.h"
#include "extensions/browser/api/declarative_net_request/indexed_rule.h"
#include "extensions/browser/api/declarative_net_request/test_utils.h"
#include "extensions/browser/api/declarative_net_request/utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
namespace declarative_net_request {
namespace {
namespace flat_rule = url_pattern_index::flat;
namespace dnr_api = api::declarative_net_request;
using FlatRulesetIndexerTest = ::testing::Test;
// Helper to convert a flatbuffer string to a std::string.
std::string ToString(const flatbuffers::String* string) {
DCHECK(string);
return CreateString<std::string>(*string);
}
// Helper to convert a flatbuffer vector of strings to a std::vector.
std::vector<std::string> ToVector(
const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>*
vec) {
if (!vec)
return std::vector<std::string>();
std::vector<std::string> result;
result.reserve(vec->size());
for (auto* str : *vec)
result.push_back(ToString(str));
return result;
}
// Helper to convert a flatbuffer vector of flat::ModifyHeaderInfo to a
// std::vector of dnr_api::ModifyHeaderInfo
std::vector<dnr_api::ModifyHeaderInfo> ToVector(
const ::flatbuffers::Vector<::flatbuffers::Offset<flat::ModifyHeaderInfo>>*
vec) {
if (!vec)
return std::vector<dnr_api::ModifyHeaderInfo>();
std::vector<dnr_api::ModifyHeaderInfo> result;
result.reserve(vec->size());
for (auto* flat_header_info : *vec) {
dnr_api::ModifyHeaderInfo header_info;
const flat::HeaderOperation flat_operation = flat_header_info->operation();
const flatbuffers::String* flat_value = flat_header_info->value();
switch (flat_operation) {
case flat::HeaderOperation_append:
header_info.operation = dnr_api::HEADER_OPERATION_APPEND;
DCHECK(flat_value);
header_info.value = std::make_unique<std::string>(ToString(flat_value));
break;
case flat::HeaderOperation_set:
header_info.operation = dnr_api::HEADER_OPERATION_SET;
DCHECK(flat_value);
header_info.value = std::make_unique<std::string>(ToString(flat_value));
break;
case flat::HeaderOperation_remove:
header_info.operation = dnr_api::HEADER_OPERATION_REMOVE;
break;
};
const flatbuffers::String* flat_header = flat_header_info->header();
DCHECK(flat_header);
header_info.header = ToString(flat_header);
result.push_back(std::move(header_info));
}
return result;
}
// Helper to create a generic URLTransform.
std::unique_ptr<dnr_api::URLTransform> CreateUrlTransform() {
const char* transform = R"(
{
"scheme" : "http",
"host" : "foo.com",
"port" : "80",
"path" : "",
"queryTransform" : {
"removeParams" : ["x1", "x2"],
"addOrReplaceParams" : [
{"key": "y1", "value" : "foo"}
]
},
"fragment" : "#xx",
"username" : "user",
"password" : "pass"
}
)";
base::Optional<base::Value> value = base::JSONReader::Read(transform);
CHECK(value);
std::u16string error;
auto result = dnr_api::URLTransform::FromValue(*value, &error);
CHECK(result);
CHECK(error.empty());
return result;
}
// Helper to verify the indexed form of URlTransform created by
// |CreateUrlTransform()|.
bool VerifyUrlTransform(const flat::UrlTransform& flat_transform) {
auto is_string_equal = [](base::StringPiece str,
const flatbuffers::String* flat_str) {
return flat_str && ToString(flat_str) == str;
};
auto verify_add_or_replace_params = [&flat_transform, &is_string_equal]() {
if (!flat_transform.add_or_replace_query_params() ||
flat_transform.add_or_replace_query_params()->size() != 1) {
return false;
}
const flat::QueryKeyValue* query_pair =
flat_transform.add_or_replace_query_params()->Get(0);
return query_pair && is_string_equal("y1", query_pair->key()) &&
is_string_equal("foo", query_pair->value());
};
return is_string_equal("http", flat_transform.scheme()) &&
is_string_equal("foo.com", flat_transform.host()) &&
!flat_transform.clear_port() &&
is_string_equal("80", flat_transform.port()) &&
flat_transform.clear_path() && !flat_transform.path() &&
!flat_transform.clear_query() && !flat_transform.query() &&
flat_transform.remove_query_params() &&
std::vector<std::string>{"x1", "x2"} ==
ToVector(flat_transform.remove_query_params()) &&
verify_add_or_replace_params() && !flat_transform.clear_fragment() &&
is_string_equal("xx", flat_transform.fragment()) &&
is_string_equal("user", flat_transform.username()) &&
is_string_equal("pass", flat_transform.password());
}
// Helper to create an IndexedRule.
IndexedRule CreateIndexedRule(
uint32_t id,
uint32_t priority,
uint8_t options,
uint16_t element_types,
uint8_t activation_types,
flat_rule::UrlPatternType url_pattern_type,
flat_rule::AnchorType anchor_left,
flat_rule::AnchorType anchor_right,
std::string url_pattern,
std::vector<std::string> domains,
std::vector<std::string> excluded_domains,
base::Optional<std::string> redirect_url,
dnr_api::RuleActionType action_type,
std::unique_ptr<dnr_api::URLTransform> url_transform,
base::Optional<std::string> regex_substitution,
std::vector<dnr_api::ModifyHeaderInfo> request_headers,
std::vector<dnr_api::ModifyHeaderInfo> response_headers) {
IndexedRule rule;
rule.id = id;
rule.priority = priority;
rule.options = options;
rule.element_types = element_types;
rule.activation_types = activation_types;
rule.url_pattern_type = url_pattern_type;
rule.anchor_left = anchor_left;
rule.anchor_right = anchor_right;
rule.url_pattern = std::move(url_pattern);
rule.domains = std::move(domains);
rule.excluded_domains = std::move(excluded_domains);
rule.redirect_url = std::move(redirect_url);
rule.action_type = action_type;
rule.url_transform = std::move(url_transform);
rule.regex_substitution = std::move(regex_substitution);
rule.request_headers = std::move(request_headers);
rule.response_headers = std::move(response_headers);
return rule;
}
// Compares |indexed_rule| and |rule| for equality. Ignores the redirect url and
// the list of request and response headers since they're not stored as part of
// flat_rule::UrlRule.
bool AreRulesEqual(const IndexedRule* indexed_rule,
const flat_rule::UrlRule* rule) {
CHECK(indexed_rule);
CHECK(rule);
return indexed_rule->id == rule->id() &&
indexed_rule->priority == rule->priority() &&
indexed_rule->options == rule->options() &&
indexed_rule->element_types == rule->element_types() &&
indexed_rule->activation_types == rule->activation_types() &&
indexed_rule->url_pattern_type == rule->url_pattern_type() &&
indexed_rule->anchor_left == rule->anchor_left() &&
indexed_rule->anchor_right == rule->anchor_right() &&
indexed_rule->url_pattern == ToString(rule->url_pattern()) &&
indexed_rule->domains == ToVector(rule->domains_included()) &&
indexed_rule->excluded_domains == ToVector(rule->domains_excluded());
}
// Returns all UrlRule(s) in the given |index|.
std::vector<const flat_rule::UrlRule*> GetAllRulesFromIndex(
const flat_rule::UrlPatternIndex* index) {
std::vector<const flat_rule::UrlRule*> result;
// Iterate over all ngrams and add their corresponding rules.
for (auto* ngram_to_rules : *index->ngram_index()) {
if (ngram_to_rules == index->ngram_index_empty_slot())
continue;
for (const auto* rule : *ngram_to_rules->rule_list())
result.push_back(rule);
}
// Add all fallback rules.
for (const auto* rule : *index->fallback_rules())
result.push_back(rule);
return result;
}
// Verifies that both |rules| and |index| correspond to the same set of rules
// (in different representations).
void VerifyIndexEquality(const std::vector<const IndexedRule*>& rules,
const flat_rule::UrlPatternIndex* index) {
struct RulePair {
const IndexedRule* indexed_rule = nullptr;
const flat_rule::UrlRule* url_rule = nullptr;
};
// Build a map from rule IDs to RulePair(s).
std::map<uint32_t, RulePair> map;
for (const auto* rule : rules) {
EXPECT_EQ(nullptr, map[rule->id].indexed_rule);
map[rule->id].indexed_rule = rule;
}
std::vector<const flat_rule::UrlRule*> flat_rules =
GetAllRulesFromIndex(index);
for (const auto* rule : flat_rules) {
EXPECT_EQ(nullptr, map[rule->id()].url_rule);
map[rule->id()].url_rule = rule;
}
// Iterate over the map and verify equality of the two representations.
for (const auto& elem : map) {
EXPECT_TRUE(AreRulesEqual(elem.second.indexed_rule, elem.second.url_rule))
<< base::StringPrintf("Rule with id %u was incorrectly indexed",
elem.first);
}
}
// Verifies that |extension_metadata| is sorted by ID and corresponds to rules
// in |rules|.
void VerifyExtensionMetadata(
const std::vector<const IndexedRule*>& rules,
const ::flatbuffers::Vector<flatbuffers::Offset<flat::UrlRuleMetadata>>*
extension_metdata) {
struct MetadataPair {
const IndexedRule* indexed_rule = nullptr;
const flat::UrlRuleMetadata* metadata = nullptr;
};
// Build a map from IDs to MetadataPair(s).
std::map<uint32_t, MetadataPair> map;
for (const auto* rule : rules) {
// It is possible for a rule to be present in multiple indices, such as a
// remove headers rule that removes more than one header.
EXPECT_TRUE(map[rule->id].indexed_rule == nullptr ||
map[rule->id].indexed_rule == rule);
map[rule->id].indexed_rule = rule;
}
int previous_id = kMinValidID - 1;
for (const auto* metadata : *extension_metdata) {
EXPECT_EQ(nullptr, map[metadata->id()].metadata);
map[metadata->id()].metadata = metadata;
// Also verify that the metadata vector is sorted by ID.
int current_id = static_cast<int>(metadata->id());
EXPECT_LT(previous_id, current_id)
<< "|extension_metdata| is not sorted by ID";
previous_id = current_id;
}
// Returns whether the metadata for the given rule was correctly indexed.
auto is_metadata_correct = [](const MetadataPair& pair) {
EXPECT_TRUE(pair.indexed_rule);
if (ConvertToFlatActionType(pair.indexed_rule->action_type) !=
pair.metadata->action()) {
return false;
}
EXPECT_FALSE(pair.indexed_rule->redirect_url &&
pair.indexed_rule->url_transform);
if (pair.indexed_rule->redirect_url) {
if (!pair.metadata->redirect_url())
return false;
return pair.indexed_rule->redirect_url ==
ToString(pair.metadata->redirect_url());
}
if (pair.indexed_rule->url_transform) {
if (!pair.metadata->transform())
return false;
return VerifyUrlTransform(*pair.metadata->transform());
}
auto are_header_modifications_equal =
[](const ::flatbuffers::Vector<
::flatbuffers::Offset<flat::ModifyHeaderInfo>>* metadata_headers,
const std::vector<dnr_api::ModifyHeaderInfo>& indexed_headers) {
return std::equal(indexed_headers.begin(), indexed_headers.end(),
ToVector(metadata_headers).begin(),
EqualsForTesting);
};
EXPECT_TRUE(are_header_modifications_equal(
pair.metadata->request_headers(), pair.indexed_rule->request_headers));
EXPECT_TRUE(
are_header_modifications_equal(pair.metadata->response_headers(),
pair.indexed_rule->response_headers));
return true;
};
// Iterate over the map and verify correctness of the metadata.
for (const auto& elem : map) {
EXPECT_TRUE(is_metadata_correct(elem.second)) << base::StringPrintf(
"Redirect rule with id %u was incorrectly indexed", elem.first);
}
}
const flat::ExtensionIndexedRuleset* AddRuleAndGetRuleset(
const std::vector<IndexedRule>& rules_to_index,
flatbuffers::DetachedBuffer* buffer) {
FlatRulesetIndexer indexer;
for (const auto& rule : rules_to_index)
indexer.AddUrlRule(rule);
*buffer = indexer.FinishAndReleaseBuffer();
EXPECT_EQ(rules_to_index.size(), indexer.indexed_rules_count());
flatbuffers::Verifier verifier(buffer->data(), buffer->size());
if (!flat::VerifyExtensionIndexedRulesetBuffer(verifier))
return nullptr;
return flat::GetExtensionIndexedRuleset(buffer->data());
}
// Helper which:
// - Constructs an ExtensionIndexedRuleset flatbuffer from the passed
// IndexedRule(s) using FlatRulesetIndexer.
// - Verifies that the ExtensionIndexedRuleset created is valid.
// Note: this does not test regex rules which are part of the
// ExtensionIndexedRuleset.
void AddRulesAndVerifyIndex(const std::vector<IndexedRule>& rules_to_index,
const std::vector<const IndexedRule*>
expected_index_lists[flat::IndexType_count]) {
flatbuffers::DetachedBuffer buffer;
const flat::ExtensionIndexedRuleset* ruleset =
AddRuleAndGetRuleset(rules_to_index, &buffer);
ASSERT_TRUE(ruleset);
for (size_t i = 0; i < flat::IndexType_count; ++i) {
SCOPED_TRACE(base::StringPrintf("Testing index %" PRIuS, i));
VerifyIndexEquality(expected_index_lists[i], ruleset->index_list()->Get(i));
}
{
SCOPED_TRACE("Testing extension metadata");
std::vector<const IndexedRule*> all_rules;
for (size_t i = 0; i < flat::IndexType_count; i++) {
all_rules.insert(all_rules.end(), expected_index_lists[i].begin(),
expected_index_lists[i].end());
}
VerifyExtensionMetadata(all_rules, ruleset->extension_metadata());
}
}
TEST_F(FlatRulesetIndexerTest, TestEmptyIndex) {
std::vector<const IndexedRule*> expected_index_lists[flat::IndexType_count];
AddRulesAndVerifyIndex({}, expected_index_lists);
}
TEST_F(FlatRulesetIndexerTest, MultipleRules) {
std::vector<IndexedRule> rules_to_index;
// Explicitly push the elements instead of using the initializer list
// constructor, because it does not support move-only types.
// Blocking rules.
rules_to_index.push_back(CreateIndexedRule(
7, kMinValidPriority, flat_rule::OptionFlag_NONE,
flat_rule::ElementType_OBJECT, flat_rule::ActivationType_NONE,
flat_rule::UrlPatternType_SUBSTRING, flat_rule::AnchorType_NONE,
flat_rule::AnchorType_BOUNDARY, "google.com", {"a.com"}, {"x.a.com"},
base::nullopt, dnr_api::RULE_ACTION_TYPE_BLOCK, nullptr, base::nullopt,
{}, {}));
rules_to_index.push_back(CreateIndexedRule(
2, kMinValidPriority, flat_rule::OptionFlag_APPLIES_TO_THIRD_PARTY,
flat_rule::ElementType_IMAGE | flat_rule::ElementType_WEBSOCKET,
flat_rule::ActivationType_NONE, flat_rule::UrlPatternType_WILDCARDED,
flat_rule::AnchorType_NONE, flat_rule::AnchorType_NONE, "*google*",
{"a.com"}, {}, base::nullopt, dnr_api::RULE_ACTION_TYPE_BLOCK, nullptr,
base::nullopt, {}, {}));
// Redirect rules.
rules_to_index.push_back(CreateIndexedRule(
15, 2, flat_rule::OptionFlag_APPLIES_TO_FIRST_PARTY,
flat_rule::ElementType_IMAGE, flat_rule::ActivationType_NONE,
flat_rule::UrlPatternType_SUBSTRING, flat_rule::AnchorType_SUBDOMAIN,
flat_rule::AnchorType_BOUNDARY, "google.com", {}, {},
"http://example1.com", dnr_api::RULE_ACTION_TYPE_REDIRECT, nullptr,
base::nullopt, {}, {}));
rules_to_index.push_back(CreateIndexedRule(
10, 2, flat_rule::OptionFlag_NONE,
flat_rule::ElementType_SUBDOCUMENT | flat_rule::ElementType_SCRIPT,
flat_rule::ActivationType_NONE, flat_rule::UrlPatternType_SUBSTRING,
flat_rule::AnchorType_NONE, flat_rule::AnchorType_NONE, "example1", {},
{"a.com"}, "http://example2.com", dnr_api::RULE_ACTION_TYPE_REDIRECT,
nullptr, base::nullopt, {}, {}));
rules_to_index.push_back(CreateIndexedRule(
9, 3, flat_rule::OptionFlag_NONE, flat_rule::ElementType_NONE,
flat_rule::ActivationType_NONE, flat_rule::UrlPatternType_WILDCARDED,
flat_rule::AnchorType_NONE, flat_rule::AnchorType_NONE, "*", {}, {},
"http://example2.com", dnr_api::RULE_ACTION_TYPE_REDIRECT, nullptr,
base::nullopt, {}, {}));
rules_to_index.push_back(CreateIndexedRule(
100, 3, flat_rule::OptionFlag_NONE, flat_rule::ElementType_NONE,
flat_rule::ActivationType_NONE, flat_rule::UrlPatternType_WILDCARDED,
flat_rule::AnchorType_NONE, flat_rule::AnchorType_NONE, "*", {}, {},
base::nullopt, dnr_api::RULE_ACTION_TYPE_REDIRECT, CreateUrlTransform(),
base::nullopt, {}, {}));
// Allow rules.
rules_to_index.push_back(CreateIndexedRule(
17, kMinValidPriority, flat_rule::OptionFlag_IS_ALLOWLIST,
flat_rule::ElementType_PING | flat_rule::ElementType_SCRIPT,
flat_rule::ActivationType_NONE, flat_rule::UrlPatternType_SUBSTRING,
flat_rule::AnchorType_SUBDOMAIN, flat_rule::AnchorType_NONE,
"example1.com", {"xyz.com"}, {}, base::nullopt,
dnr_api::RULE_ACTION_TYPE_ALLOW, nullptr, base::nullopt, {}, {}));
rules_to_index.push_back(CreateIndexedRule(
16, kMinValidPriority,
flat_rule::OptionFlag_IS_ALLOWLIST |
flat_rule::OptionFlag_IS_CASE_INSENSITIVE,
flat_rule::ElementType_IMAGE, flat_rule::ActivationType_NONE,
flat_rule::UrlPatternType_SUBSTRING, flat_rule::AnchorType_NONE,
flat_rule::AnchorType_NONE, "example3", {}, {}, base::nullopt,
dnr_api::RULE_ACTION_TYPE_ALLOW, nullptr, base::nullopt, {}, {}));
// Allow all requests rule.
rules_to_index.push_back(CreateIndexedRule(
22, 3, flat_rule::OptionFlag_NONE, flat_rule::ElementType_SUBDOCUMENT,
flat_rule::ActivationType_NONE, flat_rule::UrlPatternType_SUBSTRING,
flat_rule::AnchorType_NONE, flat_rule::AnchorType_NONE, "example.com", {},
{}, base::nullopt, dnr_api::RULE_ACTION_TYPE_ALLOWALLREQUESTS, nullptr,
base::nullopt, {}, {}));
// Modify headers rules.
std::vector<dnr_api::ModifyHeaderInfo> request_headers_1;
request_headers_1.push_back(CreateModifyHeaderInfo(
dnr_api::HEADER_OPERATION_SET, "cookie", "sample-cookie"));
std::vector<dnr_api::ModifyHeaderInfo> response_headers_1;
response_headers_1.push_back(CreateModifyHeaderInfo(
dnr_api::HEADER_OPERATION_REMOVE, "set-cookie", base::nullopt));
response_headers_1.push_back(CreateModifyHeaderInfo(
dnr_api::HEADER_OPERATION_APPEND, "custom-1", "value-1"));
response_headers_1.push_back(CreateModifyHeaderInfo(
dnr_api::HEADER_OPERATION_SET, "custom-2", "value-2"));
rules_to_index.push_back(CreateIndexedRule(
23, kMinValidPriority, flat_rule::OptionFlag_IS_CASE_INSENSITIVE,
flat_rule::ElementType_SUBDOCUMENT, flat_rule::ActivationType_NONE,
flat_rule::UrlPatternType_SUBSTRING, flat_rule::AnchorType_SUBDOMAIN,
flat_rule::AnchorType_NONE, "example.com", {}, {}, base::nullopt,
dnr_api::RULE_ACTION_TYPE_MODIFYHEADERS, nullptr, base::nullopt,
std::move(request_headers_1), std::move(response_headers_1)));
std::vector<dnr_api::ModifyHeaderInfo> request_headers_2;
request_headers_2.push_back(CreateModifyHeaderInfo(
dnr_api::HEADER_OPERATION_REMOVE, "referer", base::nullopt));
rules_to_index.push_back(CreateIndexedRule(
24, kMinValidPriority, flat_rule::OptionFlag_IS_CASE_INSENSITIVE,
flat_rule::ElementType_SUBDOCUMENT, flat_rule::ActivationType_NONE,
flat_rule::UrlPatternType_SUBSTRING, flat_rule::AnchorType_SUBDOMAIN,
flat_rule::AnchorType_NONE, "example.com", {}, {}, base::nullopt,
dnr_api::RULE_ACTION_TYPE_MODIFYHEADERS, nullptr, base::nullopt,
std::move(request_headers_2), {}));
// Note: It's unsafe to store/return pointers to a mutable vector since the
// vector can resize/reallocate invalidating the existing pointers/iterators.
// Hence we build |expected_index_lists| once the vector |rules_to_index| is
// finalized.
std::vector<const IndexedRule*> expected_index_lists[flat::IndexType_count];
expected_index_lists
[flat::IndexType_before_request_except_allow_all_requests] = {
&rules_to_index[0], &rules_to_index[1], &rules_to_index[2],
&rules_to_index[3], &rules_to_index[4], &rules_to_index[5],
&rules_to_index[6], &rules_to_index[7]};
expected_index_lists[flat::IndexType_allow_all_requests] = {
&rules_to_index[8]};
expected_index_lists[flat::IndexType_modify_headers] = {&rules_to_index[9],
&rules_to_index[10]};
AddRulesAndVerifyIndex(rules_to_index, expected_index_lists);
}
// Verify that the serialized flatbuffer data is valid for regex rules.
TEST_F(FlatRulesetIndexerTest, RegexRules) {
std::vector<IndexedRule> rules_to_index;
// Blocking rule.
rules_to_index.push_back(CreateIndexedRule(
7, kMinValidPriority, flat_rule::OptionFlag_NONE,
flat_rule::ElementType_OBJECT, flat_rule::ActivationType_NONE,
flat_rule::UrlPatternType_REGEXP, flat_rule::AnchorType_NONE,
flat_rule::AnchorType_NONE, R"(^https://(abc|def))", {"a.com"},
{"x.a.com"}, base::nullopt, dnr_api::RULE_ACTION_TYPE_BLOCK, nullptr,
base::nullopt, {}, {}));
// Redirect rule.
rules_to_index.push_back(CreateIndexedRule(
15, 2, flat_rule::OptionFlag_APPLIES_TO_FIRST_PARTY,
flat_rule::ElementType_IMAGE, flat_rule::ActivationType_NONE,
flat_rule::UrlPatternType_REGEXP, flat_rule::AnchorType_NONE,
flat_rule::AnchorType_NONE, R"(^(http|https))", {}, {},
"http://example1.com", dnr_api::RULE_ACTION_TYPE_REDIRECT, nullptr,
base::nullopt, {}, {}));
// Regex substitution rule.
rules_to_index.push_back(CreateIndexedRule(
10, 29, flat_rule::OptionFlag_APPLIES_TO_FIRST_PARTY,
flat_rule::ElementType_SCRIPT, flat_rule::ActivationType_NONE,
flat_rule::UrlPatternType_REGEXP, flat_rule::AnchorType_NONE,
flat_rule::AnchorType_NONE, R"((\d+\).google.com)", {}, {}, base::nullopt,
dnr_api::RULE_ACTION_TYPE_REDIRECT, nullptr,
R"(http://redirect.com?num=\1)", {}, {}));
// Modify headers rule.
std::vector<dnr_api::ModifyHeaderInfo> request_headers;
request_headers.push_back(CreateModifyHeaderInfo(
dnr_api::HEADER_OPERATION_REMOVE, "referer", base::nullopt));
request_headers.push_back(CreateModifyHeaderInfo(
dnr_api::HEADER_OPERATION_SET, "cookie", "sample-cookie"));
rules_to_index.push_back(CreateIndexedRule(
21, kMinValidPriority, flat_rule::OptionFlag_IS_CASE_INSENSITIVE,
flat_rule::ElementType_SUBDOCUMENT, flat_rule::ActivationType_NONE,
flat_rule::UrlPatternType_REGEXP, flat_rule::AnchorType_NONE,
flat_rule::AnchorType_NONE, "*", {}, {}, base::nullopt,
dnr_api::RULE_ACTION_TYPE_MODIFYHEADERS, nullptr, base::nullopt,
std::move(request_headers), {}));
flatbuffers::DetachedBuffer buffer;
const flat::ExtensionIndexedRuleset* ruleset =
AddRuleAndGetRuleset(rules_to_index, &buffer);
ASSERT_TRUE(ruleset);
// All the indices should be empty, since we only have regex rules.
for (size_t i = 0; i < flat::IndexType_count; ++i) {
SCOPED_TRACE(base::StringPrintf("Testing index %" PRIuS, i));
VerifyIndexEquality({}, ruleset->index_list()->Get(i));
}
{
SCOPED_TRACE("Testing extension metadata");
std::vector<const IndexedRule*> all_rules;
for (IndexedRule& rule : rules_to_index)
all_rules.push_back(&rule);
VerifyExtensionMetadata(all_rules, ruleset->extension_metadata());
}
ASSERT_TRUE(ruleset->regex_rules());
ASSERT_EQ(4u, ruleset->regex_rules()->size());
const flat::RegexRule* blocking_rule = nullptr;
const flat::RegexRule* redirect_rule = nullptr;
const flat::RegexRule* regex_substitution_rule = nullptr;
const flat::RegexRule* modify_header_rule = nullptr;
for (const auto* regex_rule : *ruleset->regex_rules()) {
if (regex_rule->action_type() == flat::ActionType_block)
blocking_rule = regex_rule;
else if (regex_rule->action_type() == flat::ActionType_redirect) {
if (regex_rule->regex_substitution())
regex_substitution_rule = regex_rule;
else
redirect_rule = regex_rule;
} else if (regex_rule->action_type() == flat::ActionType_modify_headers)
modify_header_rule = regex_rule;
}
ASSERT_TRUE(blocking_rule);
EXPECT_TRUE(AreRulesEqual(&rules_to_index[0], blocking_rule->url_rule()));
EXPECT_FALSE(blocking_rule->regex_substitution());
ASSERT_TRUE(redirect_rule);
EXPECT_TRUE(AreRulesEqual(&rules_to_index[1], redirect_rule->url_rule()));
EXPECT_FALSE(redirect_rule->regex_substitution());
ASSERT_TRUE(regex_substitution_rule);
EXPECT_TRUE(
AreRulesEqual(&rules_to_index[2], regex_substitution_rule->url_rule()));
EXPECT_EQ(R"(http://redirect.com?num=\1)",
ToString(regex_substitution_rule->regex_substitution()));
ASSERT_TRUE(modify_header_rule);
EXPECT_TRUE(
AreRulesEqual(&rules_to_index[3], modify_header_rule->url_rule()));
EXPECT_FALSE(modify_header_rule->regex_substitution());
}
} // namespace
} // namespace declarative_net_request
} // namespace extensions