refactor static network filtering, add support for csp injection

This commit is contained in:
gorhill 2017-05-12 10:35:11 -04:00
parent 8570b63bef
commit 0232382695
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
18 changed files with 1844 additions and 1771 deletions

View file

@ -35,7 +35,13 @@ var chrome = self.chrome;
var manifest = chrome.runtime.getManifest(); var manifest = chrome.runtime.getManifest();
vAPI.chrome = true; vAPI.chrome = true;
vAPI.cantWebsocket = true; vAPI.chromiumVersion = (function(){
var matches = /\bChrom(?:e|ium)\/(\d+)\b/.exec(navigator.userAgent);
return matches !== null ? parseInt(matches[1], 10) : NaN;
})();
vAPI.cantWebsocket =
chrome.webRequest.ResourceType instanceof Object === false ||
chrome.webRequest.ResourceType.WEBSOCKET !== 'websocket';
var noopFunc = function(){}; var noopFunc = function(){};

View file

@ -157,7 +157,7 @@ body.colorBlind #netInspector tr.nooped {
body.colorBlind #netInspector tr.allowed { body.colorBlind #netInspector tr.allowed {
background-color: rgba(255, 194, 57, 0.1) background-color: rgba(255, 194, 57, 0.1)
} }
#netInspector tr.cb, #netInspector tr.rr { #netInspector tr.cosmetic, #netInspector tr.redirect {
background-color: rgba(255, 255, 0, 0.1); background-color: rgba(255, 255, 0, 0.1);
} }
#netInspector tr.maindoc { #netInspector tr.maindoc {

View file

@ -439,40 +439,40 @@ body.advancedUser #firewallContainer > div > span:first-of-type ~ span {
background-color: rgb(192, 160, 0); background-color: rgb(192, 160, 0);
} }
/* Rule cells */ /* Rule cells */
body.advancedUser #firewallContainer > div > span.aRule { body.advancedUser #firewallContainer > div > span.allowRule {
background-color: rgba(0, 160, 0, 0.3); background-color: rgba(0, 160, 0, 0.3);
} }
body.advancedUser #firewallContainer.colorBlind > div > span.aRule { body.advancedUser #firewallContainer.colorBlind > div > span.allowRule {
background-color: rgba(255, 194, 57, 0.4); background-color: rgba(255, 194, 57, 0.4);
} }
body.advancedUser #firewallContainer > div > span.bRule { body.advancedUser #firewallContainer > div > span.blockRule {
background-color: rgba(192, 0, 0, 0.3); background-color: rgba(192, 0, 0, 0.3);
} }
body.advancedUser #firewallContainer.colorBlind > div > span.bRule { body.advancedUser #firewallContainer.colorBlind > div > span.blockRule {
background-color: rgba(0, 19, 110, 0.4); background-color: rgba(0, 19, 110, 0.4);
} }
body.advancedUser #firewallContainer > div > span.nRule { body.advancedUser #firewallContainer > div > span.noopRule {
background-color: rgba(108, 108, 108, 0.3); background-color: rgba(108, 108, 108, 0.3);
} }
body.advancedUser #firewallContainer.colorBlind > div > span.nRule { body.advancedUser #firewallContainer.colorBlind > div > span.noopRule {
background-color: rgba(96, 96, 96, 0.4); background-color: rgba(96, 96, 96, 0.4);
} }
body.advancedUser #firewallContainer > div > span.ownRule { body.advancedUser #firewallContainer > div > span.ownRule {
color: white; color: white;
} }
body.advancedUser #firewallContainer > div > span.aRule.ownRule { body.advancedUser #firewallContainer > div > span.allowRule.ownRule {
background-color: rgba(0, 160, 0, 1); background-color: rgba(0, 160, 0, 1);
} }
body.advancedUser #firewallContainer.colorBlind > div > span.aRule.ownRule { body.advancedUser #firewallContainer.colorBlind > div > span.allowRule.ownRule {
background-color: rgba(255, 194, 57, 1); background-color: rgba(255, 194, 57, 1);
} }
body.advancedUser #firewallContainer > div > span.bRule.ownRule { body.advancedUser #firewallContainer > div > span.blockRule.ownRule {
background-color: rgba(192, 0, 0, 1); background-color: rgba(192, 0, 0, 1);
} }
body.advancedUser #firewallContainer.colorBlind > div > span.bRule.ownRule { body.advancedUser #firewallContainer.colorBlind > div > span.blockRule.ownRule {
background-color: rgba(0, 19, 110, 1); background-color: rgba(0, 19, 110, 1);
} }
body.advancedUser #firewallContainer > div > span.nRule.ownRule { body.advancedUser #firewallContainer > div > span.noopRule.ownRule {
background-color: rgba(108, 108, 108, 1); background-color: rgba(108, 108, 108, 1);
} }

View file

@ -121,8 +121,8 @@ var µBlock = (function() { // jshint ignore:line
// read-only // read-only
systemSettings: { systemSettings: {
compiledMagic: 'fxtcjjhbhyiw', compiledMagic: 'lcmfjiajoqwe',
selfieMagic: 'fxtcjjhbhyiw' selfieMagic: 'lcmfjiajoqwe'
}, },
restoreBackupSettings: { restoreBackupSettings: {
@ -170,4 +170,3 @@ var µBlock = (function() { // jshint ignore:line
})(); })();
/******************************************************************************/ /******************************************************************************/

View file

@ -719,6 +719,8 @@ FilterContainer.prototype.freeze = function() {
this.highHighComplexGenericHideCount !== 0; this.highHighComplexGenericHideCount !== 0;
this.parser.reset(); this.parser.reset();
this.compileSelector.reset();
this.compileProceduralSelector.reset();
this.frozen = true; this.frozen = true;
}; };
@ -746,7 +748,7 @@ FilterContainer.prototype.compileSelector = (function() {
return true; return true;
}; };
return function(raw) { var entryPoint = function(raw) {
if ( isValidCSSSelector(raw) && raw.indexOf('[-abp-properties=') === -1 ) { if ( isValidCSSSelector(raw) && raw.indexOf('[-abp-properties=') === -1 ) {
return raw; return raw;
} }
@ -812,6 +814,11 @@ FilterContainer.prototype.compileSelector = (function() {
µb.logger.writeOne('', 'error', 'Cosmetic filtering invalid filter: ' + raw); µb.logger.writeOne('', 'error', 'Cosmetic filtering invalid filter: ' + raw);
}; };
entryPoint.reset = function() {
};
return entryPoint;
})(); })();
/******************************************************************************/ /******************************************************************************/
@ -927,7 +934,7 @@ FilterContainer.prototype.compileProceduralSelector = (function() {
return { selector: firstOperand, tasks: tasks }; return { selector: firstOperand, tasks: tasks };
}; };
return function(raw) { var entryPoint = function(raw) {
if ( raw === lastProceduralSelector ) { if ( raw === lastProceduralSelector ) {
return lastProceduralSelectorCompiled; return lastProceduralSelectorCompiled;
} }
@ -940,6 +947,13 @@ FilterContainer.prototype.compileProceduralSelector = (function() {
lastProceduralSelectorCompiled = compiled; lastProceduralSelectorCompiled = compiled;
return compiled; return compiled;
}; };
entryPoint.reset = function() {
lastProceduralSelector = '';
lastProceduralSelectorCompiled = undefined;
};
return entryPoint;
})(); })();
/******************************************************************************/ /******************************************************************************/
@ -1038,12 +1052,12 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, out) {
// is valid, the regex took care of this. Most generic selector falls // is valid, the regex took care of this. Most generic selector falls
// into that category. // into that category.
if ( key === selector ) { if ( key === selector ) {
out.push('c\vlg\v' + key); out.push(4, 'lg\v' + key);
return; return;
} }
// Composite CSS rule. // Composite CSS rule.
if ( this.compileSelector(selector) ) { if ( this.compileSelector(selector) ) {
out.push('c\vlg+\v' + key + '\v' + selector); out.push(4, 'lg+\v' + key + '\v' + selector);
} }
return; return;
} }
@ -1054,21 +1068,21 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, out) {
// ["title"] and ["alt"] will go in high-low generic bin. // ["title"] and ["alt"] will go in high-low generic bin.
if ( this.reHighLow.test(selector) ) { if ( this.reHighLow.test(selector) ) {
out.push('c\vhlg0\v' + selector); out.push(4, 'hlg0\v' + selector);
return; return;
} }
// [href^="..."] will go in high-medium generic bin. // [href^="..."] will go in high-medium generic bin.
matches = this.reHighMedium.exec(selector); matches = this.reHighMedium.exec(selector);
if ( matches && matches.length === 2 ) { if ( matches && matches.length === 2 ) {
out.push('c\vhmg0\v' + matches[1] + '\v' + selector); out.push(4, 'hmg0\v' + matches[1] + '\v' + selector);
return; return;
} }
// script:contains(...) // script:contains(...)
// script:inject(...) // script:inject(...)
if ( this.reScriptSelector.test(selector) ) { if ( this.reScriptSelector.test(selector) ) {
out.push('c\vjs\v0\v\v' + selector); out.push(4, 'js\v0\v\v' + selector);
return; return;
} }
@ -1077,16 +1091,16 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, out) {
// as a low generic cosmetic filter. // as a low generic cosmetic filter.
matches = this.rePlainSelectorEx.exec(selector); matches = this.rePlainSelectorEx.exec(selector);
if ( matches && matches.length === 2 ) { if ( matches && matches.length === 2 ) {
out.push('c\vlg+\v' + matches[1] + '\v' + selector); out.push(4, 'lg+\v' + matches[1] + '\v' + selector);
return; return;
} }
// All else: high-high generics. // All else: high-high generics.
// Distinguish simple vs complex selectors. // Distinguish simple vs complex selectors.
if ( selector.indexOf(' ') === -1 ) { if ( selector.indexOf(' ') === -1 ) {
out.push('c\vhhsg0\v' + selector); out.push(4, 'hhsg0\v' + selector);
} else { } else {
out.push('c\vhhcg0\v' + selector); out.push(4, 'hhcg0\v' + selector);
} }
}; };
@ -1098,7 +1112,7 @@ FilterContainer.prototype.compileGenericUnhideSelector = function(parsed, out) {
// script:contains(...) // script:contains(...)
// script:inject(...) // script:inject(...)
if ( this.reScriptSelector.test(selector) ) { if ( this.reScriptSelector.test(selector) ) {
out.push('c\vjs\v1\v\v' + selector); out.push(4, 'js\v1\v\v' + selector);
return; return;
} }
@ -1109,7 +1123,7 @@ FilterContainer.prototype.compileGenericUnhideSelector = function(parsed, out) {
// https://github.com/chrisaljoudi/uBlock/issues/497 // https://github.com/chrisaljoudi/uBlock/issues/497
// All generic exception filters are put in the same bucket: they are // All generic exception filters are put in the same bucket: they are
// expected to be very rare. // expected to be very rare.
out.push('c\vg1\v' + compiled); out.push(4, 'g1\v' + compiled);
}; };
/******************************************************************************/ /******************************************************************************/
@ -1138,7 +1152,7 @@ FilterContainer.prototype.compileHostnameSelector = function(hostname, parsed, o
if ( unhide ) { if ( unhide ) {
hash = '!' + hash; hash = '!' + hash;
} }
out.push('c\vjs\v' + hash + '\v' + hostname + '\v' + selector); out.push(4, 'js\v' + hash + '\v' + hostname + '\v' + selector);
return; return;
} }
@ -1156,12 +1170,16 @@ FilterContainer.prototype.compileHostnameSelector = function(hostname, parsed, o
hash = '!' + hash; hash = '!' + hash;
} }
out.push('c\vh\v' + hash + '\v' + hostname + '\v' + compiled); out.push(4, 'h\v' + hash + '\v' + hostname + '\v' + compiled);
}; };
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.fromCompiledContent = function(lineIter, skipGenericCosmetic, skipCosmetic) { FilterContainer.prototype.fromCompiledContent = function(
lineIter,
skipGenericCosmetic,
skipCosmetic
) {
if ( skipCosmetic ) { if ( skipCosmetic ) {
this.skipCompiledContent(lineIter); this.skipCompiledContent(lineIter);
return; return;
@ -1171,15 +1189,19 @@ FilterContainer.prototype.fromCompiledContent = function(lineIter, skipGenericCo
return; return;
} }
var line, field0, field1, field2, field3, filter, bucket, var lineBits, line, field0, field1, field2, field3, filter, bucket,
aCharCode = 'a'.charCodeAt(0),
fieldIter = new µb.FieldIterator('\v'); fieldIter = new µb.FieldIterator('\v');
while ( lineIter.eot() === false ) { while ( lineIter.eot() === false ) {
line = lineIter.next(); lineBits = lineIter.charCodeAt(0) - aCharCode;
if ( line.charCodeAt(0) !== 0x63 /* 'c' */ ) { if ( (lineBits & 0x04) === 0 ) {
lineIter.rewind();
return; return;
} }
line = lineIter.next(1);
if ( (lineBits & 0x02) !== 0 ) {
line = decodeURIComponent(line);
}
this.acceptedCount += 1; this.acceptedCount += 1;
if ( this.duplicateBuster.has(line) ) { if ( this.duplicateBuster.has(line) ) {
@ -1188,8 +1210,7 @@ FilterContainer.prototype.fromCompiledContent = function(lineIter, skipGenericCo
} }
this.duplicateBuster.add(line); this.duplicateBuster.add(line);
fieldIter.first(line); field0 = fieldIter.first(line);
field0 = fieldIter.next();
field1 = fieldIter.next(); field1 = fieldIter.next();
// h [\v] hash [\v] example.com [\v] .promoted-tweet // h [\v] hash [\v] example.com [\v] .promoted-tweet
@ -1298,15 +1319,19 @@ FilterContainer.prototype.fromCompiledContent = function(lineIter, skipGenericCo
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.skipGenericCompiledContent = function(lineIter) { FilterContainer.prototype.skipGenericCompiledContent = function(lineIter) {
var line, field0, field1, field2, field3, filter, bucket, var lineBits, line, field0, field1, field2, field3, filter, bucket,
aCharCode = 'a'.charCodeAt(0),
fieldIter = new µb.FieldIterator('\v'); fieldIter = new µb.FieldIterator('\v');
while ( lineIter.eot() === false ) { while ( lineIter.eot() === false ) {
line = lineIter.next(); lineBits = lineIter.charCodeAt(0) - aCharCode;
if ( line.charCodeAt(0) !== 0x63 /* 'c' */ ) { if ( (lineBits & 0x04) === 0 ) {
lineIter.rewind();
return; return;
} }
line = lineIter.next(1);
if ( (lineBits & 0x02) !== 0 ) {
line = decodeURIComponent(line);
}
this.acceptedCount += 1; this.acceptedCount += 1;
if ( this.duplicateBuster.has(line) ) { if ( this.duplicateBuster.has(line) ) {
@ -1361,15 +1386,19 @@ FilterContainer.prototype.skipGenericCompiledContent = function(lineIter) {
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.skipCompiledContent = function(lineIter) { FilterContainer.prototype.skipCompiledContent = function(lineIter) {
var line, field0, field1, field2, field3, var lineBits, line, field0, field1, field2, field3,
aCharCode = 'a'.charCodeAt(0),
fieldIter = new µb.FieldIterator('\v'); fieldIter = new µb.FieldIterator('\v');
while ( lineIter.eot() === false ) { while ( lineIter.eot() === false ) {
line = lineIter.next(); lineBits = lineIter.charCodeAt(0) - aCharCode;
if ( line.charCodeAt(0) !== 0x63 /* 'c' */ ) { if ( (lineBits & 0x04) === 0 ) {
lineIter.rewind();
return; return;
} }
line = lineIter.next(1);
if ( (lineBits & 0x02) !== 0 ) {
line = decodeURIComponent(line);
}
this.acceptedCount += 1; this.acceptedCount += 1;
if ( this.duplicateBuster.has(line) ) { if ( this.duplicateBuster.has(line) ) {

View file

@ -337,7 +337,7 @@ Matrix.prototype.evaluateCellZY = function(srcHostname, desHostname, type) {
var d = desHostname; var d = desHostname;
if ( d === '' ) { if ( d === '' ) {
this.r = 0; this.r = 0;
return this; return 0;
} }
// Prepare broadening handlers -- depends on whether we are dealing with // Prepare broadening handlers -- depends on whether we are dealing with
@ -350,7 +350,9 @@ Matrix.prototype.evaluateCellZY = function(srcHostname, desHostname, type) {
// Specific-destination, any party, any type // Specific-destination, any party, any type
while ( d !== '*' ) { while ( d !== '*' ) {
this.y = d; this.y = d;
if ( this.evaluateCellZ(srcHostname, d, '*', broadenSource) !== 0 ) { return this; } if ( this.evaluateCellZ(srcHostname, d, '*', broadenSource) !== 0 ) {
return this.r;
}
d = broadenDestination(d); d = broadenDestination(d);
} }
@ -363,27 +365,39 @@ Matrix.prototype.evaluateCellZY = function(srcHostname, desHostname, type) {
if ( thirdParty ) { if ( thirdParty ) {
// 3rd-party, specific type // 3rd-party, specific type
if ( type === 'script' ) { if ( type === 'script' ) {
if ( this.evaluateCellZ(srcHostname, '*', '3p-script', broadenSource) !== 0 ) { return this; } if ( this.evaluateCellZ(srcHostname, '*', '3p-script', broadenSource) !== 0 ) {
return this.r;
}
} else if ( type === 'sub_frame' ) { } else if ( type === 'sub_frame' ) {
if ( this.evaluateCellZ(srcHostname, '*', '3p-frame', broadenSource) !== 0 ) { return this; } if ( this.evaluateCellZ(srcHostname, '*', '3p-frame', broadenSource) !== 0 ) {
return this.r;
}
} }
// 3rd-party, any type // 3rd-party, any type
if ( this.evaluateCellZ(srcHostname, '*', '3p', broadenSource) !== 0 ) { return this; } if ( this.evaluateCellZ(srcHostname, '*', '3p', broadenSource) !== 0 ) {
return this.r;
}
} else if ( type === 'script' ) { } else if ( type === 'script' ) {
// 1st party, specific type // 1st party, specific type
if ( this.evaluateCellZ(srcHostname, '*', '1p-script', broadenSource) !== 0 ) { return this; } if ( this.evaluateCellZ(srcHostname, '*', '1p-script', broadenSource) !== 0 ) {
return this.r;
}
} }
// Any destination, any party, specific type // Any destination, any party, specific type
if ( supportedDynamicTypes.hasOwnProperty(type) ) { if ( supportedDynamicTypes.hasOwnProperty(type) ) {
if ( this.evaluateCellZ(srcHostname, '*', type, broadenSource) !== 0 ) { return this; } if ( this.evaluateCellZ(srcHostname, '*', type, broadenSource) !== 0 ) {
return this.r;
}
} }
// Any destination, any party, any type // Any destination, any party, any type
if ( this.evaluateCellZ(srcHostname, '*', '*', broadenSource) !== 0 ) { return this; } if ( this.evaluateCellZ(srcHostname, '*', '*', broadenSource) !== 0 ) {
return this.r;
}
this.type = ''; this.type = '';
return this; return 0;
}; };
// http://youtu.be/gSGk1bQ9rcU?t=25m6s // http://youtu.be/gSGk1bQ9rcU?t=25m6s
@ -391,7 +405,7 @@ Matrix.prototype.evaluateCellZY = function(srcHostname, desHostname, type) {
/******************************************************************************/ /******************************************************************************/
Matrix.prototype.mustAllowCellZY = function(srcHostname, desHostname, type) { Matrix.prototype.mustAllowCellZY = function(srcHostname, desHostname, type) {
return this.evaluateCellZY(srcHostname, desHostname, type).r === 2; return this.evaluateCellZY(srcHostname, desHostname, type) === 2;
}; };
/******************************************************************************/ /******************************************************************************/
@ -414,23 +428,44 @@ Matrix.prototype.mustAbort = function() {
/******************************************************************************/ /******************************************************************************/
Matrix.prototype.toFilterString = function() { Matrix.prototype.lookupRuleData = function(src, des, type) {
if ( this.r === 0 || this.type === '' ) { var r = this.evaluateCellZY(src, des, type);
return ''; if ( r === 0 ) {
return null;
} }
var body = this.z + ' ' + this.y + ' ' + this.type; return {
if ( this.r === 1 ) { src: this.z,
return 'db:' + body + ' block'; des: this.y,
} type: this.type,
if ( this.r === 2 ) { action: r === 1 ? 'block' : (r === 2 ? 'allow' : 'noop')
return 'da:' + body + ' allow'; };
}
/* this.r === 3 */
return 'dn:' + body + ' noop';
}; };
/******************************************************************************/ /******************************************************************************/
Matrix.prototype.toLogData = function() {
if ( this.r === 0 || this.type === '' ) {
return;
}
var logData = {
source: 'dynamicHost',
result: this.r,
raw: this.z + ' ' +
this.y + ' ' +
this.type + ' ' +
this.intToActionMap.get(this.r)
};
return logData;
};
Matrix.prototype.intToActionMap = new Map([
[ 1, ' block' ],
[ 2, ' allow' ],
[ 3, ' noop' ]
]);
/******************************************************************************/
Matrix.prototype.srcHostnameFromRule = function(rule) { Matrix.prototype.srcHostnameFromRule = function(rule) {
return rule.slice(0, rule.indexOf(' ')); return rule.slice(0, rule.indexOf(' '));
}; };

View file

@ -249,10 +249,12 @@ HnSwitches.prototype.evaluateZ = function(switchName, hostname) {
/******************************************************************************/ /******************************************************************************/
HnSwitches.prototype.toResultString = function() { HnSwitches.prototype.toLogData = function() {
return this.r !== 1 ? return {
'' : source: 'switch',
'ub:' + this.n + ': ' + this.z + ' true'; result: this.r,
raw: this.n + ': ' + this.z + ' true'
};
}; };
/******************************************************************************/ /******************************************************************************/

View file

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
uBlock Origin - a browser extension to block requests. uBlock Origin - a browser extension to block requests.
Copyright (C) 2015-2016 Raymond Hill Copyright (C) 2015-2017 Raymond Hill
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -92,9 +92,11 @@ var uglyRequestTypes = {
}; };
var staticFilterTypes = { var staticFilterTypes = {
'beacon': 'other',
'doc': 'document', 'doc': 'document',
'css': 'stylesheet', 'css': 'stylesheet',
'frame': 'subdocument', 'frame': 'subdocument',
'ping': 'other',
'xhr': 'xmlhttprequest' 'xhr': 'xmlhttprequest'
}; };
@ -146,212 +148,6 @@ var renderedURLTemplate = document.querySelector('#renderedURLTemplate > span');
/******************************************************************************/ /******************************************************************************/
// Pretty much same logic as found in:
// µBlock.staticNetFilteringEngine.filterStringFromCompiled
// µBlock.staticNetFilteringEngine.filterRegexFromCompiled
var filterDecompiler = (function() {
var typeValToTypeName = {
1: 'stylesheet',
2: 'image',
3: 'object',
4: 'script',
5: 'xmlhttprequest',
6: 'subdocument',
7: 'font',
8: 'media',
9: 'websocket',
10: 'other',
11: 'popunder',
12: 'document',
13: 'generichide',
14: 'inline-script',
15: 'popup'
};
var toString = function(compiled) {
var opts = [];
var vfields = compiled.split('\v');
var filter = '';
var bits = parseInt(vfields[0], 16) | 0;
if ( bits & 0x01 ) {
filter += '@@';
}
var fid = vfields[1] === '.' ? '.' : vfields[2];
var tfields = fid !== '.' ? vfields[3].split('\t') : [];
var tfield0 = tfields[0];
// Filter options
// Importance
if ( bits & 0x02 ) {
opts.push('important');
}
// Party
if ( bits & 0x08 ) {
opts.push('third-party');
} else if ( bits & 0x04 ) {
opts.push('first-party');
}
// Type
var typeVal = bits >>> 4 & 0x0F;
if ( typeVal ) {
opts.push(typeValToTypeName[typeVal]);
}
switch ( fid ) {
case '.':
filter += '||' + vfields[2] + '^';
break;
case 'a':
case 'ah':
case '0a':
case '0ah':
case '1a':
case '1ah':
case '_':
case '_h':
filter += tfield0;
// If the filter resemble a regex, add a trailing `*` as is
// customary to prevent ambiguity in logger.
if ( tfield0.charAt(0) === '/' && tfield0.slice(-1) === '/' ) {
filter += '*';
}
break;
case '|a':
case '|ah':
filter += '|' + tfield0;
break;
case 'a|':
case 'a|h':
filter += tfield0 + '|';
break;
case '||a':
case '||ah':
filter += '||' + tfield0;
break;
case '||_':
case '||_h':
filter += '||' + tfield0;
if ( tfields[1] === '1' ) { // left-anchored?
filter += '|';
}
break;
case '//':
case '//h':
filter += '/' + tfield0 + '/';
break;
// https://github.com/gorhill/uBlock/issues/465
// Unexpected: return the raw compiled representation instead of a
// blank string.
default:
return compiled.replace(/\s+/g, ' ');
}
// Domain option?
switch ( fid ) {
case '0ah':
case '1ah':
case '|ah':
case 'a|h':
case '||ah':
case '//h':
opts.push('domain=' + tfields[1]);
break;
case 'ah':
case '_h':
case '||_h':
opts.push('domain=' + tfields[2]);
break;
default:
break;
}
if ( opts.length !== 0 ) {
filter += '$' + opts.join(',');
}
return filter;
};
var reEscapeHostname = /[.[\]]/g;
var reEscape = /[.+?${}()|[\]\\]/g;
var reWildcards = /\*+/g;
var reSeparator = /\^/g;
var toRegex = function(compiled) {
var vfields = compiled.split('\v');
var fid = vfields[1] === '.' ? '.' : vfields[2];
var tfields = fid !== '.' ? vfields[3].split('\t') : [];
var reStr;
switch ( fid ) {
case '.':
reStr = vfields[2].replace(reEscapeHostname, '\\$&') +
'(?:[^%.0-9a-z_-]|$)';
break;
case 'a':
case 'ah':
case '0a':
case '0ah':
case '1a':
case '1ah':
case '|a':
case '|ah':
case 'a|':
case 'a|h':
case '_':
case '_h':
reStr = tfields[0]
.replace(reEscape, '\\$&')
.replace(reWildcards, '.*?')
.replace(reSeparator, '(?:[^%.0-9a-z_-]|$)');
break;
case '||a':
case '||ah':
case '||_':
case '||_h':
reStr = '';
if ( tfields[0].charCodeAt(0) === 0x2A ) {
reStr = '[0-9a-z.-]*?';
tfields[0] = tfields[0].slice(1);
}
reStr += tfields[0]
.replace(reEscape, '\\$&')
.replace(reWildcards, '.*?')
.replace(reSeparator, '(?:[^%.0-9a-z_-]|$)');
break;
case '//':
case '//h':
reStr = tfields[0];
break;
default:
break;
}
// Anchored?
var s = fid.slice(0, 2);
if ( s === '|a' ) {
reStr = '^' + reStr;
} else if ( s === 'a|' ) {
reStr += '$';
}
if ( reStr === undefined) {
return null;
}
return new RegExp(reStr, 'gi');
};
return {
toString: toString,
toRegex: toRegex
};
})();
/******************************************************************************/
var createCellAt = function(tr, index) { var createCellAt = function(tr, index) {
var td = tr.cells[index]; var td = tr.cells[index];
var mustAppend = !td; var mustAppend = !td;
@ -442,7 +238,7 @@ var createGap = function(tabId, url) {
var renderNetLogEntry = function(tr, entry) { var renderNetLogEntry = function(tr, entry) {
var trcl = tr.classList; var trcl = tr.classList;
var filter = entry.d0; var filter = entry.d0 || undefined;
var type = entry.d1; var type = entry.d1;
var url = entry.d2; var url = entry.d2;
var td; var td;
@ -463,52 +259,50 @@ var renderNetLogEntry = function(tr, entry) {
tr.setAttribute('data-hn-frame', entry.d4); tr.setAttribute('data-hn-frame', entry.d4);
} }
var filterCat = filter.slice(0, 3); var filteringType;
if ( filterCat.charAt(2) === ':' ) { if ( filter !== undefined && typeof filter.source === 'string' ) {
trcl.add(filterCat.slice(0, 2)); filteringType = filter.source;
trcl.add(filteringType);
} }
var filteringType = filterCat.charAt(0);
td = tr.cells[2]; td = tr.cells[2];
if ( filter !== '' ) { if ( filter !== undefined ) {
filter = filter.slice(3); if ( filteringType === 'static' ) {
if ( filteringType === 's' ) { td.textContent = filter.raw;
td.textContent = filterDecompiler.toString(filter);
trcl.add('canLookup'); trcl.add('canLookup');
tr.setAttribute('data-filter', filter); tr.setAttribute('data-filter', filter.compiled);
} else if ( filteringType === 'c' ) { } else if ( filteringType === 'cosmetic' ) {
td.textContent = filter; td.textContent = filter.raw;
trcl.add('canLookup'); trcl.add('canLookup');
} else { } else {
td.textContent = filter; td.textContent = filter.raw;
} }
} }
td = tr.cells[3]; td = tr.cells[3];
var filteringOp = filterCat.charAt(1); if ( filter !== undefined ) {
if ( filteringOp === 'b' ) { if ( filter.result === 1 ) {
trcl.add('blocked'); trcl.add('blocked');
td.textContent = '--'; td.textContent = '--';
} else if ( filteringOp === 'a' ) { } else if ( filter.result === 2 ) {
trcl.add('allowed'); trcl.add('allowed');
td.textContent = '++'; td.textContent = '++';
} else if ( filteringOp === 'n' ) { } else if ( filter.result === 3 ) {
trcl.add('nooped'); trcl.add('nooped');
td.textContent = '**'; td.textContent = '**';
} else if ( filteringOp === 'r' ) { } else if ( filter.source === 'redirect' ) {
trcl.add('redirected'); trcl.add('redirect');
td.textContent = '<<'; td.textContent = '<<';
} else { }
td.textContent = '';
} }
tr.cells[4].textContent = (prettyRequestTypes[type] || type); tr.cells[4].textContent = (prettyRequestTypes[type] || type);
var re = null; var re = null;
if ( filteringType === 's' ) { if ( filteringType === 'static' ) {
re = filterDecompiler.toRegex(filter); re = new RegExp(filter.regex, 'gi');
} else if ( filteringType === 'l' ) { } else if ( filteringType === 'dynamicUrl' ) {
re = regexFromURLFilteringResult(filter); re = regexFromURLFilteringResult(filter.rule.join(' '));
} }
tr.cells[5].appendChild(nodeFromURL(url, re)); tr.cells[5].appendChild(nodeFromURL(url, re));
}; };

View file

@ -258,28 +258,28 @@ var getHostnameDict = function(hostnameToCountMap) {
var getFirewallRules = function(srcHostname, desHostnames) { var getFirewallRules = function(srcHostname, desHostnames) {
var r = {}; var r = {};
var df = µb.sessionFirewall; var df = µb.sessionFirewall;
r['/ * *'] = df.evaluateCellZY('*', '*', '*').toFilterString(); r['/ * *'] = df.lookupRuleData('*', '*', '*');
r['/ * image'] = df.evaluateCellZY('*', '*', 'image').toFilterString(); r['/ * image'] = df.lookupRuleData('*', '*', 'image');
r['/ * 3p'] = df.evaluateCellZY('*', '*', '3p').toFilterString(); r['/ * 3p'] = df.lookupRuleData('*', '*', '3p');
r['/ * inline-script'] = df.evaluateCellZY('*', '*', 'inline-script').toFilterString(); r['/ * inline-script'] = df.lookupRuleData('*', '*', 'inline-script');
r['/ * 1p-script'] = df.evaluateCellZY('*', '*', '1p-script').toFilterString(); r['/ * 1p-script'] = df.lookupRuleData('*', '*', '1p-script');
r['/ * 3p-script'] = df.evaluateCellZY('*', '*', '3p-script').toFilterString(); r['/ * 3p-script'] = df.lookupRuleData('*', '*', '3p-script');
r['/ * 3p-frame'] = df.evaluateCellZY('*', '*', '3p-frame').toFilterString(); r['/ * 3p-frame'] = df.lookupRuleData('*', '*', '3p-frame');
if ( typeof srcHostname !== 'string' ) { if ( typeof srcHostname !== 'string' ) {
return r; return r;
} }
r['. * *'] = df.evaluateCellZY(srcHostname, '*', '*').toFilterString(); r['. * *'] = df.lookupRuleData(srcHostname, '*', '*');
r['. * image'] = df.evaluateCellZY(srcHostname, '*', 'image').toFilterString(); r['. * image'] = df.lookupRuleData(srcHostname, '*', 'image');
r['. * 3p'] = df.evaluateCellZY(srcHostname, '*', '3p').toFilterString(); r['. * 3p'] = df.lookupRuleData(srcHostname, '*', '3p');
r['. * inline-script'] = df.evaluateCellZY(srcHostname, '*', 'inline-script').toFilterString(); r['. * inline-script'] = df.lookupRuleData(srcHostname, '*', 'inline-script');
r['. * 1p-script'] = df.evaluateCellZY(srcHostname, '*', '1p-script').toFilterString(); r['. * 1p-script'] = df.lookupRuleData(srcHostname, '*', '1p-script');
r['. * 3p-script'] = df.evaluateCellZY(srcHostname, '*', '3p-script').toFilterString(); r['. * 3p-script'] = df.lookupRuleData(srcHostname, '*', '3p-script');
r['. * 3p-frame'] = df.evaluateCellZY(srcHostname, '*', '3p-frame').toFilterString(); r['. * 3p-frame'] = df.lookupRuleData(srcHostname, '*', '3p-frame');
for ( var desHostname in desHostnames ) { for ( var desHostname in desHostnames ) {
r['/ ' + desHostname + ' *'] = df.evaluateCellZY('*', desHostname, '*').toFilterString(); r['/ ' + desHostname + ' *'] = df.lookupRuleData('*', desHostname, '*');
r['. ' + desHostname + ' *'] = df.evaluateCellZY(srcHostname, desHostname, '*').toFilterString(); r['. ' + desHostname + ' *'] = df.lookupRuleData(srcHostname, desHostname, '*');
} }
return r; return r;
}; };
@ -1239,7 +1239,7 @@ var logCosmeticFilters = function(tabId, details) {
µb.logger.writeOne( µb.logger.writeOne(
tabId, tabId,
'cosmetic', 'cosmetic',
'cb:##' + selectors[i], { source: 'cosmetic', raw: '##' + selectors[i] },
'dom', 'dom',
details.frameURL, details.frameURL,
null, null,

View file

@ -48,16 +48,17 @@ var netFilteringResultCacheEntryJunkyardMax = 200;
/******************************************************************************/ /******************************************************************************/
var NetFilteringResultCacheEntry = function(result, type) { var NetFilteringResultCacheEntry = function(result, type, logData) {
this.init(result, type); this.init(result, type, logData);
}; };
/******************************************************************************/ /******************************************************************************/
NetFilteringResultCacheEntry.prototype.init = function(result, type) { NetFilteringResultCacheEntry.prototype.init = function(result, type, logData) {
this.result = result; this.result = result;
this.type = type; this.type = type;
this.time = Date.now(); this.time = Date.now();
this.logData = logData;
return this; return this;
}; };
@ -65,6 +66,7 @@ NetFilteringResultCacheEntry.prototype.init = function(result, type) {
NetFilteringResultCacheEntry.prototype.dispose = function() { NetFilteringResultCacheEntry.prototype.dispose = function() {
this.result = this.type = ''; this.result = this.type = '';
this.logData = undefined;
if ( netFilteringResultCacheEntryJunkyard.length < netFilteringResultCacheEntryJunkyardMax ) { if ( netFilteringResultCacheEntryJunkyard.length < netFilteringResultCacheEntryJunkyardMax ) {
netFilteringResultCacheEntryJunkyard.push(this); netFilteringResultCacheEntryJunkyard.push(this);
} }
@ -72,11 +74,11 @@ NetFilteringResultCacheEntry.prototype.dispose = function() {
/******************************************************************************/ /******************************************************************************/
NetFilteringResultCacheEntry.factory = function(result, type) { NetFilteringResultCacheEntry.factory = function(result, type, logData) {
if ( netFilteringResultCacheEntryJunkyard.length ) { if ( netFilteringResultCacheEntryJunkyard.length ) {
return netFilteringResultCacheEntryJunkyard.pop().init(result, type); return netFilteringResultCacheEntryJunkyard.pop().init(result, type, logData);
} }
return new NetFilteringResultCacheEntry(result, type); return new NetFilteringResultCacheEntry(result, type, logData);
}; };
/******************************************************************************/ /******************************************************************************/
@ -127,7 +129,7 @@ NetFilteringResultCache.prototype.dispose = function() {
/******************************************************************************/ /******************************************************************************/
NetFilteringResultCache.prototype.add = function(context, result) { NetFilteringResultCache.prototype.add = function(context, result, logData) {
var url = context.requestURL, var url = context.requestURL,
type = context.requestType, type = context.requestType,
key = type + ' ' + url, key = type + ' ' + url,
@ -136,9 +138,10 @@ NetFilteringResultCache.prototype.add = function(context, result) {
entry.result = result; entry.result = result;
entry.type = type; entry.type = type;
entry.time = Date.now(); entry.time = Date.now();
entry.logData = logData;
return; return;
} }
this.urls[key] = NetFilteringResultCacheEntry.factory(result, type); this.urls[key] = NetFilteringResultCacheEntry.factory(result, type, logData);
if ( this.count === 0 ) { if ( this.count === 0 ) {
this.pruneAsync(); this.pruneAsync();
} }
@ -305,6 +308,7 @@ PageStore.prototype.init = function(tabId) {
this.hostnameToCountMap = new Map(); this.hostnameToCountMap = new Map();
this.contentLastModified = 0; this.contentLastModified = 0;
this.frames = Object.create(null); this.frames = Object.create(null);
this.logData = undefined;
this.perLoadBlockedRequestCount = 0; this.perLoadBlockedRequestCount = 0;
this.perLoadAllowedRequestCount = 0; this.perLoadAllowedRequestCount = 0;
this.hiddenElementCount = ''; // Empty string means "unknown" this.hiddenElementCount = ''; // Empty string means "unknown"
@ -320,7 +324,7 @@ PageStore.prototype.init = function(tabId) {
µb.logger.writeOne( µb.logger.writeOne(
tabId, tabId,
'cosmetic', 'cosmetic',
µb.hnSwitches.toResultString(), µb.hnSwitches.toLogData(),
'dom', 'dom',
tabContext.rawURL, tabContext.rawURL,
this.tabHostname, this.tabHostname,
@ -336,12 +340,12 @@ PageStore.prototype.init = function(tabId) {
tabContext.normalURL, tabContext.normalURL,
'generichide' 'generichide'
); );
this.noGenericCosmeticFiltering = result === false; this.noGenericCosmeticFiltering = result === 2;
if ( result !== undefined && µb.logger.isEnabled() ) { if ( result !== 0 && µb.logger.isEnabled() ) {
µb.logger.writeOne( µb.logger.writeOne(
tabId, tabId,
'net', 'net',
µb.staticNetFilteringEngine.toResultString(true), µb.staticNetFilteringEngine.toLogData(),
'generichide', 'generichide',
tabContext.rawURL, tabContext.rawURL,
this.tabHostname, this.tabHostname,
@ -525,7 +529,7 @@ PageStore.prototype.journalAddRequest = function(hostname, result) {
if ( hostname === '' ) { return; } if ( hostname === '' ) { return; }
this.journal.push( this.journal.push(
hostname, hostname,
result.charCodeAt(1) === 0x62 /* 'b' */ ? 0x00000001 : 0x00010000 result === 1 ? 0x00000001 : 0x00010000
); );
if ( this.journalTimer === null ) { if ( this.journalTimer === null ) {
this.journalTimer = vAPI.setTimeout(this.journalProcess.bind(this, true), 1000); this.journalTimer = vAPI.setTimeout(this.journalProcess.bind(this, true), 1000);
@ -604,6 +608,8 @@ PageStore.prototype.journalProcess = function(fromTimer) {
/******************************************************************************/ /******************************************************************************/
PageStore.prototype.filterRequest = function(context) { PageStore.prototype.filterRequest = function(context) {
this.logData = undefined;
var requestType = context.requestType; var requestType = context.requestType;
// We want to short-term cache filtering results of collapsible types, // We want to short-term cache filtering results of collapsible types,
@ -614,35 +620,39 @@ PageStore.prototype.filterRequest = function(context) {
} }
if ( this.getNetFilteringSwitch() === false ) { if ( this.getNetFilteringSwitch() === false ) {
this.netFilteringCache.add(context, ''); this.netFilteringCache.add(context, 0);
return ''; return 0;
} }
var entry = this.netFilteringCache.lookup(context); var entry = this.netFilteringCache.lookup(context);
if ( entry !== undefined ) { if ( entry !== undefined ) {
this.logData = entry.logData;
return entry.result; return entry.result;
} }
// Dynamic URL filtering. // Dynamic URL filtering.
µb.sessionURLFiltering.evaluateZ(context.rootHostname, context.requestURL, requestType); var result = µb.sessionURLFiltering.evaluateZ(context.rootHostname, context.requestURL, requestType);
var result = µb.sessionURLFiltering.toFilterString(); if ( result !== 0 && µb.logger.isEnabled() ) {
this.logData = µb.sessionURLFiltering.toLogData();
}
// Dynamic hostname/type filtering. // Dynamic hostname/type filtering.
if ( result === '' && µb.userSettings.advancedUserEnabled ) { if ( result === 0 && µb.userSettings.advancedUserEnabled ) {
µb.sessionFirewall.evaluateCellZY( context.rootHostname, context.requestHostname, requestType); result = µb.sessionFirewall.evaluateCellZY( context.rootHostname, context.requestHostname, requestType);
if ( µb.sessionFirewall.mustBlockOrAllow() ) { if ( result !== 0 && µb.logger.isEnabled() ) {
result = µb.sessionFirewall.toFilterString(); this.logData = µb.sessionFirewall.toLogData();
} }
} }
// Static filtering: lowest filtering precedence. // Static filtering: lowest filtering precedence.
if ( result === '' || result.charCodeAt(1) === 110 /* 'n' */ ) { if ( result === 0 || result === 3 ) {
if ( µb.staticNetFilteringEngine.matchString(context) !== undefined ) { result = µb.staticNetFilteringEngine.matchString(context);
result = µb.staticNetFilteringEngine.toResultString(µb.logger.isEnabled()); if ( result !== 0 && µb.logger.isEnabled() ) {
this.logData = µb.staticNetFilteringEngine.toLogData();
} }
} }
this.netFilteringCache.add(context, result); this.netFilteringCache.add(context, result, this.logData);
return result; return result;
}; };
@ -652,14 +662,16 @@ PageStore.prototype.filterRequest = function(context) {
// The caller is responsible to check whether filtering is enabled or not. // The caller is responsible to check whether filtering is enabled or not.
PageStore.prototype.filterLargeMediaElement = function(size) { PageStore.prototype.filterLargeMediaElement = function(size) {
this.logData = undefined;
if ( Date.now() < this.allowLargeMediaElementsUntil ) { if ( Date.now() < this.allowLargeMediaElementsUntil ) {
return; return 0;
} }
if ( µb.hnSwitches.evaluateZ('no-large-media', this.tabHostname) !== true ) { if ( µb.hnSwitches.evaluateZ('no-large-media', this.tabHostname) !== true ) {
return; return 0;
} }
if ( (size >>> 10) < µb.userSettings.largeMediaSize ) { if ( (size >>> 10) < µb.userSettings.largeMediaSize ) {
return; return 0;
} }
this.largeMediaCount += 1; this.largeMediaCount += 1;
@ -670,48 +682,66 @@ PageStore.prototype.filterLargeMediaElement = function(size) {
); );
} }
return µb.hnSwitches.toResultString(); if ( µb.logger.isEnabled() ) {
this.logData = µb.hnSwitches.toLogData();
}
return 1;
}; };
/******************************************************************************/ /******************************************************************************/
PageStore.prototype.filterRequestNoCache = function(context) { PageStore.prototype.filterRequestNoCache = function(context) {
this.logData = undefined;
if ( this.getNetFilteringSwitch() === false ) { if ( this.getNetFilteringSwitch() === false ) {
return ''; return 0;
} }
var requestType = context.requestType, var requestType = context.requestType;
result = '';
if ( requestType === 'csp_report' ) { if ( requestType === 'csp_report' ) {
if ( this.internalRedirectionCount !== 0 ) { if ( this.internalRedirectionCount !== 0 ) {
result = 'gb:no-spurious-csp-report'; if ( µb.logger.isEnabled() ) {
this.logData = { result: 1, source: 'global', raw: 'no-spurious-csp-report' };
}
return 1;
} }
} else if ( requestType === 'font' ) {
if ( µb.hnSwitches.evaluateZ('no-remote-fonts', context.rootHostname) !== false ) {
result = µb.hnSwitches.toResultString();
}
this.remoteFontCount += 1;
} }
if ( requestType === 'font' ) {
this.remoteFontCount += 1;
if ( µb.hnSwitches.evaluateZ('no-remote-fonts', context.rootHostname) !== false ) {
if ( µb.logger.isEnabled() ) {
this.logData = µb.hnSwitches.toLogData();
}
return 1;
}
}
var result = 0;
// Dynamic URL filtering. // Dynamic URL filtering.
if ( result === '' ) { if ( result === 0 ) {
µb.sessionURLFiltering.evaluateZ(context.rootHostname, context.requestURL, requestType); result = µb.sessionURLFiltering.evaluateZ(context.rootHostname, context.requestURL, requestType);
result = µb.sessionURLFiltering.toFilterString(); if ( result !== 0 && µb.logger.isEnabled() ) {
this.logData = µb.sessionURLFiltering.toLogData();
}
} }
// Dynamic hostname/type filtering. // Dynamic hostname/type filtering.
if ( result === '' && µb.userSettings.advancedUserEnabled ) { if ( result === 0 && µb.userSettings.advancedUserEnabled ) {
µb.sessionFirewall.evaluateCellZY(context.rootHostname, context.requestHostname, requestType); result = µb.sessionFirewall.evaluateCellZY(context.rootHostname, context.requestHostname, requestType);
if ( µb.sessionFirewall.mustBlockOrAllow() ) { if ( result !== 0 && µb.logger.isEnabled() ) {
result = µb.sessionFirewall.toFilterString(); this.logData = µb.sessionFirewall.toLogData();
} }
} }
// Static filtering has lowest precedence. // Static filtering has lowest precedence.
if ( result === '' || result.charCodeAt(1) === 110 /* 'n' */ ) { if ( result === 0 || result === 3 ) {
if ( µb.staticNetFilteringEngine.matchString(context) !== undefined ) { result = µb.staticNetFilteringEngine.matchString(context);
result = µb.staticNetFilteringEngine.toResultString(µb.logger.isEnabled()); if ( result !== 0 && µb.logger.isEnabled() ) {
this.logData = µb.staticNetFilteringEngine.toLogData();
} }
} }

View file

@ -79,7 +79,6 @@ var messaging = vAPI.messaging;
var popupData = {}; var popupData = {};
var dfPaneBuilt = false; var dfPaneBuilt = false;
var reIP = /^\d+(?:\.\d+){1,3}$/; var reIP = /^\d+(?:\.\d+){1,3}$/;
var reSrcHostnameFromRule = /^d[abn]:([^ ]+) ([^ ]+) ([^ ]+)/;
var scopeToSrcHostnameMap = { var scopeToSrcHostnameMap = {
'/': '*', '/': '*',
'.': '' '.': ''
@ -148,16 +147,12 @@ var hashFromPopupData = function(reset) {
return; return;
} }
var hasher = []; var hasher = [],
var rules = popupData.firewallRules; rules = popupData.firewallRules;
var rule;
for ( var key in rules ) { for ( var key in rules ) {
if ( rules.hasOwnProperty(key) === false ) { var rule = rules[key];
continue; if ( rule !== null ) {
} hasher.push(rule.src + ' ' + rule.des + ' ' + rule.type + ' ' + rule.action);
rule = rules[key];
if ( rule !== '' ) {
hasher.push(rule);
} }
} }
hasher.sort(); hasher.sort();
@ -243,18 +238,16 @@ var updateFirewallCell = function(scope, des, type, rule) {
} }
cells.removeClass(); cells.removeClass();
var action = rule.charAt(1); if ( rule !== null ) {
if ( action !== '' ) { cells.toggleClass(rule.action + 'Rule', true);
cells.toggleClass(action + 'Rule', true);
} }
// Use dark shade visual cue if the rule is specific to the cell. // Use dark shade visual cue if the rule is specific to the cell.
var ownRule = false; var ownRule = false;
var matches = reSrcHostnameFromRule.exec(rule); if ( rule !== null ) {
if ( matches !== null ) { ownRule = (rule.des !== '*' || rule.type === type) &&
ownRule = (matches[2] !== '*' || matches[3] === type) && (rule.des === des) &&
(matches[2] === des) && (rule.src === scopeToSrcHostnameMap[scope]);
(matches[1] === scopeToSrcHostnameMap[scope]);
} }
cells.toggleClass('ownRule', ownRule); cells.toggleClass('ownRule', ownRule);

View file

@ -35,14 +35,14 @@ var reEscape = function(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}; };
var reSpecialChars = /[\*\^\t\v\n]/; var reSpecialNetworkChars = /[a-d]/;
/******************************************************************************/ /******************************************************************************/
var fromNetFilter = function(details) { var fromNetFilter = function(details) {
var lists = []; var lists = [];
var compiledFilter = details.compiledFilter; var compiledFilter = details.compiledFilter;
var entry, content, pos, c; var entry, content, pos, notFound;
for ( var assetKey in listEntries ) { for ( var assetKey in listEntries ) {
entry = listEntries[assetKey]; entry = listEntries[assetKey];
if ( entry === undefined ) { if ( entry === undefined ) {
@ -52,23 +52,23 @@ var fromNetFilter = function(details) {
pos = 0; pos = 0;
for (;;) { for (;;) {
pos = content.indexOf(compiledFilter, pos); pos = content.indexOf(compiledFilter, pos);
if ( pos === -1 ) { if ( pos === -1 ) { break; }
break;
}
// We need an exact match. // We need an exact match.
// https://github.com/gorhill/uBlock/issues/1392 // https://github.com/gorhill/uBlock/issues/1392
// https://github.com/gorhill/uBlock/issues/835 // https://github.com/gorhill/uBlock/issues/835
if ( pos === 0 || reSpecialChars.test(content.charAt(pos - 1)) ) { pos -= 1;
c = content.charAt(pos + compiledFilter.length); notFound =
if ( c === '' || reSpecialChars.test(c) ) { reSpecialNetworkChars.test(content.charAt(pos)) === false ||
lists.push({ pos !== 0 && content.charCodeAt(pos - 1) !== 0x0A /* '\n' */;
title: entry.title, pos += 1 + compiledFilter.length;
supportURL: entry.supportURL if ( notFound ) { continue; }
}); if ( pos === content.length || content.charCodeAt(pos) === 0x0A ) {
break; lists.push({
} title: entry.title,
supportURL: entry.supportURL
});
break;
} }
pos += compiledFilter.length;
} }
} }
@ -119,18 +119,18 @@ var fromCosmeticFilter = function(details) {
var matches = rePlainSelector.exec(filter); var matches = rePlainSelector.exec(filter);
if ( matches ) { if ( matches ) {
if ( matches[0] === filter ) { // simple CSS selector if ( matches[0] === filter ) { // simple CSS selector
reStr.push('c', 'lg', reEscape(filter)); reStr.push('[e-h]lg', reEscape(filter));
} else { // complex CSS selector } else { // complex CSS selector
reStr.push('c', reEscape('lg+'), reEscape(matches[0]), reEscape(filter)); reStr.push('[e-h]lg\\+', reEscape(matches[0]), reEscape(filter));
} }
} else if ( reHighLow.test(filter) ) { // [alt] or [title] } else if ( reHighLow.test(filter) ) { // [alt] or [title]
reStr.push('c', 'hlg0', reEscape(filter)); reStr.push('[e-h]hlg0', reEscape(filter));
} else if ( reHighMedium.test(filter) ) { // [href^="..."] } else if ( reHighMedium.test(filter) ) { // [href^="..."]
reStr.push('c', 'hmg0', '[^"]{8}', '[a-z]*' + reEscape(filter)); reStr.push('[e-h]hmg0', '[^"]{8}', '[a-z]*' + reEscape(filter));
} else if ( filter.indexOf(' ') === -1 ) { // high-high-simple selector } else if ( filter.indexOf(' ') === -1 ) { // high-high-simple selector
reStr.push('c', 'hhsg0', reEscape(filter)); reStr.push('[e-h]hhsg0', reEscape(filter));
} else { // high-high-complex selector } else { // high-high-complex selector
reStr.push('c', 'hhcg0', reEscape(filter)); reStr.push('[e-h]hhcg0', reEscape(filter));
} }
candidates[details.rawFilter] = new RegExp(reStr.join('\\v') + '(?:\\n|$)'); candidates[details.rawFilter] = new RegExp(reStr.join('\\v') + '(?:\\n|$)');
@ -150,7 +150,7 @@ var fromCosmeticFilter = function(details) {
if ( hostname !== '' ) { if ( hostname !== '' ) {
for ( ;; ) { for ( ;; ) {
candidates[hostname + '##' + filter] = new RegExp( candidates[hostname + '##' + filter] = new RegExp(
['c', 'h', '[^\\v]+', reEscape(hostname), filterEx].join('\\v') + ['[e-h]h', '[^\\v]+', reEscape(hostname), filterEx].join('\\v') +
'(?:\\n|$)' '(?:\\n|$)'
); );
pos = hostname.indexOf('.'); pos = hostname.indexOf('.');
@ -168,7 +168,7 @@ var fromCosmeticFilter = function(details) {
if ( pos !== -1 ) { if ( pos !== -1 ) {
var entity = domain.slice(0, pos) + '.*'; var entity = domain.slice(0, pos) + '.*';
candidates[entity + '##' + filter] = new RegExp( candidates[entity + '##' + filter] = new RegExp(
['c', 'h', '[^\\v]+', reEscape(entity), filterEx].join('\\v') + ['[e-h]h', '[^\\v]+', reEscape(entity), filterEx].join('\\v') +
'(?:\\n|$)' '(?:\\n|$)'
); );
} }

File diff suppressed because it is too large Load diff

View file

@ -680,6 +680,9 @@
µb.assets.get(assetKey, onRawListLoaded); µb.assets.get(assetKey, onRawListLoaded);
return; return;
} }
if ( /[^\x00-\x7F]/.test(details.content) ) {
console.log(assetKey, 'has Unicode characters');
}
details.assetKey = assetKey; details.assetKey = assetKey;
callback(details); callback(details);
}; };
@ -727,7 +730,7 @@
/******************************************************************************/ /******************************************************************************/
µBlock.compileFilters = function(rawText) { µBlock.compileFilters = function(rawText) {
var compiledFilters = []; var compiledFilters = new this.CompiledOutput();
// Useful references: // Useful references:
// https://adblockplus.org/en/filter-cheatsheet // https://adblockplus.org/en/filter-cheatsheet
@ -793,7 +796,7 @@
staticNetFilteringEngine.compile(line, compiledFilters); staticNetFilteringEngine.compile(line, compiledFilters);
} }
return compiledFilters.join('\n'); return compiledFilters.toString();
}; };
/******************************************************************************/ /******************************************************************************/

View file

@ -525,7 +525,8 @@ vAPI.tabs.onClosed = function(tabId) {
vAPI.tabs.onPopupUpdated = (function() { vAPI.tabs.onPopupUpdated = (function() {
// The same context object will be reused everytime. This also allows to // The same context object will be reused everytime. This also allows to
// remember whether a popup or popunder was matched. // remember whether a popup or popunder was matched.
var context = {}; var context = {},
logData;
// https://github.com/gorhill/uBlock/commit/1d448b85b2931412508aa01bf899e0b6f0033626#commitcomment-14944764 // https://github.com/gorhill/uBlock/commit/1d448b85b2931412508aa01bf899e0b6f0033626#commitcomment-14944764
// See if two URLs are different, disregarding scheme -- because the scheme // See if two URLs are different, disregarding scheme -- because the scheme
@ -549,8 +550,9 @@ vAPI.tabs.onPopupUpdated = (function() {
}; };
var popupMatch = function(openerURL, targetURL, clickedURL, popupType) { var popupMatch = function(openerURL, targetURL, clickedURL, popupType) {
var openerHostname = µb.URI.hostnameFromURI(openerURL); var openerHostname = µb.URI.hostnameFromURI(openerURL),
var openerDomain = µb.URI.domainFromHostname(openerHostname); openerDomain = µb.URI.domainFromHostname(openerHostname),
result;
context.pageHostname = openerHostname; context.pageHostname = openerHostname;
context.pageDomain = openerDomain; context.pageDomain = openerDomain;
@ -579,90 +581,90 @@ vAPI.tabs.onPopupUpdated = (function() {
// URL. // URL.
if ( openerHostname !== '' && targetURL !== 'about:blank' ) { if ( openerHostname !== '' && targetURL !== 'about:blank' ) {
// Check per-site switch first // Check per-site switch first
if ( µb.hnSwitches.evaluateZ('no-popups', openerHostname) ) { if ( µb.hnSwitches.evaluateZ('no-popups', openerHostname) === true ) {
if ( typeof clickedURL === 'string' && areDifferentURLs(targetURL, clickedURL) ) { if (
return 'ub:no-popups: ' + µb.hnSwitches.z + ' true'; typeof clickedURL === 'string' &&
areDifferentURLs(targetURL, clickedURL)
) {
logData = {
source: 'switch',
raw: 'no-popups: ' + µb.hnSwitches.z + ' true'
};
return 1;
} }
} }
// https://github.com/gorhill/uBlock/issues/581 // https://github.com/gorhill/uBlock/issues/581
// Take into account popup-specific rules in dynamic URL filtering, OR // Take into account popup-specific rules in dynamic URL filtering, OR
// generic allow rules. // generic allow rules.
µb.sessionURLFiltering.evaluateZ(openerHostname, targetURL, popupType); result = µb.sessionURLFiltering.evaluateZ(openerHostname, targetURL, popupType);
if ( if (
µb.sessionURLFiltering.r === 1 && µb.sessionURLFiltering.type === popupType || result === 1 && µb.sessionURLFiltering.type === popupType ||
µb.sessionURLFiltering.r === 2 result === 2
) { ) {
return µb.sessionURLFiltering.toFilterString(); logData = µb.sessionURLFiltering.toLogData();
return result;
} }
// https://github.com/gorhill/uBlock/issues/581 // https://github.com/gorhill/uBlock/issues/581
// Take into account `allow` rules in dynamic filtering: `block` rules // Take into account `allow` rules in dynamic filtering: `block` rules
// are ignored, as block rules are not meant to block specific types // are ignored, as block rules are not meant to block specific types
// like `popup` (just like with static filters). // like `popup` (just like with static filters).
µb.sessionFirewall.evaluateCellZY(openerHostname, context.requestHostname, popupType); result = µb.sessionFirewall.evaluateCellZY(openerHostname, context.requestHostname, popupType);
if ( µb.sessionFirewall.r === 2 ) { if ( result === 2 ) {
return µb.sessionFirewall.toFilterString(); logData = µb.sessionFirewall.toLogData();
return 2;
} }
} }
// https://github.com/chrisaljoudi/uBlock/issues/323 // https://github.com/chrisaljoudi/uBlock/issues/323
// https://github.com/chrisaljoudi/uBlock/issues/1142 // https://github.com/chrisaljoudi/uBlock/issues/1142
// Don't block if uBlock is turned off in popup's context // Don't block if uBlock is turned off in popup's context
var snfe = µb.staticNetFilteringEngine; if ( µb.getNetFilteringSwitch(targetURL) ) {
if ( result = µb.staticNetFilteringEngine.matchStringExactType(
µb.getNetFilteringSwitch(targetURL) && context,
snfe.matchStringExactType(context, targetURL, popupType) !== undefined targetURL,
) { popupType
return snfe.toResultString(µb.logger.isEnabled()); );
if ( result !== 0 ) {
logData = µb.staticNetFilteringEngine.toLogData();
return result;
}
} }
return ''; return 0;
}; };
var mapPopunderResult = function(popunderURL, popunderHostname, result) { var mapPopunderResult = function(popunderURL, popunderHostname, result) {
if ( result.startsWith('sb:') === false ) { if (
return ''; logData === undefined ||
} logData.source !== 'static' ||
var snfe = µb.staticNetFilteringEngine; logData.token === '*'
var token = snfe.tokenRegister; ) {
if ( token === '*' ) { return 0;
return '';
}
if ( token === '.' ) {
return result;
}
result = snfe.toResultString(true);
var re = snfe.filterRegexFromCompiled(result.slice(3));
if ( re === null ) {
return '';
}
var matches = re.exec(popunderURL);
if ( matches === null ) {
return '';
} }
if ( logData.token === '.' ) { return result; }
var re = new RegExp(logData.regex),
matches = re.exec(popunderURL);
if ( matches === null ) { return ''; }
var beg = matches.index, var beg = matches.index,
end = beg + matches[0].length, end = beg + matches[0].length,
pos = popunderURL.indexOf(popunderHostname); pos = popunderURL.indexOf(popunderHostname);
if ( pos === -1 ) { if ( pos === -1 ) { return ''; }
return '';
}
// https://github.com/gorhill/uBlock/issues/1471 // https://github.com/gorhill/uBlock/issues/1471
// We test whether the opener hostname as at least one character // We test whether the opener hostname as at least one character
// within matched portion of URL. // within matched portion of URL.
// https://github.com/gorhill/uBlock/issues/1903 // https://github.com/gorhill/uBlock/issues/1903
// Ignore filters which cause a match before the start of the // Ignore filters which cause a match before the start of the
// hostname in the URL. // hostname in the URL.
return beg >= pos && return beg >= pos && beg < pos + popunderHostname.length && end > pos
beg < pos + popunderHostname.length && ? result
end > pos ? : 0;
result :
'';
}; };
var popunderMatch = function(openerURL, targetURL) { var popunderMatch = function(openerURL, targetURL) {
var result = popupMatch(targetURL, openerURL, null, 'popunder'); var result = popupMatch(targetURL, openerURL, null, 'popunder');
if ( µb.isBlockResult(result) ) { if ( result === 1 ) {
return result; return result;
} }
// https://github.com/gorhill/uBlock/issues/1010#issuecomment-186824878 // https://github.com/gorhill/uBlock/issues/1010#issuecomment-186824878
@ -671,24 +673,24 @@ vAPI.tabs.onPopupUpdated = (function() {
// a broad one, we will consider the opener tab to be a popunder tab. // a broad one, we will consider the opener tab to be a popunder tab.
// For now, a "broad" filter is one which does not touch any part of // For now, a "broad" filter is one which does not touch any part of
// the hostname part of the opener URL. // the hostname part of the opener URL.
var popunderURL = openerURL; var popunderURL = openerURL,
var popunderHostname = µb.URI.hostnameFromURI(popunderURL); popunderHostname = µb.URI.hostnameFromURI(popunderURL);
if ( popunderHostname === '' ) { if ( popunderHostname === '' ) {
return ''; return 0;
} }
result = mapPopunderResult( result = mapPopunderResult(
popunderURL, popunderURL,
popunderHostname, popunderHostname,
popupMatch(targetURL, popunderURL, null, 'popup') popupMatch(targetURL, popunderURL, null, 'popup')
); );
if ( result !== '' ) { if ( result !== 0 ) {
return result; return result;
} }
// https://github.com/gorhill/uBlock/issues/1598 // https://github.com/gorhill/uBlock/issues/1598
// Try to find a match against origin part of the opener URL. // Try to find a match against origin part of the opener URL.
popunderURL = µb.URI.originFromURI(popunderURL); popunderURL = µb.URI.originFromURI(popunderURL);
if ( popunderURL === '' ) { if ( popunderURL === '' ) {
return ''; return 0;
} }
return mapPopunderResult( return mapPopunderResult(
popunderURL, popunderURL,
@ -731,13 +733,13 @@ vAPI.tabs.onPopupUpdated = (function() {
} }
// Popup test. // Popup test.
var popupType = 'popup'; var popupType = 'popup',
var result = popupMatch(openerURL, targetURL, µb.mouseURL, 'popup'); result = popupMatch(openerURL, targetURL, µb.mouseURL, 'popup');
// Popunder test. // Popunder test.
if ( result === '' ) { if ( result === 0 ) {
result = popunderMatch(openerURL, targetURL); result = popunderMatch(openerURL, targetURL);
if ( µb.isBlockResult(result) ) { if ( result === 1 ) {
popupType = 'popunder'; popupType = 'popunder';
} }
} }
@ -747,7 +749,7 @@ vAPI.tabs.onPopupUpdated = (function() {
µb.logger.writeOne( µb.logger.writeOne(
popupType === 'popup' ? openerTabId : targetTabId, popupType === 'popup' ? openerTabId : targetTabId,
'net', 'net',
result, logData,
popupType, popupType,
popupType === 'popup' ? targetURL : openerURL, popupType === 'popup' ? targetURL : openerURL,
µb.URI.hostnameFromURI(context.rootURL), µb.URI.hostnameFromURI(context.rootURL),
@ -756,7 +758,7 @@ vAPI.tabs.onPopupUpdated = (function() {
} }
// Not blocked // Not blocked
if ( µb.isAllowResult(result) ) { if ( result !== 1 ) {
return; return;
} }

View file

@ -116,7 +116,9 @@ var onBeforeRequest = function(details) {
var isFrame = requestType === 'sub_frame'; var isFrame = requestType === 'sub_frame';
// https://github.com/chrisaljoudi/uBlock/issues/114 // https://github.com/chrisaljoudi/uBlock/issues/114
var requestContext = pageStore.createContextFromFrameId(isFrame ? details.parentFrameId : details.frameId); var requestContext = pageStore.createContextFromFrameId(
isFrame ? details.parentFrameId : details.frameId
);
// Setup context and evaluate // Setup context and evaluate
var requestURL = details.url; var requestURL = details.url;
@ -132,7 +134,7 @@ var onBeforeRequest = function(details) {
µb.logger.writeOne( µb.logger.writeOne(
tabId, tabId,
'net', 'net',
result, pageStore.logData,
requestType, requestType,
requestURL, requestURL,
requestContext.rootHostname, requestContext.rootHostname,
@ -141,7 +143,7 @@ var onBeforeRequest = function(details) {
} }
// Not blocked // Not blocked
if ( µb.isAllowResult(result) ) { if ( result !== 1 ) {
// https://github.com/chrisaljoudi/uBlock/issues/114 // https://github.com/chrisaljoudi/uBlock/issues/114
if ( details.parentFrameId !== -1 && isFrame ) { if ( details.parentFrameId !== -1 && isFrame ) {
pageStore.setFrame(details.frameId, requestURL); pageStore.setFrame(details.frameId, requestURL);
@ -162,7 +164,7 @@ var onBeforeRequest = function(details) {
µb.logger.writeOne( µb.logger.writeOne(
tabId, tabId,
'redirect', 'redirect',
'rr:' + µb.redirectEngine.resourceNameRegister, { source: 'redirect', raw: µb.redirectEngine.resourceNameRegister },
requestType, requestType,
requestURL, requestURL,
requestContext.rootHostname, requestContext.rootHostname,
@ -193,8 +195,7 @@ var onBeforeRootFrameRequest = function(details) {
// behind-the-scene // behind-the-scene
var µburi = µb.URI, var µburi = µb.URI,
requestHostname = µburi.hostnameFromURI(requestURL), requestHostname = µburi.hostnameFromURI(requestURL),
requestDomain = µburi.domainFromHostname(requestHostname) || requestHostname, requestDomain = µburi.domainFromHostname(requestHostname) || requestHostname;
result = '';
var context = { var context = {
rootHostname: requestHostname, rootHostname: requestHostname,
rootDomain: requestDomain, rootDomain: requestDomain,
@ -204,22 +205,31 @@ var onBeforeRootFrameRequest = function(details) {
requestHostname: requestHostname, requestHostname: requestHostname,
requestType: 'main_frame' requestType: 'main_frame'
}; };
var result = 0,
logData,
logEnabled = µb.logger.isEnabled();
// If the site is whitelisted, disregard strict blocking // If the site is whitelisted, disregard strict blocking
if ( µb.getNetFilteringSwitch(requestURL) === false ) { if ( µb.getNetFilteringSwitch(requestURL) === false ) {
result = 'ua:whitelisted'; result = 2;
if ( logEnabled === true ) {
logData = { engine: 'u', result: 2, raw: 'whitelisted' };
}
} }
// Permanently unrestricted? // Permanently unrestricted?
if ( result === '' && µb.hnSwitches.evaluateZ('no-strict-blocking', requestHostname) ) { if ( result === 0 && µb.hnSwitches.evaluateZ('no-strict-blocking', requestHostname) ) {
result = 'ua:no-strict-blocking: ' + µb.hnSwitches.z + ' true'; result = 2;
if ( logEnabled === true ) {
logData = { engine: 'u', result: 2, raw: 'no-strict-blocking: ' + µb.hnSwitches.z + ' true' };
}
} }
// Temporarily whitelisted? // Temporarily whitelisted?
if ( result === '' ) { if ( result === 0 ) {
result = isTemporarilyWhitelisted(result, requestHostname); result = isTemporarilyWhitelisted(result, requestHostname);
if ( result.charAt(1) === 'a' ) { if ( result === 2 && logEnabled === true ) {
result = 'ua:no-strict-blocking true (temporary)'; logData = { engine: 'u', result: 2, raw: 'no-strict-blocking true (temporary)' };
} }
} }
@ -227,26 +237,31 @@ var onBeforeRootFrameRequest = function(details) {
var snfe = µb.staticNetFilteringEngine; var snfe = µb.staticNetFilteringEngine;
// Check for specific block // Check for specific block
if ( if ( result === 0 ) {
result === '' && result = snfe.matchStringExactType(context, requestURL, 'main_frame');
snfe.matchStringExactType(context, requestURL, 'main_frame') !== undefined if ( result !== 0 && logEnabled === true ) {
) { logData = snfe.toLogData();
result = snfe.toResultString(true); }
} }
// Check for generic block // Check for generic block
if ( if ( result === 0 ) {
result === '' && result = snfe.matchStringExactType(context, requestURL, 'no_type');
snfe.matchStringExactType(context, requestURL, 'no_type') !== undefined if ( result !== 0 ) {
) { if ( result === 1 || logEnabled === true ) {
result = snfe.toResultString(true); logData = snfe.toLogData();
// https://github.com/chrisaljoudi/uBlock/issues/1128 }
// Do not block if the match begins after the hostname, except when // https://github.com/chrisaljoudi/uBlock/issues/1128
// the filter is specifically of type `other`. // Do not block if the match begins after the hostname, except when
// https://github.com/gorhill/uBlock/issues/490 // the filter is specifically of type `other`.
// Removing this for the time being, will need a new, dedicated type. // https://github.com/gorhill/uBlock/issues/490
if ( result.charAt(1) === 'b' ) { // Removing this for the time being, will need a new, dedicated type.
result = toBlockDocResult(requestURL, requestHostname, result); if (
result === 1 &&
toBlockDocResult(requestURL, requestHostname, logData) === false
) {
result = 0;
}
} }
} }
@ -257,11 +272,11 @@ var onBeforeRootFrameRequest = function(details) {
pageStore.journalAddRequest(requestHostname, result); pageStore.journalAddRequest(requestHostname, result);
} }
if ( µb.logger.isEnabled() ) { if ( logEnabled ) {
µb.logger.writeOne( µb.logger.writeOne(
tabId, tabId,
'net', 'net',
result, logData,
'main_frame', 'main_frame',
requestURL, requestURL,
requestHostname, requestHostname,
@ -270,19 +285,19 @@ var onBeforeRootFrameRequest = function(details) {
} }
// Not blocked // Not blocked
if ( µb.isAllowResult(result) ) { if ( result !== 1 ) { return; }
return;
}
var compiled = result.slice(3); // No log data means no strict blocking (because we need to report why
// the blocking occurs.
if ( logData === undefined ) { return; }
// Blocked // Blocked
var query = btoa(JSON.stringify({ var query = btoa(JSON.stringify({
url: requestURL, url: requestURL,
hn: requestHostname, hn: requestHostname,
dn: requestDomain, dn: requestDomain,
fc: compiled, fc: logData.compiled,
fs: snfe.filterStringFromCompiled(compiled) fs: logData.raw
})); }));
vAPI.tabs.replace(tabId, vAPI.getURL('document-blocked.html?details=') + query); vAPI.tabs.replace(tabId, vAPI.getURL('document-blocked.html?details=') + query);
@ -292,26 +307,23 @@ var onBeforeRootFrameRequest = function(details) {
/******************************************************************************/ /******************************************************************************/
var toBlockDocResult = function(url, hostname, result) { var toBlockDocResult = function(url, hostname, logData) {
// Make a regex out of the result if ( typeof logData.regex !== 'string' ) { return; }
var re = µBlock.staticNetFilteringEngine var re = new RegExp(logData.regex),
.filterRegexFromCompiled(result.slice(3), 'gi'); match = re.exec(url.toLowerCase());
if ( re === null ) { if ( match === null ) { return ''; }
return '';
}
var matches = re.exec(url);
if ( matches === null ) {
return '';
}
// https://github.com/chrisaljoudi/uBlock/issues/1128 // https://github.com/chrisaljoudi/uBlock/issues/1128
// https://github.com/chrisaljoudi/uBlock/issues/1212 // https://github.com/chrisaljoudi/uBlock/issues/1212
// Relax the rule: verify that the match is completely before the path part // Relax the rule: verify that the match is completely before the path part
if ( re.lastIndex <= url.indexOf(hostname) + hostname.length + 1 ) { if (
return result; (match.index + match.length) <=
(url.indexOf(hostname) + hostname.length + 1)
) {
return true;
} }
return ''; return false;
}; };
/******************************************************************************/ /******************************************************************************/
@ -410,18 +422,20 @@ var onHeadersReceived = function(details) {
// Turns out scripts must also be considered as potential embedded // Turns out scripts must also be considered as potential embedded
// contexts (as workers) and as such we may need to inject content // contexts (as workers) and as such we may need to inject content
// security policy directives. // security policy directives.
if ( requestType === 'script' || requestType === 'main_frame' || requestType === 'sub_frame' ) { if ( requestType === 'main_frame' || requestType === 'sub_frame' ) {
return processCSP(pageStore, details); return injectCSP(pageStore, details);
} }
}; };
/******************************************************************************/ /******************************************************************************/
var processCSP = function(pageStore, details) { var injectCSP = function(pageStore, details) {
var µb = µBlock, var µb = µBlock,
tabId = details.tabId, tabId = details.tabId,
requestURL = details.url, requestURL = details.url,
loggerEnabled = µb.logger.isEnabled(); loggerEnabled = µb.logger.isEnabled(),
logger = µb.logger,
cspSubsets = [];
var context = pageStore.createContextFromPage(); var context = pageStore.createContextFromPage();
context.requestHostname = µb.URI.hostnameFromURI(requestURL); context.requestHostname = µb.URI.hostnameFromURI(requestURL);
@ -429,79 +443,125 @@ var processCSP = function(pageStore, details) {
context.pageHostname = context.pageDomain = context.requestHostname; context.pageHostname = context.pageDomain = context.requestHostname;
} }
var inlineScriptResult, blockInlineScript, // Start collecting policies >>>>>>>>
workerResult, blockWorker;
if ( details.type !== 'script' ) { // ======== built-in policies
context.requestType = 'inline-script';
context.requestURL = requestURL; context.requestType = 'inline-script';
inlineScriptResult = pageStore.filterRequestNoCache(context); context.requestURL = requestURL;
blockInlineScript = µb.isBlockResult(inlineScriptResult); if ( pageStore.filterRequestNoCache(context) === 1 ) {
// https://github.com/gorhill/uBlock/issues/2360 cspSubsets[0] = "script-src 'unsafe-eval' * blob: data:";
// https://github.com/gorhill/uBlock/issues/2440 // https://bugs.chromium.org/p/chromium/issues/detail?id=669086
context.requestType = 'script'; // TODO: remove when most users are beyond Chromium v56
context.requestURL = 'blob:'; if ( vAPI.chromiumVersion < 57 ) {
µb.staticNetFilteringEngine.matchString(context); cspSubsets[0] += '; frame-src *';
workerResult = µb.staticNetFilteringEngine.toResultString(loggerEnabled); }
blockWorker = µb.isBlockResult(workerResult);
} }
if ( loggerEnabled === true ) {
µb.staticNetFilteringEngine.matchStringExactType(context, requestURL, 'websocket'); logger.writeOne(
var websocketResult = µb.staticNetFilteringEngine.toResultString(loggerEnabled), tabId,
blockWebsocket = µb.isBlockResult(websocketResult); 'net',
pageStore.logData,
var headersChanged; 'inline-script',
if ( blockInlineScript || blockWebsocket || blockWorker ) { requestURL,
headersChanged = foilWithCSP( context.rootHostname,
details.responseHeaders, context.pageHostname
blockInlineScript,
blockWebsocket,
blockWorker
); );
} }
if ( loggerEnabled && details.type !== 'script' ) { // ======== filter-based policies
if ( blockInlineScript !== undefined ) {
µb.logger.writeOne( // Static filtering.
tabId,
'net', var logData = [];
inlineScriptResult,
'inline-script', µb.staticNetFilteringEngine.matchAndFetchData(
requestURL, 'csp',
context.rootHostname, requestURL,
context.pageHostname cspSubsets,
); loggerEnabled === true ? logData : undefined
} );
if ( websocketResult ) {
µb.logger.writeOne( // <<<<<<<< All policies have been collected
tabId,
'net',
websocketResult,
'websocket',
requestURL,
context.rootHostname,
context.pageHostname
);
}
if ( workerResult ) {
µb.logger.writeOne(
tabId,
'net',
workerResult,
'worker',
requestURL,
context.rootHostname,
context.pageHostname
);
}
}
context.dispose(); context.dispose();
if ( headersChanged !== true ) { return; } // URL filtering `allow` rules override static filtering.
if (
cspSubsets.length !== 0 &&
µb.sessionURLFiltering.evaluateZ(context.rootHostname, requestURL, 'csp') === 2
) {
if ( loggerEnabled === true ) {
logger.writeOne(
tabId,
'net',
µb.sessionURLFiltering.toLogData(),
'csp',
requestURL,
context.rootHostname,
context.pageHostname
);
}
return;
}
// Dynamic filtering rules override static filtering.
if (
cspSubsets.length !== 0 &&
µb.userSettings.advancedUserEnabled &&
µb.sessionFirewall.evaluateCellZY(context.rootHostname, context.rootHostname, '*') === 2
) {
if ( loggerEnabled === true ) {
logger.writeOne(
tabId,
'net',
µb.sessionFirewall.toLogData(),
'csp',
requestURL,
context.rootHostname,
context.pageHostname
);
}
return;
}
// Static CSP policies will be applied.
var i = logData.length;
while ( i-- ) {
logger.writeOne(
tabId,
'net',
logData[i],
'csp',
requestURL,
context.rootHostname,
context.pageHostname
);
}
if ( cspSubsets.length === 0 ) {
return;
}
µb.updateBadgeAsync(tabId); µb.updateBadgeAsync(tabId);
return { 'responseHeaders': details.responseHeaders }; var csp, headers = details.responseHeaders;
i = headerIndexFromName('content-security-policy', headers);
if ( i !== -1 ) {
csp = headers[i].value.trim();
headers.splice(i, 1);
}
cspSubsets = cspSubsets.join(', ');
// Use comma to add a new subset to potentially existing one(s). This new
// subset has its own reporting options and won't cause spurious CSP
// reports to outside world.
// Ref.: https://www.w3.org/TR/CSP2/#implementation-considerations
headers.push({
name: 'Content-Security-Policy',
value: csp === undefined ? cspSubsets : csp + ', ' + cspSubsets
});
return { 'responseHeaders': headers };
}; };
/******************************************************************************/ /******************************************************************************/
@ -518,13 +578,13 @@ var foilLargeMediaElement = function(pageStore, details) {
var tabId = details.tabId, var tabId = details.tabId,
size = parseInt(details.responseHeaders[i].value, 10) || 0, size = parseInt(details.responseHeaders[i].value, 10) || 0,
result = pageStore.filterLargeMediaElement(size); result = pageStore.filterLargeMediaElement(size);
if ( result === undefined ) { return; } if ( result === 0 ) { return; }
if ( µb.logger.isEnabled() ) { if ( µb.logger.isEnabled() ) {
µb.logger.writeOne( µb.logger.writeOne(
tabId, tabId,
'net', 'net',
result, pageStore.logData,
details.type, details.type,
details.url, details.url,
pageStore.tabHostname, pageStore.tabHostname,
@ -537,57 +597,6 @@ var foilLargeMediaElement = function(pageStore, details) {
/******************************************************************************/ /******************************************************************************/
var foilWithCSP = function(headers, noInlineScript, noWebsocket, noBlobWorker) {
var i = headerIndexFromName('content-security-policy', headers),
cspSubset = [];
if ( noInlineScript ) {
cspSubset.push("script-src 'unsafe-eval' *");
}
if ( noWebsocket ) {
cspSubset.push('connect-src http: https:');
}
// https://www.w3.org/TR/CSP2/#directive-child-src
// https://www.w3.org/TR/CSP3/#directive-worker-src
if ( noBlobWorker ) {
cspSubset.push('child-src http: https:');
}
// https://bugs.chromium.org/p/chromium/issues/detail?id=513860
// Bad Chromium bug: web pages can work around CSP directives by
// creating data:- or blob:-based URI. So if we must restrict using CSP,
// we have no choice but to also prevent the creation of nested browsing
// contexts based on data:- or blob:-based URIs.
if ( vAPI.chrome && (noInlineScript || noWebsocket) ) {
// https://w3c.github.io/webappsec-csp/#directive-frame-src
cspSubset.push('frame-src http: https:');
}
if ( cspSubset.length === 0 ) { return; }
var csp;
if ( i !== -1 ) {
csp = headers[i].value.trim();
headers.splice(i, 1);
}
// Use comma to add a new subset to potentially existing one(s). This new
// subset has its own reporting options and won't cause spurious CSP
// reports to outside world.
// Ref.: https://www.w3.org/TR/CSP2/#implementation-considerations
cspSubset = cspSubset.join('; ');
headers.push({
name: 'Content-Security-Policy',
value: csp === undefined ? cspSubset : csp + ', ' + cspSubset
});
return true;
};
/******************************************************************************/
// Caller must ensure headerName is normalized to lower case. // Caller must ensure headerName is normalized to lower case.
var headerIndexFromName = function(headerName, headers) { var headerIndexFromName = function(headerName, headers) {
@ -620,8 +629,7 @@ vAPI.net.onHeadersReceived = {
'main_frame', 'main_frame',
'sub_frame', 'sub_frame',
'image', 'image',
'media', 'media'
'script'
], ],
extra: [ 'blocking', 'responseHeaders' ], extra: [ 'blocking', 'responseHeaders' ],
callback: onHeadersReceived callback: onHeadersReceived
@ -629,8 +637,6 @@ vAPI.net.onHeadersReceived = {
vAPI.net.registerListeners(); vAPI.net.registerListeners();
//console.log('traffic.js > Beginning to intercept net requests at %s', (new Date()).toISOString());
/******************************************************************************/ /******************************************************************************/
var isTemporarilyWhitelisted = function(result, hostname) { var isTemporarilyWhitelisted = function(result, hostname) {
@ -640,17 +646,15 @@ var isTemporarilyWhitelisted = function(result, hostname) {
obsolete = documentWhitelists[hostname]; obsolete = documentWhitelists[hostname];
if ( obsolete !== undefined ) { if ( obsolete !== undefined ) {
if ( obsolete > Date.now() ) { if ( obsolete > Date.now() ) {
if ( result === '' ) { if ( result === 0 ) {
return 'ua:*' + ' ' + hostname + ' doc allow'; return 2;
} }
} else { } else {
delete documentWhitelists[hostname]; delete documentWhitelists[hostname];
} }
} }
pos = hostname.indexOf('.'); pos = hostname.indexOf('.');
if ( pos === -1 ) { if ( pos === -1 ) { break; }
break;
}
hostname = hostname.slice(pos + 1); hostname = hostname.slice(pos + 1);
} }
return result; return result;

View file

@ -210,7 +210,7 @@ URLNetFiltering.prototype.removeRule = function(srcHostname, url, type) {
URLNetFiltering.prototype.evaluateZ = function(context, target, type) { URLNetFiltering.prototype.evaluateZ = function(context, target, type) {
this.r = 0; this.r = 0;
if ( this.rules.size === 0 ) { if ( this.rules.size === 0 ) {
return this; return 0;
} }
var entries, pos, i, entry; var entries, pos, i, entry;
for (;;) { for (;;) {
@ -222,7 +222,7 @@ URLNetFiltering.prototype.evaluateZ = function(context, target, type) {
this.url = entry.url; this.url = entry.url;
this.type = type; this.type = type;
this.r = entry.action; this.r = entry.action;
return this; return this.r;
} }
} }
if ( (entries = this.rules.get(context + ' *')) ) { if ( (entries = this.rules.get(context + ' *')) ) {
@ -232,14 +232,20 @@ URLNetFiltering.prototype.evaluateZ = function(context, target, type) {
this.url = entry.url; this.url = entry.url;
this.type = '*'; this.type = '*';
this.r = entry.action; this.r = entry.action;
return this; return this.r;
} }
} }
if ( context === '*' ) { break; } if ( context === '*' ) { break; }
pos = context.indexOf('.'); pos = context.indexOf('.');
context = pos !== -1 ? context.slice(pos + 1) : '*'; context = pos !== -1 ? context.slice(pos + 1) : '*';
} }
return this; return 0;
};
/******************************************************************************/
URLNetFiltering.prototype.mustAllowCellZ = function(context, target, type) {
return this.evaluateZ(context, target, type).r === 2;
}; };
/******************************************************************************/ /******************************************************************************/
@ -250,21 +256,30 @@ URLNetFiltering.prototype.mustBlockOrAllow = function() {
/******************************************************************************/ /******************************************************************************/
URLNetFiltering.prototype.toFilterString = function() { URLNetFiltering.prototype.toLogData = function() {
if ( this.r === 0 ) { if ( this.r === 0 ) { return; }
return ''; return {
} source: 'dynamicUrl',
var body = this.context + ' ' + this.url + ' ' + this.type; result: this.r,
if ( this.r === 1 ) { rule: [
return 'lb:' + body + ' block'; this.context,
} this.url,
if ( this.r === 2 ) { this.type,
return 'la:' + body + ' allow'; this.intToActionMap.get(this.r)
} ],
/* this.r === 3 */ raw: this.context + ' ' +
return 'ln:' + body + ' noop'; this.url + ' ' +
this.type + ' ' +
this.intToActionMap.get(this.r)
};
}; };
URLNetFiltering.prototype.intToActionMap = new Map([
[ 1, ' block' ],
[ 2, ' allow' ],
[ 3, ' noop' ]
]);
/******************************************************************************/ /******************************************************************************/
URLNetFiltering.prototype.copyRules = function(other, context, urls, type) { URLNetFiltering.prototype.copyRules = function(other, context, urls, type) {

View file

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
uBlock Origin - a browser extension to block requests. uBlock Origin - a browser extension to block requests.
Copyright (C) 2014-2016 Raymond Hill Copyright (C) 2014-2017 Raymond Hill
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -150,7 +150,10 @@
this.offset = offset || 0; this.offset = offset || 0;
}; };
µBlock.LineIterator.prototype.next = function() { µBlock.LineIterator.prototype.next = function(offset) {
if ( offset !== undefined ) {
this.offset += offset;
}
var lineEnd = this.text.indexOf('\n', this.offset); var lineEnd = this.text.indexOf('\n', this.offset);
if ( lineEnd === -1 ) { if ( lineEnd === -1 ) {
lineEnd = this.text.indexOf('\r', this.offset); lineEnd = this.text.indexOf('\r', this.offset);
@ -163,18 +166,8 @@
return line; return line;
}; };
µBlock.LineIterator.prototype.rewind = function() { µBlock.LineIterator.prototype.charCodeAt = function(offset) {
if ( this.offset <= 1 ) { return this.text.charCodeAt(this.offset + offset);
this.offset = 0;
return;
}
var lineEnd = this.text.lastIndexOf('\n', this.offset - 2);
if ( lineEnd !== -1 ) {
this.offset = lineEnd + 1;
} else {
lineEnd = this.text.lastIndexOf('\r', this.offset - 2);
this.offset = lineEnd !== -1 ? lineEnd + 1 : 0;
}
}; };
µBlock.LineIterator.prototype.eot = function() { µBlock.LineIterator.prototype.eot = function() {
@ -209,6 +202,59 @@
return field; return field;
}; };
µBlock.FieldIterator.prototype.remainder = function() {
return this.text.slice(this.offset);
};
/******************************************************************************/
µBlock.CompiledOutput = function() {
this.bufferLen = 8192;
this.buffer = new Uint8Array(this.bufferLen);
this.offset = 0;
};
µBlock.CompiledOutput.prototype.push = function(lineBits, line) {
var lineLen = line.length,
offset = this.offset,
need = offset + 2 + lineLen; // lineBits, line, \n
if ( need > this.bufferLen ) {
this.grow(need);
}
var buffer = this.buffer;
if ( offset !== 0 ) {
buffer[offset++] = 0x0A /* '\n' */;
}
buffer[offset++] = 0x61 /* 'a' */ + lineBits;
for ( var i = 0, c; i < lineLen; i++ ) {
c = line.charCodeAt(i);
if ( c > 0x7F ) {
return this.push(lineBits | 0x02, encodeURIComponent(line));
}
buffer[offset++] = c;
}
this.offset = offset;
};
µBlock.CompiledOutput.prototype.grow = function(need) {
var newBufferLen = Math.min(
2097152,
1 << Math.ceil(Math.log(need) / Math.log(2))
);
while ( newBufferLen < need ) {
newBufferLen += 1048576;
}
var newBuffer = new Uint8Array(newBufferLen);
newBuffer.set(this.buffer);
this.buffer = newBuffer;
this.bufferLen = newBufferLen;
};
µBlock.CompiledOutput.prototype.toString = function() {
var decoder = new TextDecoder();
return decoder.decode(new Uint8Array(this.buffer.buffer, 0, this.offset));
};
/******************************************************************************/ /******************************************************************************/
µBlock.mapToArray = typeof Array.from === 'function' µBlock.mapToArray = typeof Array.from === 'function'