Skip to content

Commit

Permalink
[DNR] Add support for request method rule conditions
Browse files Browse the repository at this point in the history
Add support for the 'requestMethods' and 'excludedRequestMethods' rule
conditions. These conditions allow declarativeNetRequest rules to
target requests based on HTTP request method (e.g. "GET",  "POST").

Bug: 1013583
Change-Id: Idfbe5504da6aeab18f8dda86de4c806cc000bfba
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2687540
Commit-Queue: Dave Vandyke <kzar@kzar.co.uk>
Reviewed-by: Karan Bhatia <karandeepb@chromium.org>
Reviewed-by: Charlie Harrison <csharrison@chromium.org>
Reviewed-by: Matt Menke <mmenke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#869140}
  • Loading branch information
kzar authored and Chromium LUCI CQ committed Apr 5, 2021
1 parent fc60836 commit 9d560d1
Show file tree
Hide file tree
Showing 33 changed files with 523 additions and 65 deletions.
Expand Up @@ -4464,13 +4464,15 @@ class DeclarativeNetRequestAllowAllRequestsBrowserTest
std::string url_filter;
bool is_regex_rule;
base::Optional<std::vector<std::string>> resource_types;
base::Optional<std::vector<std::string>> request_methods;
};

DeclarativeNetRequestAllowAllRequestsBrowserTest() = default;

void RunTest(const std::vector<RuleData>& rule_data,
const std::vector<std::string>& paths_seen,
const std::vector<std::string>& paths_not_seen) {
const std::vector<std::string>& paths_not_seen,
bool post_navigation = false) {
std::vector<TestRule> test_rules;
for (const auto& rule : rule_data) {
TestRule test_rule = CreateGenericRule();
Expand All @@ -4483,13 +4485,15 @@ class DeclarativeNetRequestAllowAllRequestsBrowserTest
else
test_rule.condition->url_filter = rule.url_filter;
test_rule.condition->resource_types = rule.resource_types;
test_rule.condition->request_methods = rule.request_methods;
test_rules.push_back(test_rule);
}

ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(test_rules));

GURL page_url = embedded_test_server()->GetURL(
"example.com", "/page_with_two_frames.html");
"example.com", post_navigation ? "/post_to_page_with_two_frames.html"
: "/page_with_two_frames.html");
ui_test_utils::NavigateToURL(browser(), page_url);

const std::set<GURL> requests_seen = GetAndResetRequestsToServer();
Expand Down Expand Up @@ -4606,6 +4610,51 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestAllowAllRequestsBrowserTest,
{});
}

IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestAllowAllRequestsBrowserTest,
TestPostNavigationMatched) {
std::vector<RuleData> rule_data = {
{1, 6, "allowAllRequests", "page_with_two_frames\\.html", true,
std::vector<std::string>({"main_frame"}),
std::vector<std::string>({"post"})},
{2, 5, "block", "*", false},
};

// Requests:
// -/page_with_two_frames.html (Matching rule=1)
// -/subresources/script.js (Matching rule=[1,2] Winner=1)
// -/child_frame.html?frame=1 (Matching Rule=[1,2] Winner=1)
// -/subresources/script.js?frameId=1 (Matching Rule=[1,2] Winner=1)
// -/child_frame.html?frame=2 (Matching Rule=[1,2] Winner=1)
// -/subresources/script.js?frameId=2 (Matching Rule=[1,2] Winner=1)
// Hence all requests go through.
RunTest(rule_data,
{requests[0], requests[1], requests[2], requests[3], requests[4],
requests[5]},
{}, true);
}

IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestAllowAllRequestsBrowserTest,
TestPostNavigationNotMatched) {
std::vector<RuleData> rule_data = {
{1, 6, "allowAllRequests", "page_with_two_frames\\.html", true,
std::vector<std::string>({"main_frame"}),
std::vector<std::string>({"get"})},
{2, 5, "block", "*", false},
};

// Requests:
// -/page_with_two_frames.html (No matches)
// -/subresources/script.js (Matching rule 2)
// -/child_frame.html?frame=1 (Matching rule 2)
// -/subresources/script.js?frameId=1 (Matching rule 2)
// -/child_frame.html?frame=2 (Matching rule 2)
// -/subresources/script.js?frameId=2 (Matching rule 2)
// Hence requests are blocked.
RunTest(rule_data, {requests[0]},
{requests[1], requests[2], requests[3], requests[4], requests[5]},
true);
}

// Tests that when an extension is updated but loses the declarativeNetRequest
// permission, its dynamic ruleset is not enabled.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,
Expand Down Expand Up @@ -5634,6 +5683,79 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestGlobalRulesBrowserTest_Packed,
EXPECT_EQ("3", GetAvailableStaticRuleCount(last_loaded_extension_id()));
}

// Tests the "requestMethods" and "excludedRequestMethods" property of a
// declarative rule condition.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
BlockRequests_Methods) {
struct {
int id;
std::string url_filter;
std::vector<std::string> request_methods;
std::vector<std::string> excluded_request_methods;
} rules_data[] = {{1, "default", {}, {}},
{2, "included", {"head", "put"}, {}},
{3, "excluded", {}, {"options", "patch"}},
{4, "combination", {"get"}, {"put"}}};

std::vector<TestRule> rules;
for (const auto& rule_data : rules_data) {
TestRule rule = CreateGenericRule(rule_data.id);
rule.condition->url_filter = rule_data.url_filter;

// An empty list is not allowed for the "requestMethods" property.
if (!rule_data.request_methods.empty())
rule.condition->request_methods = rule_data.request_methods;

rule.condition->excluded_request_methods =
rule_data.excluded_request_methods;
rules.push_back(rule);
}
ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules));

struct {
std::string path;
std::string expected_blocked_request_methods;
} test_cases[] = {{"default", "delete,get,head,options,patch,post,put"},
{"included", "head,put"},
{"excluded", "delete,get,head,post,put"},
{"combination", "get"}};

const char kPerformRequestWithAllMethodsScript[] = R"(
{
const allRequestMethods = ["delete", "get", "head", "options", "patch",
"post", "put"];
const url = "/empty.html?%s";
let blockedMethods = [];
Promise.allSettled(
allRequestMethods.map(method =>
fetch(url, {method}).catch(() => { blockedMethods.push(method); })
)
).then(() =>
{
window.domAutomationController.send(blockedMethods.sort().join());
});
}
)";

GURL url = embedded_test_server()->GetURL("abc.com", "/empty.html");
ui_test_utils::NavigateToURL(browser(), url);
content::RenderFrameHost* main_frame = GetMainFrame();

for (const auto& test_case : test_cases) {
SCOPED_TRACE(base::StringPrintf("Path: %s", test_case.path.c_str()));
std::string actual_blocked_request_methods;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
main_frame,
base::StringPrintf(kPerformRequestWithAllMethodsScript,
test_case.path.c_str()),
&actual_blocked_request_methods));
EXPECT_EQ(test_case.expected_blocked_request_methods,
actual_blocked_request_methods);
}
}

INSTANTIATE_TEST_SUITE_P(All,
DeclarativeNetRequestBrowserTest,
::testing::Values(ExtensionLoadType::PACKED,
Expand Down
Expand Up @@ -30,6 +30,7 @@
#include "extensions/common/file_util.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "extensions/common/url_pattern.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
Expand Down Expand Up @@ -118,6 +119,7 @@ class RulesetManagerTest : public DNRTestBase {
const int kRendererId = 1;
WebRequestInfoInitParams info;
info.url = GURL(url);
info.method = net::HttpRequestHeaders::kGetMethod;
info.render_process_id = kRendererId;
info.initiator = std::move(initiator);
return info;
Expand All @@ -131,6 +133,7 @@ class RulesetManagerTest : public DNRTestBase {
const int kRendererId = 1;
WebRequestInfoInitParams info;
info.url = GURL(url);
info.method = net::HttpRequestHeaders::kGetMethod;
info.render_process_id = kRendererId;

net::HttpRequestHeaders extra_request_headers;
Expand Down
4 changes: 2 additions & 2 deletions chrome/browser/subresource_filter/ruleset_browsertest.cc
Expand Up @@ -116,8 +116,8 @@ IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest, InvalidRuleset_Checksum) {
// via the checksum, and not the Flatbuffer Verifier. This was determined
// at random by flipping elements until this test failed, then adding
// the checksum code and ensuring it passed.
testing::TestRuleset::CorruptByFilling(test_ruleset_pair.indexed, 28250,
28251, 32);
testing::TestRuleset::CorruptByFilling(test_ruleset_pair.indexed, 28246,
28247, 32);
OpenAndPublishRuleset(service, test_ruleset_pair.indexed.path);
ASSERT_TRUE(service->GetRulesetDealer());

Expand Down
@@ -0,0 +1,9 @@
<html>
<body>
<form id="form" method="POST" action="./page_with_two_frames.html"></form>

<script type="text/javascript">
document.getElementById("form").submit();
</script>
</body>
</html>
Expand Up @@ -307,7 +307,7 @@ TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
OpenAndSetRulesetFileValidNoChecksum) {
// See also SubresourceFilterBrowserTest.InvalidRuleset_Checksum, corrupting
// in this manner doesn't invalidate the Flatbuffer Verifier check.
testing::TestRuleset::CorruptByFilling(rulesets().indexed_1(), 28250, 28251,
testing::TestRuleset::CorruptByFilling(rulesets().indexed_1(), 28246, 28247,
32);
RulesetFilePtr file = ruleset_dealer()->OpenAndSetRulesetFile(
/*expected_checksum=*/0, rulesets().indexed_1().path);
Expand Down Expand Up @@ -340,7 +340,7 @@ TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
rulesets().indexed_1().contents.size());
// See also SubresourceFilterBrowserTest.InvalidRuleset_Checksum, corrupting
// in this manner doesn't invalidate the Flatbuffer Verifier check.
testing::TestRuleset::CorruptByFilling(rulesets().indexed_1(), 28250, 28251,
testing::TestRuleset::CorruptByFilling(rulesets().indexed_1(), 28246, 28247,
32);
RulesetFilePtr file = ruleset_dealer()->OpenAndSetRulesetFile(
expected_checksum, rulesets().indexed_1().path);
Expand Down
4 changes: 2 additions & 2 deletions components/subresource_filter/core/common/indexed_ruleset.cc
Expand Up @@ -52,12 +52,12 @@ VerifyStatus GetVerifyStatus(const uint8_t* buffer,

// RulesetIndexer --------------------------------------------------------------

const int RulesetIndexer::kIndexedFormatVersion = 28;
const int RulesetIndexer::kIndexedFormatVersion = 29;

// This static assert is meant to catch cases where
// url_pattern_index::kUrlPatternIndexFormatVersion is incremented without
// updating RulesetIndexer::kIndexedFormatVersion.
static_assert(url_pattern_index::kUrlPatternIndexFormatVersion == 7,
static_assert(url_pattern_index::kUrlPatternIndexFormatVersion == 8,
"kUrlPatternIndexFormatVersion has changed, make sure you've "
"also updated RulesetIndexer::kIndexedFormatVersion above.");

Expand Down
16 changes: 16 additions & 0 deletions components/url_pattern_index/flat/url_pattern_index.fbs
Expand Up @@ -61,6 +61,19 @@ enum ElementType : ushort (bit_flags) {
// adding/removing values from this enum.
}

// The request methods that a URL rule should be applied to.
enum RequestMethod : ubyte (bit_flags) {
DELETE,
GET,
HEAD,
OPTIONS,
PATCH,
POST,
PUT,
// Note: Update the default value for `request_methods` field in UrlRule, on
// adding/removing values from this enum.
}

// The flat representation of a single URL rule. For more details regarding the
// fields please see the comments to url_pattern_index::proto::UrlRule.
table UrlRule {
Expand All @@ -73,6 +86,9 @@ table UrlRule {
// url_pattern_index::kDefaultFlatElementTypesMask.
element_types : ushort = 8191;

// A bitmask of RequestMethod. Equals RequestMethod_ANY by default.
request_methods : ushort = 127;

// A bitmask of ActivationType. Disables all activation types by default.
activation_types : ubyte = 0;

Expand Down

0 comments on commit 9d560d1

Please sign in to comment.