From ee6eee131b95cb430d548a5ed6cee3534b7077ff Mon Sep 17 00:00:00 2001 From: Shivan Kaul Sahib Date: Thu, 18 Aug 2022 19:54:25 -0700 Subject: [PATCH] Allow multiple capture groups for debounce regex action (#14687) --- components/debounce/browser/debounce_rule.cc | 31 +++++++-- .../browser/test/debounce_rule_unittest.cc | 68 +++++++++++++++++++ 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/components/debounce/browser/debounce_rule.cc b/components/debounce/browser/debounce_rule.cc index bf7bb18b60f..e57d3e1fd0c 100644 --- a/components/debounce/browser/debounce_rule.cc +++ b/components/debounce/browser/debounce_rule.cc @@ -187,17 +187,36 @@ bool DebounceRule::ValidateAndParsePatternRegex( << " which is an invalid regex pattern"; return false; } - if (pattern_regex.NumberOfCapturingGroups() != 1) { + if (pattern_regex.NumberOfCapturingGroups() < 1) { VLOG(1) << "Debounce rule has param: " << pattern - << " which captures != 1 groups"; + << " which captures < 1 groups"; return false; } - if (!RE2::PartialMatch(path, pattern_regex, parsed_value)) { + // Get matching capture groups by applying regex to the path + size_t number_of_capturing_groups = + pattern_regex.NumberOfCapturingGroups() + 1; + std::vector match_results(number_of_capturing_groups); + + if (!pattern_regex.Match(path, 0, path.size(), RE2::UNANCHORED, + match_results.data(), match_results.size())) { VLOG(1) << "Debounce rule with param: " << param_ << " was unable to capture string"; return false; } + + // This will always be at least 2: the first one is the full match + DCHECK_GT(match_results.size(), 1u); + + // Build parsed_value string by appending matches, ignoring the first match + // which will be the whole match + std::for_each(std::begin(match_results) + 1, std::end(match_results), + [parsed_value](re2::StringPiece matched_string) { + if (!matched_string.empty()) { + matched_string.AppendToString(parsed_value); + } + }); + return true; } @@ -212,12 +231,14 @@ bool DebounceRule::Apply(const GURL& original_url, action_ != kDebounceRegexPath) return false; // If URL matches an explicitly excluded pattern, this rule does not apply. - if (exclude_pattern_set_.MatchesURL(original_url)) + if (exclude_pattern_set_.MatchesURL(original_url)) { return false; + } // If URL does not match an explicitly included pattern, this rule does not // apply. - if (!include_pattern_set_.MatchesURL(original_url)) + if (!include_pattern_set_.MatchesURL(original_url)) { return false; + } if (!DebounceRule::CheckPrefForRule(prefs)) { return false; diff --git a/components/debounce/browser/test/debounce_rule_unittest.cc b/components/debounce/browser/test/debounce_rule_unittest.cc index 59448c8df17..4ba28178c80 100644 --- a/components/debounce/browser/test/debounce_rule_unittest.cc +++ b/components/debounce/browser/test/debounce_rule_unittest.cc @@ -183,6 +183,74 @@ TEST(DebounceRuleUnitTest, ParamCapturesNonURLWithPrependScheme) { } } +TEST(DebounceRuleUnitTest, TwoCaptureGroups) { + const std::string contents = R"json( + + [{ + "include": [ + "*://test.com/*" + ], + "exclude": [ + ], + "action": "regex-path", + "prepend_scheme": "https", + "param": "^/([^/]+)/xyz(/.*)$" + }] + + )json"; + std::vector> rules = StringToRules(contents); + + for (const std::unique_ptr& rule : rules) { + CheckApplyResult(rule.get(), GURL("https://test.com/brave.com/xyz/abc.jpg"), + "https://brave.com/abc.jpg", false); + } +} + +TEST(DebounceRuleUnitTest, CurlyBracesInRegexGetParseError) { + const std::string contents = R"json( + + [{ + "include": [ + "*://test.com/*" + ], + "exclude": [ + ], + "action": "regex-path", + "prepend_scheme": "https", + "param": "^/turbo/([^/]+)/xyz(/\d{4})/xyzzy(/.*)$" + }] + + )json"; + + auto parsed = DebounceRule::ParseRules(contents); + EXPECT_FALSE(parsed.has_value()); +} + +TEST(DebounceRuleUnitTest, ThreeCaptureGroups) { + const std::string contents = R"json( + + [{ + "include": [ + "*://test.com/*" + ], + "exclude": [ + ], + "action": "regex-path", + "prepend_scheme": "https", + "param": "^/turbo/([^/]+)/xyz(/[0-9]+)/xyzzy(/.*)$" + }] + + )json"; + std::vector> rules = StringToRules(contents); + + for (const std::unique_ptr& rule : rules) { + CheckApplyResult( + rule.get(), + GURL("https://test.com/turbo/brave.com/xyz/2022/xyzzy/abc.jpg"), + "https://brave.com/2022/abc.jpg", false); + } +} + TEST(DebounceRuleUnitTest, ParamCapturesURLWithPrependScheme) { const std::string contents = R"json(