From b1310df6236f4f7dbe3903a6bc50ce16a93b3d7f Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Mon, 28 Oct 2019 05:03:06 -0700 Subject: [PATCH] Migrate to web_accessible_resources and scriptlets.js (#50) * allow parsing web_accessible_resources and scriptlets.js into JSON format * integrate new resources into existing structures * expose resource assembly from neon bindings * add redirect performance benchmark * moves resources into a module --- Cargo.lock | 1 + Cargo.toml | 5 + README.md | 2 +- benches/bench_redirect_performance.rs | 179 ++ data/test/fake-uBO-files/redirect-engine.js | 826 ++++++ data/test/fake-uBO-files/scriptlets.js | 1131 ++++++++ .../web_accessible_resources/1x1.gif | Bin 0 -> 43 bytes .../web_accessible_resources/2x2.png | Bin 0 -> 68 bytes .../web_accessible_resources/32x32.png | Bin 0 -> 83 bytes .../web_accessible_resources/3x2.png | Bin 0 -> 68 bytes .../web_accessible_resources/README.txt | 11 + .../addthis_widget.js | 38 + .../web_accessible_resources/amazon_ads.js | 70 + .../web_accessible_resources/ampproject_v0.js | 34 + .../web_accessible_resources/chartbeat.js | 30 + .../web_accessible_resources/disqus_embed.js | 42 + .../disqus_forums_embed.js | 52 + .../doubleclick_instream_ad_status.js | 1 + .../web_accessible_resources/empty | 0 .../google-analytics_analytics.js | 70 + .../google-analytics_cx_api.js | 36 + .../google-analytics_ga.js | 118 + .../google-analytics_inpage_linkid.js | 28 + .../googlesyndication_adsbygoogle.js | 47 + .../googletagmanager_gtm.js | 43 + .../googletagservices_gpt.js | 149 + .../web_accessible_resources/hd-main.js | 46 + .../ligatus_angular-tag.js | 29 + .../web_accessible_resources/monkeybroker.js | 43 + .../web_accessible_resources/nobab.js | 87 + .../web_accessible_resources/noeval-silent.js | 28 + .../web_accessible_resources/noeval.js | 30 + .../web_accessible_resources/nofab.js | 63 + .../web_accessible_resources/noop-0.1s.mp3 | Bin 0 -> 813 bytes .../web_accessible_resources/noop-1s.mp4 | Bin 0 -> 3753 bytes .../web_accessible_resources/noop.html | 5 + .../web_accessible_resources/noop.js | 3 + .../web_accessible_resources/noop.txt | 1 + .../outbrain-widget.js | 47 + .../web_accessible_resources/popads-dummy.js | 30 + .../web_accessible_resources/popads.js | 40 + .../scorecardresearch_beacon.js | 31 + .../window.open-defuser.js | 43 + data/uBlockOrigin/resources.txt | 2432 ----------------- lib/index.js | 1 + native/src/lib.rs | 28 +- src/blocker.rs | 22 +- src/engine.rs | 53 +- src/resources.rs | 207 -- src/resources/mod.rs | 196 ++ src/resources/resource_assembler.rs | 372 +++ tests/live.rs | 12 +- tests/matching.rs | 21 +- tests/ublock-coverage.rs | 11 +- 54 files changed, 4096 insertions(+), 2698 deletions(-) create mode 100644 benches/bench_redirect_performance.rs create mode 100644 data/test/fake-uBO-files/redirect-engine.js create mode 100644 data/test/fake-uBO-files/scriptlets.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/1x1.gif create mode 100644 data/test/fake-uBO-files/web_accessible_resources/2x2.png create mode 100644 data/test/fake-uBO-files/web_accessible_resources/32x32.png create mode 100644 data/test/fake-uBO-files/web_accessible_resources/3x2.png create mode 100644 data/test/fake-uBO-files/web_accessible_resources/README.txt create mode 100644 data/test/fake-uBO-files/web_accessible_resources/addthis_widget.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/amazon_ads.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/ampproject_v0.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/chartbeat.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/disqus_embed.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/disqus_forums_embed.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/doubleclick_instream_ad_status.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/empty create mode 100644 data/test/fake-uBO-files/web_accessible_resources/google-analytics_analytics.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/google-analytics_cx_api.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/google-analytics_ga.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/google-analytics_inpage_linkid.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/googlesyndication_adsbygoogle.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/googletagmanager_gtm.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/googletagservices_gpt.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/hd-main.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/ligatus_angular-tag.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/monkeybroker.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/nobab.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/noeval-silent.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/noeval.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/nofab.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/noop-0.1s.mp3 create mode 100644 data/test/fake-uBO-files/web_accessible_resources/noop-1s.mp4 create mode 100644 data/test/fake-uBO-files/web_accessible_resources/noop.html create mode 100644 data/test/fake-uBO-files/web_accessible_resources/noop.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/noop.txt create mode 100644 data/test/fake-uBO-files/web_accessible_resources/outbrain-widget.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/popads-dummy.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/popads.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/scorecardresearch_beacon.js create mode 100644 data/test/fake-uBO-files/web_accessible_resources/window.open-defuser.js delete mode 100644 data/uBlockOrigin/resources.txt delete mode 100644 src/resources.rs create mode 100644 src/resources/mod.rs create mode 100644 src/resources/resource_assembler.rs diff --git a/Cargo.lock b/Cargo.lock index 0dc1b0d2..ffc98099 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,6 +16,7 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lifeguard 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "psl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)", "rmp-serde 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index a4e46a44..66e51f12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ criterion = "0.2" csv = "1" serde_json = "1.0" reqwest = "0.9" +psl = "0.4.1" [lib] bench = false @@ -61,6 +62,10 @@ harness = false name = "bench_rules" harness = false +[[bench]] +name = "bench_redirect_performance" +harness = false + [features] default = ["full-regex-handling", "object-pooling"] full-domain-matching = [] # feature has no explicit dependencies diff --git a/README.md b/README.md index 44b0cc64..0a9ae9e0 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ const AdBlockClient = require('adblock-rs'); let el_rules = fs.readFileSync('./data/easylist.to/easylist/easylist.txt', { encoding: 'utf-8' }).split('\n'); let ubo_unbreak_rules = fs.readFileSync('./data/uBlockOrigin/unbreak.txt', { encoding: 'utf-8' }).split('\n'); let rules = el_rules.concat(ubo_unbreak_rules); -let resources = fs.readFileSync('./data/uBlockOrigin/resources.txt', { encoding: 'utf-8' }); +let resources = AdBlockClient.uBlockResources('uBlockOrigin/src/web_accessible_resources', 'uBlockOrigin/src/js/redirect-engine.js', 'uBlockOrigin/assets/resources/scriptlets.js'); // create client with debug = true const client = new AdBlockClient.Engine(rules, true); diff --git a/benches/bench_redirect_performance.rs b/benches/bench_redirect_performance.rs new file mode 100644 index 00000000..8175174a --- /dev/null +++ b/benches/bench_redirect_performance.rs @@ -0,0 +1,179 @@ +use criterion::*; +use std::path::Path; +use psl::Psl; +use lazy_static::lazy_static; + +use adblock; +use adblock::filters::network::{NetworkFilter, NetworkFilterMask}; +use adblock::request::Request; +use adblock::blocker::{Blocker, BlockerOptions}; +use adblock::resource_assembler::{assemble_web_accessible_resources, assemble_scriptlet_resources}; + +lazy_static! { + static ref PSL_LIST: psl::List = psl::List::new(); +} + +/// Gets all rules with redirects, and modifies them to apply to resources at `a{0-n}.com/bad.js` +fn get_redirect_rules() -> Vec { + adblock::filter_lists::default::default_lists() + .into_iter() + .map(|list| { + let filters: Vec = reqwest::get(&list.url).expect("Could not request rules") + .text().expect("Could not get rules as text") + .lines() + .map(|s| s.to_owned()) + .collect(); + + let (network_filters, _) = adblock::lists::parse_filters(&filters, true, false, true); + network_filters + }) + .flatten() + .filter(|rule| { + if let Some(ref redirect) = rule.redirect { + if redirect != "none" { + return true; + } + } + false + }) + .enumerate() + .map(|(index, mut rule)| { + rule.mask.insert(NetworkFilterMask::IS_LEFT_ANCHOR); + rule.mask.insert(NetworkFilterMask::IS_RIGHT_ANCHOR); + rule.hostname = Some(format!("a{}.com/bad.js", index)); + + rule.filter = adblock::filters::network::FilterPart::Empty; + rule.mask.remove(NetworkFilterMask::IS_HOSTNAME_ANCHOR); + rule.mask.remove(NetworkFilterMask::IS_HOSTNAME_REGEX); + rule.mask.remove(NetworkFilterMask::IS_REGEX); + rule.mask.remove(NetworkFilterMask::IS_COMPLETE_REGEX); + rule.mask.remove(NetworkFilterMask::FUZZY_MATCH); + + rule + }) + .collect() +} + +/// Loads the supplied rules, and the test set of resources, into a Blocker +fn get_preloaded_blocker(rules: Vec) -> Blocker { + let blocker_options = BlockerOptions { + debug: false, + enable_optimizations: true, + load_cosmetic_filters: false, + load_network_filters: true + }; + + let mut blocker = Blocker::new(rules, &blocker_options); + + let mut resources = assemble_web_accessible_resources( + Path::new("data/test/fake-uBO-files/web_accessible_resources"), + Path::new("data/test/fake-uBO-files/redirect-engine.js") + ); + resources.append(&mut assemble_scriptlet_resources( + Path::new("data/test/fake-uBO-files/scriptlets.js"), + )); + + blocker.with_resources(&resources); + + blocker +} + +/// Maps network filter rules into `Request`s that would trigger those rules +pub fn build_custom_requests(rules: Vec) -> Vec { + rules.iter().map(|rule| { + let raw_type = if rule.mask.contains(NetworkFilterMask::FROM_IMAGE) { + "image" + } else if rule.mask.contains(NetworkFilterMask::FROM_MEDIA) { + "media" + } else if rule.mask.contains(NetworkFilterMask::FROM_OBJECT) { + "object" + } else if rule.mask.contains(NetworkFilterMask::FROM_OTHER) { + "other" + } else if rule.mask.contains(NetworkFilterMask::FROM_PING) { + "ping" + } else if rule.mask.contains(NetworkFilterMask::FROM_SCRIPT) { + "script" + } else if rule.mask.contains(NetworkFilterMask::FROM_STYLESHEET) { + "stylesheet" + } else if rule.mask.contains(NetworkFilterMask::FROM_SUBDOCUMENT) { + "subdocument" + } else if rule.mask.contains(NetworkFilterMask::FROM_DOCUMENT) { + "main_frame" + } else if rule.mask.contains(NetworkFilterMask::FROM_XMLHTTPREQUEST) { + "xhr" + } else if rule.mask.contains(NetworkFilterMask::FROM_WEBSOCKET) { + "websocket" + } else if rule.mask.contains(NetworkFilterMask::FROM_FONT) { + "font" + } else { + unreachable!() + }; + + let rule_hostname = rule.hostname.clone().unwrap(); + let url = format!("https://{}", rule_hostname.clone()); + let domain = &rule_hostname[..rule_hostname.find('/').unwrap()]; + let hostname = domain; + + let raw_line = rule.raw_line.clone().unwrap(); + let (source_hostname, source_domain) = if rule.opt_domains.is_some() { + let domain_start = raw_line.rfind("domain=").unwrap() + "domain=".len(); + let from_start = &raw_line[domain_start..]; + let domain_end = from_start.find('|').or_else(|| from_start.find(",")).or_else(|| Some(from_start.len())).unwrap() + domain_start; + let source_hostname = &raw_line[domain_start..domain_end]; + + let suffix = PSL_LIST.suffix(source_hostname).unwrap(); + let suffix = suffix.to_str(); + let domain_start = source_hostname[..source_hostname.len()-suffix.len()-1].rfind('.'); + let source_domain = if let Some(domain_start) = domain_start { + &source_hostname[domain_start+1..] + } else { + source_hostname + }; + (source_hostname, source_domain) + } else { + (hostname, domain) + }; + + Request::new( + raw_type, + &url, + "https", + hostname, + domain, + source_hostname, + source_domain, + ) + }).collect::>() +} + +fn bench_fn(blocker: &Blocker, requests: &[Request]) { + requests.iter().for_each(|request| { + let block_result = blocker.check(&request); + assert!(block_result.redirect.is_some()); + }); +} + +fn redirect_performance(c: &mut Criterion) { + let rules = get_redirect_rules(); + + let blocker = get_preloaded_blocker(rules.clone()); + let requests = build_custom_requests(rules.clone()); + let requests_len = requests.len() as u32; + + c.bench( + "redirect_performance", + Benchmark::new( + "without_alias_lookup", + move |b| { + b.iter(|| bench_fn(&blocker, &requests)) + }, + ).throughput(Throughput::Elements(requests_len)) + .sample_size(10) + ); +} + +criterion_group!( + benches, + redirect_performance, +); +criterion_main!(benches); diff --git a/data/test/fake-uBO-files/redirect-engine.js b/data/test/fake-uBO-files/redirect-engine.js new file mode 100644 index 00000000..c5720f34 --- /dev/null +++ b/data/test/fake-uBO-files/redirect-engine.js @@ -0,0 +1,826 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2015-present Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +'use strict'; + +/******************************************************************************/ + +µBlock.redirectEngine = (( ) => { + +/******************************************************************************/ +/******************************************************************************/ + +// The resources referenced below are found in ./web_accessible_resources/ +// +// The content of the resources which declare a `data` property will be loaded +// in memory, and converted to a suitable internal format depending on the +// type of the loaded data. The `data` property allows for manual injection +// through `+js(...)`, or for redirection to a data: URI when a redirection +// to a web accessible resource is not desirable. + +const redirectableResources = new Map([ + [ '1x1.gif', { + alias: '1x1-transparent.gif', + data: 'blob', + } ], + [ '2x2.png', { + alias: '2x2-transparent.png', + data: 'blob', + } ], + [ '3x2.png', { + alias: '3x2-transparent.png', + data: 'blob', + } ], + [ '32x32.png', { + alias: '32x32-transparent.png', + data: 'blob', + } ], + [ 'addthis_widget.js', { + alias: 'addthis.com/addthis_widget.js', + } ], + [ 'amazon_ads.js', { + alias: 'amazon-adsystem.com/aax2/amzn_ads.js', + } ], + [ 'ampproject_v0.js', { + alias: 'ampproject.org/v0.js', + } ], + [ 'chartbeat.js', { + alias: 'static.chartbeat.com/chartbeat.js', + } ], + [ 'disqus_embed.js', { + alias: 'disqus.com/embed.js', + } ], + [ 'disqus_forums_embed.js', { + alias: 'disqus.com/forums/*/embed.js', + } ], + [ 'doubleclick_instream_ad_status.js', { + alias: 'doubleclick.net/instream/ad_status.js', + } ], + [ 'empty', { + data: 'text', // Important! + } ], + [ 'google-analytics_analytics.js', { + alias: 'google-analytics.com/analytics.js', + } ], + [ 'google-analytics_cx_api.js', { + alias: 'google-analytics.com/cx/api.js', + } ], + [ 'google-analytics_ga.js', { + alias: 'google-analytics.com/ga.js', + } ], + [ 'google-analytics_inpage_linkid.js', { + alias: 'google-analytics.com/inpage_linkid.js', + } ], + [ 'googlesyndication_adsbygoogle.js', { + alias: 'googlesyndication.com/adsbygoogle.js', + } ], + [ 'googletagmanager_gtm.js', { + alias: 'googletagmanager.com/gtm.js', + } ], + [ 'googletagservices_gpt.js', { + alias: 'googletagservices.com/gpt.js', + } ], + [ 'hd-main.js', { + } ], + [ 'ligatus_angular-tag.js', { + alias: 'ligatus.com/*/angular-tag.js', + } ], + [ 'monkeybroker.js', { + alias: 'd3pkae9owd2lcf.cloudfront.net/mb105.js', + } ], + [ 'noeval.js', { + data: 'text', + } ], + [ 'noeval-silent.js', { + alias: 'silent-noeval.js', + data: 'text', + } ], + [ 'nobab.js', { + alias: 'bab-defuser.js', + data: 'text', + } ], + [ 'nofab.js', { + alias: 'fuckadblock.js-3.2.0', + data: 'text', + } ], + [ 'noop-0.1s.mp3', { + alias: 'noopmp3-0.1s', + data: 'blob', + } ], + [ 'noop-1s.mp4', { + alias: 'noopmp4-1s', + data: 'blob', + } ], + [ 'noop.html', { + alias: 'noopframe', + } ], + [ 'noop.js', { + alias: 'noopjs', + data: 'text', + } ], + [ 'noop.txt', { + alias: 'nooptext', + data: 'text', + } ], + [ 'outbrain-widget.js', { + alias: 'widgets.outbrain.com/outbrain.js', + } ], + [ 'popads.js', { + alias: 'popads.net.js', + data: 'text', + } ], + [ 'popads-dummy.js', { + data: 'text', + } ], + [ 'scorecardresearch_beacon.js', { + alias: 'scorecardresearch.com/beacon.js', + } ], + [ 'window.open-defuser.js', { + data: 'text', + } ], +]); + +const extToMimeMap = new Map([ + [ 'gif', 'image/gif' ], + [ 'html', 'text/html' ], + [ 'js', 'application/javascript' ], + [ 'mp3', 'audio/mp3' ], + [ 'mp4', 'video/mp4' ], + [ 'png', 'image/png' ], + [ 'txt', 'text/plain' ], +]); + +const typeToMimeMap = new Map([ + [ 'main_frame', 'text/html' ], + [ 'other', 'text/plain' ], + [ 'script', 'application/javascript' ], + [ 'stylesheet', 'text/css' ], + [ 'sub_frame', 'text/html' ], + [ 'xmlhttprequest', 'text/plain' ], +]); + +const validMimes = new Set(extToMimeMap.values()); + +const mimeFromName = function(name) { + const match = /\.([^.]+)$/.exec(name); + if ( match !== null ) { + return extToMimeMap.get(match[1]); + } +}; + +/******************************************************************************/ +/******************************************************************************/ + +const RedirectEntry = class { + constructor() { + this.mime = ''; + this.data = ''; + this.warURL = undefined; + } + + // Prevent redirection to web accessible resources when the request is + // of type 'xmlhttprequest', because XMLHttpRequest.responseURL would + // cause leakage of extension id. See: + // - https://stackoverflow.com/a/8056313 + // - https://bugzilla.mozilla.org/show_bug.cgi?id=998076 + // https://www.reddit.com/r/uBlockOrigin/comments/cpxm1v/ + // User-supplied resources may already be base64 encoded. + + toURL(fctxt, asDataURI = false) { + if ( + this.warURL !== undefined && + asDataURI !== true && + fctxt instanceof Object && + fctxt.type !== 'xmlhttprequest' + ) { + return `${this.warURL}${vAPI.warSecret()}`; + } + if ( this.data === undefined ) { return; } + // https://github.com/uBlockOrigin/uBlock-issues/issues/701 + if ( this.data === '' ) { + const mime = typeToMimeMap.get(fctxt.type); + if ( mime === undefined ) { return; } + return `data:${mime},`; + } + if ( this.data.startsWith('data:') === false ) { + if ( this.mime.indexOf(';') === -1 ) { + this.data = `data:${this.mime};base64,${btoa(this.data)}`; + } else { + this.data = `data:${this.mime},${this.data}`; + } + } + return this.data; + } + + toContent() { + if ( this.data.startsWith('data:') ) { + const pos = this.data.indexOf(','); + const base64 = this.data.endsWith(';base64', pos); + this.data = this.data.slice(pos + 1); + if ( base64 ) { + this.data = atob(this.data); + } + } + return this.data; + } + + static fromContent(mime, content) { + const r = new RedirectEntry(); + r.mime = mime; + r.data = content; + return r; + } + + static fromSelfie(selfie) { + const r = new RedirectEntry(); + r.mime = selfie.mime; + r.data = selfie.data; + r.warURL = selfie.warURL; + return r; + } +}; + +/******************************************************************************/ +/******************************************************************************/ + +const RedirectEngine = function() { + this.aliases = new Map(); + this.resources = new Map(); + this.reset(); + this.resourceNameRegister = ''; +}; + +/******************************************************************************/ + +RedirectEngine.prototype.reset = function() { + this.rules = new Map(); + this.ruleSources = new Set(); + this.ruleDestinations = new Set(); + this.resetCache(); + this.modifyTime = Date.now(); +}; + +RedirectEngine.prototype.resetCache = function() { + this._src = ''; + this._srcAll = [ '*' ]; + this._des = ''; + this._desAll = [ '*' ]; +}; + +/******************************************************************************/ + +RedirectEngine.prototype.freeze = function() { +}; + +/******************************************************************************/ + +RedirectEngine.prototype.toBroaderHostname = function(hostname) { + const pos = hostname.indexOf('.'); + if ( pos !== -1 ) { + return hostname.slice(pos + 1); + } + return hostname !== '*' ? '*' : ''; +}; + +/******************************************************************************/ + +RedirectEngine.prototype.decomposeHostname = function(hn, dict, out) { + let i = 0; + for (;;) { + if ( dict.has(hn) ) { + out[i] = hn; i += 1; + } + hn = this.toBroaderHostname(hn); + if ( hn === '' ) { break; } + } + out.length = i; +}; + +/******************************************************************************/ + +RedirectEngine.prototype.lookup = function(fctxt) { + const src = fctxt.getDocHostname(); + const des = fctxt.getHostname(); + const type = fctxt.type; + if ( src !== this._src ) { + this._src = src; + this.decomposeHostname(src, this.ruleSources, this._srcAll); + } + if ( this._srcAll.length === 0 ) { return; } + if ( des !== this._des ) { + this._des = des; + this.decomposeHostname(des, this.ruleDestinations, this._desAll); + } + if ( this._desAll.length === 0 ) { return; } + const reqURL = fctxt.url; + for ( const src of this._srcAll ) { + for ( const des of this._desAll ) { + let entries = this.rules.get(`${src} ${des} ${type}`); + if ( entries !== undefined ) { + const rule = this.lookupRule(entries, reqURL); + if ( rule !== undefined ) { return rule; } + } + entries = this.rules.get(`${src} ${des} *`); + if ( entries !== undefined ) { + const rule = this.lookupRule(entries, reqURL); + if ( rule !== undefined ) { return rule; } + } + } + } +}; + +RedirectEngine.prototype.lookupRule = function(entries, reqURL) { + for ( const entry of entries ) { + if ( entry.pat instanceof RegExp === false ) { + entry.pat = new RegExp(entry.pat, 'i'); + } + if ( entry.pat.test(reqURL) ) { + return entry; + } + } +}; + +/******************************************************************************/ + +RedirectEngine.prototype.toURL = function(fctxt) { + const rule = this.lookup(fctxt); + if ( rule === undefined ) { return; } + let token = this.resourceNameRegister = rule.tok; + const asDataURI = token.charCodeAt(0) === 0x25 /* '%' */; + if ( asDataURI ) { + token = token.slice(1); + } + const entry = this.resources.get(this.aliases.get(token) || token); + if ( entry !== undefined ) { + return entry.toURL(fctxt, asDataURI); + } +}; + +/******************************************************************************/ + +RedirectEngine.prototype.addRule = function(src, des, type, pattern, redirect) { + this.ruleSources.add(src); + this.ruleDestinations.add(des); + const key = `${src} ${des} ${type}`, + entries = this.rules.get(key); + if ( entries === undefined ) { + this.rules.set(key, [ { tok: redirect, pat: pattern } ]); + this.modifyTime = Date.now(); + return; + } + let entry; + for ( var i = 0, n = entries.length; i < n; i++ ) { + entry = entries[i]; + if ( redirect === entry.tok ) { break; } + } + if ( i === n ) { + entries.push({ tok: redirect, pat: pattern }); + return; + } + let p = entry.pat; + if ( p instanceof RegExp ) { + p = p.source; + } + // Duplicate? + let pos = p.indexOf(pattern); + if ( pos !== -1 ) { + if ( pos === 0 || p.charAt(pos - 1) === '|' ) { + pos += pattern.length; + if ( pos === p.length || p.charAt(pos) === '|' ) { return; } + } + } + entry.pat = p + '|' + pattern; +}; + +/******************************************************************************/ + +RedirectEngine.prototype.fromCompiledRule = function(line) { + const fields = line.split('\t'); + if ( fields.length !== 5 ) { return; } + this.addRule(fields[0], fields[1], fields[2], fields[3], fields[4]); +}; + +/******************************************************************************/ + +RedirectEngine.prototype.compileRuleFromStaticFilter = function(line) { + const matches = this.reFilterParser.exec(line); + if ( matches === null || matches.length !== 4 ) { return; } + + const des = matches[1] || ''; + + // https://github.com/uBlockOrigin/uBlock-issues/issues/572 + // Extract best possible hostname. + let deshn = des; + let pos = deshn.lastIndexOf('*'); + if ( pos !== -1 ) { + deshn = deshn.slice(pos + 1); + pos = deshn.indexOf('.'); + if ( pos !== -1 ) { + deshn = deshn.slice(pos + 1); + } else { + deshn = ''; + } + } + + const path = matches[2] || ''; + let pattern = + des + .replace(/\*/g, '[\\w.%-]*') + .replace(/\./g, '\\.') + + path + .replace(/[.+?{}()|[\]\/\\]/g, '\\$&') + .replace(/\^/g, '[^\\w.%-]') + .replace(/\*/g, '.*?'); + if ( pattern === '' ) { + pattern = '^'; + } + + let type, + redirect = '', + srchns = []; + for ( const option of matches[3].split(',') ) { + if ( option.startsWith('redirect=') ) { + redirect = option.slice(9); + continue; + } + if ( option.startsWith('redirect-rule=') ) { + redirect = option.slice(14); + continue; + } + if ( option === 'empty' ) { + redirect = 'empty'; + continue; + } + if ( option === 'mp4' ) { + redirect = 'noopmp4-1s'; + continue; + } + if ( option.startsWith('domain=') ) { + srchns = option.slice(7).split('|'); + continue; + } + if ( (option === 'first-party' || option === '1p') && deshn !== '' ) { + srchns.push(µBlock.URI.domainFromHostname(deshn) || deshn); + continue; + } + // One and only one type must be specified. + if ( this.supportedTypes.has(option) ) { + if ( type !== undefined ) { return; } + type = this.supportedTypes.get(option); + continue; + } + } + + // Need a resource token. + if ( redirect === '' ) { return; } + + // Need one single type -- not negated. + if ( type === undefined ) { + if ( redirect === 'empty' ) { + type = '*'; + } else if ( redirect === 'noopmp4-1s' ) { + type = 'media'; + } else { + return; + } + } + + if ( deshn === '' ) { + deshn = '*'; + } + + if ( srchns.length === 0 ) { + srchns.push('*'); + } + + const out = []; + for ( const srchn of srchns ) { + if ( srchn === '' ) { continue; } + if ( srchn.startsWith('~') ) { continue; } + out.push(`${srchn}\t${deshn}\t${type}\t${pattern}\t${redirect}`); + } + + if ( out.length === 0 ) { return; } + + return out; +}; + +/******************************************************************************/ + +RedirectEngine.prototype.reFilterParser = /^(?:\|\|([^\/:?#^]+)|\*)([^$]+)?\$([^$]+)$/; + +RedirectEngine.prototype.supportedTypes = new Map([ + [ 'css', 'stylesheet' ], + [ 'font', 'font' ], + [ 'image', 'image' ], + [ 'media', 'media' ], + [ 'object', 'object' ], + [ 'script', 'script' ], + [ 'stylesheet', 'stylesheet' ], + [ 'frame', 'sub_frame' ], + [ 'subdocument', 'sub_frame' ], + [ 'xhr', 'xmlhttprequest' ], + [ 'xmlhttprequest', 'xmlhttprequest' ], +]); + +/******************************************************************************/ + +RedirectEngine.prototype.toSelfie = function(path) { + // Because rules may contains RegExp instances, we need to manually + // convert it to a serializable format. The serialized format must be + // suitable to be used as an argument to the Map() constructor. + const rules = []; + for ( const item of this.rules ) { + const rule = [ item[0], [] ]; + const entries = item[1]; + let i = entries.length; + while ( i-- ) { + const entry = entries[i]; + rule[1].push({ + tok: entry.tok, + pat: entry.pat instanceof RegExp ? entry.pat.source : entry.pat + }); + } + rules.push(rule); + } + return µBlock.assets.put( + `${path}/main`, + JSON.stringify({ + rules: rules, + ruleSources: Array.from(this.ruleSources), + ruleDestinations: Array.from(this.ruleDestinations) + }) + ); +}; + +/******************************************************************************/ + +RedirectEngine.prototype.fromSelfie = async function(path) { + const result = await µBlock.assets.get(`${path}/main`); + let selfie; + try { + selfie = JSON.parse(result.content); + } catch (ex) { + } + if ( selfie instanceof Object === false ) { return false; } + this.rules = new Map(selfie.rules); + this.ruleSources = new Set(selfie.ruleSources); + this.ruleDestinations = new Set(selfie.ruleDestinations); + this.resetCache(); + this.modifyTime = Date.now(); + return true; +}; + +/******************************************************************************/ + +RedirectEngine.prototype.resourceContentFromName = function(name, mime) { + const entry = this.resources.get(this.aliases.get(name) || name); + if ( entry === undefined ) { return; } + if ( mime === undefined || entry.mime.startsWith(mime) ) { + return entry.toContent(); + } +}; + +/******************************************************************************/ + +// TODO: combine same key-redirect pairs into a single regex. + +// https://github.com/uBlockOrigin/uAssets/commit/deefe875551197d655f79cb540e62dfc17c95f42 +// Consider 'none' a reserved keyword, to be used to disable redirection. + +RedirectEngine.prototype.resourcesFromString = function(text) { + const lineIter = new µBlock.LineIterator(removeTopCommentBlock(text)); + const reNonEmptyLine = /\S/; + let fields, encoded, details; + + while ( lineIter.eot() === false ) { + let line = lineIter.next(); + if ( line.startsWith('#') ) { continue; } + if ( line.startsWith('// ') ) { continue; } + + if ( fields === undefined ) { + if ( line === '' ) { continue; } + // Modern parser + if ( line.startsWith('/// ') ) { + const name = line.slice(4).trim(); + fields = [ name, mimeFromName(name) ]; + continue; + } + // Legacy parser + const head = line.trim().split(/\s+/); + if ( head.length !== 2 ) { continue; } + if ( head[0] === 'none' ) { continue; } + let pos = head[1].indexOf(';'); + if ( pos === -1 ) { pos = head[1].length; } + if ( validMimes.has(head[1].slice(0, pos)) === false ) { + continue; + } + encoded = head[1].indexOf(';') !== -1; + fields = head; + continue; + } + + if ( line.startsWith('/// ') ) { + if ( details === undefined ) { + details = {}; + } + const [ prop, value ] = line.slice(4).trim().split(/\s+/); + if ( value !== undefined ) { + details[prop] = value; + } + continue; + } + + if ( reNonEmptyLine.test(line) ) { + fields.push(encoded ? line.trim() : line); + continue; + } + + const name = this.aliases.get(fields[0]) || fields[0]; + const mime = fields[1]; + const content = µBlock.orphanizeString( + fields.slice(2).join(encoded ? '' : '\n') + ); + + // No more data, add the resource. + this.resources.set( + name, + RedirectEntry.fromContent(mime, content) + ); + + if ( details instanceof Object && details.alias ) { + this.aliases.set(details.alias, name); + } + + fields = undefined; + details = undefined; + } + + // Process pending resource data. + if ( fields !== undefined ) { + const name = fields[0]; + const mime = fields[1]; + const content = µBlock.orphanizeString( + fields.slice(2).join(encoded ? '' : '\n') + ); + this.resources.set( + name, + RedirectEntry.fromContent(mime, content) + ); + if ( details instanceof Object && details.alias ) { + this.aliases.set(details.alias, name); + } + } + + this.modifyTime = Date.now(); +}; + +const removeTopCommentBlock = function(text) { + return text.replace(/^\/\*[\S\s]+?\n\*\/\s*/, ''); +}; + +/******************************************************************************/ + +RedirectEngine.prototype.loadBuiltinResources = function() { + // TODO: remove once usage of uBO 1.20.4 is widespread. + µBlock.assets.remove('ublock-resources'); + + this.resources = new Map(); + this.aliases = new Map(); + + const fetches = [ + µBlock.assets.fetchText( + '/assets/resources/scriptlets.js' + ).then(result => { + const content = result.content; + if ( typeof content === 'string' && content.length !== 0 ) { + this.resourcesFromString(content); + } + }), + ]; + + const store = (name, data = undefined) => { + const details = redirectableResources.get(name); + const entry = RedirectEntry.fromSelfie({ + mime: mimeFromName(name), + data, + warURL: vAPI.getURL(`/web_accessible_resources/${name}`), + }); + this.resources.set(name, entry); + if ( details.alias !== undefined ) { + this.aliases.set(details.alias, name); + } + }; + + const processBlob = (name, blob) => { + return new Promise(resolve => { + const reader = new FileReader(); + reader.onload = ( ) => { + store(name, reader.result); + resolve(); + }; + reader.readAsDataURL(blob); + }); + }; + + const processText = (name, text) => { + store(name, removeTopCommentBlock(text)); + }; + + const process = result => { + const match = /^\/web_accessible_resources\/([^?]+)/.exec(result.url); + if ( match === null ) { return; } + const name = match[1]; + return result.content instanceof Blob + ? processBlob(name, result.content) + : processText(name, result.content); + }; + + for ( const [ name, details ] of redirectableResources ) { + if ( typeof details.data !== 'string' ) { + store(name); + continue; + } + fetches.push( + µBlock.assets.fetch( + `/web_accessible_resources/${name}${vAPI.warSecret()}`, + { responseType: details.data } + ).then( + result => process(result) + ) + ); + } + + return Promise.all(fetches); +}; + +/******************************************************************************/ + +const resourcesSelfieVersion = 5; + +RedirectEngine.prototype.selfieFromResources = function() { + µBlock.assets.put( + 'compiled/redirectEngine/resources', + JSON.stringify({ + version: resourcesSelfieVersion, + aliases: Array.from(this.aliases), + resources: Array.from(this.resources), + }) + ); +}; + +RedirectEngine.prototype.resourcesFromSelfie = async function() { + const result = await µBlock.assets.get('compiled/redirectEngine/resources'); + let selfie; + try { + selfie = JSON.parse(result.content); + } catch(ex) { + } + if ( + selfie instanceof Object === false || + selfie.version !== resourcesSelfieVersion || + Array.isArray(selfie.resources) === false + ) { + return false; + } + this.aliases = new Map(selfie.aliases); + this.resources = new Map(); + for ( const [ token, entry ] of selfie.resources ) { + this.resources.set(token, RedirectEntry.fromSelfie(entry)); + } + return true; +}; + +RedirectEngine.prototype.invalidateResourcesSelfie = function() { + µBlock.assets.remove('compiled/redirectEngine/resources'); + + // TODO: obsolete, remove eventually + µBlock.cacheStorage.remove('resourcesSelfie'); +}; + +/******************************************************************************/ +/******************************************************************************/ + +return new RedirectEngine(); + +/******************************************************************************/ + +})(); diff --git a/data/test/fake-uBO-files/scriptlets.js b/data/test/fake-uBO-files/scriptlets.js new file mode 100644 index 00000000..e75da6e6 --- /dev/null +++ b/data/test/fake-uBO-files/scriptlets.js @@ -0,0 +1,1131 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2019-present Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock + + The scriptlets below are meant to be injected only into a + web page context. +*/ + +// The lines below are skipped by the resource parser. Purpose is clean +// jshinting. +(function() { +// >>>> start of private namespace +'use strict'; + + + + + +/// abort-current-inline-script.js +/// alias acis.js +(function() { + const target = '{{1}}'; + if ( target === '' || target === '{{1}}' ) { return; } + const needle = '{{2}}'; + let reText = '.?'; + if ( needle !== '' && needle !== '{{2}}' ) { + reText = /^\/.+\/$/.test(needle) + ? needle.slice(1,-1) + : needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + const thisScript = document.currentScript; + const re = new RegExp(reText); + const chain = target.split('.'); + let owner = window; + let prop; + for (;;) { + prop = chain.shift(); + if ( chain.length === 0 ) { break; } + owner = owner[prop]; + if ( owner instanceof Object === false ) { return; } + } + const desc = Object.getOwnPropertyDescriptor(owner, prop); + if ( desc && desc.get !== undefined ) { return; } + const magic = String.fromCharCode(Date.now() % 26 + 97) + + Math.floor(Math.random() * 982451653 + 982451653).toString(36); + let value = owner[prop]; + const validate = function() { + const e = document.currentScript; + if ( + e instanceof HTMLScriptElement && + e.src === '' && + e !== thisScript && + re.test(e.textContent) + ) { + throw new ReferenceError(magic); + } + }; + Object.defineProperty(owner, prop, { + get: function() { + validate(); + return value; + }, + set: function(a) { + validate(); + value = a; + } + }); + const oe = window.onerror; + window.onerror = function(msg) { + if ( typeof msg === 'string' && msg.indexOf(magic) !== -1 ) { + return true; + } + if ( oe instanceof Function ) { + return oe.apply(this, arguments); + } + }.bind(); +})(); + + +/// abort-on-property-read.js +/// alias aopr.js +(function() { + const magic = String.fromCharCode(Date.now() % 26 + 97) + + Math.floor(Math.random() * 982451653 + 982451653).toString(36); + const abort = function() { + throw new ReferenceError(magic); + }; + const makeProxy = function(owner, chain) { + const pos = chain.indexOf('.'); + if ( pos === -1 ) { + const desc = Object.getOwnPropertyDescriptor(owner, chain); + if ( !desc || desc.get !== abort ) { + Object.defineProperty(owner, chain, { + get: abort, + set: function(){} + }); + } + return; + } + const prop = chain.slice(0, pos); + let v = owner[prop]; + chain = chain.slice(pos + 1); + if ( v ) { + makeProxy(v, chain); + return; + } + const desc = Object.getOwnPropertyDescriptor(owner, prop); + if ( desc && desc.set !== undefined ) { return; } + Object.defineProperty(owner, prop, { + get: function() { return v; }, + set: function(a) { + v = a; + if ( a instanceof Object ) { + makeProxy(a, chain); + } + } + }); + }; + const owner = window; + let chain = '{{1}}'; + makeProxy(owner, chain); + const oe = window.onerror; + window.onerror = function(msg, src, line, col, error) { + if ( typeof msg === 'string' && msg.indexOf(magic) !== -1 ) { + return true; + } + if ( oe instanceof Function ) { + return oe(msg, src, line, col, error); + } + }.bind(); +})(); + + +/// abort-on-property-write.js +/// alias aopw.js +(function() { + const magic = String.fromCharCode(Date.now() % 26 + 97) + + Math.floor(Math.random() * 982451653 + 982451653).toString(36); + let prop = '{{1}}'; + let owner = window; + for (;;) { + const pos = prop.indexOf('.'); + if ( pos === -1 ) { break; } + owner = owner[prop.slice(0, pos)]; + if ( owner instanceof Object === false ) { return; } + prop = prop.slice(pos + 1); + } + delete owner[prop]; + Object.defineProperty(owner, prop, { + set: function() { + throw new ReferenceError(magic); + } + }); + const oe = window.onerror; + window.onerror = function(msg, src, line, col, error) { + if ( typeof msg === 'string' && msg.indexOf(magic) !== -1 ) { + return true; + } + if ( oe instanceof Function ) { + return oe(msg, src, line, col, error); + } + }.bind(); +})(); + + +/// addEventListener-defuser.js +/// alias aeld.js +(function() { + let needle1 = '{{1}}'; + if ( needle1 === '' || needle1 === '{{1}}' ) { + needle1 = '.?'; + } else if ( /^\/.+\/$/.test(needle1) ) { + needle1 = needle1.slice(1,-1); + } else { + needle1 = needle1.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + needle1 = new RegExp(needle1); + let needle2 = '{{2}}'; + if ( needle2 === '' || needle2 === '{{2}}' ) { + needle2 = '.?'; + } else if ( /^\/.+\/$/.test(needle2) ) { + needle2 = needle2.slice(1,-1); + } else { + needle2 = needle2.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + needle2 = new RegExp(needle2); + self.EventTarget.prototype.addEventListener = new Proxy( + self.EventTarget.prototype.addEventListener, + { + apply: function(target, thisArg, args) { + const type = args[0].toString(); + const handler = String(args[1]); + if ( + needle1.test(type) === false || + needle2.test(handler) === false + ) { + return target.apply(thisArg, args); + } + } + } + ); +})(); + + +/// addEventListener-logger.js +/// alias aell.js +(function() { + const log = console.log.bind(console); + self.EventTarget.prototype.addEventListener = new Proxy( + self.EventTarget.prototype.addEventListener, + { + apply: function(target, thisArg, args) { + const type = args[0].toString(); + const handler = String(args[1]); + log('addEventListener("%s", %s)', type, handler); + return target.apply(thisArg, args); + } + } + ); +})(); + + +/// json-prune.js +(function() { + const log = console.log.bind(console); + const rawPrunePaths = '{{1}}'; + const rawNeedlePaths = '{{2}}'; + const prunePaths = rawPrunePaths !== '{{1}}' && rawPrunePaths !== '' + ? rawPrunePaths.split(/ +/) + : []; + const needlePaths = rawNeedlePaths !== '{{2}}' && rawNeedlePaths !== '' + ? rawNeedlePaths.split(/ +/) + : []; + const findOwner = function(root, path) { + let owner = root; + let chain = path; + for (;;) { + if ( owner instanceof Object === false ) { return; } + const pos = chain.indexOf('.'); + if ( pos === -1 ) { + return owner.hasOwnProperty(chain) + ? [ owner, chain ] + : undefined; + } + const prop = chain.slice(0, pos); + if ( owner.hasOwnProperty(prop) === false ) { return; } + owner = owner[prop]; + chain = chain.slice(pos + 1); + } + }; + const mustProcess = function(root) { + for ( const needlePath of needlePaths ) { + const details = findOwner(root, needlePath); + if ( details === undefined ) { return false; } + } + return true; + }; + JSON.parse = new Proxy(JSON.parse, { + apply: function() { + const r = Reflect.apply(...arguments); + if ( prunePaths.length === 0 ) { + log(location.hostname, r); + return r; + } + if ( mustProcess(r) === false ) { return r; } + for ( const path of prunePaths ) { + const details = findOwner(r, path); + if ( details !== undefined ) { + delete details[0][details[1]]; + } + } + return r; + }, + }); +})(); + + +// Imported from: +// https://github.com/NanoAdblocker/NanoFilters/blob/1f3be7211bb0809c5106996f52564bf10c4525f7/NanoFiltersSource/NanoResources.txt#L126 +// +// Speed up or down setInterval, 3 optional arguments. +// The payload matcher, a string literal or a JavaScript RegExp, defaults +// to match all. +// delayMatcher +// The delay matcher, an integer, defaults to 1000. +// boostRatio - The delay multiplier when there is a match, 0.5 speeds up by +// 2 times and 2 slows down by 2 times, defaults to 0.05 or speed up +// 20 times. Speed up and down both cap at 50 times. +/// nano-setInterval-booster.js +/// alias nano-sib.js +(function() { + let needle = '{{1}}'; + let delay = parseInt('{{2}}', 10); + let boost = parseFloat('{{3}}'); + if ( needle === '' || needle === '{{1}}' ) { + needle = '.?'; + } else if ( needle.charAt(0) === '/' && needle.slice(-1) === '/' ) { + needle = needle.slice(1, -1); + } else { + needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + needle = new RegExp(needle); + if ( isNaN(delay) || !isFinite(delay) ) { + delay = 1000; + } + if ( isNaN(boost) || !isFinite(boost) ) { + boost = 0.05; + } + if ( boost < 0.02 ) { + boost = 0.02; + } + if ( boost > 50 ) { + boost = 50; + } + window.setInterval = new Proxy(window.setInterval, { + apply: function(target, thisArg, args) { + const a = args[0]; + const b = args[1]; + if ( b === delay && needle.test(a.toString()) ) { + args[1] = b * boost; + } + return target.apply(thisArg, args); + } + }); +})(); + + +// Imported from: +// https://github.com/NanoAdblocker/NanoFilters/blob/1f3be7211bb0809c5106996f52564bf10c4525f7/NanoFiltersSource/NanoResources.txt#L82 +// +// Speed up or down setTimeout, 3 optional arguments. +// funcMatcher +// The payload matcher, a string literal or a JavaScript RegExp, defaults +// to match all. +// delayMatcher +// The delay matcher, an integer, defaults to 1000. +// boostRatio - The delay multiplier when there is a match, 0.5 speeds up by +// 2 times and 2 slows down by 2 times, defaults to 0.05 or speed up +// 20 times. Speed up and down both cap at 50 times. +/// nano-setTimeout-booster.js +/// alias nano-stb.js +(function() { + let needle = '{{1}}'; + let delay = parseInt('{{2}}', 10); + let boost = parseFloat('{{3}}'); + if ( needle === '' || needle === '{{1}}' ) { + needle = '.?'; + } else if ( needle.startsWith('/') && needle.endsWith('/') ) { + needle = needle.slice(1, -1); + } else { + needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + needle = new RegExp(needle); + if ( isNaN(delay) || !isFinite(delay) ) { + delay = 1000; + } + if ( isNaN(boost) || !isFinite(boost) ) { + boost = 0.05; + } + if ( boost < 0.02 ) { + boost = 0.02; + } + if ( boost > 50 ) { + boost = 50; + } + window.setTimeout = new Proxy(window.setTimeout, { + apply: function(target, thisArg, args) { + const a = args[0]; + const b = args[1]; + if ( b === delay && needle.test(a.toString()) ) { + args[1] = b * boost; + } + return target.apply(thisArg, args); + } + }); +})(); + + +/// noeval-if.js +(function() { + let needle = '{{1}}'; + if ( needle === '' || needle === '{{1}}' ) { + needle = '.?'; + } else if ( needle.slice(0,1) === '/' && needle.slice(-1) === '/' ) { + needle = needle.slice(1,-1); + } else { + needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + needle = new RegExp(needle); + window.eval = new Proxy(window.eval, { // jshint ignore: line + apply: function(target, thisArg, args) { + const a = args[0]; + if ( needle.test(a.toString()) === false ) { + return target.apply(thisArg, args); + } + } + }); +})(); + + +/// remove-attr.js +/// alias ra.js +(function() { + const token = '{{1}}'; + if ( token === '' || token === '{{1}}' ) { return; } + const tokens = token.split(/\s*\|\s*/); + let selector = '{{2}}'; + if ( selector === '' || selector === '{{2}}' ) { + selector = `[${tokens.join('],[')}]`; + } + const rmattr = function(ev) { + if ( ev ) { + window.removeEventListener(ev.type, rmattr, true); + } + try { + const nodes = document.querySelectorAll(selector); + for ( const node of nodes ) { + for ( const attr of tokens ) { + node.removeAttribute(attr); + } + } + } catch(ex) { + } + }; + if ( document.readyState === 'loading' ) { + window.addEventListener('DOMContentLoaded', rmattr, true); + } else { + rmattr(); + } +})(); + + +/// requestAnimationFrame-if.js +/// alias raf-if.js +(function() { + let needle = '{{1}}'; + const not = needle.charAt(0) === '!'; + if ( not ) { needle = needle.slice(1); } + if ( needle === '' || needle === '{{1}}' ) { + needle = '.?'; + } else if ( needle.startsWith('/') && needle.endsWith('/') ) { + needle = needle.slice(1,-1); + } else { + needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + const log = needle === '.?' && not === false ? console.log : undefined; + needle = new RegExp(needle); + window.requestAnimationFrame = new Proxy(window.requestAnimationFrame, { + apply: function(target, thisArg, args) { + const a = String(args[0]); + if ( log !== undefined ) { + log('uBO: requestAnimationFrame("%s")', a); + } else if ( needle.test(a) === not ) { + args[0] = function(){}; + } + return target.apply(thisArg, args); + } + }); +})(); + + +/// set-constant.js +/// alias set.js +(function() { + const thisScript = document.currentScript; + let cValue = '{{2}}'; + if ( cValue === 'undefined' ) { + cValue = undefined; + } else if ( cValue === 'false' ) { + cValue = false; + } else if ( cValue === 'true' ) { + cValue = true; + } else if ( cValue === 'null' ) { + cValue = null; + } else if ( cValue === 'noopFunc' ) { + cValue = function(){}; + } else if ( cValue === 'trueFunc' ) { + cValue = function(){ return true; }; + } else if ( cValue === 'falseFunc' ) { + cValue = function(){ return false; }; + } else if ( /^\d+$/.test(cValue) ) { + cValue = parseFloat(cValue); + if ( isNaN(cValue) ) { return; } + if ( Math.abs(cValue) > 0x7FFF ) { return; } + } else if ( cValue === "''" ) { + cValue = ''; + } else { + return; + } + let aborted = false; + const mustAbort = function(v) { + if ( aborted ) { return true; } + aborted = v !== undefined && cValue !== undefined && typeof v !== typeof cValue; + return aborted; + }; + const makeProxy = function(owner, chain) { + const pos = chain.indexOf('.'); + if ( pos === -1 ) { + const original = owner[chain]; + if ( mustAbort(original) ) { return; } + const desc = Object.getOwnPropertyDescriptor(owner, chain); + if ( desc === undefined || desc.get === undefined ) { + Object.defineProperty(owner, chain, { + get: function() { + return document.currentScript === thisScript + ? original + : cValue; + }, + set: function(a) { + if ( mustAbort(a) ) { + cValue = a; + } + } + }); + } + return; + } + const prop = chain.slice(0, pos); + let v = owner[prop]; + chain = chain.slice(pos + 1); + if ( v !== undefined ) { + makeProxy(v, chain); + return; + } + const desc = Object.getOwnPropertyDescriptor(owner, prop); + if ( desc && desc.set !== undefined ) { return; } + Object.defineProperty(owner, prop, { + get: function() { + return v; + }, + set: function(a) { + v = a; + if ( a instanceof Object ) { + makeProxy(a, chain); + } + } + }); + }; + makeProxy(window, '{{1}}'); +})(); + + +/// setInterval-defuser.js +/// alias sid.js +(function() { + let needle = '{{1}}'; + const delay = parseInt('{{2}}', 10); + if ( needle === '' || needle === '{{1}}' ) { + needle = '.?'; + } else if ( needle.startsWith('/') && needle.endsWith('/') ) { + needle = needle.slice(1,-1); + } else { + needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + needle = new RegExp(needle); + window.setInterval = new Proxy(window.setInterval, { + apply: function(target, thisArg, args) { + const a = args[0]; + const b = args[1]; + if ( (isNaN(delay) || b === delay) && needle.test(a.toString()) ) { + args[0] = function(){}; + } + return target.apply(thisArg, args); + } + }); +})(); + + +/// no-setInterval-if.js +/// alias nosiif.js +(function() { + let needle = '{{1}}'; + const needleNot = needle.charAt(0) === '!'; + if ( needleNot ) { needle = needle.slice(1); } + let delay = '{{2}}'; + const delayNot = delay.charAt(0) === '!'; + if ( delayNot ) { delay = delay.slice(1); } + delay = parseInt(delay, 10); + if ( needle === '' || needle === '{{1}}' ) { + needle = ''; + } else if ( needle.startsWith('/') && needle.endsWith('/') ) { + needle = needle.slice(1,-1); + } else { + needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + const log = needleNot === false && needle === '' && + delayNot === false && isNaN(delay) + ? console.log + : undefined; + const reNeedle = new RegExp(needle); + window.setInterval = new Proxy(window.setInterval, { + apply: function(target, thisArg, args) { + const a = String(args[0]); + const b = args[1]; + let defuse = false; + if ( log !== undefined ) { + log('uBO: setInterval("%s", %s)', a, b); + } else if ( isNaN(delay) ) { + defuse = reNeedle.test(a) !== needleNot; + } else if ( needle === '' ) { + defuse = (b === delay) !== delayNot; + } else { + defuse = reNeedle.test(a) !== needleNot && (b === delay) !== delayNot; + } + if ( defuse ) { + args[0] = function(){}; + } + return target.apply(thisArg, args); + } + }); +})(); + + +/// setTimeout-defuser.js +/// alias std.js +(function() { + let needle = '{{1}}'; + const delay = parseInt('{{2}}', 10); + if ( needle === '' || needle === '{{1}}' ) { + needle = '.?'; + } else if ( needle.startsWith('/') && needle.endsWith('/') ) { + needle = needle.slice(1,-1); + } else { + needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + needle = new RegExp(needle); + window.setTimeout = new Proxy(window.setTimeout, { + apply: function(target, thisArg, args) { + const a = args[0]; + const b = args[1]; + if ( (isNaN(delay) || b === delay) && needle.test(a.toString()) ) { + args[0] = function(){}; + } + return target.apply(thisArg, args); + } + }); +})(); + + +/// no-setTimeout-if.js +/// alias nostif.js +(function() { + let needle = '{{1}}'; + const needleNot = needle.charAt(0) === '!'; + if ( needleNot ) { needle = needle.slice(1); } + let delay = '{{2}}'; + const delayNot = delay.charAt(0) === '!'; + if ( delayNot ) { delay = delay.slice(1); } + delay = parseInt(delay, 10); + if ( needle === '' || needle === '{{1}}' ) { + needle = ''; + } else if ( needle.startsWith('/') && needle.endsWith('/') ) { + needle = needle.slice(1,-1); + } else { + needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + const log = needleNot === false && needle === '' && + delayNot === false && isNaN(delay) + ? console.log + : undefined; + const reNeedle = new RegExp(needle); + window.setTimeout = new Proxy(window.setTimeout, { + apply: function(target, thisArg, args) { + const a = String(args[0]); + const b = args[1]; + let defuse = false; + if ( log !== undefined ) { + log('uBO: setTimeout("%s", %s)', a, b); + } else if ( isNaN(delay) ) { + defuse = reNeedle.test(a) !== needleNot; + } else if ( needle === '' ) { + defuse = (b === delay) !== delayNot; + } else { + defuse = reNeedle.test(a) !== needleNot && (b === delay) !== delayNot; + } + if ( defuse ) { + args[0] = function(){}; + } + return target.apply(thisArg, args); + } + }); +})(); + + +/// webrtc-if.js +(function() { + let good = '{{1}}'; + if ( good.startsWith('/') && good.endsWith('/') ) { + good = good.slice(1, -1); + } else { + good = good.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + let reGood; + try { + reGood = new RegExp(good); + } catch(ex) { + return; + } + const rtcName = window.RTCPeerConnection + ? 'RTCPeerConnection' + : (window.webkitRTCPeerConnection ? 'webkitRTCPeerConnection' : ''); + if ( rtcName === '' ) { return; } + const log = console.log.bind(console); + const neuteredPeerConnections = new WeakSet(); + const isGoodConfig = function(instance, config) { + if ( neuteredPeerConnections.has(instance) ) { return false; } + if ( config instanceof Object === false ) { return true; } + if ( Array.isArray(config.iceServers) === false ) { return true; } + for ( const server of config.iceServers ) { + const urls = typeof server.urls === 'string' + ? [ server.urls ] + : server.urls; + if ( Array.isArray(urls) ) { + for ( const url of urls ) { + if ( reGood.test(url) ) { return true; } + } + } + if ( typeof server.username === 'string' ) { + if ( reGood.test(server.username) ) { return true; } + } + if ( typeof server.credential === 'string' ) { + if ( reGood.test(server.credential) ) { return true; } + } + } + neuteredPeerConnections.add(instance); + return false; + }; + const peerConnectionCtor = window[rtcName]; + const peerConnectionProto = peerConnectionCtor.prototype; + peerConnectionProto.createDataChannel = + new Proxy(peerConnectionProto.createDataChannel, { + apply: function(target, thisArg, args) { + if ( isGoodConfig(target, args[1]) === false ) { + log(args[1]); + return Reflect.apply(target, thisArg, args.slice(0, 1)); + } + return Reflect.apply(target, thisArg, args); + }, + }); + window[rtcName] = + new Proxy(peerConnectionCtor, { + construct: function(target, args) { + if ( isGoodConfig(target, args[0]) === false ) { + log(args[0]); + return Reflect.construct(target); + } + return Reflect.construct(target, args); + } + }); +})(); + + +// https://github.com/gorhill/uBlock/issues/1228 +/// window.name-defuser +(function() { + if ( window === window.top ) { + window.name = ''; + } +})(); + + +// Experimental: Generic nuisance overlay buster. +// if this works well and proves to be useful, this may end up +// as a stock tool in uBO's popup panel. +/// overlay-buster.js +(function() { + if ( window !== window.top ) { + return; + } + var tstart; + var ttl = 30000; + var delay = 0; + var delayStep = 50; + var buster = function() { + var docEl = document.documentElement, + bodyEl = document.body, + vw = Math.min(docEl.clientWidth, window.innerWidth), + vh = Math.min(docEl.clientHeight, window.innerHeight), + tol = Math.min(vw, vh) * 0.05, + el = document.elementFromPoint(vw/2, vh/2), + style, rect; + for (;;) { + if ( el === null || el.parentNode === null || el === bodyEl ) { + break; + } + style = window.getComputedStyle(el); + if ( parseInt(style.zIndex, 10) >= 1000 || style.position === 'fixed' ) { + rect = el.getBoundingClientRect(); + if ( rect.left <= tol && rect.top <= tol && (vw - rect.right) <= tol && (vh - rect.bottom) < tol ) { + el.parentNode.removeChild(el); + tstart = Date.now(); + el = document.elementFromPoint(vw/2, vh/2); + bodyEl.style.setProperty('overflow', 'auto', 'important'); + docEl.style.setProperty('overflow', 'auto', 'important'); + continue; + } + } + el = el.parentNode; + } + if ( (Date.now() - tstart) < ttl ) { + delay = Math.min(delay + delayStep, 1000); + setTimeout(buster, delay); + } + }; + var domReady = function(ev) { + if ( ev ) { + document.removeEventListener(ev.type, domReady); + } + tstart = Date.now(); + setTimeout(buster, delay); + }; + if ( document.readyState === 'loading' ) { + document.addEventListener('DOMContentLoaded', domReady); + } else { + domReady(); + } +})(); + + +// https://github.com/uBlockOrigin/uAssets/issues/8 +/// alert-buster.js +(function() { + window.alert = function(a) { + console.info(a); + }; +})(); + + +// https://github.com/uBlockOrigin/uAssets/issues/58 +/// gpt-defuser.js +(function() { + const noopfn = function() { + }; + let props = '_resetGPT resetGPT resetAndLoadGPTRecovery _resetAndLoadGPTRecovery setupGPT setupGPTuo'; + props = props.split(/\s+/); + while ( props.length ) { + var prop = props.pop(); + if ( typeof window[prop] === 'function' ) { + window[prop] = noopfn; + } else { + Object.defineProperty(window, prop, { + get: function() { return noopfn; }, + set: noopfn + }); + } + } +})(); + + +// Prevent web pages from using RTCPeerConnection(), and report attempts in console. +/// nowebrtc.js +(function() { + var rtcName = window.RTCPeerConnection ? 'RTCPeerConnection' : ( + window.webkitRTCPeerConnection ? 'webkitRTCPeerConnection' : '' + ); + if ( rtcName === '' ) { return; } + var log = console.log.bind(console); + var pc = function(cfg) { + log('Document tried to create an RTCPeerConnection: %o', cfg); + }; + const noop = function() { + }; + pc.prototype = { + close: noop, + createDataChannel: noop, + createOffer: noop, + setRemoteDescription: noop, + toString: function() { + return '[object RTCPeerConnection]'; + } + }; + var z = window[rtcName]; + window[rtcName] = pc.bind(window); + if ( z.prototype ) { + z.prototype.createDataChannel = function() { + return { + close: function() {}, + send: function() {} + }; + }.bind(null); + } +})(); + + +// https://github.com/uBlockOrigin/uAssets/issues/88 +/// golem.de.js +(function() { + const rael = window.addEventListener; + window.addEventListener = function(a, b) { + rael(...arguments); + let haystack; + try { + haystack = b.toString(); + } catch(ex) { + } + if ( + typeof haystack === 'string' && + /^\s*function\s*\(\)\s*\{\s*window\.clearTimeout\(r\)\s*\}\s*$/.test(haystack) + ) { + b(); + } + }.bind(window); +})(); + + +// https://forums.lanik.us/viewtopic.php?f=64&t=32278 +// https://www.reddit.com/r/chrome/comments/58eix6/ublock_origin_not_working_on_certain_sites/ +/// upmanager-defuser.js +(function() { + var onerror = window.onerror; + window.onerror = function(msg, source, lineno, colno, error) { + if ( typeof msg === 'string' && msg.indexOf('upManager') !== -1 ) { + return true; + } + if ( onerror instanceof Function ) { + onerror.call(window, msg, source, lineno, colno, error); + } + }; + Object.defineProperty(window, 'upManager', { value: function() {} }); +})(); + + +// https://github.com/uBlockOrigin/uAssets/issues/110 +/// smartadserver.com.js +(function() { + Object.defineProperties(window, { + SmartAdObject: { value: function(){} }, + SmartAdServerAjax: { value: function(){} }, + smartAd: { value: { LoadAds: function() {}, Register: function() {} } } + }); +})(); + + +// https://github.com/reek/anti-adblock-killer/issues/3774#issuecomment-348536138 +// https://github.com/uBlockOrigin/uAssets/issues/883 +/// adfly-defuser.js +(function() { + // Based on AdsBypasser + // License: + // https://github.com/adsbypasser/adsbypasser/blob/master/LICENSE + var isDigit = /^\d$/; + var handler = function(encodedURL) { + var var1 = "", var2 = "", i; + for (i = 0; i < encodedURL.length; i++) { + if (i % 2 === 0) { + var1 = var1 + encodedURL.charAt(i); + } else { + var2 = encodedURL.charAt(i) + var2; + } + } + var data = (var1 + var2).split(""); + for (i = 0; i < data.length; i++) { + if (isDigit.test(data[i])) { + for (var ii = i + 1; ii < data.length; ii++) { + if (isDigit.test(data[ii])) { + var temp = parseInt(data[i],10) ^ parseInt(data[ii],10); + if (temp < 10) { + data[i] = temp.toString(); + } + i = ii; + break; + } + } + } + } + data = data.join(""); + var decodedURL = window.atob(data).slice(16, -16); + window.stop(); + window.onbeforeunload = null; + window.location.href = decodedURL; + }; + try { + var val; + var flag = true; + window.Object.defineProperty(window, "ysmm", { + configurable: false, + set: function(value) { + if (flag) { + flag = false; + try { + if (typeof value === "string") { + handler(value); + } + } catch (err) { } + } + val = value; + }, + get: function() { + return val; + } + }); + } catch (err) { + window.console.error("Failed to set up Adfly bypasser!"); + } +})(); + + +// https://github.com/uBlockOrigin/uAssets/issues/913 +/// disable-newtab-links.js +(function() { + document.addEventListener('click', function(ev) { + var target = ev.target; + while ( target !== null ) { + if ( target.localName === 'a' && target.hasAttribute('target') ) { + ev.stopPropagation(); + ev.preventDefault(); + break; + } + target = target.parentNode; + } + }); +})(); + + +/// damoh-defuser.js +(function() { + var handled = new WeakSet(); + var asyncTimer; + var cleanVideo = function() { + asyncTimer = undefined; + var v = document.querySelector('video'); + if ( v === null ) { return; } + if ( handled.has(v) ) { return; } + handled.add(v); + v.pause(); + v.controls = true; + var el = v.querySelector('meta[itemprop="contentURL"][content]'); + if ( el === null ) { return; } + v.src = el.getAttribute('content'); + el = v.querySelector('meta[itemprop="thumbnailUrl"][content]'); + if ( el !== null ) { v.poster = el.getAttribute('content'); } + }; + var cleanVideoAsync = function() { + if ( asyncTimer !== undefined ) { return; } + asyncTimer = window.requestAnimationFrame(cleanVideo); + }; + var observer = new MutationObserver(cleanVideoAsync); + observer.observe(document.documentElement, { childList: true, subtree: true }); +})(); + + +// https://github.com/uBlockOrigin/uAssets/pull/3517 +/// twitch-videoad.js +(function() { + if ( /(^|\.)twitch\.tv$/.test(document.location.hostname) === false ) { return; } + var realFetch = window.fetch; + window.fetch = function(input) { + if ( arguments.length >= 2 && typeof input === 'string' && input.includes('/access_token') ) { + var url = new URL(arguments[0]); + url.searchParams.set('platform', '_'); + arguments[0] = url.href; + } + return realFetch.apply(this, arguments); + }; +})(); + + +// https://github.com/uBlockOrigin/uAssets/issues/2912 +/// fingerprint2.js +(function() { + let fp2 = function(){}; + fp2.prototype = { + get: function(cb) { + setTimeout(function() { cb('', []); }, 1); + } + }; + window.Fingerprint2 = fp2; +})(); + + +// https://github.com/NanoAdblocker/NanoFilters/issues/149 +/// cookie-remover.js +(function() { + let needle = '{{1}}', + reName = /./; + if ( /^\/.+\/$/.test(needle) ) { + reName = new RegExp(needle.slice(1,-1)); + } else if ( needle !== '' && needle !== '{{1}}' ) { + reName = new RegExp(needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); + } + let removeCookie = function() { + document.cookie.split(';').forEach(cookieStr => { + let pos = cookieStr.indexOf('='); + if ( pos === -1 ) { return; } + let cookieName = cookieStr.slice(0, pos).trim(); + if ( !reName.test(cookieName) ) { return; } + let part1 = cookieName + '='; + let part2a = '; domain=' + document.location.hostname; + let part2b = '; domain=.' + document.location.hostname; + let domain = document.domain; + let part2c = domain && domain !== document.location.hostname ? '; domain=.' + domain : undefined; + let part3 = '; path=/'; + let part4 = '; Max-Age=-1000; expires=Thu, 01 Jan 1970 00:00:00 GMT'; + document.cookie = part1 + part4; + document.cookie = part1 + part2a + part4; + document.cookie = part1 + part2b + part4; + document.cookie = part1 + part3 + part4; + document.cookie = part1 + part2a + part3 + part4; + document.cookie = part1 + part2b + part3 + part4; + if ( part2c !== undefined ) { + document.cookie = part1 + part2c + part3 + part4; + } + }); + }; + removeCookie(); + window.addEventListener('beforeunload', removeCookie); +})(); + + + + + +// These lines below are skipped by the resource parser. +// <<<< end of private namespace +})(); diff --git a/data/test/fake-uBO-files/web_accessible_resources/1x1.gif b/data/test/fake-uBO-files/web_accessible_resources/1x1.gif new file mode 100644 index 0000000000000000000000000000000000000000..e565824aafafe632011b281cba976baf8b3ba89a GIT binary patch literal 43 qcmZ?wbhEHbWMp7uXkcLY4+e@qSs1y10y+#p0Fq%~V)9{Rum%7ZWeN!Z literal 0 HcmV?d00001 diff --git a/data/test/fake-uBO-files/web_accessible_resources/2x2.png b/data/test/fake-uBO-files/web_accessible_resources/2x2.png new file mode 100644 index 0000000000000000000000000000000000000000..3639dc75ab9fe6ff6d118c8ac2440aed5bc8eec9 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`1|*BN@u~nRZci7-5RU7~2@dQG3_=Wy?>j@5 Q0EHPmUHx3vIVCg!0BRBp)Bpeg literal 0 HcmV?d00001 diff --git a/data/test/fake-uBO-files/web_accessible_resources/32x32.png b/data/test/fake-uBO-files/web_accessible_resources/32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..7bee0a002b75fb20e9b790068b7950121cdf779e GIT binary patch literal 83 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzDNh&2kch)?4>B?Wc})uc*XMaS cfSB*u1QZw;r+*K>4&*R+y85}Sb4q9e0KdKysQ>@~ literal 0 HcmV?d00001 diff --git a/data/test/fake-uBO-files/web_accessible_resources/3x2.png b/data/test/fake-uBO-files/web_accessible_resources/3x2.png new file mode 100644 index 0000000000000000000000000000000000000000..a056d8677db3e35cf6f1ec71d11bedccdb1d09f8 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^%s|Y>4', + 'sessionStorage', + 'clientWidth', + 'localStorage', + 'Math', + 'random' + ], + ]; + const check = function(s) { + for ( let i = 0; i < signatures.length; i++ ) { + const tokens = signatures[i]; + let match = 0; + for ( let j = 0; j < tokens.length; j++ ) { + const token = tokens[j]; + const pos = token instanceof RegExp + ? s.search(token) + : s.indexOf(token); + if ( pos !== -1 ) { match += 1; } + } + if ( (match / tokens.length) >= 0.8 ) { return true; } + } + return false; + }; + window.eval = new Proxy(window.eval, { // jshint ignore: line + apply: function(target, thisArg, args) { + const a = args[0]; + if ( typeof a !== 'string' || !check(a) ) { + return target.apply(thisArg, args); + } + if ( document.body ) { + document.body.style.removeProperty('visibility'); + } + let el = document.getElementById('babasbmsgx'); + if ( el ) { + el.parentNode.removeChild(el); + } + } + }); + window.setTimeout = new Proxy(window.setTimeout, { + apply: function(target, thisArg, args) { + const a = args[0]; + if ( + typeof a !== 'string' || + /\.bab_elementid.$/.test(a) === false + ) { + return target.apply(thisArg, args); + } + } + }); +})(); diff --git a/data/test/fake-uBO-files/web_accessible_resources/noeval-silent.js b/data/test/fake-uBO-files/web_accessible_resources/noeval-silent.js new file mode 100644 index 00000000..cfe6e3b0 --- /dev/null +++ b/data/test/fake-uBO-files/web_accessible_resources/noeval-silent.js @@ -0,0 +1,28 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2019-present Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(function() { + 'use strict'; + window.eval = new Proxy(window.eval, { // jshint ignore: line + apply: function() { + } + }); +})(); diff --git a/data/test/fake-uBO-files/web_accessible_resources/noeval.js b/data/test/fake-uBO-files/web_accessible_resources/noeval.js new file mode 100644 index 00000000..e1f2a748 --- /dev/null +++ b/data/test/fake-uBO-files/web_accessible_resources/noeval.js @@ -0,0 +1,30 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2019-present Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(function() { + 'use strict'; + const log = console.log.bind(console); + window.eval = new Proxy(window.eval, { // jshint ignore: line + apply: function(target, thisArg, args) { + log(`Document tried to eval... ${args[0]}\n`); + } + }); +})(); diff --git a/data/test/fake-uBO-files/web_accessible_resources/nofab.js b/data/test/fake-uBO-files/web_accessible_resources/nofab.js new file mode 100644 index 00000000..6f30fc20 --- /dev/null +++ b/data/test/fake-uBO-files/web_accessible_resources/nofab.js @@ -0,0 +1,63 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2019-present Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(function() { + 'use strict'; + const noopfn = function() { + }; + const Fab = function() {}; + Fab.prototype.check = noopfn; + Fab.prototype.clearEvent = noopfn; + Fab.prototype.emitEvent = noopfn; + Fab.prototype.on = function(a, b) { + if ( !a ) { b(); } + return this; + }; + Fab.prototype.onDetected = function() { + return this; + }; + Fab.prototype.onNotDetected = function(a) { + a(); + return this; + }; + Fab.prototype.setOption = noopfn; + const fab = new Fab(), + getSetFab = { + get: function() { return Fab; }, + set: function() {} + }, + getsetfab = { + get: function() { return fab; }, + set: function() {} + }; + if ( window.hasOwnProperty('FuckAdBlock') ) { window.FuckAdBlock = Fab; } + else { Object.defineProperty(window, 'FuckAdBlock', getSetFab); } + if ( window.hasOwnProperty('BlockAdBlock') ) { window.BlockAdBlock = Fab; } + else { Object.defineProperty(window, 'BlockAdBlock', getSetFab); } + if ( window.hasOwnProperty('SniffAdBlock') ) { window.SniffAdBlock = Fab; } + else { Object.defineProperty(window, 'SniffAdBlock', getSetFab); } + if ( window.hasOwnProperty('fuckAdBlock') ) { window.fuckAdBlock = fab; } + else { Object.defineProperty(window, 'fuckAdBlock', getsetfab); } + if ( window.hasOwnProperty('blockAdBlock') ) { window.blockAdBlock = fab; } + else { Object.defineProperty(window, 'blockAdBlock', getsetfab); } + if ( window.hasOwnProperty('sniffAdBlock') ) { window.sniffAdBlock = fab; } + else { Object.defineProperty(window, 'sniffAdBlock', getsetfab); } +})(); diff --git a/data/test/fake-uBO-files/web_accessible_resources/noop-0.1s.mp3 b/data/test/fake-uBO-files/web_accessible_resources/noop-0.1s.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..147d71b0921f6079af834b2b1ea5a6b69316c558 GIT binary patch literal 813 zcmeZtF=k-^0p*b3U{@f`&%nU!lUSB!YNlsmpl4`c2$qEq|9^)d@vt*J^V0HxGC*S( z*nmbcFeIRX26V6h4IDrR575DXGyrl5(9Ov}H=7xNI540BV=yzEO<5W7|F=jA1OEpG zhD@M12Ll7I0s{jh5G?_6flQMI1_l-%M_*TCJxfbH(@>HD$b2~h=96qFk_M3ZHZ(Rr PipJ(w(AfNGG&COo>^AHA literal 0 HcmV?d00001 diff --git a/data/test/fake-uBO-files/web_accessible_resources/noop-1s.mp4 b/data/test/fake-uBO-files/web_accessible_resources/noop-1s.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..5689d4a3c99916415a367854f05f28fcd4a0b54f GIT binary patch literal 3753 zcmds4UuYaf7@ypqw6<1TO>JvaothLMY%Y6uNty;0uPwC^trgow5bJercP_VN_ja?p zmt5-0i6RC2P%MVo`#$h;31PvIQv-L0a(-RuR#M_?z9k&E!mrAo}2t z-_AF`@0)LCzL{?(M+j*z`V-Ffo*_y|j2ND60U0)?r3oPmi!SGcw63sp=3hw=Vj6`a zV;W7TGW~>XCWmMuL$;ykJFUr?nYX^&dhLU6E)TwUc>N`Mx#Nc)tC_wY+C??nBq5?CsO~^?tB}DQLU5KfZNaR}bAZv;}55*Pz&9I};`^`ZS{`*{+O|7CuV8?+kQz zkB^V1#tfa?CM&0Gceq;&kt+F?2^-t-4ZG|OP>mIsrYh9sMKw!xUNCKKL`@GU0}5qj zW=?oqRjO!L)9Ey|xLV>>>QxGmHc-c#P}Aa@cXc(LQqTz>Y8h3o=Y>bnBk!{EFjv!k zR4ci*#q#KyroPKf)9@hmSNnC%#}u?t3tycxeV<+CY9_rNBNmzG=N)gva9~<@FzV#( zV$tKi+LfVx$wd!O?W3k`kFXLN`39G!o@r>9OM_5o*$u|f42$_<9!A;cuE}5ozF<~d zHj&qC%VEKc5F+1YMj0+(=rUnbbeY9HNaNfXF8L0m5k3L6n(1wbdCMrHNj;61xmNMj z9z~4gG7%M*dnI&owR|*Vp?5V^bBoj}AT(lTXsDTNN};1-KB|)HgXjpyLCvaa-$oFg z&mFagjupo87OT2qRU7*HgLa`(^o)I6%_`UwxDy8OgCH<7U&5A^5ocO7Op}YK7!I}y z#{xTv>9A3p07waH0$zu*W!qy|i)oFObktIbUnL~5fwU0u zH(Ta3KsU8>vzGHoaYEt>?wb)PpY}(BF~LQp?k#>TVj4ozIP;Es3dRS64xL>0JxJ}` z^!&ODc&Pgge8t|dbb~=hExmc|6@`$>W4q4=J-bS}>DGM^@p3g}Q4fiiY><_8+*N4C zcH$x|LTDSa0;lq{JM~c4uH&{9ol%7T){1M=P>$W{`2`d5+n(<=HbmH*sK4hBo0kse z6qY@RT0j(e)R1ZZuAG?x)djCxS~gEDnfZz=A4YvScJ{kOj*#DTWZ+2r+@hCTNV0U{ z`3n@r?Ujn5kCbxP^PM}+y}$9b)t|*Xeg-efO3x4CEo3qHJ@r_(VHsi{L=n6|J2*`c zC=ISrAAoLOs0zNq&+U*F*Rcroe}(;?ui3#Yg7uJxFqdKAS>OopF7Pq%HSjBP@vIKofH_4Y+I6vg+ zY-tg}3?e(r{iU6gb%fj?UlN^In-ltqPv1CS&eRAwpPMgVJiAhshuVp@KASJ`m+JYl z)C=>aVf#07CBCDcE5l8>@}%YTu)3ol7J(ciOGPcFijw7?9vsYgtya4a|2HR4x8;^6 zPhTR7Ps3oz`6QUwvV*OtT11?wbjC$HgS|l|q7%!Z+9jRq)gd25PE0}A3kX@t2uJu^Yf! z5`PhR75EVN9QZyAOQIchH}D96`zV3?GI1C<37i8i0l(n>z#b<%fCqsm0OVovIp8>O z3b+Vd1^y5}4>5M3qWcW>Qx*?uh-9t&_96dW<~ZVcib|IeKCtUf`nI?%*WhQSa3VI3 RJj%w3y}hYyHkDSCKLO>qqxS#+ literal 0 HcmV?d00001 diff --git a/data/test/fake-uBO-files/web_accessible_resources/noop.html b/data/test/fake-uBO-files/web_accessible_resources/noop.html new file mode 100644 index 00000000..8aaae14d --- /dev/null +++ b/data/test/fake-uBO-files/web_accessible_resources/noop.html @@ -0,0 +1,5 @@ + + + + + diff --git a/data/test/fake-uBO-files/web_accessible_resources/noop.js b/data/test/fake-uBO-files/web_accessible_resources/noop.js new file mode 100644 index 00000000..b977b08a --- /dev/null +++ b/data/test/fake-uBO-files/web_accessible_resources/noop.js @@ -0,0 +1,3 @@ +(function() { + 'use strict'; +})(); diff --git a/data/test/fake-uBO-files/web_accessible_resources/noop.txt b/data/test/fake-uBO-files/web_accessible_resources/noop.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/data/test/fake-uBO-files/web_accessible_resources/noop.txt @@ -0,0 +1 @@ + diff --git a/data/test/fake-uBO-files/web_accessible_resources/outbrain-widget.js b/data/test/fake-uBO-files/web_accessible_resources/outbrain-widget.js new file mode 100644 index 00000000..498f6803 --- /dev/null +++ b/data/test/fake-uBO-files/web_accessible_resources/outbrain-widget.js @@ -0,0 +1,47 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2019-present Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(function() { + 'use strict'; + const noopfn = function() { + }; + const obr = {}; + const methods = [ + 'callClick', 'callLoadMore', 'callRecs', 'callUserZapping', + 'callWhatIs', 'cancelRecommendation', 'cancelRecs', 'closeCard', + 'closeModal', 'closeTbx', 'errorInjectionHandler', 'getCountOfRecs', + 'getStat', 'imageError', 'manualVideoClicked', 'onOdbReturn', + 'onVideoClick', 'pagerLoad', 'recClicked', 'refreshSpecificWidget', + 'refreshWidget', 'reloadWidget', 'researchWidget', 'returnedError', + 'returnedHtmlData', 'returnedIrdData', 'returnedJsonData', 'scrollLoad', + 'showDescription', 'showRecInIframe', 'userZappingMessage', 'zappingFormAction' + ]; + obr.extern = { + video: { + getVideoRecs: noopfn, + videoClicked: noopfn + } + }; + methods.forEach(function(a) { + obr.extern[a] = noopfn; + }); + window.OBR = window.OBR || obr; +})(); diff --git a/data/test/fake-uBO-files/web_accessible_resources/popads-dummy.js b/data/test/fake-uBO-files/web_accessible_resources/popads-dummy.js new file mode 100644 index 00000000..0ca0b1ef --- /dev/null +++ b/data/test/fake-uBO-files/web_accessible_resources/popads-dummy.js @@ -0,0 +1,30 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2019-present Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(function() { + 'use strict'; + delete window.PopAds; + delete window.popns; + Object.defineProperties(window, { + PopAds: { value: {} }, + popns: { value: {} } + }); +})(); diff --git a/data/test/fake-uBO-files/web_accessible_resources/popads.js b/data/test/fake-uBO-files/web_accessible_resources/popads.js new file mode 100644 index 00000000..4b6d0b60 --- /dev/null +++ b/data/test/fake-uBO-files/web_accessible_resources/popads.js @@ -0,0 +1,40 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2019-present Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(function() { + 'use strict'; + const magic = String.fromCharCode(Date.now() % 26 + 97) + + Math.floor(Math.random() * 982451653 + 982451653).toString(36); + const oe = window.onerror; + window.onerror = function(msg, src, line, col, error) { + if ( typeof msg === 'string' && msg.indexOf(magic) !== -1 ) { return true; } + if ( oe instanceof Function ) { + return oe(msg, src, line, col, error); + } + }.bind(); + const throwMagic = function() { throw magic; }; + delete window.PopAds; + delete window.popns; + Object.defineProperties(window, { + PopAds: { set: throwMagic }, + popns: { set: throwMagic } + }); +})(); diff --git a/data/test/fake-uBO-files/web_accessible_resources/scorecardresearch_beacon.js b/data/test/fake-uBO-files/web_accessible_resources/scorecardresearch_beacon.js new file mode 100644 index 00000000..5ca7203a --- /dev/null +++ b/data/test/fake-uBO-files/web_accessible_resources/scorecardresearch_beacon.js @@ -0,0 +1,31 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2019-present Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(function() { + 'use strict'; + window.COMSCORE = { + purge: function() { + window._comscore = []; + }, + beacon: function() { + } + }; +})(); diff --git a/data/test/fake-uBO-files/web_accessible_resources/window.open-defuser.js b/data/test/fake-uBO-files/web_accessible_resources/window.open-defuser.js new file mode 100644 index 00000000..72fba013 --- /dev/null +++ b/data/test/fake-uBO-files/web_accessible_resources/window.open-defuser.js @@ -0,0 +1,43 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2019-present Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(function() { + 'use strict'; + let result = parseInt('{{1}}', 10); + result = isNaN(result) === false && result === 0; + let needle = '{{2}}'; + if ( needle === '' || needle === '{{2}}' ) { + needle = '.?'; + } else if ( /^\/.+\/$/.test(needle) ) { + needle = needle.slice(1,-1); + } else { + needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + needle = new RegExp(needle); + window.open = new Proxy(window.open, { + apply: function(target, thisArg, args) { + const url = args[0]; + if ( needle.test(url) === result ) { + return target.apply(thisArg, args); + } + } + }); +})(); diff --git a/data/uBlockOrigin/resources.txt b/data/uBlockOrigin/resources.txt deleted file mode 100644 index 4a19c67a..00000000 --- a/data/uBlockOrigin/resources.txt +++ /dev/null @@ -1,2432 +0,0 @@ -# Resources to be used as redirect destinations. -# -# - Each distinct resource entry is separated by an empty line. -# - The first line in a resource entry is: token mime-type[;encoding] -# - All following lines are the data. An empty line signals the end of the -# data. -# -# If the encoding is absent, the data will be converted to base64, and the -# encoding will be set to `;base64`. - - -# http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever -1x1-transparent.gif image/gif;base64 -R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== - - -2x2-transparent.png image/png;base64 -iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAC0lEQVQI12NgQAcAABIAAe+JVKQAAAAA -SUVORK5CYII= - - -3x2-transparent.png image/png;base64 -iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAC0lEQVQI12NgwAUAABoAASRETuUAAAAA -SUVORK5CYII= - - -32x32-transparent.png image/png;base64 -iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGklEQVRYw+3BAQEAAACCIP+vbkhAAQAA -AO8GECAAAZf3V9cAAAAASUVORK5CYII= - - -nooptext text/plain - - -noopcss text/css - - -noopjs application/javascript -(function() { - ; -})(); - - -noopframe text/html - - - - - - - -# From: -# https://ubuntuforums.org/showthread.php?t=1911430&p=11624471#post11624471 -# ffmpeg -ar 48000 -t 0.1 -f s16le -acodec pcm_s16le -ac 2 -i /dev/zero -acodec libmp3lame -aq 4 output.mp3 -# But with 0.1 (second) instead of 60. -noopmp3-0.1s audio/mp3;base64 -SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU2LjQwLjEwMQAAAAAAAAAAAAAA//tUAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAAGAAADAABgYGBgYGBgYGBgYGBgYGBggICAgICA -gICAgICAgICAgICgoKCgoKCgoKCgoKCgoKCgwMDAwMDAwMDAwMDAwMDAwMDg4ODg4ODg4ODg4ODg4ODg -4P////////////////////8AAAAATGF2YzU2LjYwAAAAAAAAAAAAAAAAJAAAAAAAAAAAAwDNZKlY//sU -ZAAP8AAAaQAAAAgAAA0gAAABAAABpAAAACAAADSAAAAETEFNRTMuOTkuNVVVVVVVVVVVVVVVVVVVVVVV -VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUZB4P8AAAaQAAAAgAAA0gAAABAAABpAAA -ACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV -VVVVVVVVVVVV//sUZDwP8AAAaQAAAAgAAA0gAAABAAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVV -VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUZFoP8AAAaQAAAAgA -AA0gAAABAAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV -VVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUZHgP8AAAaQAAAAgAAA0gAAABAAABpAAAACAAADSAAAAEVVVV -VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sU -ZJYP8AAAaQAAAAgAAA0gAAABAAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV -VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV - -# From: -# https://github.com/kud/blank-video -noopmp4-1s video/mp4;base64 -AAAAHGZ0eXBNNFYgAAACAGlzb21pc28yYXZjMQAAAAhmcmVlAAAGF21kYXTeBAAAbGliZmFhYyAxLjI4 -AABCAJMgBDIARwAAArEGBf//rdxF6b3m2Ui3lizYINkj7u94MjY0IC0gY29yZSAxNDIgcjIgOTU2Yzhk -OCAtIEguMjY0L01QRUctNCBBVkMgY29kZWMgLSBDb3B5bGVmdCAyMDAzLTIwMTQgLSBodHRwOi8vd3d3 -LnZpZGVvbGFuLm9yZy94MjY0Lmh0bWwgLSBvcHRpb25zOiBjYWJhYz0wIHJlZj0zIGRlYmxvY2s9MTow -OjAgYW5hbHlzZT0weDE6MHgxMTEgbWU9aGV4IHN1Ym1lPTcgcHN5PTEgcHN5X3JkPTEuMDA6MC4wMCBt -aXhlZF9yZWY9MSBtZV9yYW5nZT0xNiBjaHJvbWFfbWU9MSB0cmVsbGlzPTEgOHg4ZGN0PTAgY3FtPTAg -ZGVhZHpvbmU9MjEsMTEgZmFzdF9wc2tpcD0xIGNocm9tYV9xcF9vZmZzZXQ9LTIgdGhyZWFkcz02IGxv -b2thaGVhZF90aHJlYWRzPTEgc2xpY2VkX3RocmVhZHM9MCBucj0wIGRlY2ltYXRlPTEgaW50ZXJsYWNl -ZD0wIGJsdXJheV9jb21wYXQ9MCBjb25zdHJhaW5lZF9pbnRyYT0wIGJmcmFtZXM9MCB3ZWlnaHRwPTAg -a2V5aW50PTI1MCBrZXlpbnRfbWluPTI1IHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29r -YWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02 -OSBxcHN0ZXA9NCB2YnZfbWF4cmF0ZT03NjggdmJ2X2J1ZnNpemU9MzAwMCBjcmZfbWF4PTAuMCBuYWxf -aHJkPW5vbmUgZmlsbGVyPTAgaXBfcmF0aW89MS40MCBhcT0xOjEuMDAAgAAAAFZliIQL8mKAAKvMnJyc -nJycnJycnXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXiEASZACGQAjgCEASZACGQAjgAAAAAdBmjgX4GSAIQBJkAIZACOAAAAAB0GaVAX4 -GSAhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZpgL8DJIQBJkAIZACOAIQBJkAIZACOAAAAABkGagC/AySEA -SZACGQAjgAAAAAZBmqAvwMkhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZrAL8DJIQBJkAIZACOAAAAABkGa -4C/AySEASZACGQAjgCEASZACGQAjgAAAAAZBmwAvwMkhAEmQAhkAI4AAAAAGQZsgL8DJIQBJkAIZACOA -IQBJkAIZACOAAAAABkGbQC/AySEASZACGQAjgCEASZACGQAjgAAAAAZBm2AvwMkhAEmQAhkAI4AAAAAG -QZuAL8DJIQBJkAIZACOAIQBJkAIZACOAAAAABkGboC/AySEASZACGQAjgAAAAAZBm8AvwMkhAEmQAhkA -I4AhAEmQAhkAI4AAAAAGQZvgL8DJIQBJkAIZACOAAAAABkGaAC/AySEASZACGQAjgCEASZACGQAjgAAA -AAZBmiAvwMkhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZpAL8DJIQBJkAIZACOAAAAABkGaYC/AySEASZAC -GQAjgCEASZACGQAjgAAAAAZBmoAvwMkhAEmQAhkAI4AAAAAGQZqgL8DJIQBJkAIZACOAIQBJkAIZACOA -AAAABkGawC/AySEASZACGQAjgAAAAAZBmuAvwMkhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZsAL8DJIQBJ -kAIZACOAAAAABkGbIC/AySEASZACGQAjgCEASZACGQAjgAAAAAZBm0AvwMkhAEmQAhkAI4AhAEmQAhkA -I4AAAAAGQZtgL8DJIQBJkAIZACOAAAAABkGbgCvAySEASZACGQAjgCEASZACGQAjgAAAAAZBm6AnwMkh -AEmQAhkAI4AhAEmQAhkAI4AhAEmQAhkAI4AhAEmQAhkAI4AAAAhubW9vdgAAAGxtdmhkAAAAAAAAAAAA -AAAAAAAD6AAABDcAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAzB0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAA -AAABAAAAAAAAA+kAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABA -AAAAALAAAACQAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAAPpAAAAAAABAAAAAAKobWRpYQAAACBt -ZGhkAAAAAAAAAAAAAAAAAAB1MAAAdU5VxAAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABW -aWRlb0hhbmRsZXIAAAACU21pbmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAA -AAAAAQAAAAx1cmwgAAAAAQAAAhNzdGJsAAAAr3N0c2QAAAAAAAAAAQAAAJ9hdmMxAAAAAAAAAAEAAAAA -AAAAAAAAAAAAAAAAALAAkABIAAAASAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAGP//AAAALWF2Y0MBQsAN/+EAFWdCwA3ZAsTsBEAAAPpAADqYA8UKkgEABWjLg8sgAAAAHHV1aWRr -aEDyXyRPxbo5pRvPAyPzAAAAAAAAABhzdHRzAAAAAAAAAAEAAAAeAAAD6QAAABRzdHNzAAAAAAAAAAEA -AAABAAAAHHN0c2MAAAAAAAAAAQAAAAEAAAABAAAAAQAAAIxzdHN6AAAAAAAAAAAAAAAeAAADDwAAAAsA -AAALAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoA -AAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAAiHN0Y28A -AAAAAAAAHgAAAEYAAANnAAADewAAA5gAAAO0AAADxwAAA+MAAAP2AAAEEgAABCUAAARBAAAEXQAABHAA -AASMAAAEnwAABLsAAATOAAAE6gAABQYAAAUZAAAFNQAABUgAAAVkAAAFdwAABZMAAAWmAAAFwgAABd4A -AAXxAAAGDQAABGh0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAACAAAAAAAABDcAAAAAAAAAAAAAAAEB -AAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAkZWR0cwAAABxl -bHN0AAAAAAAAAAEAAAQkAAADcAABAAAAAAPgbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAC7gAAAykBV -xAAAAAAALWhkbHIAAAAAAAAAAHNvdW4AAAAAAAAAAAAAAABTb3VuZEhhbmRsZXIAAAADi21pbmYAAAAQ -c21oZAAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAADT3N0YmwAAABn -c3RzZAAAAAAAAAABAAAAV21wNGEAAAAAAAAAAQAAAAAAAAAAAAIAEAAAAAC7gAAAAAAAM2VzZHMAAAAA -A4CAgCIAAgAEgICAFEAVBbjYAAu4AAAADcoFgICAAhGQBoCAgAECAAAAIHN0dHMAAAAAAAAAAgAAADIA -AAQAAAAAAQAAAkAAAAFUc3RzYwAAAAAAAAAbAAAAAQAAAAEAAAABAAAAAgAAAAIAAAABAAAAAwAAAAEA -AAABAAAABAAAAAIAAAABAAAABgAAAAEAAAABAAAABwAAAAIAAAABAAAACAAAAAEAAAABAAAACQAAAAIA -AAABAAAACgAAAAEAAAABAAAACwAAAAIAAAABAAAADQAAAAEAAAABAAAADgAAAAIAAAABAAAADwAAAAEA -AAABAAAAEAAAAAIAAAABAAAAEQAAAAEAAAABAAAAEgAAAAIAAAABAAAAFAAAAAEAAAABAAAAFQAAAAIA -AAABAAAAFgAAAAEAAAABAAAAFwAAAAIAAAABAAAAGAAAAAEAAAABAAAAGQAAAAIAAAABAAAAGgAAAAEA -AAABAAAAGwAAAAIAAAABAAAAHQAAAAEAAAABAAAAHgAAAAIAAAABAAAAHwAAAAQAAAABAAAA4HN0c3oA -AAAAAAAAAAAAADMAAAAaAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkA -AAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkA -AAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkA -AAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAACMc3RjbwAAAAAAAAAfAAAALAAAA1UA -AANyAAADhgAAA6IAAAO+AAAD0QAAA+0AAAQAAAAEHAAABC8AAARLAAAEZwAABHoAAASWAAAEqQAABMUA -AATYAAAE9AAABRAAAAUjAAAFPwAABVIAAAVuAAAFgQAABZ0AAAWwAAAFzAAABegAAAX7AAAGFwAAAGJ1 -ZHRhAAAAWm1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAALWlsc3QAAAAl -qXRvbwAAAB1kYXRhAAAAAQAAAABMYXZmNTUuMzMuMTAw - - -hd-main.js application/javascript -(function(){ - var l = {}; - var noopfn = function() { - ; - }; - var props = [ - "$j","Ad","Bd","Cd","Dd","Ed","Fd","Gd","Hd","Id","Jd","Nj","Oc","Pc","Pe", - "Qc","Qe","Rc","Re","Ri","Sc","Tc","Uc","Vc","Wc","Wg","Xc","Xg","Yc","Yd", - "ad","ae","bd","bf","cd","dd","ed","ef","ek","fd","fg","fh","fk","gd","hd", - "ig","ij","jd","kd","ke","ld","md","mi","nd","od","oh","pd","pf","qd","rd", - "sd","td","ud","vd","wd","wg","xd","xh","yd","zd", - "$d","$e","$k","Ae","Af","Aj","Be","Ce","De","Ee","Ek","Eo","Ep","Fe","Fo", - "Ge","Gh","Hk","Ie","Ip","Je","Ke","Kk","Kq","Le","Lh","Lk","Me","Mm","Ne", - "Oe","Pe","Qe","Re","Rp","Se","Te","Ue","Ve","Vp","We","Xd","Xe","Yd","Ye", - "Zd","Ze","Zf","Zk","ae","af","al","be","bf","bg","ce","cp","df","di","ee", - "ef","fe","ff","gf","gm","he","hf","ie","je","jf","ke","kf","kl","le","lf", - "lk","mf","mg","mn","nf","oe","of","pe","pf","pg","qe","qf","re","rf","se", - "sf","te","tf","ti","ue","uf","ve","vf","we","wf","wg","wi","xe","ye","yf", - "yk","yl","ze","zf","zk" - ]; - for (var i = 0; i < props.length; i++) { - l[props[i]] = noopfn; - } - window.L = window.J = l; -})(); - - -uAssets-17 application/javascript -(function() { - var sto = window.setTimeout; - var mysto = function(a, b) { - if ( b === 15000 && a.toString().indexOf('a()') !== -1 ) { - return; - } - return sto(a, b); - }; - Object.defineProperty(window, 'setTimeout', { - get: function() { - return mysto; - } - }); -})(); - - -# fuckadblock defuser -fuckadblock.js-3.2.0 application/javascript -(function() { - var noopfn = function() { - ; - }; - // - var Fab = function() {}; - Fab.prototype.check = noopfn; - Fab.prototype.clearEvent = noopfn; - Fab.prototype.emitEvent = noopfn; - Fab.prototype.on = function(a, b) { - if ( !a ) { b(); } - return this; - }; - Fab.prototype.onDetected = function() { - return this; - }; - Fab.prototype.onNotDetected = function(a) { - a(); - return this; - }; - Fab.prototype.setOption = noopfn; - var fab = new Fab(), - getSetFab = { - get: function() { return Fab; }, - set: function() {} - }, - getsetfab = { - get: function() { return fab; }, - set: function() {} - }; - if ( window.hasOwnProperty('FuckAdBlock') ) { window.FuckAdBlock = Fab; } - else { Object.defineProperty(window, 'FuckAdBlock', getSetFab); } - if ( window.hasOwnProperty('BlockAdBlock') ) { window.BlockAdBlock = Fab; } - else { Object.defineProperty(window, 'BlockAdBlock', getSetFab); } - if ( window.hasOwnProperty('SniffAdBlock') ) { window.SniffAdBlock = Fab; } - else { Object.defineProperty(window, 'SniffAdBlock', getSetFab); } - if ( window.hasOwnProperty('fuckAdBlock') ) { window.fuckAdBlock = fab; } - else { Object.defineProperty(window, 'fuckAdBlock', getsetfab); } - if ( window.hasOwnProperty('blockAdBlock') ) { window.blockAdBlock = fab; } - else { Object.defineProperty(window, 'blockAdBlock', getsetfab); } - if ( window.hasOwnProperty('sniffAdBlock') ) { window.sniffAdBlock = fab; } - else { Object.defineProperty(window, 'sniffAdBlock', getsetfab); } -})(); - - -antiAdBlock.js application/javascript -(function() { - window.antiAdBlock = { - onDetected: function() { - ; - }, - onNotDetected: function(a) { - a(); - } - }; -})(); - - -# To neutralize GA scripts. The goal is to provide the minimal API -# expected by clients of these scripts so that the end users are able -# to wholly block GA while minimizing risks of page breakage. -# Test cases (need way more): -# - https://github.com/chrisaljoudi/uBlock/issues/119 -# Reference API: -# - https://developers.google.com/analytics/devguides/collection/gajs/ -google-analytics.com/ga.js application/javascript -(function() { - var noopfn = function() { - ; - }; - // - var Gaq = function() { - ; - }; - Gaq.prototype.Na = noopfn; - Gaq.prototype.O = noopfn; - Gaq.prototype.Sa = noopfn; - Gaq.prototype.Ta = noopfn; - Gaq.prototype.Va = noopfn; - Gaq.prototype._createAsyncTracker = noopfn; - Gaq.prototype._getAsyncTracker = noopfn; - Gaq.prototype._getPlugin = noopfn; - Gaq.prototype.push = function(a) { - if ( typeof a === 'function' ) { - a(); return; - } - if ( Array.isArray(a) === false ) { - return; - } - // https://twitter.com/catovitch/status/776442930345218048 - // https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiDomainDirectory#_gat.GA_Tracker_._link - if ( a[0] === '_link' && typeof a[1] === 'string' ) { - window.location.assign(a[1]); - } - // https://github.com/gorhill/uBlock/issues/2162 - if ( a[0] === '_set' && a[1] === 'hitCallback' && typeof a[2] === 'function' ) { - a[2](); - } - }; - // - var tracker = (function() { - var out = {}; - var api = [ - '_addIgnoredOrganic _addIgnoredRef _addItem _addOrganic', - '_addTrans _clearIgnoredOrganic _clearIgnoredRef _clearOrganic', - '_cookiePathCopy _deleteCustomVar _getName _setAccount', - '_getAccount _getClientInfo _getDetectFlash _getDetectTitle', - '_getLinkerUrl _getLocalGifPath _getServiceMode _getVersion', - '_getVisitorCustomVar _initData _link _linkByPost', - '_setAllowAnchor _setAllowHash _setAllowLinker _setCampContentKey', - '_setCampMediumKey _setCampNameKey _setCampNOKey _setCampSourceKey', - '_setCampTermKey _setCampaignCookieTimeout _setCampaignTrack _setClientInfo', - '_setCookiePath _setCookiePersistence _setCookieTimeout _setCustomVar', - '_setDetectFlash _setDetectTitle _setDomainName _setLocalGifPath', - '_setLocalRemoteServerMode _setLocalServerMode _setReferrerOverride _setRemoteServerMode', - '_setSampleRate _setSessionTimeout _setSiteSpeedSampleRate _setSessionCookieTimeout', - '_setVar _setVisitorCookieTimeout _trackEvent _trackPageLoadTime', - '_trackPageview _trackSocial _trackTiming _trackTrans', - '_visitCode' - ].join(' ').split(/\s+/); - var i = api.length; - while ( i-- ) { - out[api[i]] = noopfn; - } - out._getLinkerUrl = function(a) { - return a; - }; - return out; - })(); - // - var Gat = function() { - ; - }; - Gat.prototype._anonymizeIP = noopfn; - Gat.prototype._createTracker = noopfn; - Gat.prototype._forceSSL = noopfn; - Gat.prototype._getPlugin = noopfn; - Gat.prototype._getTracker = function() { - return tracker; - }; - Gat.prototype._getTrackerByName = function() { - return tracker; - }; - Gat.prototype._getTrackers = noopfn; - Gat.prototype.aa = noopfn; - Gat.prototype.ab = noopfn; - Gat.prototype.hb = noopfn; - Gat.prototype.la = noopfn; - Gat.prototype.oa = noopfn; - Gat.prototype.pa = noopfn; - Gat.prototype.u = noopfn; - var gat = new Gat(); - window._gat = gat; - // - var gaq = new Gaq(); - (function() { - var aa = window._gaq || []; - if ( Array.isArray(aa) ) { - while ( aa[0] ) { - gaq.push(aa.shift()); - } - } - })(); - window._gaq = gaq.qf = gaq; -})(); - -google-analytics.com/analytics.js application/javascript -(function() { - // https://developers.google.com/analytics/devguides/collection/analyticsjs/ - var noopfn = function() { - ; - }; - var noopnullfn = function() { - return null; - }; - // - var Tracker = function() { - ; - }; - var p = Tracker.prototype; - p.get = noopfn; - p.set = noopfn; - p.send = noopfn; - // - var w = window, - gaName = w.GoogleAnalyticsObject || 'ga'; - var ga = function() { - var len = arguments.length; - if ( len === 0 ) { - return; - } - var f = arguments[len-1]; - if ( typeof f !== 'object' || f === null || typeof f.hitCallback !== 'function' ) { - return; - } - try { - f.hitCallback(); - } catch (ex) { - } - }; - ga.create = function() { - return new Tracker(); - }; - ga.getByName = noopnullfn; - ga.getAll = function() { - return []; - }; - ga.remove = noopfn; - // https://github.com/uBlockOrigin/uAssets/issues/2107 - ga.loaded = true; - w[gaName] = ga; - // https://github.com/gorhill/uBlock/issues/3075 - var dl = w.dataLayer; - if ( dl instanceof Object && dl.hide instanceof Object && typeof dl.hide.end === 'function' ) { - dl.hide.end(); - } -})(); - -google-analytics.com/inpage_linkid.js application/javascript -(function() { - window._gaq = window._gaq || { - push: function() { - ; - } - }; -})(); - -# https://github.com/gorhill/uBlock/issues/2480 -# https://developers.google.com/analytics/devguides/collection/gajs/experiments#cxjs -google-analytics.com/cx/api.js application/javascript -(function() { - var noopfn = function() { - }; - window.cxApi = { - chooseVariation: function() { - return 0; - }, - getChosenVariation: noopfn, - setAllowHash: noopfn, - setChosenVariation: noopfn, - setCookiePath: noopfn, - setDomainName: noopfn - }; -})(); - -# Ubiquitous googletagservices.com: not blocked by EasyPrivacy. -# "Tags are tiny bits of website code that let you measure traffic and -# "visitor behavior" -googletagservices.com/gpt.js application/javascript -(function() { - var p; - // https://developers.google.com/doubleclick-gpt/reference - var noopfn = function() { - ; - }.bind(); - var noopthisfn = function() { - return this; - }; - var noopnullfn = function() { - return null; - }; - var nooparrayfn = function() { - return []; - }; - var noopstrfn = function() { - return ''; - }; - // - var companionAdsService = { - addEventListener: noopthisfn, - enableSyncLoading: noopfn, - setRefreshUnfilledSlots: noopfn - }; - var contentService = { - addEventListener: noopthisfn, - setContent: noopfn - }; - var PassbackSlot = function() { - ; - }; - p = PassbackSlot.prototype; - p.display = noopfn; - p.get = noopnullfn; - p.set = noopthisfn; - p.setClickUrl = noopthisfn; - p.setTagForChildDirectedTreatment = noopthisfn; - p.setTargeting = noopthisfn; - p.updateTargetingFromMap = noopthisfn; - var pubAdsService = { - addEventListener: noopthisfn, - clear: noopfn, - clearCategoryExclusions: noopthisfn, - clearTagForChildDirectedTreatment: noopthisfn, - clearTargeting: noopthisfn, - collapseEmptyDivs: noopfn, - defineOutOfPagePassback: function() { return new PassbackSlot(); }, - definePassback: function() { return new PassbackSlot(); }, - disableInitialLoad: noopfn, - display: noopfn, - enableAsyncRendering: noopfn, - enableSingleRequest: noopfn, - enableSyncRendering: noopfn, - enableVideoAds: noopfn, - get: noopnullfn, - getAttributeKeys: nooparrayfn, - getTargeting: noopfn, - getTargetingKeys: nooparrayfn, - getSlots: nooparrayfn, - refresh: noopfn, - set: noopthisfn, - setCategoryExclusion: noopthisfn, - setCentering: noopfn, - setCookieOptions: noopthisfn, - setForceSafeFrame: noopthisfn, - setLocation: noopthisfn, - setPublisherProvidedId: noopthisfn, - setRequestNonPersonalizedAds: noopthisfn, - setSafeFrameConfig: noopthisfn, - setTagForChildDirectedTreatment: noopthisfn, - setTargeting: noopthisfn, - setVideoContent: noopthisfn, - updateCorrelator: noopfn - }; - var SizeMappingBuilder = function() { - ; - }; - p = SizeMappingBuilder.prototype; - p.addSize = noopthisfn; - p.build = noopnullfn; - var Slot = function() { - ; - }; - p = Slot.prototype; - p.addService = noopthisfn; - p.clearCategoryExclusions = noopthisfn; - p.clearTargeting = noopthisfn; - p.defineSizeMapping = noopthisfn; - p.get = noopnullfn; - p.getAdUnitPath = nooparrayfn; - p.getAttributeKeys = nooparrayfn; - p.getCategoryExclusions = nooparrayfn; - p.getDomId = noopstrfn; - p.getSlotElementId = noopstrfn; - p.getSlotId = noopthisfn; - p.getTargeting = nooparrayfn; - p.getTargetingKeys = nooparrayfn; - p.set = noopthisfn; - p.setCategoryExclusion = noopthisfn; - p.setClickUrl = noopthisfn; - p.setCollapseEmptyDiv = noopthisfn; - p.setTargeting = noopthisfn; - // - var gpt = window.googletag || {}; - var cmd = gpt.cmd || []; - gpt.apiReady = true; - gpt.cmd = []; - gpt.cmd.push = function(a) { - try { - a(); - } catch (ex) { - } - return 1; - }; - gpt.companionAds = function() { return companionAdsService; }; - gpt.content = function() { return contentService; }; - gpt.defineOutOfPageSlot = function() { return new Slot(); }; - gpt.defineSlot = function() { return new Slot(); }; - gpt.destroySlots = noopfn; - gpt.disablePublisherConsole = noopfn; - gpt.display = noopfn; - gpt.enableServices = noopfn; - gpt.getVersion = noopstrfn; - gpt.pubads = function() { return pubAdsService; }; - gpt.pubadsReady = true; - gpt.setAdIframeTitle = noopfn; - gpt.sizeMapping = function() { return new SizeMappingBuilder(); }; - window.googletag = gpt; - while ( cmd.length !== 0 ) { - gpt.cmd.push(cmd.shift()); - } -})(); - - -# Obviously more work needs to be done, but at least for now it takes care of: -# See related filter in assets/ublock/privacy.txt -# Also: -# - https://github.com/gorhill/uBlock/issues/2569 -# - https://github.com/uBlockOrigin/uAssets/issues/420 -googletagmanager.com/gtm.js application/javascript -(function() { - var noopfn = function() { - }; - var w = window; - w.ga = w.ga || noopfn; - var dl = w.dataLayer; - if ( dl instanceof Object === false ) { return; } - if ( dl.hide instanceof Object && typeof dl.hide.end === 'function' ) { - dl.hide.end(); - } - if ( typeof dl.push === 'function' ) { - dl.push = function(o) { - if ( - o instanceof Object && - typeof o.eventCallback === 'function' - ) { - setTimeout(o.eventCallback, 1); - } - }; - } -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/282 -# https://github.com/uBlockOrigin/uAssets/issues/418 -googlesyndication.com/adsbygoogle.js application/javascript -(function() { - window.adsbygoogle = window.adsbygoogle || { - length: 0, - loaded: true, - push: function Si(a) { - /* - client = client || google_ad_client || google_ad_client; - slotname = slotname || google_ad_slot; - tag_origin = tag_origin || google_tag_origin - */ - this.length += 1; - } - }; - var phs = document.querySelectorAll('.adsbygoogle'); - var css = 'height:1px!important;max-height:1px!important;max-width:1px!important;width:1px!important;'; - for ( var i = 0; i < phs.length; i++ ) { - var fr = document.createElement('iframe'); - fr.id = 'aswift_' + (i+1); - fr.style = css; - var cfr = document.createElement('iframe'); - cfr.id = 'google_ads_frame' + i; - fr.appendChild(cfr); - document.body.appendChild(fr); - } -})(); - - -# https://forums.lanik.us/viewtopic.php?f=91&t=27188 -# https://forums.lanik.us/viewtopic.php?f=91&t=29609 -ligatus.com/*/angular-tag.js application/javascript -(function() { - self.adProtect = true; - Object.defineProperties(window, { - uabpdl: { value: true }, - uabDetect: { value: true } - }); -})(); - - -# Addefend defuser -uabinject-defuser.js application/javascript -(function() { - window.trckd = window.uabpdl = window.uabInject = window.uabDetect = true; -})(); - - -# https://github.com/gorhill/uBlock/issues/1094 -pornhub-popup-defuser.js application/javascript -(function() { - var w = window; - var count = Math.ceil(8+Math.random()*4); - var tomorrow = new Date(Date.now() + 86400000); - document.cookie = 'FastPopSessionRequestNumber=' + count + '; expires=' + tomorrow.toUTCString(); - var db; - if ( (db = w.localStorage) ) { - db.setItem('InfNumFastPops', count); - db.setItem('InfNumFastPopsExpire', tomorrow.toISOString()); - } - if ( (db = w.sessionStorage) ) { - db.setItem('InfNumFastPops', count); - db.setItem('InfNumFastPopsExpire', tomorrow.toISOString()); - } -})(); - - -# https://archive.rebeccablacktech.com/g/thread/S54040346#p54046766 -pornhub-sanitizer.js application/javascript -(function() { - var removeAdFrames = function(aa) { - var el; - for ( var i = 0; i < aa.length; i++ ) { - el = document.getElementById(aa[i]); - if ( el !== null ) { - el.parentNode.removeChild(el); - } - } - }; - Object.defineProperty(window, 'block_logic', { - get: function() { return removeAdFrames; }, - set: function() {} - }); -})(); - - -goyavelab-defuser.js application/javascript -(function() { - var noopfn = function() { - ; - }; - Object.defineProperty(window, '_$14', { - get: function() { return noopfn; }, - set: noopfn - }); -})(); - - -# https://news.ycombinator.com/item?id=10814913 -forbes-defuser.js application/javascript -(function() { - if ( window.location.pathname.lastIndexOf('/forbes/welcome/', 0) !== 0 ) { - return; - } - document.cookie = 'welcomeAd=true'; - window.addEventListener('load', function() { - // https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#Example_2_Get_a_sample_cookie_named_test2 - var toURL = document.cookie.replace(/(?:(?:^|.*;\s*)toURL\s*\=\s*([^;]*).*$)|^.*$/, "$1"); - if ( toURL.lastIndexOf('http', 0) === 0 ) { - window.location.replace(toURL); - } - }); -})(); - - -# https://github.com/gorhill/uBlock/issues/733 -wpredirect-defuser.js application/javascript -(function() { - var twp = window.TWP = window.TWP || {}; - twp.Identity = twp.Identity || {}; - twp.Identity.initComplete = true; -})(); - - -# https://github.com/gorhill/uBlock/issues/1194 -# https://github.com/uBlockOrigin/uAssets/issues/106 -openload.co.js application/javascript -(function() { - Object.defineProperties(window, { - adblock2: { value: false }, - OlPopup: { value: null }, - preserve: { - get: function() { return true; }, - set: function() {} - }, - turnoff: { value: true } - }); -})(); - - -bab-defuser.js application/javascript -(function() { - var sto = window.setTimeout, - re = /\.bab_elementid.$/; - window.setTimeout = function(a, b) { - if ( typeof a !== 'string' || !re.test(a) ) { - return sto.apply(this, arguments); - } - }.bind(window); - var signatures = [ - ['blockadblock'], - ['babasbm'], - [/getItem\('babn'\)/], - ['getElementById','String.fromCharCode','ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789','charAt','DOMContentLoaded','AdBlock','addEventListener','doScroll','fromCharCode','<<2|r>>4','sessionStorage','clientWidth','localStorage','Math','random'] - ]; - var check = function(s) { - var tokens, match, j, token, pos; - for ( var i = 0; i < signatures.length; i++ ) { - tokens = signatures[i]; - match = 0; - for ( j = 0, pos = 0; j < tokens.length; j++ ) { - token = tokens[j]; - pos = token instanceof RegExp ? s.search(token) : s.indexOf(token); - if ( pos !== -1 ) { match += 1; } - } - if ( (match / tokens.length) >= 0.8 ) { return true; } - } - return false; - }; - var realEval = window.eval; - window.eval = function(a) { - if ( typeof a !== 'string' || !check(a) ) { - return realEval(a); - } - var el = document.body; - if ( el ) { - el.style.removeProperty('visibility'); - } - el = document.getElementById('babasbmsgx'); - if ( el ) { - el.parentNode.removeChild(el); - } - }.bind(window); -})(); - - -kissanime-defuser.js application/javascript -(function() { - Object.defineProperties(window, { - DoDetect1: { value: function() {} }, - DoDetect2: { value: function() {} }, - isBlockAds2: { value: false } - }); - window.onerror = function(msg, src, line, col, error) { - if ( /DoDetect\d?|isBlockAds\d/.test(msg) ) { return true; } - }; -})(); - - -phenv-defuser.js application/javascript -(function() { - var phenv = window.PHENV; - var noopfn = function() { - ; - }; - var trapOnDetection = function() { - if ( phenv.onDetection === undefined ) { - Object.defineProperty(phenv, 'onDetection', { - get: function() { - return noopfn; - }, - set: noopfn - }); - return; - } - phenv.onDetection = noopfn; - }; - if ( phenv === undefined ) { - Object.defineProperty(window, 'PHENV', { - get: function() { - return phenv; - }, - set: function(a) { - phenv = a; - trapOnDetection(); - } - }); - return; - } - trapOnDetection(); -})(); - - -scorecardresearch.com/beacon.js application/javascript -(function() { - window.COMSCORE = { - purge: function() { - _comscore = []; - }, - beacon: function() { - ; - } - }; -})(); - - -# https://github.com/gorhill/uBlock/issues/1250#issuecomment-173533894 -widgets.outbrain.com/outbrain.js application/javascript -(function() { - var noopfn = function() { - ; - }; - var obr = {}; - var methods = [ - 'callClick', 'callLoadMore', 'callRecs', 'callUserZapping', - 'callWhatIs', 'cancelRecommendation', 'cancelRecs', 'closeCard', - 'closeModal', 'closeTbx', 'errorInjectionHandler', 'getCountOfRecs', - 'getStat', 'imageError', 'manualVideoClicked', 'onOdbReturn', - 'onVideoClick', 'pagerLoad', 'recClicked', 'refreshSpecificWidget', - 'refreshWidget', 'reloadWidget', 'researchWidget', 'returnedError', - 'returnedHtmlData', 'returnedIrdData', 'returnedJsonData', 'scrollLoad', - 'showDescription', 'showRecInIframe', 'userZappingMessage', 'zappingFormAction' - ]; - obr.extern = { - video: { - getVideoRecs: noopfn, - videoClicked: noopfn - } - }; - methods.forEach(function(a) { - obr.extern[a] = noopfn; - }); - window.OBR = window.OBR || obr; -})(); - - -# https://github.com/gorhill/uBlock/issues/1228 -window.name-defuser application/javascript -if ( window === window.top ) { - window.name = ''; -} - - -# https://github.com/gorhill/uBlock/issues/897#issuecomment-180871042 -doubleclick.net/instream/ad_status.js application/javascript -window.google_ad_status = 1; - - -# https://github.com/gorhill/uBlock/issues/1384 -addthis.com/addthis_widget.js application/javascript -(function() { - var noopfn = function() { - ; - }; - window.addthis = { - addEventListener: noopfn, - button: noopfn, - init: noopfn, - layers: noopfn, - ready: noopfn, - sharecounters: { - getShareCounts: noopfn - }, - toolbox: noopfn, - update: noopfn - }; -})(); - - -# https://github.com/gorhill/uBlock/issues/1059#issuecomment-185434177 -# Works for stern.de, will see if reusable. -bcplayer-defuser.js application/javascript -(function() { - var bcPlayer; - Object.defineProperty(window, 'bcPlayer', { - get: function() { - return bcPlayer; - }, - set: function(a) { - bcPlayer = a; - a.ads = function(a) { - ; - }; - } - }); -})(); - - -# Completely experimental: load Disqus on demand only. Purpose is to avoid -# connecting to Disqus' servers, unless the user explicitly asks for the -# comments to be loaded. -# Works with following filters: -# ||disqus.com/forums/*/embed.js$script,redirect=disqus.com/forums/*/embed.js -# ||disqus.com/embed.js$script,redirect=disqus.com/embed.js -# ||disqus.com/count.js$script -# @@||disqus.com/embed.js?_=1457540*$script -# If you want a site you regularly visit to always have the comment loaded, -# just use an exception static filter. Example for wired.com: -# @@||wired.disqus.com/embed.js - -# Sometimes a site will use this one script: -disqus.com/forums/*/embed.js application/javascript -(function() { - var ee = document.getElementsByTagName('script'); - var i = ee.length, src; - while ( i-- ) { - src = ee[i].src || ''; - if ( src === '' ) { - continue; - } - if ( src.lastIndexOf('disqus.com/embed.js') === (src.length - 19) ) { - return; - } - } - var e = document.createElement('script'); - e.async = true; - e.src = '//' + window.disqus_shortname + '.disqus.com/embed.js'; - document.body.appendChild(e); -})(); - -# Most use this one though: -disqus.com/embed.js application/javascript -(function() { - var p = document.getElementById(window.disqus_container_id || 'disqus_thread'); - if ( p === null ) { - return; - } - var b = document.createElement('button'); - b.textContent = 'Disqus blocked by uBlock Origin: click to unblock'; - b.type = 'button'; - p.appendChild(b); - var loadDisqus = function(ev) { - b.removeEventListener('click', loadDisqus); - p.removeChild(b); - var script = document.createElement('script'); - script.async = true; - var t = Date.now().toString(); - script.src = '//' + window.disqus_shortname + '.disqus.com/embed.js?_=1457540' + t.slice(-6); - document.body.appendChild(script); - ev.preventDefault(); - ev.stopPropagation(); - }; - b.addEventListener('click', loadDisqus); -})(); - - -# https://github.com/gorhill/uBlock/issues/1461 -ytad-defuser.js application/javascript -(function() { - var bind = Function.prototype.bind; - Function.prototype.bind = function() { - 'native code'; - var bound = bind.apply(this, arguments); - bound.xpizqvtnrfkcjixkmdnu = this.toString(); - return bound; - }; - var sto = window.setTimeout; - window.setTimeout = function(a, b) { - if ( b === 15000 && b < 20000 && 'xpizqvtnrfkcjixkmdnu' in a ) { - console.log('uBO>', a.xpizqvtnrfkcjixkmdnu); - } - return sto(a, b); - }; -})(); - - -amazon-adsystem.com/aax2/amzn_ads.js application/javascript -(function() { - if ( amznads ) { - return; - } - var w = window; - var noopfn = function() { - ; - }.bind(); - var amznads = { - appendScriptTag: noopfn, - appendTargetingToAdServerUrl: noopfn, - appendTargetingToQueryString: noopfn, - clearTargetingFromGPTAsync: noopfn, - doAllTasks: noopfn, - doGetAdsAsync: noopfn, - doTask: noopfn, - detectIframeAndGetURL: noopfn, - getAds: noopfn, - getAdsAsync: noopfn, - getAdForSlot: noopfn, - getAdsCallback: noopfn, - getDisplayAds: noopfn, - getDisplayAdsAsync: noopfn, - getDisplayAdsCallback: noopfn, - getKeys: noopfn, - getReferrerURL: noopfn, - getScriptSource: noopfn, - getTargeting: noopfn, - getTokens: noopfn, - getValidMilliseconds: noopfn, - getVideoAds: noopfn, - getVideoAdsAsync: noopfn, - getVideoAdsCallback: noopfn, - handleCallBack: noopfn, - hasAds: noopfn, - renderAd: noopfn, - saveAds: noopfn, - setTargeting: noopfn, - setTargetingForGPTAsync: noopfn, - setTargetingForGPTSync: noopfn, - tryGetAdsAsync: noopfn, - updateAds: noopfn - }; - w.amznads = amznads; - w.amzn_ads = w.amzn_ads || noopfn; - w.aax_write = w.aax_write || noopfn; - w.aax_render_ad = w.aax_render_ad || noopfn; -})(); - - -# https://adblockplus.org/forum/viewtopic.php?f=2&t=25216#p149739 -sas-defuser.js application/javascript -(function() { - var noopfn = function() { - ; - }; - var ads = { - display: noopfn, - refresh: noopfn - }; - Object.defineProperty(window, 'Ads', { - get: function() { - return ads; - }, - set: noopfn - }); -})(); - -# https://adblockplus.org/forum/viewtopic.php?f=2&t=25216#p149739 -figaro-defuser.js application/javascript -(function() { - Object.defineProperty(window, 'adisplaynormal', { - get: function() { - return true; - }, - set: function() {} - }); -})(); - -# https://adblockplus.org/forum/viewtopic.php?f=2&t=25216#p149739 -lemonde-defuser.js application/javascript -(function() { - localStorage.setItem('lmd_me_displayed',JSON.stringify({data:true,timeout:Date.now()+31536000000})); -})(); - -# Good case for https://github.com/gorhill/uBlock/issues/781 -rtlfr-defuser.js application/javascript -(function() { - window.addEventListener('load', function() { - document.body.style.setProperty('overflow', 'auto'); - }); -})(); - - -# Experimental: Generic nuisance overlay buster. -# if this works well and proves to be useful, this may end up as a stock tool -# in uBO's popup panel. -overlay-buster.js application/javascript -(function() { - if ( window !== window.top ) { - return; - } - var tstart; - var ttl = 30000; - var delay = 0; - var delayStep = 50; - var buster = function(mutations) { - var docEl = document.documentElement, - bodyEl = document.body, - vw = Math.min(docEl.clientWidth, window.innerWidth), - vh = Math.min(docEl.clientHeight, window.innerHeight), - tol = Math.min(vw, vh) * 0.05, - el = document.elementFromPoint(vw/2, vh/2), - style, rect; - for (;;) { - if ( el === null || el.parentNode === null || el === bodyEl ) { - break; - } - style = window.getComputedStyle(el); - if ( parseInt(style.zIndex, 10) >= 1000 || style.position === 'fixed' ) { - rect = el.getBoundingClientRect(); - if ( rect.left <= tol && rect.top <= tol && (vw - rect.right) <= tol && (vh - rect.bottom) < tol ) { - el.parentNode.removeChild(el); - tstart = Date.now(); - el = document.elementFromPoint(vw/2, vh/2); - bodyEl.style.setProperty('overflow', 'auto', 'important'); - docEl.style.setProperty('overflow', 'auto', 'important'); - continue; - } - } - el = el.parentNode; - } - if ( (Date.now() - tstart) < ttl ) { - delay = Math.min(delay + delayStep, 1000); - setTimeout(buster, delay); - } - }; - var domReady = function(ev) { - if ( ev ) { - document.removeEventListener(ev.type, domReady); - } - tstart = Date.now(); - setTimeout(buster, delay); - }; - if ( document.readyState === 'loading' ) { - document.addEventListener('DOMContentLoaded', domReady); - } else { - domReady(); - } -})(); - - -# http://www.wilderssecurity.com/threads/ublock-a-lean-and-fast-blocker.365273/page-77 -imore-sanitizer.js application/javascript -(function() { - if ( window.mbn_zones ) { - delete window.mbn_zones; - return; - } - Object.defineProperty(window, 'mbn_zones', { - value: undefined - }); -})(); - - -# https://forums.lanik.us/viewtopic.php?f=62&t=25212 -# https://github.com/uBlockOrigin/uAssets/issues/3 -impspcabe-defuser.js application/javascript -(function(){ - window._impspcabe = false; - window._impspcabe_alpha = false; - window._impspcabe_beta = false; - window._impspcabe_path = 'about:blank'; -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/8 -alert-buster.js application/javascript -(function() { - window.alert = function(a) { - console.info(a); - }; -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/36 -nr-unwrapper.js application/javascript -(function(){ - var wrapped = ' \ - EventTarget.prototype.addEventListener \ - EventTarget.prototype.removeEventListener \ - XMLHttpRequest.prototype.open \ - XMLHttpRequest.prototype.send \ - addEventListener \ - clearTimeout \ - removeEventListener \ - requestAnimationFrame \ - setInterval \ - setTimeout \ - '.split(/\s+/); - var path, o, p, fn; - for ( var i = 0; i < wrapped.length; i++ ) { - path = wrapped[i].split('.'); - p = ''; - for ( var j = 0; j < path.length; j++ ) { - if ( path[j] === '' ) { - continue; - } - o = p !== '' ? o = o[p] : window; - p = path[j]; - } - if ( p === '' ) { - continue; - } - fn = o[p]; - if ( - typeof fn === 'function' && - fn.toString().indexOf('nrWrapper') !== -1 && - typeof fn['nr@original'] === 'function' - ) { - o[p] = fn['nr@original']; - } - } -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/58 -gpt-defuser.js application/javascript -(function() { - var noopfn = function() { - ; - }; - var props = '_resetGPT resetGPT resetAndLoadGPTRecovery _resetAndLoadGPTRecovery setupGPT setupGPTuo'; - props = props.split(/\s+/); - while ( props.length ) { - var prop = props.pop(); - if ( typeof window[prop] === 'function' ) { - window[prop] = noopfn; - } else { - Object.defineProperty(window, prop, { - get: function() { return noopfn; }, - set: noopfn - }); - } - } -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/66 -folha-de-sp.js application/javascript -(function() { - if ( window.folha_ads !== undefined ) { - window.folha_ads = true; - return - } - Object.defineProperty(window, 'folha_ads', { - get: function() { - return true; - }, - set: function() { - ; - } - }); -})(); - - -# Prevent web pages from using eval(), and report attempts in console. -noeval.js application/javascript -(function() { - window.eval = function(s) { - console.log('Document tried to eval... \n' + s); - }.bind(window); -})(); - -silent-noeval.js application/javascript -(function() { - window.eval = function(s) { - ; - }.bind(window); -})(); - -noeval-if.js application/javascript -(function() { - var needle = '{{1}}'; - if ( needle === '' || needle === '{{1}}' ) { - needle = '.?'; - } else if ( needle.slice(0,1) === '/' && needle.slice(-1) === '/' ) { - needle = needle.slice(1,-1); - } else { - needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - needle = new RegExp(needle); - realNoEval = window.eval; - window.eval = function(a) { - if ( !needle.test(a.toString()) ) { - realNoEval.call(window, a); - } - }.bind(window); -})(); - - -# Prevent web pages from using RTCPeerConnection(), and report attempts in console. -nowebrtc.js application/javascript -(function() { - var rtcName = window.RTCPeerConnection ? 'RTCPeerConnection' : ( - window.webkitRTCPeerConnection ? 'webkitRTCPeerConnection' : '' - ); - if ( rtcName === '' ) { return; } - var log = console.log.bind(console); - var pc = function(cfg) { - log('Document tried to create an RTCPeerConnection: %o', cfg); - }; - var noop = function() { - ; - }; - pc.prototype = { - close: noop, - createDataChannel: noop, - createOffer: noop, - setRemoteDescription: noop - }; - var z = window[rtcName]; - window[rtcName] = pc.bind(window); - if ( z.prototype ) { - z.prototype.createDataChannel = function(a, b) { - return { - close: function() {}, - send: function() {} - }; - }.bind(null); - } -})(); - - -# https://adblockplus.org/forum/viewtopic.php?f=10&t=46010 -indiatoday.intoday.in.js application/javascript -(function() { - Object.defineProperties(window, { - adBlock: { value: false }, - checkAds: { value: function() {} } - }); -})(); - - -# Prevent web pages from using eval(), and report attempts in console. -videowood.tv.js application/javascript -(function() { - var realEval = window.eval; - window.eval = function(s) { - var r = realEval(s); - if ( window.config && window.config.adb_remind ) { - window.config.adb_remind = false; - } - return r; - }.bind(window); -})(); - - -# https://github.com/gorhill/uBlock/issues/1340#issuecomment-230495317 -__$dc-defuser.js application/javascript -(function() { - if ( Math.mt_random === undefined ) { - Object.defineProperty(Math, 'mt_random', { - value: function() { throw new TypeError(); }.bind() - }); - } else { - Math.mt_random = function() { throw new TypeError(); }.bind(); - } -})(); - - -# https://forums.lanik.us/viewtopic.php?f=62&t=31285 -indiatimes.com.js application/javascript -(function() { - var dael = document.addEventListener.bind(document), - evts = ['error', 'mousemove', 'scroll']; - document.addEventListener = function(a, b, c) { - if ( evts.indexOf(a) !== -1 ) { - var s = b.toString(); - if ( /^\s*function\s+(\(?_0x[0-9a-z]+\)?|detected)(?:.|\n)*?\1/.test(s) ) { - return; - } - } - return dael(a, b, c); - }.bind(); -})(); - - -# https://forums.lanik.us/viewtopic.php?f=23&t=31545 -adf.ly.js application/javascript -(function() { - var realEval = window.eval; - var noopfn = function(){ - ; - }.bind(); - window.eval = function(s) { - var r = realEval(s); - if ( typeof window.abgo === 'function' ) { - window.abgo = noopfn; - } - return r; - }.bind(window); -})(); - - -# https://m.reddit.com/r/firefox/comments/4ucrvy/can_ublock_origin_disable_javascript_for_a/ -hindustantimes.com.js application/javascript -window.canRun = true; - - -# https://m.reddit.com/r/firefox/comments/4ucrvy/can_ublock_origin_disable_javascript_for_a/ -bhaskar.com.js application/javascript -window.canABP = true; - - -# https://adblockplus.org/forum/viewtopic.php?f=1&t=47754 -thesimsresource.com.js application/javascript -Object.defineProperties(window, { - gadsize: { value: 1 }, - iHaveLoadedAds: { value: true }, - OX: { value: true } -}); - - -# https://github.com/uBlockOrigin/uAssets/issues/88 -golem.de.js application/javascript -(function() { - var rael = window.addEventListener; - window.addEventListener = function(a, b, c) { - rael(a, b, c); - if ( /^\s*function\s*\(\)\s*{\s*window\.clearTimeout\(r\)\s*\}\s*$/.test(b.toString()) ) { - b(); - } - }.bind(window); -})(); - - -# https://www.reddit.com/r/firefox/comments/4wpd23/popads_just_announced_that_they_have_a_new_method/ -popads.net.js application/javascript -(function() { - var magic = String.fromCharCode(Date.now() % 26 + 97) + - Math.floor(Math.random() * 982451653 + 982451653).toString(36), - oe = window.onerror; - window.onerror = function(msg, src, line, col, error) { - if ( typeof msg === 'string' && msg.indexOf(magic) !== -1 ) { return true; } - if ( oe instanceof Function ) { - return oe(msg, src, line, col, error); - } - }.bind(); - var throwMagic = function() { throw magic; }; - delete window.PopAds; - delete window.popns; - Object.defineProperties(window, { - PopAds: { set: throwMagic }, - popns: { set: throwMagic } - }); -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/503 -popads-dummy.js application/javascript -(function() { - delete window.PopAds; - delete window.popns; - Object.defineProperties(window, { - PopAds: { value: {} }, - popns: { value: {} } - }); -})(); - - -# https://forums.lanik.us/viewtopic.php?f=62&t=32070 -xvideos.com.js application/javascript -(function() { - var z = function(ads) { - var banners = ads && ads.banners; - if ( !banners ) { return; } - var banner, div; - while ( banners.length !== 0 ) { - banner = banners[0]; - if ( !banner ) { continue; } - while ( (div = document.getElementById(banner.div_id)) !== null ) { - div.parentNode.removeChild(div); - } - banners.shift(); - } - }; - window.addEventListener('DOMContentLoaded', function() { - var xv = window.xv; - z(xv && xv.conf && xv.conf.ads); - }); - var dfn, - dfntrap = function(a, b, c) { - if ( a !== 'config/ads' ) { dfn(a, b, c); return; } - var ads = b; - while ( typeof ads === 'function' ) { ads = ads(); } - z(ads); - b = function() { return function() { return JSON.parse(JSON.stringify(ads)); }; }; - dfn(a, ads, c); - }; - Object.defineProperty(window, 'define', { - get: function() { return dfn ? dfntrap : undefined; }, - set: function(a) { dfn = a; } - }); -})(); - - -# https://forums.lanik.us/viewtopic.php?f=62&t=31322&start=30 -ndtv.com.js application/javascript -Object.defineProperty(window, '___p_p', { value: 1 }); - - -# https://forums.lanik.us/viewtopic.php?f=64&t=32278 -# https://www.reddit.com/r/chrome/comments/58eix6/ublock_origin_not_working_on_certain_sites/ -upmanager-defuser.js application/javascript -(function() { - var onerror = window.onerror; - window.onerror = function(msg, source, lineno, colno, error) { - if ( typeof msg === 'string' && msg.indexOf('upManager') !== -1 ) { - return true; - } - if ( onerror instanceof Function ) { - onerror.call(window, msg, source, lineno, colno, error); - } - }; - Object.defineProperty(window, 'upManager', { value: function() {} }); -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/108 -wetteronline.de.js application/javascript -(function() { - var nopefn = function() { throw new TypeError('Nope'); }; - Object.defineProperty(window, 'xq5UgyIZx', { - get: nopefn, - set: nopefn - }); - window.WO = {}; - Object.defineProperty(window.WO, 'doAbCheck', { - value: function() {} - }); -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/108 -trafictube.ro.js application/javascript -(function() { - var z = window.setInterval.bind(window), - re = /\(new\s+\w\)\.start\(\)/; - window.setInterval = function(a, b) { - if ( re.test(a.toString()) ) { - return 0; - } - return z(a, b); - }.bind(window); -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/110 -smartadserver.com.js application/javascript -Object.defineProperties(window, { - SmartAdObject: { value: function(){} }, - SmartAdServerAjax: { value: function(){} }, - smartAd: { value: { LoadAds: function() {}, Register: function() {} } } -}); - - -# http://www.linuxtricks.fr/news/10-logiciels-libres/169-contourner-les-anti-bloqueurs-de-pub-la-revanche/ -lesechos.fr.js application/javascript -Object.defineProperty(window, 'checkAdBlock', { - value: function(){} -}); - - -# https://github.com/uBlockOrigin/uAssets/issues/141 -criteo.net.js application/javascript -(function() { - var noopfn = function() {}; - var criteo = { - DisplayAd: noopfn - }; - if ( window.hasOwnProperty('Criteo') ) { - window.Criteo = criteo; - } else { - Object.defineProperty(window, 'Criteo', { value: criteo }); - } -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/38 -# https://github.com/uBlockOrigin/uAssets/issues/150 -r3z-defuser.js application/javascript -window._r3z = {}; -Object.defineProperties(window._r3z, { - jq: { value: undefined }, - pub: { value: {} } -}); - - -# https://forums.lanik.us/viewtopic.php?f=103&t=31558 -ideal.es.js application/javascript -Object.defineProperty(window, 'is_block_adb_enabled', { value: false }); - - -# https://github.com/uBlockOrigin/uAssets/issues/125 -d3pkae9owd2lcf.cloudfront.net/mb105.js application/javascript -(function() { - var noopfn = function(){}; - window.pbjs = { libLoaded: true }; - var mb = window.MonkeyBroker || { - addAttribute: noopfn, - addSlot: function(a) { - this.slots[a.slot] = {}; - }, - defineSlot: noopfn, - fillSlot: noopfn, - go: noopfn, - inventoryConditionalPlacement: noopfn, - registerSizeCallback: noopfn, - registerSlotCallback: noopfn, - slots: {}, - version: '' - }; - mb.regSlotsMap = mb.slots; - window.MonkeyBroker = mb; -})(); - - -# https://www.reddit.com/r/uBlockOrigin/comments/55pu1e/lastfm_super_aggressive_adblock_warning_message/ -last.fm.js application/javascript -(function() { - var st = window.setTimeout; - var z = function(a, b) { - if ( b === 2000 && a.name.indexOf('bound') !== -1 ) { - return; - } - return st(a, b); - }.bind(window); - Object.defineProperty(window, 'setTimeout', { value: z }); -})(); - - -# https://news.ycombinator.com/item?id=12677179 -livescience.com.js application/javascript -Object.defineProperty(window, 'tmnramp', { value: {} }); - - -# https://twitter.com/Scarbir/status/785551814460571648 -static.chartbeat.com/chartbeat.js application/javascript -(function() { - var noopfn = function(){}; - window.pSUPERFLY = { - activity: noopfn, - virtualPage: noopfn - }; -})(); - - -# https://discourse.mozilla-community.org/t/support-ublock-origin/6746/214 -lachainemeteo.com.js application/javascript -Object.defineProperty(window, 'pliga', { value: { push: function(){} } }); - - -# https://github.com/gorhill/uBlock/issues/2132 -abort-on-property-write.js application/javascript -(function() { - var magic = String.fromCharCode(Date.now() % 26 + 97) + - Math.floor(Math.random() * 982451653 + 982451653).toString(36); - var prop = '{{1}}', - owner = window, - pos; - for (;;) { - pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - owner = owner[prop.slice(0, pos)]; - if ( owner instanceof Object === false ) { return; } - prop = prop.slice(pos + 1); - } - delete owner[prop]; - Object.defineProperty(owner, prop, { - set: function() { - throw new ReferenceError(magic); - } - }); - var oe = window.onerror; - window.onerror = function(msg, src, line, col, error) { - if ( typeof msg === 'string' && msg.indexOf(magic) !== -1 ) { - return true; - } - if ( oe instanceof Function ) { - return oe(msg, src, line, col, error); - } - }.bind(); -})(); - - -abort-on-property-read.js application/javascript -(function() { - var magic = String.fromCharCode(Date.now() % 26 + 97) + - Math.floor(Math.random() * 982451653 + 982451653).toString(36); - var abort = function() { - throw new ReferenceError(magic); - }; - var chain = '{{1}}', owner = window, pos, desc; - var makeProxy = function(owner, chain) { - pos = chain.indexOf('.'); - if ( pos === -1 ) { - desc = Object.getOwnPropertyDescriptor(owner, chain); - if ( !desc || desc.get !== abort ) { - Object.defineProperty(owner, chain, { get: abort, set: function(){} }); - } - return; - } - var prop = chain.slice(0, pos), - v = owner[prop]; - chain = chain.slice(pos + 1); - if ( v !== undefined ) { - makeProxy(v, chain); - return; - } - desc = Object.getOwnPropertyDescriptor(owner, prop); - if ( desc && desc.set && desc.set.hasOwnProperty(magic) ) { - return; - } - var setter = function(a) { - v = a; - if ( a instanceof Object ) { - makeProxy(a, chain); - } - }; - setter[magic] = undefined; - Object.defineProperty(owner, prop, { - get: function() { return v; }, - set: setter - }); - }; - makeProxy(owner, chain); - var oe = window.onerror; - window.onerror = function(msg, src, line, col, error) { - if ( typeof msg === 'string' && msg.indexOf(magic) !== -1 ) { - return true; - } - if ( oe instanceof Function ) { - return oe(msg, src, line, col, error); - } - }.bind(); -})(); - - -setTimeout-defuser.js application/javascript -(function() { - var z = window.setTimeout, - needle = '{{1}}', - delay = parseInt('{{2}}', 10); - if ( needle === '' || needle === '{{1}}' ) { - needle = '.?'; - } else if ( needle.slice(0,1) === '/' && needle.slice(-1) === '/' ) { - needle = needle.slice(1,-1); - } else { - needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - needle = new RegExp(needle); - window.setTimeout = function(a, b) { - if ( (isNaN(delay) || b == delay) && needle.test(a.toString()) ) { - return z(function(){}, b); - } - return z.apply(this, arguments); - }.bind(window); -})(); - - -setTimeout-logger.js application/javascript -(function() { - var z = window.setTimeout, - log = console.log.bind(console); - window.setTimeout = function(a, b) { - log('uBO: setTimeout("%s", %s)', a.toString(), b); - return z.apply(this, arguments); - }.bind(window); -})(); - - -setInterval-defuser.js application/javascript -(function() { - var w = window, - z = w.setInterval, - needle = '{{1}}', - delay = parseInt('{{2}}', 10); - if ( needle === '' || needle === '{{1}}' ) { - needle = '.?'; - } else if ( needle.slice(0,1) === '/' && needle.slice(-1) === '/' ) { - needle = needle.slice(1,-1); - } else { - needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - needle = new RegExp(needle); - w.setInterval = function(a, b) { - if ( (isNaN(delay) || b == delay) && needle.test(a.toString()) ) { - return z(function(){}, b); - } - return z.apply(this, arguments); - }.bind(w); - w.setInterval.toString = function() { return z.toString(); }; -})(); - - -setInterval-logger.js application/javascript -(function() { - var z = window.setInterval, - log = console.log.bind(console); - window.setInterval = function(a, b) { - log('uBO: setInterval("%s", %s)', a.toString(), b); - return z.apply(this, arguments); - }.bind(window); -})(); - - -sharedWorker-defuser.js application/javascript -(function() { - if ( window.SharedWorker instanceof Function === false ) { return; } - var needle = '{{1}}'; - if ( needle === '' || needle === '{{1}}' ) { needle = '.?'; } - else if ( /^\/.+\/$/.test(needle) ) { needle = needle.slice(1,-1); } - else { needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } - needle = new RegExp(needle); - var RealSharedWorker = window.SharedWorker; - var WrappedSharedWorker = function(a, b) { - if ( this instanceof WrappedSharedWorker === false ) { return RealSharedWorker(); } - if ( needle.test(a) ) { - return new RealSharedWorker(window.URL.createObjectURL(new Blob([';'], {type:'text/javascript'}))); - } - return new RealSharedWorker(a, b); - }; - WrappedSharedWorker.prototype = RealSharedWorker.prototype; - window.SharedWorker = WrappedSharedWorker.bind(window); -})(); - - -sidereel.com.js application/javascript -(function() { - window.localStorage.setItem('__trex', Date.now() + 86400000); -})(); - - -# Ability to inject CSP in document, to further restrict what web pages can do. -# Reference: https://www.w3.org/TR/CSP3/ -csp.js application/javascript -(function() { - var doc = document; - if ( doc.head === null ) { return; } - var meta = doc.createElement('meta'); - meta.setAttribute('http-equiv', 'Content-Security-Policy'); - meta.setAttribute('content', '{{1}}'); - doc.head.appendChild(meta); -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/513 -entrepreneur.com.js application/javascript -window.analyticsEvent = function(){}; - - -# https://github.com/uBlockOrigin/uAssets/issues/521 -addEventListener-logger.js application/javascript -(function() { - var c = console, l = c.log; - var z = self.EventTarget.prototype.addEventListener; - self.EventTarget.prototype.addEventListener = function(a, b) { - l.call(c, 'addEventListener("%s", %s)', a, b.toString()); - return z.apply(this, arguments); - }; -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/521 -addEventListener-defuser.js application/javascript -(function() { - var needle1 = '{{1}}', - needle2 = '{{2}}', - z = self.EventTarget.prototype.addEventListener; - if ( needle1 === '' || needle1 === '{{1}}' ) { - needle1 = '.?'; - } else if ( /^\/.+\/$/.test(needle1) ) { - needle1 = needle1.slice(1,-1); - } else { - needle1 = needle1.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - needle1 = new RegExp(needle1); - if ( needle2 === '' || needle2 === '{{2}}' ) { - needle2 = '.?'; - } else if ( /^\/.+\/$/.test(needle2) ) { - needle2 = needle2.slice(1,-1); - } else { - needle2 = needle2.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - needle2 = new RegExp(needle2); - self.EventTarget.prototype.addEventListener = function(a, b) { - if ( needle1.test(a.toString()) && needle2.test(b.toString()) ) { - return; - } - return z.apply(this, arguments); - }; -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/531 -# Scriptlet below borrowed from: -# https://github.com/AdguardTeam/AdguardFilters/blob/master/RussianFilter/sections/antiadblock.txt -palacesquare.rambler.ru-defuser.js application/javascript -(function() { - var w = window, - pr = w.Promise, - tostr = Function.prototype.toString; - w.Promise = function(executor) { - if ( tostr.call(executor).indexOf('getRandomSelector') !== -1 ) { - throw new Error(); - } else { - return new pr(executor); - } - }.bind(); - Object.getOwnPropertyNames(pr).forEach(function(propName) { - Object.defineProperty(w.Promise, propName, Object.getOwnPropertyDescriptor(pr, propName)); - }); - pr.prototype.constructor = w.Promise; -})(); - - -# https://www.reddit.com/r/uBlockOrigin/comments/6nrayv/new_to_using_ublock_origin_its_breaking_videos_on/ -# https://github.com/gorhill/uBlock/issues/2835 -gamespot.com.js application/javascript -(function() { - if ( typeof window.pf_notify === 'function' ) { - window.pf_notify(false); - } -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/618 -abort-current-inline-script.js application/javascript -(function() { - var target = '{{1}}'; - if ( target === '' || target === '{{1}}' ) { return; } - var needle = '{{2}}', reText = '.?'; - if ( needle !== '' && needle !== '{{2}}' ) { - reText = /^\/.+\/$/.test(needle) - ? needle.slice(1,-1) - : needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - var thisScript = document.currentScript; - var re = new RegExp(reText); - var chain = target.split('.'); - var owner = window, prop; - for (;;) { - prop = chain.shift(); - if ( chain.length === 0 ) { break; } - owner = owner[prop]; - if ( owner instanceof Object === false ) { return; } - } - var desc = Object.getOwnPropertyDescriptor(owner, prop); - if ( desc && desc.get !== undefined ) { return; } - var magic = String.fromCharCode(Date.now() % 26 + 97) + - Math.floor(Math.random() * 982451653 + 982451653).toString(36); - var value = owner[prop]; - var validate = function() { - var e = document.currentScript; - if ( - e instanceof HTMLScriptElement && - e.src === '' && - e !== thisScript && - re.test(e.textContent) - ) { - throw new ReferenceError(magic); - } - }; - Object.defineProperty(owner, prop, { - get: function() { - validate(); - return value; - }, - set: function(a) { - validate(); - value = a; - } - }); - var oe = window.onerror; - window.onerror = function(msg) { - if ( typeof msg === 'string' && msg.indexOf(magic) !== -1 ) { - return true; - } - if ( oe instanceof Function ) { - return oe.apply(this, arguments); - } - }.bind(); -})(); - - -# https://github.com/uBlockOrigin/uAssets/commit/a6c77af4afb45800d4fd7c268a2a5eab5a64daf3#commitcomment-24611606 -window.open-defuser.js application/javascript -(function() { - var wo = window.open, - target = '{{1}}', - needle = '{{2}}'; - if ( target === '' || target === '{{1}}' ) { - target = false; - } else { - target = !(+target); - } - if ( needle === '' || needle === '{{2}}' ) { - needle = '.?'; - } else if ( /^\/.+\/$/.test(needle) ) { - needle = needle.slice(1,-1); - } else { - needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - needle = new RegExp(needle); - window.open = (function(a) { - if ( needle.test(a) === target ) { - return wo.apply(window, arguments); - } - }).bind(window); -})(); - - -# https://github.com/reek/anti-adblock-killer/issues/3774#issuecomment-348536138 -# https://github.com/uBlockOrigin/uAssets/issues/883 -adfly-defuser.js application/javascript -(function() { - // Based on AdsBypasser - // License: - // https://github.com/adsbypasser/adsbypasser/blob/master/LICENSE - var isDigit = /^\d$/; - var handler = function(encodedURL) { - var var1 = "", var2 = "", i; - for (i = 0; i < encodedURL.length; i++) { - if (i % 2 === 0) { - var1 = var1 + encodedURL.charAt(i); - } else { - var2 = encodedURL.charAt(i) + var2; - } - } - var data = (var1 + var2).split(""); - for (i = 0; i < data.length; i++) { - if (isDigit.test(data[i])) { - for (var ii = i + 1; ii < data.length; ii++) { - if (isDigit.test(data[ii])) { - var temp = parseInt(data[i],10) ^ parseInt(data[ii],10); - if (temp < 10) { - data[i] = temp.toString(); - } - i = ii; - break; - } - } - } - } - data = data.join(""); - var decodedURL = window.atob(data).slice(16, -16); - window.stop(); - window.onbeforeunload = null; - window.location.href = decodedURL; - }; - try { - var val; - var flag = true; - window.Object.defineProperty(window, "ysmm", { - configurable: false, - set: function(value) { - if (flag) { - flag = false; - try { - if (typeof value === "string") { - handler(value); - } - } catch (err) { } - } - val = value; - }, - get: function() { - return val; - } - }); - } catch (err) { - window.console.error("Failed to set up Adfly bypasser!"); - } -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/913 -disable-newtab-links.js application/javascript -(function() { - document.addEventListener('click', function(ev) { - var target = ev.target; - while ( target !== null ) { - if ( target.localName === 'a' && target.hasAttribute('target') ) { - ev.stopPropagation(); - ev.preventDefault(); - break; - } - target = target.parentNode; - } - }); -})(); - - -set-constant.js application/javascript -(function() { - var cValue = '{{2}}'; - if ( cValue === 'undefined' ) { - cValue = undefined; - } else if ( cValue === 'false' ) { - cValue = false; - } else if ( cValue === 'true' ) { - cValue = true; - } else if ( cValue === 'null' ) { - cValue = null; - } else if ( cValue === 'noopFunc' ) { - cValue = function(){}; - } else if ( cValue === 'trueFunc' ) { - cValue = function(){ return true; }; - } else if ( cValue === 'falseFunc' ) { - cValue = function(){ return false; }; - } else if ( /^\d+$/.test(cValue) ) { - cValue = parseFloat(cValue); - if ( isNaN(cValue) ) { return; } - if ( Math.abs(cValue) > 0x7FFF ) { return; } - } else if ( cValue === "''" ) { - cValue = ''; - } else { - return; - } - var aborted = false; - var mustAbort = function(v) { - if ( aborted ) { return true; } - aborted = v !== undefined && cValue !== undefined && typeof v !== typeof cValue; - return aborted; - }; - var chain = '{{1}}', owner = window; - var makeProxy = function(owner, chain) { - var desc; - var pos = chain.indexOf('.'); - if ( pos === -1 ) { - if ( mustAbort(owner[chain]) ) { return; } - desc = Object.getOwnPropertyDescriptor(owner, chain); - if ( desc === undefined || desc.get === undefined ) { - Object.defineProperty(owner, chain, { - get: function() { - return cValue; - }, - set: function(a) { - if ( mustAbort(a) ) { - cValue = a; - } - } - }); - } - return; - } - var prop = chain.slice(0, pos), - v = owner[prop]; - chain = chain.slice(pos + 1); - if ( v !== undefined ) { - makeProxy(v, chain); - return; - } - desc = Object.getOwnPropertyDescriptor(owner, prop); - if ( desc && desc.set ) { return; } - Object.defineProperty(owner, prop, { - get: function() { - return v; - }, - set: function(a) { - v = a; - if ( a instanceof Object ) { - makeProxy(a, chain); - } - } - }); - }; - makeProxy(owner, chain); -})(); - - -# Imported from: -# https://github.com/NanoAdblocker/NanoFilters/blob/1f3be7211bb0809c5106996f52564bf10c4525f7/NanoFiltersSource/NanoResources.txt#L82 -# -# Speed up or down setTimeout, 3 optional arguments. -# funcMatcher - The payload matcher, a string literal or a JavaScript RegExp, -# defaults to match all. -# delayMatcher - The delay matcher, an integer, defaults to 1000. -# boostRatio - The delay multiplier when there is a match, 0.5 speeds up by -# 2 times and 2 slows down by 2 times, defaults to 0.05 or speed up 20 times. -# Speed up and down both cap at 50 times. -nano-setTimeout-booster.js application/javascript -(function() { - // Based on uAssets - // License: https://github.com/uBlockOrigin/uAssets/blob/master/LICENSE - var z = window.setTimeout, - needle = '{{1}}', - delay = parseInt('{{2}}', 10), - boost = parseFloat('{{3}}'); - if ( needle === '' || needle === '{{1}}' ) { - needle = '.?'; - } else if ( needle.charAt(0) === '/' && needle.slice(-1) === '/' ) { - needle = needle.slice(1, -1); - } else { - needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - needle = new RegExp(needle); - if ( isNaN(delay) || !isFinite(delay) ) { - delay = 1000; - } - if ( isNaN(boost) || !isFinite(boost) ) { - boost = 0.05; - } - if ( boost < 0.02 ) { - boost = 0.02; - } - if ( boost > 50 ) { - boost = 50; - } - window.setTimeout = function(a, b) { - if ( b === delay && needle.test(a.toString()) ) { - b *= boost; - } - return z.apply(this, arguments); - }.bind(window); -})(); - -# Imported from: -# https://github.com/NanoAdblocker/NanoFilters/blob/1f3be7211bb0809c5106996f52564bf10c4525f7/NanoFiltersSource/NanoResources.txt#L126 -# -# Same as the last one except it is for setInterval instead of setTimeout. -nano-setInterval-booster.js application/javascript -(function() { - // Based on uAssets - // License: https://github.com/uBlockOrigin/uAssets/blob/master/LICENSE - var z = window.setInterval, - needle = '{{1}}', - delay = parseInt('{{2}}', 10), - boost = parseFloat('{{3}}'); - if ( needle === '' || needle === '{{1}}' ) { - needle = '.?'; - } else if ( needle.charAt(0) === '/' && needle.slice(-1) === '/' ) { - needle = needle.slice(1, -1); - } else { - needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - needle = new RegExp(needle); - if ( isNaN(delay) || !isFinite(delay) ) { - delay = 1000; - } - if ( isNaN(boost) || !isFinite(boost) ) { - boost = 0.05; - } - if ( boost < 0.02 ) { - boost = 0.02; - } - if ( boost > 50 ) { - boost = 50; - } - window.setInterval = function(a, b) { - if ( b === delay && needle.test(a.toString()) ) { - b *= boost; - } - return z.apply(this, arguments); - }.bind(window); -})(); - - -remove-attr.js application/javascript -(function() { - var attr = '{{1}}'; - if ( attr === '' || attr === '{{1}}' ) { return; } - var attrs = attr.split(/\s*\|\s*/); - var selector = '{{2}}'; - if ( selector === '' || selector === '{{2}}' ) { - selector = '[' + attrs.join('],[') + ']'; - } - var rmattr = function(ev) { - if ( ev ) { window.removeEventListener(ev.type, rmattr, true); } - try { - var nodes = document.querySelectorAll(selector), i = nodes.length; - while ( i-- ) { - var node = nodes[i], j = attrs.length; - while ( j-- ) { node.removeAttribute(attrs[j]); } - } - } catch(ex) { - } - }; - if ( document.readyState === 'loading' ) { - window.addEventListener('DOMContentLoaded', rmattr, true); - } else { - rmattr(); - } -})(); - - -damoh-defuser.js application/javascript -(function() { - var handled = new WeakSet(); - var asyncTimer; - var cleanVideo = function() { - asyncTimer = undefined; - var v = document.querySelector('video'); - if ( v === null ) { return; } - if ( handled.has(v) ) { return; } - handled.add(v); - v.pause(); - v.controls = true; - var el = v.querySelector('meta[itemprop="contentURL"][content]'); - if ( el === null ) { return; } - v.src = el.getAttribute('content'); - el = v.querySelector('meta[itemprop="thumbnailUrl"][content]'); - if ( el !== null ) { v.poster = el.getAttribute('content'); } - }; - var cleanVideoAsync = function() { - if ( asyncTimer !== undefined ) { return; } - asyncTimer = window.requestAnimationFrame(cleanVideo); - }; - var observer = new MutationObserver(cleanVideoAsync); - observer.observe(document.documentElement, { childList: true, subtree: true }); -})(); - - -# https://github.com/uBlockOrigin/uAssets/pull/3517 -twitch-videoad.js application/javascript -(function() { - if ( /(^|\.)twitch\.tv$/.test(document.location.hostname) === false ) { return; } - var ourMediaPlayer; - Object.defineProperty(window, 'MediaPlayer', { - set: function(newMediaPlayer) { - if ( ourMediaPlayer !== undefined ) { return; } - var oldLoad = newMediaPlayer.MediaPlayer.prototype.load; - newMediaPlayer.MediaPlayer.prototype.load = function(e) { - try { - if ( e.startsWith('https://usher.ttvnw.net/api/channel/hls/') ) { - var url = new URL(e); - url.searchParams.delete('baking_bread'); - url.searchParams.delete('baking_brownies'); - url.searchParams.delete('baking_brownies_timeout'); - e = url.href; - } - } catch (err) { - //console.error('Failed to bypass Twitch livestream ad'); - } - return oldLoad.call(this, e); - }; - ourMediaPlayer = newMediaPlayer; - }, - get: function() { - return ourMediaPlayer; - } - }); - var realFetch = window.fetch; - window.fetch = function(input, init) { - if ( arguments.length >= 2 && typeof input === 'string' && input.includes('/access_token') ) { - var url = new URL(arguments[0]); - url.searchParams.delete('player_type'); - arguments[0] = url.href; - } - return realFetch.apply(this, arguments); - }; -})(); - - -# https://github.com/uBlockOrigin/uAssets/issues/2912 -fingerprint2.js application/javascript -(function() { - let fp2 = function(){}; - fp2.prototype = { - get: function(cb) { - setTimeout(function() { cb('', []); }, 1); - } - }; - window.Fingerprint2 = fp2; -})(); - - -# https://github.com/NanoAdblocker/NanoFilters/issues/149 -cookie-remover.js application/javascript -(function() { - let needle = '{{1}}', - reName = /./; - if ( /^\/.+\/$/.test(needle) ) { - reName = new RegExp(needle.slice(1,-1)); - } else if ( needle !== '' && needle !== '{{1}}' ) { - reName = new RegExp(needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } - let removeCookie = function() { - document.cookie.split(';').forEach(cookieStr => { - let pos = cookieStr.indexOf('='); - if ( pos === -1 ) { return; } - let cookieName = cookieStr.slice(0, pos).trim(); - if ( !reName.test(cookieName) ) { return; } - let part1 = cookieName + '='; - let part2a = '; domain=' + document.location.hostname; - let part2b = '; domain=.' + document.location.hostname; - let part3 = '; path=/'; - let part4 = '; Max-Age=-1000; expires=Thu, 01 Jan 1970 00:00:00 GMT'; - document.cookie = part1 + part4; - document.cookie = part1 + part2a + part4; - document.cookie = part1 + part2b + part4; - document.cookie = part1 + part3 + part4; - document.cookie = part1 + part2a + part3 + part4; - document.cookie = part1 + part2b + part3 + part4; - }); - }; - removeCookie(); - window.addEventListener('beforeunload', removeCookie); -})(); - - -# https://www.reddit.com/r/firefox/comments/9dudod/the_mysterious_case_of_missing_urls_and_googles/e5kgkkh -ampproject.org/v0.js application/javascript -(function() { - let head = document.head; - if ( !head ) { return; } - let style = document.createElement('style'); - style.textContent = [ - 'body {', - ' animation: none !important;', - ' overflow: unset !important;', - '}' - ].join('\n'); - head.appendChild(style); -})(); diff --git a/lib/index.js b/lib/index.js index e3d50b6a..33fceab1 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,3 +2,4 @@ const blocker = require('../native'); exports.Engine = blocker.Engine; exports.lists = blocker.lists; +exports.uBlockResources = blocker.uBlockResources; diff --git a/native/src/lib.rs b/native/src/lib.rs index dc8ee9ad..d48160ad 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -7,8 +7,12 @@ extern crate serde; use neon::prelude::*; use serde::{Deserialize, Serialize}; +use std::path::Path; use adblock::engine::Engine; use adblock::filter_lists; +use adblock::resources::Resource; +use adblock::resources::resource_assembler::{assemble_web_accessible_resources, assemble_scriptlet_resources}; + #[derive(Serialize, Deserialize)] struct EngineOptions { @@ -127,7 +131,8 @@ declare_types! { } method updateResources(mut cx) { - let resources: String = cx.argument::(0)?.value(); + let resources_arg = cx.argument::(0)?; + let resources: Vec = neon_serde::from_value(&mut cx, resources_arg)?; let mut this = cx.this(); let guard = cx.lock(); @@ -174,15 +179,14 @@ declare_types! { } method addResource(mut cx) { - let name: String = cx.argument::(0)?.value(); - let content_type: String = cx.argument::(1)?.value(); - let data: String = cx.argument::(2)?.value(); + let resource_arg = cx.argument::(0)?; + let resource: Resource = neon_serde::from_value(&mut cx, resource_arg)?; let mut this = cx.this(); let guard = cx.lock(); { let mut engine = this.borrow_mut(&guard); - engine.resource_add(&name, &content_type, &data); + engine.resource_add(resource); } Ok(JsNull::new().upcast()) } @@ -225,10 +229,24 @@ fn validate_request(mut cx: FunctionContext) -> JsResult { Ok(cx.boolean(request_ok)) } +fn ublock_resources(mut cx: FunctionContext) -> JsResult { + let web_accessible_resource_dir: String = cx.argument::(0)?.value(); + let redirect_engine_path: String = cx.argument::(1)?.value(); + let scriptlets_path: String = cx.argument::(2)?.value(); + + let mut resources = assemble_web_accessible_resources(&Path::new(&web_accessible_resource_dir), &Path::new(&redirect_engine_path)); + resources.append(&mut assemble_scriptlet_resources(&Path::new(&scriptlets_path))); + + let js_resources = neon_serde::to_value(&mut cx, &resources)?; + + Ok(js_resources) +} + register_module!(mut m, { // Export the `JsEngine` class m.export_class::("Engine")?; m.export_function("lists", lists)?; m.export_function("validateRequest", validate_request)?; + m.export_function("uBlockResources", ublock_resources)?; Ok(()) }); diff --git a/src/blocker.rs b/src/blocker.rs index fa0c9485..8a83a32f 100644 --- a/src/blocker.rs +++ b/src/blocker.rs @@ -11,8 +11,7 @@ use crate::filters::network::{NetworkFilter, NetworkMatchable, FilterError}; use crate::request::Request; use crate::utils::{fast_hash, Hash}; use crate::optimizer; -use crate::resources::{Resources, Resource}; -use base64; +use crate::resources::{Resource, RedirectResourceStorage, RedirectResource}; use crate::utils; pub struct BlockerOptions { @@ -104,7 +103,7 @@ pub struct Blocker { load_network_filters: bool, #[serde(default)] - resources: Resources, + resources: RedirectResourceStorage, #[cfg(feature = "object-pooling")] #[serde(skip_serializing, skip_deserializing)] pool: TokenPool, @@ -207,11 +206,7 @@ impl Blocker { if let Some(redirect) = f.redirect.as_ref() { // And we have a matching redirect resource if let Some(resource) = self.resources.get_resource(redirect) { - let data_url = if resource.content_type.contains(';') { - format!("data:{},{}", resource.content_type, resource.data) - } else { - format!("data:{};base64,{}", resource.content_type, base64::encode(&resource.data)) - }; + let data_url = format!("data:{};base64,{}", resource.content_type, &resource.data); Some(data_url.trim().to_owned()) } else { // TOOD: handle error - throw? @@ -320,7 +315,7 @@ impl Blocker { load_cosmetic_filters: options.load_cosmetic_filters, load_network_filters: options.load_network_filters, - resources: Resources::default(), + resources: RedirectResourceStorage::default(), #[cfg(feature = "object-pooling")] pool: TokenPool::default(), } @@ -404,17 +399,18 @@ impl Blocker { self.tags_enabled.iter().cloned().collect() } - pub fn with_resources(&mut self, resources: Resources) -> &mut Blocker { + pub fn with_resources(&mut self, resources: &[Resource]) -> &mut Blocker { + let resources = RedirectResourceStorage::from_resources(resources); self.resources = resources; self } - pub fn resource_add(&mut self, key: String, resource: Resource) -> &mut Blocker { - self.resources.add_resource(key, resource); + pub fn resource_add(&mut self, resource: Resource) -> &mut Blocker { + self.resources.add_resource(resource); self } - pub fn resource_get(&self, key: &str) -> Option<&Resource> { + pub fn resource_get(&self, key: &str) -> Option<&RedirectResource> { self.resources.get_resource(key) } } diff --git a/src/engine.rs b/src/engine.rs index af94ba45..5cbf8148 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -3,7 +3,7 @@ use crate::blocker::{Blocker, BlockerError, BlockerOptions, BlockerResult}; use crate::lists::parse_filters; use crate::request::Request; use crate::filters::network::NetworkFilter; -use crate::resources::{Resources, Resource}; +use crate::resources::{Resource, RedirectResource}; use rmps; use flate2::write::GzEncoder; use flate2::read::GzDecoder; @@ -162,22 +162,17 @@ impl Engine { self.blocker.tags_enabled().contains(&tag.to_owned()) } - pub fn with_resources<'a>(&'a mut self, resources: &'a str) -> &'a mut Engine { - let resources = Resources::parse(resources); + pub fn with_resources<'a>(&'a mut self, resources: &[Resource]) -> &'a mut Engine { self.blocker.with_resources(resources); self } - pub fn resource_add<'a>(&'a mut self, key: &str, content_type: &str, data: &str) -> &'a mut Engine { - let resource = Resource { - content_type: content_type.to_owned(), - data: data.to_owned() - }; - self.blocker.resource_add(key.to_owned(), resource); + pub fn resource_add<'a>(&'a mut self, resource: Resource) -> &'a mut Engine { + self.blocker.resource_add(resource); self } - pub fn resource_get(&self, key: &str) -> Option { + pub fn resource_get(&self, key: &str) -> Option { self.blocker.resource_get(key).cloned() } } @@ -186,6 +181,7 @@ impl Engine { #[cfg(test)] mod tests { use super::*; + use crate::resources::{ResourceType, MimeType}; #[test] fn tags_enable_adds_tags() { @@ -413,15 +409,22 @@ mod tests { let mut engine = Engine::from_rules(&[ "ad-banner$redirect=nooptext".to_owned() ]); - let resources_str = r###" -nooptext text/plain - - -noopcss text/css - -"###; - engine.with_resources(&resources_str); + let resources = vec![ + Resource { + name: "nooptext".to_string(), + aliases: vec![], + kind: ResourceType::Mime(MimeType::TextPlain), + content: base64::encode(""), + }, + Resource { + name: "noopcss".to_string(), + aliases: vec![], + kind: ResourceType::Mime(MimeType::TextPlain), + content: base64::encode(""), + }, + ]; + engine.with_resources(&resources); let serialized = engine.serialize().unwrap(); println!("Engine serialized: {:?}", serialized); @@ -433,7 +436,12 @@ noopcss text/css "ad-banner$redirect=nooptext".to_owned() ]); - engine.resource_add("nooptext", "text/plain", ""); + engine.resource_add(Resource { + name: "nooptext".to_owned(), + aliases: vec![], + kind: ResourceType::Mime(MimeType::TextPlain), + content: "".to_owned(), + }); let url = "http://example.com/ad-banner.gif"; let matched_rule = engine.check_network_urls(url, "", ""); @@ -452,7 +460,12 @@ noopcss text/css let mut engine = Engine::from_rules(&[]); - engine.resource_add("noopjs", "application/javascript", script); + engine.resource_add(Resource { + name: "noopjs".to_owned(), + aliases: vec![], + kind: ResourceType::Mime(MimeType::ApplicationJavascript), + content: script.to_owned(), + }); let inserted_resource = engine.resource_get("noopjs"); assert!(inserted_resource.is_some()); let resource = inserted_resource.unwrap(); diff --git a/src/resources.rs b/src/resources.rs deleted file mode 100644 index af10b4e7..00000000 --- a/src/resources.rs +++ /dev/null @@ -1,207 +0,0 @@ -use std::collections::HashMap; -use regex::Regex; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] -pub struct Resource { - pub content_type: String, - pub data: String -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct Resources { - pub resources: HashMap -} - -impl Default for Resources { - fn default() -> Resources { - Resources { - resources: HashMap::new() - } - } -} - -impl Resources { - pub fn parse(data: &str) -> Resources { - let chunks = data.split("\n\n"); - let mut type_to_resource: HashMap> = HashMap::new(); - - lazy_static! { - static ref COMMENTS_RE: Regex = Regex::new(r"(?m:^\s*#.*$)").unwrap(); - } - - for chunk in chunks { - let resource: String = COMMENTS_RE.replace_all(&chunk, "").to_string(); - let resource: String = resource.trim().to_owned(); - if resource.is_empty() { - continue; - } - let first_new_line = resource.find('\n'); - let first_new_line_pos; - // No new line, but appears to encode mime type and teh content is not base64, so can be empty - if first_new_line.is_none() && resource.contains(' ') && resource.contains('/') && !resource.contains(";base64") { - first_new_line_pos = resource.len(); - } else if first_new_line.is_none() { - continue; - } else { - first_new_line_pos = first_new_line.unwrap(); - } - let (first_line, body) = resource.split_at(first_new_line_pos); - let mut first_line_items = first_line.split_whitespace(); - let (name, rtype) = ( - first_line_items.next(), - first_line_items.next() - ); - if name.is_none() || rtype.is_none() { - continue; - } - let rtype = rtype.unwrap().to_owned(); - let name = name.unwrap().to_owned(); - let body = body.trim().to_owned(); - - let ttr = type_to_resource.entry(rtype).or_insert_with(HashMap::new); - ttr.insert(name, body); - } - - // Create a mapping from resource name to { contentType, data } - // used for request redirection. - let mut resources: HashMap = HashMap::new(); - for (content_type, type_resources) in type_to_resource { - for (name, resource) in type_resources { - resources.insert(name, Resource { - content_type: content_type.to_owned(), - data: resource - }); - } - } - - Resources { - resources, - } - } - - pub fn get_resource(&self, name: &str) -> Option<&Resource> { - self.resources.get(name) - } - - pub fn add_resource(&mut self, name: String, resource: Resource) { - self.resources.insert(name, resource); - } -} - -#[cfg(test)] -mod tests { - - use super::*; - use crate::utils; - - #[test] - fn parses_empty_resources() { - let resources = Resources::parse(""); - assert!(resources.resources.is_empty()); - } - - #[test] - fn parses_one_resource() { - let resources_str = "foo application/javascript\ncontent"; - let resources = Resources::parse(resources_str); - assert!(resources.resources.is_empty() == false); - let mut expected = HashMap::new(); - expected.insert("foo".to_owned(), Resource { - content_type: "application/javascript".to_owned(), - data: "content".to_owned() - }); - assert_eq!(resources.resources, expected); - } - - #[test] - fn parses_two_resources() { - let resources_str = r###" -foo application/javascript -content1 - -pixel.png image/png;base64 -content2"###; - let resources = Resources::parse(resources_str); - assert!(resources.resources.is_empty() == false); - let mut expected = HashMap::new(); - expected.insert("foo".to_owned(), Resource { - content_type: "application/javascript".to_owned(), - data: "content1".to_owned() - }); - expected.insert("pixel.png".to_owned(), Resource { - content_type: "image/png;base64".to_owned(), - data: "content2".to_owned() - }); - assert_eq!(resources.resources, expected); - } - - #[test] - fn robust_to_weird_format() { - let resources_str = r###" -# Comment - # Comment 2 -foo application/javascript -content1 -# Comment 3 - -# Type missing -pixel.png -content - -# Content missing -pixel.png image/png;base64 - -# This one is good! -pixel.png image/png;base64 -content2 -"###; - - let resources = Resources::parse(resources_str); - assert!(resources.resources.is_empty() == false); - let mut expected = HashMap::new(); - expected.insert("foo".to_owned(), Resource { - content_type: "application/javascript".to_owned(), - data: "content1".to_owned() - }); - expected.insert("pixel.png".to_owned(), Resource { - content_type: "image/png;base64".to_owned(), - data: "content2".to_owned() - }); - assert_eq!(resources.resources, expected); - } - - #[test] - fn parses_noop_resources() { - let resources_str = r###" -nooptext text/plain - - -noopcss text/css - - -"###; - let resources = Resources::parse(resources_str); - assert!(resources.resources.is_empty() == false); - let mut expected = HashMap::new(); - expected.insert("nooptext".to_owned(), Resource { - content_type: "text/plain".to_owned(), - data: "".to_owned() - }); - expected.insert("noopcss".to_owned(), Resource { - content_type: "text/css".to_owned(), - data: "".to_owned() - }); - assert_eq!(resources.resources, expected); - } - - #[test] - fn handles_ubo_resources() { - let resources_lines = utils::read_file_lines("data/uBlockOrigin/resources.txt"); - let resources_str = resources_lines.join("\n"); - assert!(!resources_str.is_empty()); - let resources = Resources::parse(&resources_str); - assert!(resources.resources.is_empty() == false); - assert_eq!(resources.resources.len(), 110); - } -} diff --git a/src/resources/mod.rs b/src/resources/mod.rs new file mode 100644 index 00000000..a51bda8a --- /dev/null +++ b/src/resources/mod.rs @@ -0,0 +1,196 @@ +pub mod resource_assembler; + +use serde::{Serialize, Deserialize}; +use std::collections::HashMap; + +/// Struct representing a resource that can be used by an adblocking engine. +/// +/// - `name`: Represents the primary name of the resource, often a filename +/// +/// - `aliases`: Represents secondary names that can be used to access the resource +/// +/// - `kind`: How to interpret the resource data within `content` +/// +/// - `content`: The resource data, encoded using standard base64 configuration +#[derive(Serialize, Deserialize)] +pub struct Resource { + pub name: String, + pub aliases: Vec, + pub kind: ResourceType, + pub content: String, +} + +/// Different ways that the data within the `content` field of a `Resource` can be interpreted. +/// +/// - `Mime(type)` - interpret the data according to the MIME type represented by `type` +/// +/// - `Template` - interpret the data as a Javascript scriptlet template, with embedded template +/// parameters in the form of `{{1}}`, `{{2}}`, etc. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum ResourceType { + Mime(MimeType), + Template, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[serde(into = "String")] +#[serde(from = "&str")] +pub enum MimeType { + ImageGif, + TextHtml, + ApplicationJavascript, + AudioMp3, + VideoMp4, + ImagePng, + TextPlain, + Unknown, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub struct RedirectResource { + pub content_type: String, + pub data: String +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Default)] +pub struct RedirectResourceStorage { + pub resources: HashMap, +} + + +impl MimeType { + /// Infers a resource's MIME type according to the extension of its path + pub fn from_extension(resource_path: &str) -> Self { + if let Some(extension_index) = resource_path.rfind('.') { + match &resource_path[extension_index + 1..] { + "gif" => MimeType::ImageGif, + "html" => MimeType::TextHtml, + "js" => MimeType::ApplicationJavascript, + "mp3" => MimeType::AudioMp3, + "mp4" => MimeType::VideoMp4, + "png" => MimeType::ImagePng, + "txt" => MimeType::TextPlain, + _ => { + eprintln!("Unrecognized file extension on: {:?}", resource_path); + MimeType::Unknown + } + } + } else { + return MimeType::Unknown + } + } +} + +impl RedirectResourceStorage { + pub fn from_resources(resources: &[Resource]) -> Self { + let mut redirectable_resources: HashMap = HashMap::new(); + + resources.iter().filter_map(|descriptor| { + if let ResourceType::Mime(ref content_type) = descriptor.kind { + let resource = RedirectResource { + content_type: content_type.clone().into(), + data: descriptor.content.to_owned(), + }; + Some((descriptor.name.to_owned(), descriptor.aliases.to_owned(), resource)) + } else { + None + } + }).for_each(|(name, res_aliases, resource)| { + res_aliases.iter().for_each(|alias| { + redirectable_resources.insert(alias.to_owned(), resource.clone()); + }); + redirectable_resources.insert(name, resource); + }); + + Self { + resources: redirectable_resources, + } + } + + pub fn get_resource(&self, name: &str) -> Option<&RedirectResource> { + self.resources.get(name) + } + + pub fn add_resource(&mut self, resource: Resource) { + if let ResourceType::Mime(content_type) = resource.kind { + let name = resource.name.to_owned(); + let redirect_resource = RedirectResource { + content_type: content_type.into(), + data: resource.content.to_owned(), + }; + resource.aliases.iter().for_each(|alias| { + self.resources.insert(alias.to_owned(), redirect_resource.clone()); + }); + self.resources.insert(name, redirect_resource); + } + } +} + +impl From<&str> for MimeType { + fn from(v: &str) -> Self { + match v { + "image/gif" => MimeType::ImageGif, + "text/html" => MimeType::TextHtml, + "application/javascript" => MimeType::ApplicationJavascript, + "audio/mp3" => MimeType::AudioMp3, + "video/mp4" => MimeType::VideoMp4, + "image/png" => MimeType::ImagePng, + "text/plain" => MimeType::TextPlain, + _ => MimeType::Unknown, + } + } +} + +impl From for String { + fn from(v: MimeType) -> Self { + match v { + MimeType::ImageGif => "image/gif", + MimeType::TextHtml => "text/html", + MimeType::ApplicationJavascript => "application/javascript", + MimeType::AudioMp3 => "audio/mp3", + MimeType::VideoMp4 => "video/mp4", + MimeType::ImagePng => "image/png", + MimeType::TextPlain => "text/plain", + MimeType::Unknown => "application/octet-stream", + }.to_owned() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn get_resource_by_name() { + let mut storage = RedirectResourceStorage::default(); + storage.add_resource(Resource { + name: "name.js".to_owned(), + aliases: vec![], + kind: ResourceType::Mime(MimeType::ApplicationJavascript), + content: base64::encode("resource data"), + }); + + assert_eq!(storage.get_resource("name.js"), Some(&RedirectResource { + content_type: "application/javascript".to_owned(), + data: base64::encode("resource data"), + })); + } + + #[test] + fn get_resource_by_alias() { + let mut storage = RedirectResourceStorage::default(); + storage.add_resource(Resource { + name: "name.js".to_owned(), + aliases: vec!["alias.js".to_owned()], + kind: ResourceType::Mime(MimeType::ApplicationJavascript), + content: base64::encode("resource data"), + }); + + assert_eq!(storage.get_resource("alias.js"), Some(&RedirectResource { + content_type: "application/javascript".to_owned(), + data: base64::encode("resource data"), + })); + } +} + diff --git a/src/resources/resource_assembler.rs b/src/resources/resource_assembler.rs new file mode 100644 index 00000000..86c53ce8 --- /dev/null +++ b/src/resources/resource_assembler.rs @@ -0,0 +1,372 @@ +//! Contains methods useful for building `Resource` descriptors from resources directly from files +//! in the uBlock Origin repository. + +use std::io::Read; +use regex::Regex; +use std::fs::File; +use std::path::Path; +use crate::resources::{Resource, ResourceType, MimeType}; + +lazy_static! { + // [ '1x1.gif', { + static ref MAP_NAME_KEY_RE: Regex = Regex::new(r#"^\s*\[\s*'([a-zA-Z0-9\-_\.]+)',\s*\{"#).unwrap(); + // alias: '1x1-transparent.gif', + static ref MAP_PROPERTY_KEY_RE: Regex = Regex::new(r#"^\s*([a-zA-Z0-9_]+):\s*'([a-zA-Z0-9\-_\./\*]+)',"#).unwrap(); + // } ], + static ref MAP_END_KEY_RE: Regex = Regex::new(r#"^\s*\}\s*\]"#).unwrap(); + // ]); + static ref MAP_END_RE: Regex = Regex::new(r#"^\s*\]\s*\)"#).unwrap(); + + static ref TEMPLATE_ARGUMENT_RE: Regex = Regex::new(r"\{\{\d\}\}").unwrap(); + static ref ESCAPE_SCRIPTLET_ARG_RE: Regex = Regex::new(r#"[\\'"]"#).unwrap(); + static ref TOP_COMMENT_RE: Regex = Regex::new(r#"^/\*[\S\s]+?\n\*/\s*"#).unwrap(); + static ref NON_EMPTY_LINE_RE: Regex = Regex::new(r#"\S"#).unwrap(); +} + +/// Represents a single entry of the `redirectableResources` map from uBlock Origin's +/// `redirect-engine.js`. +/// +/// - `name` is the name of a resource, corresponding to its path in the `web_accessible_resources` +/// directory +/// +/// - `alias` is an optional additional name that can be used to reference the resource +/// +/// - `data` is either `"text"` or `"blob"`, but is currently unused in `adblock-rust`. Within +/// uBlock Origin, it's used to prevent text files from being encoded in base64 in a data URL. +struct ResourceProperties { + name: String, + alias: Option, + data: Option, +} + +const REDIRECTABLE_RESOURCES_DECLARATION: &'static str = "const redirectableResources = new Map(["; + +/// Reads data from a a file in the format of uBlock Origin's `redirect-engine.js` file to +/// determine the files in the `web_accessible_resources` directory, as well as any of their +/// aliases. +/// +/// This is read from the `redirectableResources` map. +fn read_redirectable_resource_mapping(mapfile_data: &str) -> Vec { + let mut resource_properties = Vec::new(); + + let mut current_resource: Option = None; + let mut found_mapping = false; + + for line in mapfile_data.lines().skip_while(|line| *line != REDIRECTABLE_RESOURCES_DECLARATION).skip(1) { + found_mapping = true; + + let key_capture = MAP_NAME_KEY_RE.captures(line); + if let Some(key_capture) = key_capture { + // unwrap is safe because first capture group must be populated + let key = key_capture.get(1).unwrap().as_str(); + current_resource = Some(ResourceProperties { name: key.to_owned(), alias: None, data: None}); + continue; + } + + let prop_captures = MAP_PROPERTY_KEY_RE.captures(line); + if let Some(prop_captures) = prop_captures { + // unwraps are safe because both capture groups must be populated + let property_key = prop_captures.get(1).unwrap().as_str(); + let property_value = prop_captures.get(2).unwrap().as_str(); + + if let Some(ref mut current_resource) = current_resource { + match property_key { + "alias" => current_resource.alias = Some(property_value.to_owned()), + "data" => current_resource.data = Some(property_value.to_owned()), + other => panic!("unexpected key: {}", other), + } + } else { + panic!("could not find beginning of resource in alias map"); + } + continue; + } + + if MAP_END_KEY_RE.is_match(line) { + resource_properties.push(current_resource.take().expect("closed a resource entry before opening a new one")); + continue; + } + + if MAP_END_RE.is_match(line) { + if let Some(current_resource) = current_resource { + panic!("encountered end of alias map while {} was unclosed", current_resource.name); + } + break; + } + + panic!("encountered unexpected line {:?}", line); + } + + if !found_mapping { + panic!("failed to find mapping"); + } + + resource_properties +} + +/// Reads data from a file in the form of uBlock Origin's `scriptlets.js` file and produces +/// templatable scriptlets for use in cosmetic filtering. +fn read_template_resources(scriptlets_data: &str) -> Vec { + let mut resources = Vec::new(); + + let uncommented = TOP_COMMENT_RE.replace_all(&scriptlets_data, ""); + let mut name: Option<&str> = None; + let mut details = std::collections::HashMap::new(); + let mut script = String::new(); + + for line in uncommented.lines() { + if line.starts_with('#') || line.starts_with("// ") { + continue; + } + + if name.is_none() { + if line.starts_with("/// ") { + name = Some(line[4..].trim()); + } + continue; + } + + if line.starts_with("/// ") { + let mut line = line[4..].split_whitespace(); + let prop = line.next().expect("Detail line has property name"); + let value = line.next().expect("Detail line has property value"); + details.insert(prop, value); + continue; + } + + if NON_EMPTY_LINE_RE.is_match(line) { + script += line.trim(); + continue; + } + + let kind = if script.contains("{{1}}") { + ResourceType::Template + } else { + ResourceType::Mime(MimeType::ApplicationJavascript) + }; + resources.push(Resource { + name: name.expect("Resource name must be specified").to_owned(), + aliases: details.get("alias").iter().map(|alias| alias.to_string()).collect(), + kind, + content: base64::encode(&script), + }); + + name = None; + details.clear(); + script.clear(); + } + + resources +} + +/// Reads byte data from an arbitrary resource file, and assembles a `Resource` from it with the +/// provided `resource_info`. +fn build_resource_from_file_contents(resource_contents: &[u8], resource_info: &ResourceProperties) -> Resource { + let name = resource_info.name.to_owned(); + let aliases = resource_info.alias.iter().map(|alias| alias.to_string()).collect(); + let mimetype = MimeType::from_extension(&resource_info.name[..]); + let content = match mimetype { + MimeType::ApplicationJavascript | MimeType::TextHtml | MimeType::TextPlain => { + let utf8string = std::str::from_utf8(resource_contents).unwrap(); + base64::encode(&utf8string.replace('\r', "")) + } + _ => { + base64::encode(&resource_contents) + } + }; + + Resource { + name, + aliases, + kind: ResourceType::Mime(mimetype), + content, + } +} + +/// Produces a `Resource` from the `web_accessible_resource_dir` directory according to the +/// information in `resource_info. +fn read_resource_from_web_accessible_dir(web_accessible_resource_dir: &Path, resource_info: &ResourceProperties) -> Resource { + let resource_path = web_accessible_resource_dir.join(&resource_info.name); + if !resource_path.is_file() { + panic!("Expected {:?} to be a file", resource_path); + } + let mut resource_file = File::open(resource_path).expect("open resource file for reading"); + let mut resource_contents = Vec::new(); + resource_file.read_to_end(&mut resource_contents).expect("read resource file contents"); + + build_resource_from_file_contents(&resource_contents, resource_info) +} + +/// Builds a `Vec` of `Resource`s from the specified paths on the filesystem: +/// +/// - `web_accessible_resource_dir`: A folder full of resource files +/// +/// - `redirect_engine_path`: A file in the format of uBlock Origin's `redirect-engine.js` +/// containing an index of the resources in `web_accessible_resource_dir` +/// +/// - `scriptlets_path`: A file in the format of uBlock Origin's `scriptlets.js` containing +/// templatable scriptlet files for use in cosmetic filtering +/// +/// The resulting resources can be serialized into JSON using `serde_json`. +pub fn assemble_web_accessible_resources(web_accessible_resource_dir: &Path, redirect_engine_path: &Path) -> Vec { + let mapfile_data = std::fs::read_to_string(redirect_engine_path).expect("read aliases path"); + let resource_properties = read_redirectable_resource_mapping(&mapfile_data); + + resource_properties.iter().map(|resource_info| { + read_resource_from_web_accessible_dir(web_accessible_resource_dir, resource_info) + }).collect() +} + +pub fn assemble_scriptlet_resources(scriptlets_path: &Path) -> Vec { + let scriptlets_data = std::fs::read_to_string(scriptlets_path).expect("read scriptlets path"); + read_template_resources(&scriptlets_data) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_war_resource_assembly() { + let web_accessible_resource_dir = Path::new("data/test/fake-uBO-files/web_accessible_resources"); + let redirect_engine_path = Path::new("data/test/fake-uBO-files/redirect-engine.js"); + let resources = assemble_web_accessible_resources(web_accessible_resource_dir, redirect_engine_path); + + let expected_resource_names = vec![ + "1x1.gif", + "2x2.png", + "3x2.png", + "32x32.png", + "addthis_widget.js", + "amazon_ads.js", + "ampproject_v0.js", + "chartbeat.js", + "disqus_embed.js", + "disqus_forums_embed.js", + "doubleclick_instream_ad_status.js", + "empty", + "google-analytics_analytics.js", + "google-analytics_cx_api.js", + "google-analytics_ga.js", + "google-analytics_inpage_linkid.js", + "googlesyndication_adsbygoogle.js", + "googletagmanager_gtm.js", + "googletagservices_gpt.js", + "hd-main.js", + "ligatus_angular-tag.js", + "monkeybroker.js", + "noeval.js", + "noeval-silent.js", + "nobab.js", + "nofab.js", + "noop-0.1s.mp3", + "noop-1s.mp4", + "noop.html", + "noop.js", + "noop.txt", + "outbrain-widget.js", + "popads.js", + "popads-dummy.js", + "scorecardresearch_beacon.js", + "window.open-defuser.js", + ]; + + for name in expected_resource_names { + assert!(resources.iter() + .find(|resource| { + if let ResourceType::Mime(_) = resource.kind { + resource.name == name + } else { + false + } + }).is_some()); + } + + let serialized = serde_json::to_string(&resources).expect("serialize resources"); + + let reserialized: Vec = serde_json::from_str(&serialized).expect("deserialize resources"); + + assert_eq!(reserialized[0].name, "1x1.gif"); + assert_eq!(reserialized[0].aliases, vec!["1x1-transparent.gif"]); + assert_eq!(reserialized[0].kind, ResourceType::Mime(MimeType::ImageGif)); + + assert_eq!(reserialized[29].name, "noop.js"); + assert_eq!(reserialized[29].aliases, vec!["noopjs"]); + assert_eq!(reserialized[29].kind, ResourceType::Mime(MimeType::ApplicationJavascript)); + let noopjs_contents = std::fs::read_to_string(Path::new("data/test/fake-uBO-files/web_accessible_resources/noop.js")).unwrap().replace('\r', ""); + assert_eq!( + std::str::from_utf8( + &base64::decode(&reserialized[29].content).expect("decode base64 content") + ).expect("convert to utf8 string"), + noopjs_contents, + ); + } + + #[test] + fn test_scriptlet_resource_assembly() { + let scriptlets_path = Path::new("data/test/fake-uBO-files/scriptlets.js"); + let resources = assemble_scriptlet_resources(scriptlets_path); + + let expected_resource_names = vec![ + "abort-current-inline-script.js", + "abort-on-property-read.js", + "abort-on-property-write.js", + "addEventListener-defuser.js", + "addEventListener-logger.js", + "json-prune.js", + "nano-setInterval-booster.js", + "nano-setTimeout-booster.js", + "noeval-if.js", + "remove-attr.js", + "requestAnimationFrame-if.js", + "set-constant.js", + "setInterval-defuser.js", + "no-setInterval-if.js", + "setTimeout-defuser.js", + "no-setTimeout-if.js", + "webrtc-if.js", + "window.name-defuser", + "overlay-buster.js", + "alert-buster.js", + "gpt-defuser.js", + "nowebrtc.js", + "golem.de.js", + "upmanager-defuser.js", + "smartadserver.com.js", + "adfly-defuser.js", + "disable-newtab-links.js", + "damoh-defuser.js", + "twitch-videoad.js", + "fingerprint2.js", + "cookie-remover.js", + ]; + + for name in expected_resource_names { + assert!(resources.iter() + .find(|resource| { + match resource.kind { + ResourceType::Template | ResourceType::Mime(MimeType::ApplicationJavascript) => resource.name == name, + _ => false, + } + }) + .is_some(), "failed to find {}", name); + } + + let serialized = serde_json::to_string(&resources).expect("serialize resources"); + + let reserialized: Vec = serde_json::from_str(&serialized).expect("deserialize resources"); + + assert_eq!(reserialized[0].name, "abort-current-inline-script.js"); + assert_eq!(reserialized[0].aliases, vec!["acis.js"]); + assert_eq!(reserialized[0].kind, ResourceType::Template); + + assert_eq!(reserialized[18].name, "overlay-buster.js"); + assert_eq!(reserialized[18].aliases, Vec::::new()); + assert_eq!(reserialized[18].kind, ResourceType::Mime(MimeType::ApplicationJavascript)); + assert_eq!( + std::str::from_utf8( + &base64::decode(&reserialized[18].content).expect("decode base64 content") + ).expect("convert to utf8 string"), + "(function() {if ( window !== window.top ) {return;}var tstart;var ttl = 30000;var delay = 0;var delayStep = 50;var buster = function() {var docEl = document.documentElement,bodyEl = document.body,vw = Math.min(docEl.clientWidth, window.innerWidth),vh = Math.min(docEl.clientHeight, window.innerHeight),tol = Math.min(vw, vh) * 0.05,el = document.elementFromPoint(vw/2, vh/2),style, rect;for (;;) {if ( el === null || el.parentNode === null || el === bodyEl ) {break;}style = window.getComputedStyle(el);if ( parseInt(style.zIndex, 10) >= 1000 || style.position === \'fixed\' ) {rect = el.getBoundingClientRect();if ( rect.left <= tol && rect.top <= tol && (vw - rect.right) <= tol && (vh - rect.bottom) < tol ) {el.parentNode.removeChild(el);tstart = Date.now();el = document.elementFromPoint(vw/2, vh/2);bodyEl.style.setProperty(\'overflow\', \'auto\', \'important\');docEl.style.setProperty(\'overflow\', \'auto\', \'important\');continue;}}el = el.parentNode;}if ( (Date.now() - tstart) < ttl ) {delay = Math.min(delay + delayStep, 1000);setTimeout(buster, delay);}};var domReady = function(ev) {if ( ev ) {document.removeEventListener(ev.type, domReady);}tstart = Date.now();setTimeout(buster, delay);};if ( document.readyState === \'loading\' ) {document.addEventListener(\'DOMContentLoaded\', domReady);} else {domReady();}})();", + ); + } +} diff --git a/tests/live.rs b/tests/live.rs index c71691d9..9f365e3e 100644 --- a/tests/live.rs +++ b/tests/live.rs @@ -4,7 +4,7 @@ extern crate reqwest; use adblock::blocker::{Blocker, BlockerOptions}; use adblock::engine::Engine; use adblock::filters::network::NetworkFilter; -use adblock::utils; +use adblock::resources::resource_assembler::assemble_web_accessible_resources; use serde::{Deserialize}; use std::fs::File; @@ -128,9 +128,11 @@ fn check_live_deserialized() { #[test] fn check_live_redirects() { let mut engine = get_blocker_engine(); - let resources_lines = utils::read_file_lines("data/uBlockOrigin/resources.txt"); - let resources_str = resources_lines.join("\n"); - engine.with_resources(&resources_str); + let redirect_engine_path = std::path::Path::new("data/test/fake-uBO-files/redirect-engine.js"); + let war_dir = std::path::Path::new("data/test/fake-uBO-files/web_accessible_resources"); + let resources = assemble_web_accessible_resources(war_dir, redirect_engine_path); + + engine.with_resources(&resources); { let checked = engine.check_network_urls( "https://c.amazon-adsystem.com/aax2/amzn_ads.js", @@ -141,7 +143,7 @@ fn check_live_redirects() { checked.filter, checked.exception); assert!(checked.redirect.is_some()); // Check for the specific expected return script value in base64 - assert_eq!(checked.redirect.unwrap(), "data:application/javascript;base64,KGZ1bmN0aW9uKCkgewoJaWYgKCBhbXpuYWRzICkgewoJCXJldHVybjsKCX0KCXZhciB3ID0gd2luZG93OwoJdmFyIG5vb3BmbiA9IGZ1bmN0aW9uKCkgewoJCTsKCX0uYmluZCgpOwoJdmFyIGFtem5hZHMgPSB7CgkJYXBwZW5kU2NyaXB0VGFnOiBub29wZm4sCgkJYXBwZW5kVGFyZ2V0aW5nVG9BZFNlcnZlclVybDogbm9vcGZuLAoJCWFwcGVuZFRhcmdldGluZ1RvUXVlcnlTdHJpbmc6IG5vb3BmbiwKCQljbGVhclRhcmdldGluZ0Zyb21HUFRBc3luYzogbm9vcGZuLAoJCWRvQWxsVGFza3M6IG5vb3BmbiwKCQlkb0dldEFkc0FzeW5jOiBub29wZm4sCgkJZG9UYXNrOiBub29wZm4sCgkJZGV0ZWN0SWZyYW1lQW5kR2V0VVJMOiBub29wZm4sCgkJZ2V0QWRzOiBub29wZm4sCgkJZ2V0QWRzQXN5bmM6IG5vb3BmbiwKCQlnZXRBZEZvclNsb3Q6IG5vb3BmbiwKCQlnZXRBZHNDYWxsYmFjazogbm9vcGZuLAoJCWdldERpc3BsYXlBZHM6IG5vb3BmbiwKCQlnZXREaXNwbGF5QWRzQXN5bmM6IG5vb3BmbiwKCQlnZXREaXNwbGF5QWRzQ2FsbGJhY2s6IG5vb3BmbiwKCQlnZXRLZXlzOiBub29wZm4sCgkJZ2V0UmVmZXJyZXJVUkw6IG5vb3BmbiwKCQlnZXRTY3JpcHRTb3VyY2U6IG5vb3BmbiwKCQlnZXRUYXJnZXRpbmc6IG5vb3BmbiwKCQlnZXRUb2tlbnM6IG5vb3BmbiwKCQlnZXRWYWxpZE1pbGxpc2Vjb25kczogbm9vcGZuLAoJCWdldFZpZGVvQWRzOiBub29wZm4sCgkJZ2V0VmlkZW9BZHNBc3luYzogbm9vcGZuLAoJCWdldFZpZGVvQWRzQ2FsbGJhY2s6IG5vb3BmbiwKCQloYW5kbGVDYWxsQmFjazogbm9vcGZuLAoJCWhhc0Fkczogbm9vcGZuLAoJCXJlbmRlckFkOiBub29wZm4sCgkJc2F2ZUFkczogbm9vcGZuLAoJCXNldFRhcmdldGluZzogbm9vcGZuLAoJCXNldFRhcmdldGluZ0ZvckdQVEFzeW5jOiBub29wZm4sCgkJc2V0VGFyZ2V0aW5nRm9yR1BUU3luYzogbm9vcGZuLAoJCXRyeUdldEFkc0FzeW5jOiBub29wZm4sCgkJdXBkYXRlQWRzOiBub29wZm4KCX07Cgl3LmFtem5hZHMgPSBhbXpuYWRzOwoJdy5hbXpuX2FkcyA9IHcuYW16bl9hZHMgfHwgbm9vcGZuOwoJdy5hYXhfd3JpdGUgPSB3LmFheF93cml0ZSB8fCBub29wZm47Cgl3LmFheF9yZW5kZXJfYWQgPSB3LmFheF9yZW5kZXJfYWQgfHwgbm9vcGZuOwp9KSgpOw==") + assert_eq!(checked.redirect.unwrap(), "data:application/javascript;base64,LyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioKCiAgICB1QmxvY2sgT3JpZ2luIC0gYSBicm93c2VyIGV4dGVuc2lvbiB0byBibG9jayByZXF1ZXN0cy4KICAgIENvcHlyaWdodCAoQykgMjAxOS1wcmVzZW50IFJheW1vbmQgSGlsbAoKICAgIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5CiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieQogICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3IKICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uCgogICAgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgogICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQogICAgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KCiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQogICAgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW0uICBJZiBub3QsIHNlZSB7aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzL30uCgogICAgSG9tZTogaHR0cHM6Ly9naXRodWIuY29tL2dvcmhpbGwvdUJsb2NrCiovCgooZnVuY3Rpb24oKSB7CiAgICAndXNlIHN0cmljdCc7CiAgICBpZiAoIGFtem5hZHMgKSB7CiAgICAgICAgcmV0dXJuOwogICAgfQogICAgdmFyIHcgPSB3aW5kb3c7CiAgICB2YXIgbm9vcGZuID0gZnVuY3Rpb24oKSB7CiAgICAgICAgOwogICAgfS5iaW5kKCk7CiAgICB2YXIgYW16bmFkcyA9IHsKICAgICAgICBhcHBlbmRTY3JpcHRUYWc6IG5vb3BmbiwKICAgICAgICBhcHBlbmRUYXJnZXRpbmdUb0FkU2VydmVyVXJsOiBub29wZm4sCiAgICAgICAgYXBwZW5kVGFyZ2V0aW5nVG9RdWVyeVN0cmluZzogbm9vcGZuLAogICAgICAgIGNsZWFyVGFyZ2V0aW5nRnJvbUdQVEFzeW5jOiBub29wZm4sCiAgICAgICAgZG9BbGxUYXNrczogbm9vcGZuLAogICAgICAgIGRvR2V0QWRzQXN5bmM6IG5vb3BmbiwKICAgICAgICBkb1Rhc2s6IG5vb3BmbiwKICAgICAgICBkZXRlY3RJZnJhbWVBbmRHZXRVUkw6IG5vb3BmbiwKICAgICAgICBnZXRBZHM6IG5vb3BmbiwKICAgICAgICBnZXRBZHNBc3luYzogbm9vcGZuLAogICAgICAgIGdldEFkRm9yU2xvdDogbm9vcGZuLAogICAgICAgIGdldEFkc0NhbGxiYWNrOiBub29wZm4sCiAgICAgICAgZ2V0RGlzcGxheUFkczogbm9vcGZuLAogICAgICAgIGdldERpc3BsYXlBZHNBc3luYzogbm9vcGZuLAogICAgICAgIGdldERpc3BsYXlBZHNDYWxsYmFjazogbm9vcGZuLAogICAgICAgIGdldEtleXM6IG5vb3BmbiwKICAgICAgICBnZXRSZWZlcnJlclVSTDogbm9vcGZuLAogICAgICAgIGdldFNjcmlwdFNvdXJjZTogbm9vcGZuLAogICAgICAgIGdldFRhcmdldGluZzogbm9vcGZuLAogICAgICAgIGdldFRva2Vuczogbm9vcGZuLAogICAgICAgIGdldFZhbGlkTWlsbGlzZWNvbmRzOiBub29wZm4sCiAgICAgICAgZ2V0VmlkZW9BZHM6IG5vb3BmbiwKICAgICAgICBnZXRWaWRlb0Fkc0FzeW5jOiBub29wZm4sCiAgICAgICAgZ2V0VmlkZW9BZHNDYWxsYmFjazogbm9vcGZuLAogICAgICAgIGhhbmRsZUNhbGxCYWNrOiBub29wZm4sCiAgICAgICAgaGFzQWRzOiBub29wZm4sCiAgICAgICAgcmVuZGVyQWQ6IG5vb3BmbiwKICAgICAgICBzYXZlQWRzOiBub29wZm4sCiAgICAgICAgc2V0VGFyZ2V0aW5nOiBub29wZm4sCiAgICAgICAgc2V0VGFyZ2V0aW5nRm9yR1BUQXN5bmM6IG5vb3BmbiwKICAgICAgICBzZXRUYXJnZXRpbmdGb3JHUFRTeW5jOiBub29wZm4sCiAgICAgICAgdHJ5R2V0QWRzQXN5bmM6IG5vb3BmbiwKICAgICAgICB1cGRhdGVBZHM6IG5vb3BmbgogICAgfTsKICAgIHcuYW16bmFkcyA9IGFtem5hZHM7CiAgICB3LmFtem5fYWRzID0gdy5hbXpuX2FkcyB8fCBub29wZm47CiAgICB3LmFheF93cml0ZSA9IHcuYWF4X3dyaXRlIHx8IG5vb3BmbjsKICAgIHcuYWF4X3JlbmRlcl9hZCA9IHcuYWF4X3JlbmRlcl9hZCB8fCBub29wZm47Cn0pKCk7Cg==") } { let checked = engine.check_network_urls( diff --git a/tests/matching.rs b/tests/matching.rs index 1a115792..8172a109 100644 --- a/tests/matching.rs +++ b/tests/matching.rs @@ -4,6 +4,7 @@ use adblock::request::Request; use adblock::filters::network::NetworkFilter; use adblock::filters::network::NetworkMatchable; use adblock::engine::Engine; +use adblock::resources::{Resource, ResourceType, MimeType}; use serde::{Deserialize, Serialize}; use serde_json; @@ -30,24 +31,22 @@ fn load_requests() -> Vec { reqs } -fn build_resources_from_filters(filters: &[String]) -> String { - let resources: Vec<_> = filters.iter() +fn build_resources_from_filters(filters: &[String]) -> Vec { + filters.iter() .map(|r| NetworkFilter::parse(&r, true)) .filter_map(Result::ok) .filter(|f| f.is_redirect()) .map(|f| { let redirect = f.redirect.unwrap(); - let rtype = if redirect.ends_with(".gif") { - "image/gif;base64" - } else { - "application/javascript" - }; - format!("{} {}\n{}", redirect, rtype, redirect) + Resource { + name: redirect.to_owned(), + aliases: vec![], + kind: ResourceType::Mime(MimeType::from_extension(&redirect)), + content: redirect, + } }) - .collect(); - - resources.join("\n\n") + .collect() } diff --git a/tests/ublock-coverage.rs b/tests/ublock-coverage.rs index 0fdecd1a..b4ffdd70 100644 --- a/tests/ublock-coverage.rs +++ b/tests/ublock-coverage.rs @@ -8,6 +8,7 @@ use adblock::utils::rules_from_lists; use serde::{Deserialize}; use std::fs::File; +use std::path::Path; use std::io::BufReader; use std::collections::HashMap; @@ -107,13 +108,15 @@ fn check_specific_rules() { let mut engine = Engine::from_rules_debug(&[ String::from("||cdn.taboola.com/libtrc/*/loader.js$script,redirect=noopjs,important,domain=cnet.com"), ]); - let resources_lines = adblock::utils::read_file_lines("data/uBlockOrigin/resources.txt"); - let resources_str = resources_lines.join("\n"); - engine.with_resources(&resources_str); + let resources = adblock::resources::resource_assembler::assemble_web_accessible_resources( + Path::new("data/test/fake-uBO-files/web_accessible_resources"), + Path::new("data/test/fake-uBO-files/redirect-engine.js") + ); + engine.with_resources(&resources); let checked = engine.check_network_urls("http://cdn.taboola.com/libtrc/test/loader.js", "http://cnet.com", "script"); assert_eq!(checked.matched, true); - assert_eq!(checked.redirect, Some("data:application/javascript;base64,KGZ1bmN0aW9uKCkgewoJOwp9KSgpOw==".to_owned())); + assert_eq!(checked.redirect, Some("data:application/javascript;base64,KGZ1bmN0aW9uKCkgewogICAgJ3VzZSBzdHJpY3QnOwp9KSgpOwo=".to_owned())); } }