Skip to content

Support generichide rules #97

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions src/blocker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ pub struct Blocker {
#[cfg(feature = "object-pooling")]
#[serde(skip_serializing, skip_deserializing)]
pool: TokenPool,

#[serde(default)]
generic_hide: NetworkFilterList,
}

impl Blocker {
Expand All @@ -109,6 +112,21 @@ impl Blocker {
self.check_parameterised(request, false, false)
}

pub fn check_generic_hide(&self, hostname_request: &Request) -> bool {
let mut request_tokens;
#[cfg(feature = "object-pooling")]
{
request_tokens = self.pool.pool.new();
}
#[cfg(not(feature = "object-pooling"))]
{
request_tokens = Vec::with_capacity(utils::TOKENS_BUFFER_SIZE);
}
hostname_request.get_tokens(&mut request_tokens);

self.generic_hide.check(hostname_request, &request_tokens, &HashSet::new()).is_some()
}

pub fn check_parameterised(&self, request: &Request, matched_rule: bool, force_check_exceptions: bool) -> BlockerResult {
if !request.is_supported {
return BlockerResult::default();
Expand Down Expand Up @@ -247,6 +265,8 @@ impl Blocker {
let mut tagged_filters_all = Vec::with_capacity(200);
// $badfilter
let mut badfilters = Vec::with_capacity(100);
// $generichide
let mut generic_hide = Vec::with_capacity(4000);
// All other filters
let mut filters = Vec::with_capacity(network_filters.len());

Expand All @@ -268,6 +288,8 @@ impl Blocker {
}
if filter.is_csp() {
csp.push(filter);
} else if filter.is_generic_hide() {
generic_hide.push(filter);
} else if filter.is_exception() {
exceptions.push(filter);
} else if filter.is_important() {
Expand All @@ -283,6 +305,7 @@ impl Blocker {
}

csp.shrink_to_fit();
generic_hide.shrink_to_fit();
exceptions.shrink_to_fit();
importants.shrink_to_fit();
redirects.shrink_to_fit();
Expand All @@ -296,6 +319,7 @@ impl Blocker {
redirects: NetworkFilterList::new(redirects, options.enable_optimizations),
filters_tagged: NetworkFilterList::new(Vec::new(), options.enable_optimizations),
filters: NetworkFilterList::new(filters, options.enable_optimizations),
generic_hide: NetworkFilterList::new(generic_hide, options.enable_optimizations),
// Tags special case for enabling/disabling them dynamically
tags_enabled: HashSet::new(),
tagged_filters_all,
Expand All @@ -315,6 +339,8 @@ impl Blocker {
pub fn filter_exists(&self, filter: &NetworkFilter) -> bool {
if filter.is_csp() {
self.csp.filter_exists(filter)
} else if filter.is_generic_hide() {
self.generic_hide.filter_exists(filter)
} else if filter.is_exception() {
self.exceptions.filter_exists(filter)
} else if filter.is_important() {
Expand All @@ -336,6 +362,9 @@ impl Blocker {
} else if filter.is_csp() {
self.csp.filter_add(filter);
Ok(self)
} else if filter.is_generic_hide() {
self.generic_hide.filter_add(filter);
Ok(self)
} else if filter.is_exception() {
self.exceptions.filter_add(filter);
Ok(self)
Expand Down Expand Up @@ -1327,6 +1356,20 @@ mod blocker_tests {
assert!(!matched_rule.matched);
assert!(matched_rule.exception.is_some());
}

#[test]
fn generichide() {
let blocker_options: BlockerOptions = BlockerOptions {
debug: true,
enable_optimizations: true,
};

let mut blocker = Blocker::new(Vec::new(), &blocker_options);

blocker.filter_add(NetworkFilter::parse("@@||example.com$generichide", true).unwrap()).unwrap();

assert!(blocker.check_generic_hide(&Request::from_url("https://example.com").unwrap()));
}
}

#[cfg(test)]
Expand All @@ -1346,14 +1389,12 @@ mod legacy_rule_parsing_tests {
// easyList = { 24478, 31144, 0, 5589 };
// not handling (and not including) filters with the following options:
// - $popup
// - $generichide
// - $subdocument
// - $document
// - $elemhide
// difference from original counts caused by not handling document/subdocument options and possibly miscounting on the blocker side.
// Printing all non-cosmetic, non-html, non-comment/-empty rules and ones with no unsupported options yields 29142 items
// This engine also handles 3 rules that old one does not
const EASY_LIST: ListCounts = ListCounts { filters: 24062+3, cosmetic_filters: 31163, exceptions: 5080 };
const EASY_LIST: ListCounts = ListCounts { filters: 24062+3, cosmetic_filters: 31163, exceptions: 5800 };
// easyPrivacy = { 11817, 0, 0, 1020 };
// differences in counts explained by hashset size underreporting as detailed in the next two cases
const EASY_PRIVACY: ListCounts = ListCounts { filters: 11889, cosmetic_filters: 0, exceptions: 1021 };
Expand Down Expand Up @@ -1391,7 +1432,8 @@ mod legacy_rule_parsing_tests {
let blocker = Blocker::new(network_filters, &blocker_options);

// Some filters in the filter_map are pointed at by multiple tokens, increasing the total number of items
assert!(vec_hashmap_len(&blocker.exceptions.filter_map) >= expectation.exceptions, "Number of collected exceptions does not match expectation");
assert!(vec_hashmap_len(&blocker.exceptions.filter_map) + vec_hashmap_len(&blocker.generic_hide.filter_map)
>= expectation.exceptions, "Number of collected exceptions does not match expectation");

assert!(vec_hashmap_len(&blocker.filters.filter_map) +
vec_hashmap_len(&blocker.importants.filter_map) +
Expand Down
80 changes: 46 additions & 34 deletions src/cosmetic_filter_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ lazy_static! {
static ref PUBLIC_SUFFIXES: psl::List = psl::List::new();
}

/// Contains cosmetic filter information intended to be injected into a particular hostname.
/// Contains cosmetic filter information intended to be used on a particular URL.
///
/// `hide_selectors` is a set of any CSS selector on the page that should be hidden, i.e. styled as
/// `{ display: none !important; }`.
Expand All @@ -25,21 +25,27 @@ lazy_static! {
///
/// `injected_script` is the Javascript code for any scriptlets that should be injected into the
/// page.
///
/// `generichide` is set to true if there is a corresponding `$generichide` exception network
/// filter. If so, the page should not query for additional generic rules using
/// `hidden_class_id_selectors`.
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct HostnameSpecificResources {
pub struct UrlSpecificResources {
pub hide_selectors: HashSet<String>,
pub style_selectors: HashMap<String, Vec<String>>,
pub exceptions: HashSet<String>,
pub injected_script: String,
pub generichide: bool,
}

impl HostnameSpecificResources {
impl UrlSpecificResources {
pub fn empty() -> Self {
Self {
hide_selectors: HashSet::new(),
style_selectors: HashMap::new(),
exceptions: HashSet::new(),
injected_script: String::new(),
generichide: false,
}
}
}
Expand Down Expand Up @@ -197,10 +203,10 @@ impl CosmeticFilterCache {
.collect::<Vec<_>>()
}

pub fn hostname_cosmetic_resources(&self, hostname: &str) -> HostnameSpecificResources {
pub fn hostname_cosmetic_resources(&self, hostname: &str, generichide: bool) -> UrlSpecificResources {
let domain = match PUBLIC_SUFFIXES.domain(hostname) {
Some(domain) => domain,
None => return HostnameSpecificResources::empty(),
None => return UrlSpecificResources::empty(),
};
let domain_str = domain.to_str();

Expand All @@ -225,8 +231,13 @@ impl CosmeticFilterCache {

let (hostname_hide_selectors, style_selectors, script_injections) = hostname_specific_rules(&rules_that_apply[..]);

let mut hide_selectors = self.misc_generic_selectors.difference(&exceptions.hide_exceptions).cloned().collect::<HashSet<_>>();
hostname_hide_selectors.into_iter().for_each(|sel| { hide_selectors.insert(sel); });
let hide_selectors = if generichide {
hostname_hide_selectors
} else {
let mut hide_selectors = self.misc_generic_selectors.difference(&exceptions.hide_exceptions).cloned().collect::<HashSet<_>>();
hostname_hide_selectors.into_iter().for_each(|sel| { hide_selectors.insert(sel); });
hide_selectors
};

let mut injected_script = String::new();
script_injections.iter().for_each(|s| {
Expand All @@ -236,11 +247,12 @@ impl CosmeticFilterCache {
}
});

HostnameSpecificResources {
UrlSpecificResources {
hide_selectors,
style_selectors,
exceptions: exceptions.hide_exceptions,
injected_script,
generichide,
}
}

Expand Down Expand Up @@ -446,15 +458,15 @@ mod cosmetic_cache_tests {
"sub.example.com#@#.item2",
]);

let out = cfcache.hostname_cosmetic_resources("test.com");
let mut expected = HostnameSpecificResources::empty();
let out = cfcache.hostname_cosmetic_resources("test.com", false);
let mut expected = UrlSpecificResources::empty();
assert_eq!(out, expected);

let out = cfcache.hostname_cosmetic_resources("example.com");
let out = cfcache.hostname_cosmetic_resources("example.com", false);
expected.exceptions.insert(".item".into());
assert_eq!(out, expected);

let out = cfcache.hostname_cosmetic_resources("sub.example.com");
let out = cfcache.hostname_cosmetic_resources("sub.example.com", false);
expected.exceptions.insert(".item2".into());
assert_eq!(out, expected);
}
Expand All @@ -465,16 +477,16 @@ mod cosmetic_cache_tests {
"example.com,~sub.example.com##.item",
]);

let out = cfcache.hostname_cosmetic_resources("test.com");
let mut expected = HostnameSpecificResources::empty();
let out = cfcache.hostname_cosmetic_resources("test.com", false);
let mut expected = UrlSpecificResources::empty();
assert_eq!(out, expected);

let out = cfcache.hostname_cosmetic_resources("example.com");
let out = cfcache.hostname_cosmetic_resources("example.com", false);
expected.hide_selectors.insert(".item".to_owned());
assert_eq!(out, expected);

let out = cfcache.hostname_cosmetic_resources("sub.example.com");
let mut expected = HostnameSpecificResources::empty();
let out = cfcache.hostname_cosmetic_resources("sub.example.com", false);
let mut expected = UrlSpecificResources::empty();
expected.exceptions.insert(".item".into());
assert_eq!(out, expected);
}
Expand All @@ -488,23 +500,23 @@ mod cosmetic_cache_tests {
"a2.sub.example.com##.element:style(background: #000)",
]);

let out = cfcache.hostname_cosmetic_resources("sub.example.com");
let mut expected = HostnameSpecificResources::empty();
let out = cfcache.hostname_cosmetic_resources("sub.example.com", false);
let mut expected = UrlSpecificResources::empty();
assert_eq!(out, expected);

let out = cfcache.hostname_cosmetic_resources("sub.test.example.com");
let out = cfcache.hostname_cosmetic_resources("sub.test.example.com", false);
assert_eq!(out, expected);

let out = cfcache.hostname_cosmetic_resources("a1.sub.example.com");
let out = cfcache.hostname_cosmetic_resources("a1.sub.example.com", false);
expected.hide_selectors.insert(".element".to_owned());
assert_eq!(out, expected);

let out = cfcache.hostname_cosmetic_resources("test.example.com");
let out = cfcache.hostname_cosmetic_resources("test.example.com", false);
expected.hide_selectors.clear();
expected.style_selectors.insert(".element".to_owned(), vec!["background: #fff".to_owned()]);
assert_eq!(out, expected);

let out = cfcache.hostname_cosmetic_resources("a2.sub.example.com");
let out = cfcache.hostname_cosmetic_resources("a2.sub.example.com", false);
expected.style_selectors.clear();
expected.style_selectors.insert(".element".to_owned(), vec!["background: #000".to_owned()]);
assert_eq!(out, expected);
Expand Down Expand Up @@ -547,26 +559,26 @@ mod cosmetic_cache_tests {
},
]);

let out = cfcache.hostname_cosmetic_resources("sub.example.com");
let mut expected = HostnameSpecificResources::empty();
let out = cfcache.hostname_cosmetic_resources("sub.example.com", false);
let mut expected = UrlSpecificResources::empty();
assert_eq!(out, expected);

let out = cfcache.hostname_cosmetic_resources("sub.test.example.com");
let out = cfcache.hostname_cosmetic_resources("sub.test.example.com", false);
assert_eq!(out, expected);

let out = cfcache.hostname_cosmetic_resources("test.example.com");
let out = cfcache.hostname_cosmetic_resources("test.example.com", false);
expected.injected_script = "set-constant.js, atob, trueFunc\n".to_owned();
assert_eq!(out, expected);

let out = cfcache.hostname_cosmetic_resources("cosmetic.net");
let out = cfcache.hostname_cosmetic_resources("cosmetic.net", false);
expected.injected_script = "nowebrtc.js\n".to_owned();
assert_eq!(out, expected);

let out = cfcache.hostname_cosmetic_resources("g.cosmetic.net");
let out = cfcache.hostname_cosmetic_resources("g.cosmetic.net", false);
expected.injected_script = "nowebrtc.js\nwindow.open-defuser.js\n".to_owned();
assert_eq!(out, expected);

let out = cfcache.hostname_cosmetic_resources("c.g.cosmetic.net");
let out = cfcache.hostname_cosmetic_resources("c.g.cosmetic.net", false);
expected.injected_script = "window.open-defuser.js\n".to_owned();
assert_eq!(out, expected);
}
Expand Down Expand Up @@ -619,7 +631,7 @@ mod cosmetic_cache_tests {
"~test.com###test-element",
];
let cfcache = CosmeticFilterCache::new(rules.iter().map(|r| CosmeticFilter::parse(r, false).unwrap()).collect::<Vec<_>>());
let exceptions = cfcache.hostname_cosmetic_resources("example.co.uk").exceptions;
let exceptions = cfcache.hostname_cosmetic_resources("example.co.uk", false).exceptions;

let out = cfcache.hidden_class_id_selectors(&["a-class".into()], &[], &exceptions);
assert_eq!(out, [".a-class .with .children"]);
Expand All @@ -630,7 +642,7 @@ mod cosmetic_cache_tests {
let out = cfcache.hidden_class_id_selectors(&[], &["test-element".into()], &exceptions);
assert_eq!(out, ["#test-element"]);

let exceptions = cfcache.hostname_cosmetic_resources("a1.test.com").exceptions;
let exceptions = cfcache.hostname_cosmetic_resources("a1.test.com", false).exceptions;

let out = cfcache.hidden_class_id_selectors(&["a-class".into()], &[], &exceptions);
assert_eq!(out, [".a-class", ".a-class .with .children"]);
Expand All @@ -653,14 +665,14 @@ mod cosmetic_cache_tests {
];
let cfcache = CosmeticFilterCache::new(rules.iter().map(|r| CosmeticFilter::parse(r, false).unwrap()).collect::<Vec<_>>());

let hide_selectors = cfcache.hostname_cosmetic_resources("test.com").hide_selectors;
let hide_selectors = cfcache.hostname_cosmetic_resources("test.com", false).hide_selectors;
let mut expected_hides = HashSet::new();
expected_hides.insert("a[href=\"bad.com\"]".to_owned());
expected_hides.insert("div > p".to_owned());
expected_hides.insert("a[href=\"notbad.com\"]".to_owned());
assert_eq!(hide_selectors, expected_hides);

let hide_selectors = cfcache.hostname_cosmetic_resources("example.com").hide_selectors;
let hide_selectors = cfcache.hostname_cosmetic_resources("example.com", false).hide_selectors;
let mut expected_hides = HashSet::new();
expected_hides.insert("a[href=\"bad.com\"]".to_owned());
assert_eq!(hide_selectors, expected_hides);
Expand Down
Loading