blob: 18bd0ef876abcfb95fcbe461a573ebecf485813d [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// 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/ruleset_matcher.h"
#include <limits>
#include <utility>
#include <vector>
#include "base/check.h"
#include "base/files/file_util.h"
#include "base/format_macros.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "components/url_pattern_index/flat/url_pattern_index_generated.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/mock_navigation_handle.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "extensions/browser/api/declarative_net_request/constants.h"
#include "extensions/browser/api/declarative_net_request/file_backed_ruleset_source.h"
#include "extensions/browser/api/declarative_net_request/request_action.h"
#include "extensions/browser/api/declarative_net_request/request_params.h"
#include "extensions/browser/api/declarative_net_request/test_utils.h"
#include "extensions/browser/api/declarative_net_request/utils.h"
#include "extensions/browser/extensions_test.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/test_utils.h"
#include "net/http/http_request_headers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace extensions {
namespace declarative_net_request {
namespace {
namespace dnr_api = api::declarative_net_request;
using RulesetMatcherTest = ExtensionsTest;
// Tests a simple blocking rule.
TEST_F(RulesetMatcherTest, BlockingRule) {
TestRule rule = CreateGenericRule();
rule.condition->url_filter = std::string("google.com");
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher({rule}, CreateTemporarySource(), &matcher));
auto should_block_request = [&matcher](const RequestParams& params) {
auto action = matcher->GetBeforeRequestAction(params);
return action.has_value() && action->IsBlockOrCollapse();
};
GURL google_url("http://google.com");
RequestParams params;
params.url = &google_url;
params.element_type = url_pattern_index::flat::ElementType_SUBDOCUMENT;
params.is_third_party = true;
EXPECT_TRUE(should_block_request(params));
GURL yahoo_url("http://yahoo.com");
params.url = &yahoo_url;
EXPECT_FALSE(should_block_request(params));
}
// Tests a simple redirect rule.
TEST_F(RulesetMatcherTest, RedirectRule) {
TestRule rule = CreateGenericRule();
rule.condition->url_filter = std::string("google.com");
rule.priority = kMinValidPriority;
rule.action->type = std::string("redirect");
rule.action->redirect.emplace();
rule.action->redirect->url = std::string("http://yahoo.com");
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher({rule}, CreateTemporarySource(), &matcher));
GURL google_url("http://google.com");
GURL yahoo_url("http://yahoo.com");
RequestParams params;
params.url = &google_url;
params.element_type = url_pattern_index::flat::ElementType_SUBDOCUMENT;
params.is_third_party = true;
absl::optional<RequestAction> redirect_action =
matcher->GetBeforeRequestAction(params);
ASSERT_TRUE(redirect_action);
ASSERT_EQ(redirect_action->type, RequestAction::Type::REDIRECT);
EXPECT_EQ(yahoo_url, redirect_action->redirect_url);
params.url = &yahoo_url;
EXPECT_FALSE(matcher->GetBeforeRequestAction(params));
}
// Test that a URL cannot redirect to itself, as filed in crbug.com/954646.
TEST_F(RulesetMatcherTest, PreventSelfRedirect) {
TestRule rule = CreateGenericRule();
rule.condition->url_filter = std::string("go*");
rule.priority = kMinValidPriority;
rule.action->type = std::string("redirect");
rule.action->redirect.emplace();
rule.action->redirect->url = std::string("http://google.com");
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher({rule}, CreateTemporarySource(), &matcher));
GURL url("http://google.com");
RequestParams params;
params.url = &url;
params.element_type = url_pattern_index::flat::ElementType_SUBDOCUMENT;
params.is_third_party = true;
EXPECT_FALSE(matcher->GetBeforeRequestAction(params));
}
// Tests a simple upgrade scheme rule.
TEST_F(RulesetMatcherTest, UpgradeRule) {
TestRule rule = CreateGenericRule();
rule.condition->url_filter = std::string("google.com");
rule.priority = kMinValidPriority;
rule.action->type = std::string("upgradeScheme");
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher({rule}, CreateTemporarySource(), &matcher));
auto should_upgrade_request = [&matcher](const RequestParams& params) {
auto action = matcher->GetBeforeRequestAction(params);
return action.has_value() && action->type == RequestAction::Type::UPGRADE;
};
GURL google_url("http://google.com");
GURL yahoo_url("http://yahoo.com");
GURL non_upgradeable_url("https://google.com");
RequestParams params;
params.url = &google_url;
params.element_type = url_pattern_index::flat::ElementType_SUBDOCUMENT;
params.is_third_party = true;
EXPECT_TRUE(should_upgrade_request(params));
params.url = &yahoo_url;
EXPECT_FALSE(should_upgrade_request(params));
params.url = &non_upgradeable_url;
EXPECT_FALSE(should_upgrade_request(params));
}
// Tests that a modified ruleset file fails verification.
TEST_F(RulesetMatcherTest, FailedVerification) {
FileBackedRulesetSource source = CreateTemporarySource();
std::unique_ptr<RulesetMatcher> matcher;
int expected_checksum;
ASSERT_TRUE(CreateVerifiedMatcher({}, source, &matcher, &expected_checksum));
// Persist invalid data to the ruleset file and ensure that a version mismatch
// occurs.
std::string data = "invalid data";
ASSERT_EQ(static_cast<int>(data.size()),
base::WriteFile(source.indexed_path(), data.c_str(), data.size()));
EXPECT_EQ(LoadRulesetResult::kErrorVersionMismatch,
source.CreateVerifiedMatcher(expected_checksum, &matcher));
// Now, persist invalid data to the ruleset file, while maintaining the
// correct version header. Ensure that it fails verification due to checksum
// mismatch.
data = GetVersionHeaderForTesting() + "invalid data";
ASSERT_EQ(static_cast<int>(data.size()),
base::WriteFile(source.indexed_path(), data.c_str(), data.size()));
EXPECT_EQ(LoadRulesetResult::kErrorChecksumMismatch,
source.CreateVerifiedMatcher(expected_checksum, &matcher));
}
TEST_F(RulesetMatcherTest, ModifyHeaders_IsExtraHeaderMatcher) {
TestRule rule = CreateGenericRule();
rule.condition->url_filter = std::string("example.com");
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher({rule}, CreateTemporarySource(), &matcher));
EXPECT_FALSE(matcher->IsExtraHeadersMatcher());
rule.action->type = std::string("modifyHeaders");
rule.action->response_headers = std::vector<TestHeaderInfo>(
{TestHeaderInfo("HEADER3", "append", "VALUE3")});
ASSERT_TRUE(CreateVerifiedMatcher({rule}, CreateTemporarySource(), &matcher));
EXPECT_TRUE(matcher->IsExtraHeadersMatcher());
}
TEST_F(RulesetMatcherTest, ModifyHeaders) {
TestRule rule_1 = CreateGenericRule();
rule_1.id = kMinValidID;
rule_1.priority = kMinValidPriority + 1;
rule_1.condition->url_filter = std::string("example.com");
rule_1.action->type = std::string("modifyHeaders");
rule_1.action->request_headers = std::vector<TestHeaderInfo>(
{TestHeaderInfo("header1", "remove", absl::nullopt)});
TestRule rule_2 = CreateGenericRule();
rule_2.id = kMinValidID + 1;
rule_2.priority = kMinValidPriority;
rule_2.condition->url_filter = std::string("example.com");
rule_2.action->type = std::string("modifyHeaders");
rule_2.action->request_headers = std::vector<TestHeaderInfo>(
{TestHeaderInfo("header1", "set", "value1"),
TestHeaderInfo("header2", "remove", absl::nullopt)});
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher({rule_1, rule_2}, CreateTemporarySource(),
&matcher));
EXPECT_TRUE(matcher->IsExtraHeadersMatcher());
GURL example_url("http://example.com");
RequestParams params;
params.url = &example_url;
params.element_type = url_pattern_index::flat::ElementType_SUBDOCUMENT;
params.is_third_party = true;
std::vector<RequestAction> modify_header_actions =
matcher->GetModifyHeadersActions(params, 0u /* min_priority */);
RequestAction expected_rule_1_action = CreateRequestActionForTesting(
RequestAction::Type::MODIFY_HEADERS, *rule_1.id, *rule_1.priority);
expected_rule_1_action.request_headers_to_modify = {RequestAction::HeaderInfo(
"header1", dnr_api::HEADER_OPERATION_REMOVE, absl::nullopt)};
RequestAction expected_rule_2_action = CreateRequestActionForTesting(
RequestAction::Type::MODIFY_HEADERS, *rule_2.id, *rule_2.priority);
expected_rule_2_action.request_headers_to_modify = {
RequestAction::HeaderInfo("header1", dnr_api::HEADER_OPERATION_SET,
"value1"),
RequestAction::HeaderInfo("header2", dnr_api::HEADER_OPERATION_REMOVE,
absl::nullopt)};
EXPECT_THAT(modify_header_actions,
testing::UnorderedElementsAre(
testing::Eq(testing::ByRef(expected_rule_1_action)),
testing::Eq(testing::ByRef(expected_rule_2_action))));
}
// Tests a rule to redirect to an extension path.
TEST_F(RulesetMatcherTest, RedirectToExtensionPath) {
TestRule rule = CreateGenericRule();
rule.condition->url_filter = std::string("example.com");
rule.action->type = std::string("redirect");
rule.priority = kMinValidPriority;
rule.action->redirect.emplace();
rule.action->redirect->extension_path = "/path/newfile.js?query#fragment";
std::unique_ptr<RulesetMatcher> matcher;
const RulesetID kRulesetId(5);
const size_t kRuleCountLimit = 10;
ASSERT_TRUE(CreateVerifiedMatcher(
{rule}, CreateTemporarySource(kRulesetId, kRuleCountLimit), &matcher));
GURL example_url("http://example.com");
RequestParams params;
params.url = &example_url;
absl::optional<RequestAction> redirect_action =
matcher->GetBeforeRequestAction(params);
RequestAction expected_action = CreateRequestActionForTesting(
RequestAction::Type::REDIRECT, *rule.id, *rule.priority, kRulesetId);
expected_action.redirect_url =
GURL("chrome-extension://extensionid/path/newfile.js?query#fragment");
EXPECT_EQ(expected_action, redirect_action);
}
// Tests a rule to redirect to a static url.
TEST_F(RulesetMatcherTest, RedirectToStaticUrl) {
TestRule rule = CreateGenericRule();
rule.condition->url_filter = std::string("example.com");
rule.action->type = std::string("redirect");
rule.priority = kMinValidPriority;
rule.action->redirect.emplace();
rule.action->redirect->url = "https://google.com";
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher({rule}, CreateTemporarySource(), &matcher));
GURL example_url("http://example.com");
RequestParams params;
params.url = &example_url;
absl::optional<RequestAction> redirect_action =
matcher->GetBeforeRequestAction(params);
ASSERT_TRUE(redirect_action.has_value());
EXPECT_EQ(redirect_action->type, RequestAction::Type::REDIRECT);
GURL expected_redirect_url("https://google.com");
EXPECT_EQ(expected_redirect_url, redirect_action->redirect_url);
}
// Tests url transformation rules.
TEST_F(RulesetMatcherTest, UrlTransform) {
struct TestCase {
std::string url;
// Valid if a redirect is expected.
absl::optional<std::string> expected_redirect_url;
};
std::vector<TestCase> cases;
std::vector<TestRule> rules;
auto create_transform_rule = [](size_t id, const std::string& filter) {
TestRule rule = CreateGenericRule();
rule.id = id;
rule.condition->url_filter = filter;
rule.priority = kMinValidPriority;
rule.action->type = std::string("redirect");
rule.action->redirect.emplace();
rule.action->redirect->transform.emplace();
return rule;
};
TestRule rule = create_transform_rule(1, "||1.com");
rule.action->redirect->transform->scheme = "https";
rules.push_back(rule);
cases.push_back({"http://1.com/path?query", "https://1.com/path?query"});
rule = create_transform_rule(2, "||2.com");
rule.action->redirect->transform->scheme = "ftp";
rule.action->redirect->transform->host = "ftp.host.com";
rule.action->redirect->transform->port = "70";
rules.push_back(rule);
cases.push_back(
{"http://2.com:100/path?query", "ftp://ftp.host.com:70/path?query"});
rule = create_transform_rule(3, "||3.com");
rule.action->redirect->transform->port = "";
rule.action->redirect->transform->path = "";
rule.action->redirect->transform->query = "?new_query";
rule.action->redirect->transform->fragment = "#fragment";
rules.push_back(rule);
// The path separator '/' is output even when cleared, except when there is no
// authority, query and fragment.
cases.push_back(
{"http://3.com:100/path?query", "http://3.com/?new_query#fragment"});
rule = create_transform_rule(4, "||4.com");
rule.action->redirect->transform->scheme = "http";
rule.action->redirect->transform->path = " ";
rules.push_back(rule);
cases.push_back({"http://4.com/xyz", "http://4.com/%20"});
rule = create_transform_rule(5, "||5.com");
rule.action->redirect->transform->path = "/";
rule.action->redirect->transform->query = "";
rule.action->redirect->transform->fragment = "#";
rules.push_back(rule);
cases.push_back(
{"http://5.com:100/path?query#fragment", "http://5.com:100/#"});
rule = create_transform_rule(6, "||6.com");
rule.action->redirect->transform->path = "/path?query";
rules.push_back(rule);
// The "?" in path is url encoded since it's not part of the query.
cases.push_back({"http://6.com/xyz?q1", "http://6.com/path%3Fquery?q1"});
rule = create_transform_rule(7, "||7.com");
rule.action->redirect->transform->username = "new_user";
rule.action->redirect->transform->password = "new_pass";
rules.push_back(rule);
cases.push_back(
{"http://user@7.com/xyz", "http://new_user:new_pass@7.com/xyz"});
auto make_query = [](const std::string& key, const std::string& value,
const absl::optional<bool>& replace_only =
absl::nullopt) {
TestRuleQueryKeyValue query;
query.key = key;
query.value = value;
query.replace_only = replace_only;
return query;
};
rule = create_transform_rule(8, "||8.com");
rule.action->redirect->transform->query_transform.emplace();
rule.action->redirect->transform->query_transform->remove_params =
std::vector<std::string>({"r1", "r2"});
rule.action->redirect->transform->query_transform->add_or_replace_params =
std::vector<TestRuleQueryKeyValue>(
{make_query("a1", "#"), make_query("a2", ""),
make_query("a1", "new2"), make_query("a1", "new3")});
rules.push_back(rule);
cases.push_back(
{"http://8.com/"
"path?r1&r1=val1&a1=val1&r2=val&x3=val&a1=val2&a2=val&r1=val2",
"http://8.com/path?a1=%23&x3=val&a1=new2&a2=&a1=new3"});
cases.push_back({"http://8.com/path?query",
"http://8.com/path?query=&a1=%23&a2=&a1=new2&a1=new3"});
rule = create_transform_rule(9, "||9.com");
rule.action->redirect->transform->query_transform.emplace();
rule.action->redirect->transform->query_transform->remove_params =
std::vector<std::string>({"r1", "r2"});
rules.push_back(rule);
// No redirect is performed since the url won't change.
cases.push_back({"http://9.com/path?query#fragment", absl::nullopt});
rule = create_transform_rule(10, "||10.com");
rule.action->redirect->transform->query_transform.emplace();
rule.action->redirect->transform->query_transform->remove_params =
std::vector<std::string>({"q1"});
rule.action->redirect->transform->query_transform->add_or_replace_params =
std::vector<TestRuleQueryKeyValue>({make_query("q1", "new")});
rules.push_back(rule);
cases.push_back(
{"https://10.com/path?q1=1&q1=2&q1=3", "https://10.com/path?q1=new"});
rule = create_transform_rule(11, "||11.com");
rule.action->redirect->transform->query_transform.emplace();
rule.action->redirect->transform->query_transform->add_or_replace_params =
std::vector<TestRuleQueryKeyValue>(
{make_query("foo", "bar"), make_query("hello", "world", false),
make_query("abc", "123", true), make_query("abc", "456", true)});
rules.push_back(rule);
cases.push_back(
{"https://11.com/path", "https://11.com/path?foo=bar&hello=world"});
cases.push_back({"https://11.com/path?abc=def",
"https://11.com/path?abc=123&foo=bar&hello=world"});
cases.push_back({"https://11.com/path?hello=goodbye&abc=def",
"https://11.com/path?hello=world&abc=123&foo=bar"});
cases.push_back({"https://11.com/path?foo=1&foo=2",
"https://11.com/path?foo=bar&foo=2&hello=world"});
cases.push_back(
{"https://11.com/path?abc=1&abc=2&abc=3",
"https://11.com/path?abc=123&abc=456&abc=3&foo=bar&hello=world"});
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher(rules, CreateTemporarySource(), &matcher));
for (const auto& test_case : cases) {
SCOPED_TRACE(base::StringPrintf("Testing url %s", test_case.url.c_str()));
GURL url(test_case.url);
ASSERT_TRUE(url.is_valid()) << test_case.url;
RequestParams params;
params.url = &url;
absl::optional<RequestAction> redirect_action =
matcher->GetBeforeRequestAction(params);
if (!test_case.expected_redirect_url) {
EXPECT_FALSE(redirect_action) << redirect_action->redirect_url->spec();
continue;
}
ASSERT_TRUE(redirect_action.has_value());
EXPECT_EQ(redirect_action->type, RequestAction::Type::REDIRECT);
ASSERT_TRUE(GURL(*test_case.expected_redirect_url).is_valid())
<< *test_case.expected_redirect_url;
ASSERT_TRUE(redirect_action.has_value());
EXPECT_EQ(GURL(*test_case.expected_redirect_url),
redirect_action->redirect_url);
}
}
// Tests regex rules are evaluated correctly for different action types.
TEST_F(RulesetMatcherTest, RegexRules) {
auto create_regex_rule = [](size_t id, const std::string& regex_filter) {
TestRule rule = CreateGenericRule();
rule.id = id;
rule.condition->url_filter.reset();
rule.condition->regex_filter = regex_filter;
return rule;
};
std::vector<TestRule> rules;
// Add a blocking rule.
TestRule block_rule = create_regex_rule(1, R"((?:block|collapse)\.com/path)");
rules.push_back(block_rule);
// Add an allowlist rule.
TestRule allow_rule = create_regex_rule(2, R"(http://(\w+\.)+allow\.com)");
allow_rule.action->type = "allow";
rules.push_back(allow_rule);
// Add a redirect rule.
TestRule redirect_rule = create_regex_rule(3, R"(redirect\.com)");
redirect_rule.action->type = "redirect";
redirect_rule.action->redirect.emplace();
redirect_rule.priority = kMinValidPriority;
redirect_rule.action->redirect->url = "https://google.com";
rules.push_back(redirect_rule);
// Add a upgrade rule.
TestRule upgrade_rule = create_regex_rule(4, "upgrade");
upgrade_rule.action->type = "upgradeScheme";
upgrade_rule.priority = kMinValidPriority;
rules.push_back(upgrade_rule);
// Add a modify headers rule.
TestRule modify_headers_rule =
create_regex_rule(6, R"(^(?:http|https)://[a-z\.]+\.org)");
modify_headers_rule.action->type = "modifyHeaders";
modify_headers_rule.action->request_headers =
std::vector<TestHeaderInfo>({TestHeaderInfo("header1", "set", "value1")});
rules.push_back(modify_headers_rule);
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher(rules, CreateTemporarySource(), &matcher));
struct TestCase {
const char* url = nullptr;
absl::optional<RequestAction> expected_action;
absl::optional<RequestAction> expected_modify_header_action;
};
std::vector<TestCase> test_cases;
{
TestCase test_case = {"http://www.block.com/path"};
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::BLOCK, *block_rule.id);
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://www.collapse.com/PATH"};
// Filters are case sensitive by default, hence the request doesn't match.
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://abc.xyz.allow.com/path"};
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::ALLOW, *allow_rule.id);
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://allow.com/path"};
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://redirect.com?path=abc"};
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::REDIRECT, *redirect_rule.id);
test_case.expected_action->redirect_url =
GURL(*redirect_rule.action->redirect->url);
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://redirect.eu#query"};
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://example.com/upgrade"};
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::UPGRADE, *upgrade_rule.id);
test_case.expected_action->redirect_url.emplace(
"https://example.com/upgrade");
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://abc.org/path"};
test_case.expected_modify_header_action = CreateRequestActionForTesting(
RequestAction::Type::MODIFY_HEADERS, *modify_headers_rule.id);
test_case.expected_modify_header_action->request_headers_to_modify = {
RequestAction::HeaderInfo("header1", dnr_api::HEADER_OPERATION_SET,
"value1")};
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://example.com"};
test_cases.push_back(std::move(test_case));
}
for (const auto& test_case : test_cases) {
SCOPED_TRACE(test_case.url);
GURL url(test_case.url);
RequestParams params;
params.url = &url;
EXPECT_EQ(test_case.expected_action,
matcher->GetBeforeRequestAction(params));
std::vector<RequestAction> modify_header_actions =
matcher->GetModifyHeadersActions(params, 0u /* min_priority */);
if (test_case.expected_modify_header_action) {
EXPECT_THAT(modify_header_actions,
testing::ElementsAre(testing::Eq(testing::ByRef(
test_case.expected_modify_header_action))));
} else {
EXPECT_TRUE(modify_header_actions.empty());
}
}
EXPECT_TRUE(matcher->IsExtraHeadersMatcher());
}
// Ensure that the rule metadata is checked correctly for regex rules.
TEST_F(RulesetMatcherTest, RegexRules_Metadata) {
auto create_regex_rule = [](size_t id, const std::string& regex_filter) {
TestRule rule = CreateGenericRule();
rule.id = id;
rule.condition->url_filter.reset();
rule.condition->regex_filter = regex_filter;
return rule;
};
std::vector<TestRule> rules;
// Add a case sensitive rule.
TestRule path_rule = create_regex_rule(1, "/PATH");
rules.push_back(path_rule);
// Add a case insensitive rule.
TestRule xyz_rule = create_regex_rule(2, "/XYZ");
xyz_rule.condition->is_url_filter_case_sensitive = false;
rules.push_back(xyz_rule);
// Test `domains`, `excludedDomains`.
TestRule domains_rule = create_regex_rule(3, "deprecated_domains");
domains_rule.condition->domains = std::vector<std::string>({"example.com"});
domains_rule.condition->excluded_domains =
std::vector<std::string>({"b.example.com"});
rules.push_back(domains_rule);
// Test `initiatorDomains`, `excludedInitiatorDomains`.
TestRule initiator_domains_rule = create_regex_rule(4, "initiator_domains");
initiator_domains_rule.condition->initiator_domains =
std::vector<std::string>({"example.com"});
initiator_domains_rule.condition->excluded_initiator_domains =
std::vector<std::string>({"b.example.com"});
rules.push_back(initiator_domains_rule);
// Test `requestDomains`, `excludedRequestDomains`.
TestRule request_domains_rule = create_regex_rule(5, "request_domains");
request_domains_rule.condition->request_domains =
std::vector<std::string>({"example.com"});
request_domains_rule.condition->excluded_request_domains =
std::vector<std::string>({"b.example.com"});
rules.push_back(request_domains_rule);
// Test |resourceTypes|.
TestRule sub_frame_rule = create_regex_rule(6, R"((abc|def)\.com)");
sub_frame_rule.condition->resource_types =
std::vector<std::string>({"sub_frame"});
rules.push_back(sub_frame_rule);
// Test |domainType|.
TestRule third_party_rule = create_regex_rule(7, R"(http://(\d+)\.com)");
third_party_rule.condition->domain_type = "thirdParty";
rules.push_back(third_party_rule);
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher(rules, CreateTemporarySource(), &matcher));
struct TestCase {
const char* url = nullptr;
url::Origin first_party_origin;
url_pattern_index::flat::ElementType element_type =
url_pattern_index::flat::ElementType_OTHER;
bool is_third_party = false;
absl::optional<RequestAction> expected_action;
};
std::vector<TestCase> test_cases;
{
TestCase test_case = {"http://example.com/path/abc"};
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://example.com/PATH/abc"};
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::BLOCK, *path_rule.id);
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://example.com/xyz/abc"};
test_case.expected_action =
CreateRequestActionForTesting(RequestAction::Type::BLOCK, *xyz_rule.id);
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://example.com/XYZ/abc"};
test_case.expected_action =
CreateRequestActionForTesting(RequestAction::Type::BLOCK, *xyz_rule.id);
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://example.com/deprecated_domains"};
test_case.first_party_origin =
url::Origin::Create(GURL("http://a.example.com"));
test_case.is_third_party = true;
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::BLOCK, *domains_rule.id);
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://example.com/deprecated_domains"};
test_case.first_party_origin =
url::Origin::Create(GURL("http://b.example.com"));
test_case.is_third_party = true;
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://example.com/initiator_domains"};
test_case.first_party_origin =
url::Origin::Create(GURL("http://a.example.com"));
test_case.is_third_party = true;
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::BLOCK, *initiator_domains_rule.id);
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://example.com/initiator_domains"};
test_case.first_party_origin =
url::Origin::Create(GURL("http://b.example.com"));
test_case.is_third_party = true;
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://example.com/request_domains"};
test_case.first_party_origin =
url::Origin::Create(GURL("http://foobar.com"));
test_case.is_third_party = true;
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::BLOCK, *request_domains_rule.id);
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://example.com/request_domains"};
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::BLOCK, *request_domains_rule.id);
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://b.example.com/request_domains"};
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://foobar.com/request_domains"};
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://abc.com"};
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://abc.com"};
test_case.element_type = url_pattern_index::flat::ElementType_SUBDOCUMENT;
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::COLLAPSE, *sub_frame_rule.id);
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://243.com"};
test_case.is_third_party = true;
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::BLOCK, *third_party_rule.id);
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://243.com"};
test_case.first_party_origin = url::Origin::Create(GURL(test_case.url));
test_case.is_third_party = false;
test_cases.push_back(std::move(test_case));
}
for (size_t i = 0; i < std::size(test_cases); ++i) {
SCOPED_TRACE(base::StringPrintf("Case number-%" PRIuS " url-%s", i,
test_cases[i].url));
GURL url(test_cases[i].url);
RequestParams params;
params.url = &url;
params.first_party_origin = test_cases[i].first_party_origin;
params.element_type = test_cases[i].element_type;
params.is_third_party = test_cases[i].is_third_party;
EXPECT_EQ(test_cases[i].expected_action,
matcher->GetBeforeRequestAction(params));
}
}
// Ensures that RulesetMatcher combines the results of regex and filter-list
// style redirect rules correctly.
TEST_F(RulesetMatcherTest, RegexAndFilterListRules_RedirectPriority) {
struct {
size_t id;
size_t priority;
const char* action_type;
const char* filter;
bool is_regex_rule;
absl::optional<std::string> redirect_url;
} rule_info[] = {
{1, 1, "redirect", "filter.com", false, "http://redirect_filter.com"},
{2, 1, "upgradeScheme", "regex\\.com", true, absl::nullopt},
{3, 9, "redirect", "common1.com", false, "http://common1_filter.com"},
{4, 10, "redirect", "common1\\.com", true, "http://common1_regex.com"},
{5, 10, "upgradeScheme", "common2.com", false, absl::nullopt},
{6, 9, "upgradeScheme", "common2\\.com", true, absl::nullopt},
{7, 10, "redirect", "abc\\.com", true, "http://example1.com"},
{8, 9, "redirect", "abc", true, "http://example2.com"},
};
std::vector<TestRule> rules;
for (const auto& info : rule_info) {
TestRule rule = CreateGenericRule();
rule.id = info.id;
rule.priority = info.priority;
rule.action->type = info.action_type;
rule.condition->url_filter.reset();
if (info.is_regex_rule)
rule.condition->regex_filter = info.filter;
else
rule.condition->url_filter = info.filter;
if (info.redirect_url) {
rule.action->redirect.emplace();
rule.action->redirect->url = info.redirect_url;
}
rules.push_back(rule);
}
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher(rules, CreateTemporarySource(), &matcher));
struct TestCase {
const char* url = nullptr;
absl::optional<RequestAction> expected_action;
};
std::vector<TestCase> test_cases;
{
TestCase test_case = {"http://filter.com"};
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::REDIRECT, rule_info[0].id, rule_info[0].priority);
test_case.expected_action->redirect_url.emplace(
"http://redirect_filter.com");
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://regex.com"};
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::UPGRADE, rule_info[1].id, rule_info[1].priority);
test_case.expected_action->redirect_url.emplace("https://regex.com");
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://common1.com"};
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::REDIRECT, rule_info[3].id, rule_info[3].priority);
test_case.expected_action->redirect_url.emplace("http://common1_regex.com");
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://common2.com"};
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::UPGRADE, rule_info[4].id, rule_info[4].priority);
test_case.expected_action->redirect_url.emplace("https://common2.com");
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"https://common2.com"};
// No action since request is not upgradeable.
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://example.com"};
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://abc.com"};
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::REDIRECT, rule_info[6].id, rule_info[6].priority);
test_case.expected_action->redirect_url.emplace("http://example1.com");
test_cases.push_back(std::move(test_case));
}
{
TestCase test_case = {"http://xyz.com/abc"};
test_case.expected_action = CreateRequestActionForTesting(
RequestAction::Type::REDIRECT, rule_info[7].id, rule_info[7].priority);
test_case.expected_action->redirect_url.emplace("http://example2.com");
test_cases.push_back(std::move(test_case));
}
for (const auto& test_case : test_cases) {
SCOPED_TRACE(test_case.url);
GURL url(test_case.url);
RequestParams params;
params.url = &url;
EXPECT_EQ(test_case.expected_action,
matcher->GetBeforeRequestAction(params));
}
}
TEST_F(RulesetMatcherTest, RegexAndFilterListRules_ModifyHeaders) {
std::vector<TestRule> rules;
TestRule rule = CreateGenericRule();
rule.id = 1;
rule.priority = kMinValidPriority + 1;
rule.action->type = "modifyHeaders";
rule.condition->url_filter = "abc";
rule.action->request_headers = std::vector<TestHeaderInfo>(
{TestHeaderInfo("header1", "remove", absl::nullopt),
TestHeaderInfo("header2", "remove", absl::nullopt)});
rules.push_back(rule);
RequestAction action_1 = CreateRequestActionForTesting(
RequestAction::Type::MODIFY_HEADERS, 1, *rule.priority);
action_1.request_headers_to_modify = {
RequestAction::HeaderInfo("header1", dnr_api::HEADER_OPERATION_REMOVE,
absl::nullopt),
RequestAction::HeaderInfo("header2", dnr_api::HEADER_OPERATION_REMOVE,
absl::nullopt)};
rule = CreateGenericRule();
rule.id = 2;
rule.priority = kMinValidPriority;
rule.condition->url_filter.reset();
rule.condition->regex_filter = "example";
rule.action->type = "modifyHeaders";
rule.action->request_headers = std::vector<TestHeaderInfo>(
{TestHeaderInfo("header1", "remove", absl::nullopt),
TestHeaderInfo("header3", "remove", absl::nullopt)});
rules.push_back(rule);
RequestAction action_2 = CreateRequestActionForTesting(
RequestAction::Type::MODIFY_HEADERS, 2, *rule.priority);
action_2.request_headers_to_modify = {
RequestAction::HeaderInfo("header1", dnr_api::HEADER_OPERATION_REMOVE,
absl::nullopt),
RequestAction::HeaderInfo("header3", dnr_api::HEADER_OPERATION_REMOVE,
absl::nullopt)};
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher(rules, CreateTemporarySource(), &matcher));
{
GURL url("http://nomatch.com");
SCOPED_TRACE(url.spec());
RequestParams params;
params.url = &url;
EXPECT_TRUE(matcher->GetModifyHeadersActions(params, 0u /* min_priority */)
.empty());
}
{
GURL url("http://abc.com");
SCOPED_TRACE(url.spec());
RequestParams params;
params.url = &url;
std::vector<RequestAction> actions =
matcher->GetModifyHeadersActions(params, 0u /* min_priority */);
EXPECT_THAT(actions, testing::UnorderedElementsAre(
testing::Eq(testing::ByRef(action_1))));
}
{
GURL url("http://example.com");
SCOPED_TRACE(url.spec());
RequestParams params;
params.url = &url;
std::vector<RequestAction> actions =
matcher->GetModifyHeadersActions(params, 0u /* min_priority */);
EXPECT_THAT(actions, testing::UnorderedElementsAre(
testing::Eq(testing::ByRef(action_2))));
}
{
// Send a request that matches both the filter list and regex rules.
GURL url("http://abc.com/example");
SCOPED_TRACE(url.spec());
RequestParams params;
params.url = &url;
std::vector<RequestAction> actions =
matcher->GetModifyHeadersActions(params, 0u /* min_priority */);
EXPECT_THAT(actions, testing::UnorderedElementsAre(
testing::Eq(testing::ByRef(action_1)),
testing::Eq(testing::ByRef(action_2))));
// GetModifyHeadersActions specifies a minimum priority greater than the
// rules' priority, so no actions should be returned.
EXPECT_TRUE(
matcher
->GetModifyHeadersActions(
params, std::numeric_limits<uint64_t>::max() /* min_priority */)
.empty());
}
}
// Tests that regex substitution works correctly.
TEST_F(RulesetMatcherTest, RegexSubstitution) {
struct {
int id;
std::string regex_filter;
std::string regex_substitution;
} rule_info[] = {
// "\0" captures the complete matched string.
{1, R"(^.*google\.com.*$)", R"(https://redirect.com?original=\0)"},
{2, R"(http://((?:abc|def)\.xyz.com.*)$)", R"(https://www.\1)"},
{3, R"(^(http|https)://example\.com.*(\?|&)redirect=(.*?)(?:&|$).*$)",
R"(\1://\3)"},
{4, R"(reddit\.com)", "abc.com"},
{5, R"(^http://www\.(pqr|rst)\.xyz\.com)", R"(https://\1.xyz.com)"},
{6, R"(\.in)", ".co.in"},
};
std::vector<TestRule> rules;
for (const auto& info : rule_info) {
TestRule rule = CreateGenericRule();
rule.id = info.id;
rule.priority = kMinValidPriority;
rule.condition->url_filter.reset();
rule.condition->regex_filter = info.regex_filter;
rule.action->type = std::string("redirect");
rule.action->redirect.emplace();
rule.action->redirect->regex_substitution = info.regex_substitution;
rules.push_back(rule);
}
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher(rules, CreateTemporarySource(), &matcher));
auto create_redirect_action = [](int rule_id, std::string redirect_url) {
RequestAction action =
CreateRequestActionForTesting(RequestAction::Type::REDIRECT, rule_id);
action.redirect_url.emplace(redirect_url);
return action;
};
struct {
std::string url;
absl::optional<RequestAction> expected_action;
} test_cases[] = {
{"http://google.com/path?query",
create_redirect_action(
1, "https://redirect.com?original=http://google.com/path?query")},
{"http://def.xyz.com/path?query",
create_redirect_action(2, "https://www.def.xyz.com/path?query")},
{"http://example.com/path?q1=1&redirect=facebook.com&q2=2",
create_redirect_action(3, "http://facebook.com")},
// The redirect url here would have been "http://" which is invalid.
{"http://example.com/path?q1=1&redirect=&q2=2", absl::nullopt},
{"https://reddit.com", create_redirect_action(4, "https://abc.com")},
{"http://www.rst.xyz.com/path",
create_redirect_action(5, "https://rst.xyz.com/path")},
{"http://yahoo.in/path",
create_redirect_action(6, "http://yahoo.co.in/path")},
// No match.
{"http://example.com", absl::nullopt}};
for (const auto& test_case : test_cases) {
SCOPED_TRACE(test_case.url);
GURL url(test_case.url);
CHECK(url.is_valid());
RequestParams params;
params.url = &url;
ASSERT_EQ(test_case.expected_action,
matcher->GetBeforeRequestAction(params));
}
}
TEST_F(RulesetMatcherTest, RulesCount) {
size_t kNumNonRegexRules = 20;
size_t kNumRegexRules = 10;
std::vector<TestRule> rules;
int id = kMinValidID;
for (size_t i = 0; i < kNumNonRegexRules; ++i, ++id) {
TestRule rule = CreateGenericRule();
rule.id = id;
rule.condition->url_filter = std::to_string(id);
rules.push_back(rule);
}
for (size_t i = 0; i < kNumRegexRules; ++i, ++id) {
TestRule rule = CreateGenericRule();
rule.id = id;
rule.condition->url_filter.reset();
rule.condition->regex_filter = std::to_string(id);
rules.push_back(rule);
}
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(CreateVerifiedMatcher(rules, CreateTemporarySource(), &matcher));
ASSERT_TRUE(matcher);
EXPECT_EQ(kNumRegexRules + kNumNonRegexRules, matcher->GetRulesCount());
EXPECT_EQ(kNumRegexRules, matcher->GetRegexRulesCount());
// Also verify the rules count for an empty matcher.
ASSERT_TRUE(
CreateVerifiedMatcher({} /* rules */, CreateTemporarySource(), &matcher));
ASSERT_TRUE(matcher);
EXPECT_EQ(0u, matcher->GetRulesCount());
EXPECT_EQ(0u, matcher->GetRegexRulesCount());
}
// Test that rules with the same priority will override each other correctly
// based on action.
TEST_F(RulesetMatcherTest, BreakTiesByActionPriority) {
struct {
int rule_id;
std::string rule_action;
// The expected action, assuming this rule and all previous rules match and
// have the same priority.
RequestAction::Type expected_action;
// The ID of the rule expected to match.
int expected_rule_id = 0;
} test_cases[] = {
{1, "redirect", RequestAction::Type::REDIRECT, 1},
{2, "upgradeScheme", RequestAction::Type::UPGRADE, 2},
{3, "block", RequestAction::Type::BLOCK, 3},
{4, "allowAllRequests", RequestAction::Type::ALLOW_ALL_REQUESTS, 4},
{5, "allow", RequestAction::Type::ALLOW, 5},
{6, "block", RequestAction::Type::ALLOW, 5},
};
std::vector<TestRule> rules;
for (const auto& test_case : test_cases) {
SCOPED_TRACE(test_case.rule_action);
TestRule rule = CreateGenericRule();
rule.id = test_case.rule_id;
rule.priority = kMinValidPriority;
rule.condition->url_filter = "http://example.com";
rule.action->type = test_case.rule_action;
rule.condition->resource_types = std::vector<std::string>{"main_frame"};
if (test_case.rule_action == "redirect") {
rule.action->redirect.emplace();
rule.action->redirect->url = "http://google.com";
}
rules.push_back(rule);
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(
CreateVerifiedMatcher(rules, CreateTemporarySource(), &matcher));
GURL url("http://example.com");
RequestParams params;
params.url = &url;
params.element_type = url_pattern_index::flat::ElementType_MAIN_FRAME;
int expected_rule_id = test_case.expected_rule_id;
if (expected_rule_id == 0)
expected_rule_id = *rule.id;
RequestAction expected_action = CreateRequestActionForTesting(
test_case.expected_action, expected_rule_id);
if (test_case.expected_action == RequestAction::Type::REDIRECT) {
expected_action.redirect_url = GURL("http://google.com");
} else if (test_case.expected_action == RequestAction::Type::UPGRADE) {
expected_action.redirect_url = GURL("https://example.com");
}
EXPECT_EQ(expected_action, matcher->GetBeforeRequestAction(params));
}
}
// Test fixture to test allowAllRequests rules. We inherit from ExtensionsTest
// to ensure we can work with WebContentsTester and associated classes.
using AllowAllRequestsTest = ExtensionsTest;
// Tests that we track allowlisted frames (frames matching allowAllRequests
// rules) correctly.
TEST_F(AllowAllRequestsTest, AllowlistedFrameTracking) {
TestRule google_rule_1 = CreateGenericRule();
google_rule_1.id = kMinValidID;
google_rule_1.condition->url_filter = "google.com/xyz";
google_rule_1.condition->resource_types =
std::vector<std::string>({"main_frame"});
google_rule_1.action->type = std::string("allowAllRequests");
google_rule_1.priority = 2;
TestRule google_rule_2 = CreateGenericRule();
google_rule_2.id = kMinValidID + 1;
google_rule_2.condition->url_filter.reset();
google_rule_2.condition->regex_filter = "xyz";
google_rule_2.condition->resource_types =
std::vector<std::string>({"main_frame"});
google_rule_2.action->type = std::string("allowAllRequests");
google_rule_2.priority = 3;
TestRule example_rule = CreateGenericRule();
example_rule.id = kMinValidID + 2;
example_rule.condition->url_filter.reset();
example_rule.condition->regex_filter = std::string("example");
example_rule.condition->resource_types =
std::vector<std::string>({"sub_frame"});
example_rule.action->type = std::string("allowAllRequests");
example_rule.priority = 4;
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(
CreateVerifiedMatcher({google_rule_1, google_rule_2, example_rule},
CreateTemporarySource(), &matcher));
auto simulate_navigation = [&matcher](content::RenderFrameHost* host,
const GURL& url) {
content::RenderFrameHost* new_host =
content::NavigationSimulator::NavigateAndCommitFromDocument(url, host);
EXPECT_TRUE(new_host);
// Note |host| might have been freed by now.
testing::NiceMock<content::MockNavigationHandle> navigation_handle(
url, new_host);
navigation_handle.set_has_committed(true);
matcher->OnDidFinishNavigation(&navigation_handle);
return new_host;
};
auto simulate_frame_destroyed = [&matcher](content::RenderFrameHost* host) {
matcher->OnRenderFrameDeleted(host);
};
std::unique_ptr<content::WebContents> web_contents =
content::WebContentsTester::CreateTestWebContents(
browser_context(), content::SiteInstance::Create(browser_context()));
ASSERT_TRUE(web_contents);
GURL example_url("http://example.com");
simulate_navigation(web_contents->GetPrimaryMainFrame(), example_url);
absl::optional<RequestAction> action =
matcher->GetAllowlistedFrameActionForTesting(
web_contents->GetPrimaryMainFrame());
EXPECT_FALSE(action);
GURL google_url_1("http://google.com/xyz");
simulate_navigation(web_contents->GetPrimaryMainFrame(), google_url_1);
action = matcher->GetAllowlistedFrameActionForTesting(
web_contents->GetPrimaryMainFrame());
RequestAction google_rule_2_action =
CreateRequestActionForTesting(RequestAction::Type::ALLOW_ALL_REQUESTS,
*google_rule_2.id, *google_rule_2.priority);
EXPECT_EQ(google_rule_2_action, action);
auto* rfh_tester =
content::RenderFrameHostTester::For(web_contents->GetPrimaryMainFrame());
content::RenderFrameHost* child = rfh_tester->AppendChild("sub_frame");
ASSERT_TRUE(child);
child = simulate_navigation(child, example_url);
action = matcher->GetAllowlistedFrameActionForTesting(child);
RequestAction example_rule_action =
CreateRequestActionForTesting(RequestAction::Type::ALLOW_ALL_REQUESTS,
*example_rule.id, *example_rule.priority);
EXPECT_EQ(example_rule_action, action);
GURL yahoo_url("http://yahoo.com");
child = simulate_navigation(child, yahoo_url);
action = matcher->GetAllowlistedFrameActionForTesting(child);
EXPECT_EQ(google_rule_2_action, action);
simulate_frame_destroyed(child);
action = matcher->GetAllowlistedFrameActionForTesting(child);
EXPECT_FALSE(action);
simulate_frame_destroyed(web_contents->GetPrimaryMainFrame());
action = matcher->GetAllowlistedFrameActionForTesting(
web_contents->GetPrimaryMainFrame());
EXPECT_FALSE(action);
}
// Ensures that GetBeforeRequestAction correctly incorporates allowAllRequests
// rules.
TEST_F(AllowAllRequestsTest, GetBeforeRequestAction) {
struct {
int id;
int priority;
std::string action_type;
std::string url_filter;
bool is_regex_rule;
} rule_data[] = {{1, 1, "allowAllRequests", "google", true},
{2, 3, "block", "||match", false},
{3, 2, "allowAllRequests", "match1", true},
{4, 4, "allowAllRequests", "match2", false}};
std::vector<TestRule> test_rules;
for (const auto& rule : rule_data) {
TestRule test_rule = CreateGenericRule();
test_rule.id = rule.id;
test_rule.priority = rule.priority;
test_rule.action->type = rule.action_type;
test_rule.condition->url_filter.reset();
if (rule.is_regex_rule)
test_rule.condition->regex_filter = rule.url_filter;
else
test_rule.condition->url_filter = rule.url_filter;
if (rule.action_type == "allowAllRequests") {
test_rule.condition->resource_types =
std::vector<std::string>({"main_frame", "sub_frame"});
}
test_rules.push_back(test_rule);
}
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_TRUE(
CreateVerifiedMatcher(test_rules, CreateTemporarySource(), &matcher));
std::unique_ptr<content::WebContents> web_contents =
content::WebContentsTester::CreateTestWebContents(
browser_context(), content::SiteInstance::Create(browser_context()));
ASSERT_TRUE(web_contents);
GURL google_url("http://google.com");
content::WebContentsTester::For(web_contents.get())
->NavigateAndCommit(google_url);
testing::NiceMock<content::MockNavigationHandle> navigation_handle(
google_url, web_contents->GetPrimaryMainFrame());
navigation_handle.set_has_committed(true);
matcher->OnDidFinishNavigation(&navigation_handle);
struct {
std::string url;
RequestAction expected_action;
} cases[] = {
{"http://nomatch.com",
CreateRequestActionForTesting(RequestAction::Type::ALLOW_ALL_REQUESTS,
rule_data[0].id, rule_data[0].priority)},
{"http://match1.com",
CreateRequestActionForTesting(RequestAction::Type::COLLAPSE,
rule_data[1].id, rule_data[1].priority)},
{"http://match2.com",
CreateRequestActionForTesting(RequestAction::Type::ALLOW_ALL_REQUESTS,
rule_data[3].id, rule_data[3].priority)},
};
// Simulate sub-frame requests.
for (const auto& test_case : cases) {
SCOPED_TRACE(test_case.url);
RequestParams params;
GURL url(test_case.url);
ASSERT_TRUE(url.is_valid());
params.url = &url;
params.first_party_origin = url::Origin::Create(google_url);
params.is_third_party = true;
params.element_type = url_pattern_index::flat::ElementType_SUBDOCUMENT;
params.parent_routing_id =
web_contents->GetPrimaryMainFrame()->GetGlobalId();
EXPECT_EQ(test_case.expected_action,
matcher->GetBeforeRequestAction(params));
}
}
} // namespace
} // namespace declarative_net_request
} // namespace extensions