Skip to content

Commit

Permalink
Regex action support for debouncing
Browse files Browse the repository at this point in the history
  • Loading branch information
ShivanKaul committed Jun 2, 2022
1 parent 28ba6f3 commit c094248
Show file tree
Hide file tree
Showing 12 changed files with 483 additions and 42 deletions.
4 changes: 3 additions & 1 deletion browser/debounce/debounce_service_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "brave/components/debounce/browser/debounce_service.h"
#include "brave/components/debounce/common/features.h"
#include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/keyed_service/core/keyed_service.h"

Expand Down Expand Up @@ -49,7 +50,8 @@ KeyedService* DebounceServiceFactory::BuildServiceInstanceFor(
if (g_brave_browser_process)
component_installer =
g_brave_browser_process->debounce_component_installer();
return new DebounceService(component_installer);
return new DebounceService(component_installer,
Profile::FromBrowserContext(context)->GetPrefs());
}

content::BrowserContext* DebounceServiceFactory::GetBrowserContextToUse(
Expand Down
2 changes: 2 additions & 0 deletions components/debounce/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ source_set("browser") {
"//brave/components/debounce/common",
"//brave/extensions:common",
"//components/content_settings/core/browser",
"//components/prefs:prefs",
"//content/public/browser",
"//content/public/common",
"//services/network/public/cpp",
"//services/network/public/mojom",
"//third_party/blink/public/common",
"//third_party/re2:re2",
"//url",
]
}
1 change: 1 addition & 0 deletions components/debounce/browser/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ include_rules = [
"+services/network/public/cpp",
"+services/network/public/mojom",
"+third_party/blink/public/common/loader",
"+third_party/re2",
]
24 changes: 1 addition & 23 deletions components/debounce/browser/debounce_component_installer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,7 @@ void DebounceComponentInstaller::OnDATFileDataReady(
}
rules_.clear();
host_cache_.clear();
std::vector<std::string> hosts;
base::JSONValueConverter<DebounceRule> converter;
for (base::Value& it : root->GetList()) {
std::unique_ptr<DebounceRule> rule = std::make_unique<DebounceRule>();
if (!converter.Convert(it, rule.get()))
continue;
for (const URLPattern& pattern : rule->include_pattern_set()) {
if (!pattern.host().empty()) {
const std::string etldp1 = GetETLDForDebounce(pattern.host());
if (!etldp1.empty())
hosts.push_back(std::move(etldp1));
}
}
rules_.push_back(std::move(rule));
}
host_cache_ = std::move(hosts);
DebounceRule::ParseRules(std::move(root->GetList()), &rules_, &host_cache_);
for (Observer& observer : observers_)
observer.OnRulesReady(this);
}
Expand All @@ -83,11 +68,4 @@ void DebounceComponentInstaller::OnComponentReady(
LoadDirectlyFromResourcePath();
}

const std::string DebounceComponentInstaller::GetETLDForDebounce(
const std::string& host) const {
return net::registry_controlled_domains::GetDomainAndRegistry(
host, net::registry_controlled_domains::PrivateRegistryFilter::
EXCLUDE_PRIVATE_REGISTRIES);
}

} // namespace debounce
1 change: 0 additions & 1 deletion components/debounce/browser/debounce_component_installer.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ class DebounceComponentInstaller
void OnComponentReady(const std::string& component_id,
const base::FilePath& install_dir,
const std::string& manifest) override;
const std::string GetETLDForDebounce(const std::string& host) const;

// implementation of our own observers
class Observer : public base::CheckedObserver {
Expand Down
151 changes: 140 additions & 11 deletions components/debounce/browser/debounce_rule.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,33 @@

#include <memory>
#include <utility>
#include <vector>

#include "base/base64url.h"
#include "base/strings/stringprintf.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/common/url_pattern.h"
#include "net/base/url_util.h"
#include "third_party/re2/src/re2/re2.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/url_constants.h"

namespace {
// debounce.json keys
const char kInclude[] = "include";
const char kExclude[] = "exclude";
const char kAction[] = "action";
const char kPrependScheme[] = "prepend_scheme";
const char kParam[] = "param";
const char kPref[] = "pref";
} // namespace

namespace debounce {

DebounceRule::DebounceRule() : action_(kDebounceNoAction) {}
DebounceRule::DebounceRule()
: action_(kDebounceNoAction), prepend_scheme_(kDebounceNoSchemePrepend) {}

DebounceRule::~DebounceRule() = default;

Expand All @@ -34,8 +44,24 @@ bool DebounceRule::ParseDebounceAction(base::StringPiece value,
*field = kDebounceRedirectToParam;
} else if (value == "base64,redirect") {
*field = kDebounceBase64DecodeAndRedirectToParam;
} else if (value == "regex-path") {
*field = kDebounceRegexPath;
} else {
LOG(INFO) << "Found unknown debouncing action: " << value;
LOG(WARNING) << "Found unknown debouncing action: " << value;
return false;
}
return true;
}

// static
bool DebounceRule::ParsePrependScheme(base::StringPiece value,
DebouncePrependScheme* field) {
if (value == "http") {
*field = kDebounceSchemePrependHttp;
} else if (value == "https") {
*field = kDebounceSchemePrependHttps;
} else {
LOG(WARNING) << "Found unknown scheme: " << value;
return false;
}
return true;
Expand Down Expand Up @@ -68,15 +94,69 @@ void DebounceRule::RegisterJSONConverter(
kExclude, &DebounceRule::exclude_pattern_set_, GetURLPatternSetFromValue);
converter->RegisterCustomField<DebounceAction>(
kAction, &DebounceRule::action_, &ParseDebounceAction);
converter->RegisterCustomField<DebouncePrependScheme>(
kPrependScheme, &DebounceRule::prepend_scheme_, &ParsePrependScheme);
converter->RegisterStringField(kParam, &DebounceRule::param_);
converter->RegisterStringField(kPref, &DebounceRule::pref_);
}

bool DebounceRule::Apply(const GURL& original_url, GURL* final_url) const {
// static
const std::string DebounceRule::GetETLDForDebounce(const std::string& host) {
return net::registry_controlled_domains::GetDomainAndRegistry(
host, net::registry_controlled_domains::PrivateRegistryFilter::
EXCLUDE_PRIVATE_REGISTRIES);
}

// static
void DebounceRule::ParseRules(base::Value::List root,
std::vector<std::unique_ptr<DebounceRule>>* rules,
base::flat_set<std::string>* host_cache) {
DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
std::vector<std::string> hosts;
base::JSONValueConverter<DebounceRule> converter;
for (base::Value& it : root) {
std::unique_ptr<DebounceRule> rule = std::make_unique<DebounceRule>();
if (!converter.Convert(it, rule.get()))
continue;
for (const URLPattern& pattern : rule->include_pattern_set()) {
if (!pattern.host().empty()) {
const std::string etldp1 =
DebounceRule::GetETLDForDebounce(pattern.host());
if (!etldp1.empty())
hosts.push_back(std::move(etldp1));
}
}
rules->push_back(std::move(rule));
}
*host_cache = std::move(hosts);
}

bool DebounceRule::CheckPrefForRule(const PrefService* prefs) const {
// Check pref specified in rules, if any
if (!pref_.empty()) {
auto* pref = prefs->FindPreference(pref_);
if (!pref) {
LOG(WARNING) << "Pref specified in debounce.json not valid: " << pref_;
return false;
}
if (!pref->GetValue()->GetBool()) {
LOG(INFO) << "Pref " << pref->name()
<< " specified in debounce.json is false";
return false;
}
}
return true;
}

bool DebounceRule::Apply(const GURL& original_url,
GURL* final_url,
const PrefService* prefs) const {
// Unknown actions always return false, to allow for future updates to the
// rules file which may be pushed to users before a new version of the code
// that parses it.
if (action_ != kDebounceRedirectToParam &&
action_ != kDebounceBase64DecodeAndRedirectToParam)
action_ != kDebounceBase64DecodeAndRedirectToParam &&
action_ != kDebounceRegexPath)
return false;
// If URL matches an explicitly excluded pattern, this rule does not apply.
if (exclude_pattern_set_.MatchesURL(original_url))
Expand All @@ -86,16 +166,65 @@ bool DebounceRule::Apply(const GURL& original_url, GURL* final_url) const {
if (!include_pattern_set_.MatchesURL(original_url))
return false;

std::string unescaped_value;
if (!net::GetValueForKeyInQuery(original_url, param_, &unescaped_value))
return false;
if ((action_ == kDebounceBase64DecodeAndRedirectToParam) &&
(!base::Base64UrlDecode(unescaped_value,
base::Base64UrlDecodePolicy::IGNORE_PADDING,
&unescaped_value))) {
if (!DebounceRule::CheckPrefForRule(prefs)) {
return false;
}

std::string unescaped_value;

if (action_ == kDebounceRegexPath) {
// Important: Apply param regex to ONLY the path of original URL.
auto path = original_url.path();
const re2::RE2 pattern_regex(param_);
if (!pattern_regex.ok()) {
LOG(WARNING) << "Debounce rule has param: " << param_
<< " which is an invalid regex pattern";
return false;
}
if (pattern_regex.NumberOfCapturingGroups() != 1) {
LOG(WARNING) << "Debounce rule has param: " << param_
<< " which captures != 1 groups";
return false;
}
if (!RE2::PartialMatch(path, pattern_regex, &unescaped_value)) {
LOG(INFO) << "Debounce rule with param: " << param_
<< " was unable to capture string despite match";
return false;
}

// unescaped_value now has a string; we will check if the captured value is
// a valid URL down below
} else {
if (!net::GetValueForKeyInQuery(original_url, param_, &unescaped_value))
return false;
if ((action_ == kDebounceBase64DecodeAndRedirectToParam) &&
(!base::Base64UrlDecode(unescaped_value,
base::Base64UrlDecodePolicy::IGNORE_PADDING,
&unescaped_value))) {
return false;
}
}

GURL new_url(unescaped_value);
// Important: If there is a prepend_scheme in the rule BUT the URL is already
// valid i.e. has a scheme, we treat this as an erroneous rule and do not
// apply it.
if (prepend_scheme_ && new_url.is_valid()) {
LOG(WARNING) << "Debounce rule with param: " << param_
<< " and prepend scheme " << prepend_scheme_
<< " got a valid URL, treating as erroneous rule";
return false;
}
// If there is a prepend_scheme specified AND the URL is not valid, prepend
// the specified scheme and try again
if (prepend_scheme_ && !new_url.is_valid()) {
std::string scheme = (prepend_scheme_ == kDebounceSchemePrependHttp)
? url::kHttpScheme
: url::kHttpsScheme;
auto new_url_spec =
base::StringPrintf("%s://%s", scheme.c_str(), unescaped_value.c_str());
new_url = GURL(new_url_spec);
}

// Failsafe: ensure we got a valid URL out of the param.
if (!new_url.is_valid() || !new_url.SchemeIsHTTPOrHTTPS())
Expand Down
24 changes: 23 additions & 1 deletion components/debounce/browser/debounce_rule.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@
#ifndef BRAVE_COMPONENTS_DEBOUNCE_BROWSER_DEBOUNCE_RULE_H_
#define BRAVE_COMPONENTS_DEBOUNCE_BROWSER_DEBOUNCE_RULE_H_

#include <memory>
#include <string>
#include <vector>

#include "base/containers/flat_set.h"
#include "base/json/json_value_converter.h"
#include "base/values.h"
#include "components/prefs/pref_service.h"
#include "extensions/common/url_pattern_set.h"

class GURL;
Expand All @@ -19,9 +23,16 @@ namespace debounce {
enum DebounceAction {
kDebounceNoAction,
kDebounceRedirectToParam,
kDebounceRegexPath,
kDebounceBase64DecodeAndRedirectToParam
};

enum DebouncePrependScheme {
kDebounceNoSchemePrepend,
kDebounceSchemePrependHttp,
kDebounceSchemePrependHttps
};

class DebounceRule {
public:
DebounceRule();
Expand All @@ -33,19 +44,30 @@ class DebounceRule {
base::JSONValueConverter<DebounceRule>* converter);
static bool ParseDebounceAction(base::StringPiece value,
DebounceAction* field);
static bool ParsePrependScheme(base::StringPiece value,
DebouncePrependScheme* field);
static void ParseRules(base::Value::List root,
std::vector<std::unique_ptr<DebounceRule>>* rules,
base::flat_set<std::string>* host_cache);
static const std::string GetETLDForDebounce(const std::string& host);
static bool GetURLPatternSetFromValue(const base::Value* value,
extensions::URLPatternSet* result);

bool Apply(const GURL& original_url, GURL* final_url) const;
bool Apply(const GURL& original_url,
GURL* final_url,
const PrefService* prefs) const;
const extensions::URLPatternSet& include_pattern_set() const {
return include_pattern_set_;
}

private:
bool CheckPrefForRule(const PrefService* prefs) const;
extensions::URLPatternSet include_pattern_set_;
extensions::URLPatternSet exclude_pattern_set_;
DebounceAction action_;
DebouncePrependScheme prepend_scheme_;
std::string param_;
std::string pref_;
};

} // namespace debounce
Expand Down
9 changes: 5 additions & 4 deletions components/debounce/browser/debounce_service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
namespace debounce {

DebounceService::DebounceService(
DebounceComponentInstaller* component_installer)
: component_installer_(component_installer) {}
DebounceComponentInstaller* component_installer,
PrefService* prefs)
: component_installer_(component_installer), prefs_(prefs) {}

DebounceService::~DebounceService() {}

Expand All @@ -31,7 +32,7 @@ bool DebounceService::Debounce(const GURL& original_url,
const base::flat_set<std::string>& host_cache =
component_installer_->host_cache();
const std::string etldp1 =
component_installer_->GetETLDForDebounce(original_url.host());
DebounceRule::GetETLDForDebounce(original_url.host());
if (!base::Contains(host_cache, etldp1))
return false;

Expand All @@ -45,7 +46,7 @@ bool DebounceService::Debounce(const GURL& original_url,
// to apply the rest of the rules to the new URL. Previously checked rules are
// not reapplied; i.e. we never restart the loop.
for (const std::unique_ptr<DebounceRule>& rule : rules) {
if (rule->Apply(current_url, final_url)) {
if (rule->Apply(current_url, final_url, prefs_)) {
if (current_url != *final_url) {
changed = true;
current_url = *final_url;
Expand Down
Loading

0 comments on commit c094248

Please sign in to comment.