New static network filter option `ipaddress=`

The purpose is to block according to the ip address of a network
request. In the current implementation, the filter option can only
be enforced at onHeadersReceived time.

The new filter option cannot be enforced in Chromium-based browsers
since the ip address of network requests is available only at
onResponseStarted time, which is not blocking.

The value assigned to `ipaddress` can either be a plain string which
must match exactly a given ip address, or a regex which will be
matched against the ip address.

The `ipaddress` option can only be enforced when the extension
framework does provide a valid ip address in a onHeadersReceived
listener. For instance, cached resources do not have a valid ip
address and thus can't be a match to `ipaddress` option.

Example:

  *$script,ipaddress=93.184.215.14
This commit is contained in:
Raymond Hill 2024-09-09 09:35:23 -04:00
parent 20115697e5
commit c6dedd253f
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
4 changed files with 194 additions and 100 deletions

View File

@ -307,6 +307,7 @@ const µBlock = { // jshint ignore:line
this.setMethod(details.method); this.setMethod(details.method);
this.setURL(details.url); this.setURL(details.url);
this.aliasURL = details.aliasURL || undefined; this.aliasURL = details.aliasURL || undefined;
this.ipaddress = details.ip || undefined;
this.redirectURL = undefined; this.redirectURL = undefined;
this.filter = undefined; this.filter = undefined;
if ( this.itype !== this.SUB_FRAME ) { if ( this.itype !== this.SUB_FRAME ) {

View File

@ -19,13 +19,9 @@
Home: https://github.com/gorhill/uBlock Home: https://github.com/gorhill/uBlock
*/ */
'use strict';
/******************************************************************************/
import { import {
hostnameFromURI,
domainFromHostname, domainFromHostname,
hostnameFromURI,
originFromURI, originFromURI,
} from './uri-utils.js'; } from './uri-utils.js';
@ -140,6 +136,7 @@ export const FilteringContext = class {
this.stype = undefined; this.stype = undefined;
this.url = undefined; this.url = undefined;
this.aliasURL = undefined; this.aliasURL = undefined;
this.ipaddress = undefined;
this.hostname = undefined; this.hostname = undefined;
this.domain = undefined; this.domain = undefined;
this.docId = -1; this.docId = -1;
@ -418,42 +415,72 @@ export const FilteringContext = class {
static getMethodName(a) { static getMethodName(a) {
return methodBitToStrMap.get(a) || ''; return methodBitToStrMap.get(a) || '';
} }
BEACON = BEACON;
CSP_REPORT = CSP_REPORT;
FONT = FONT;
IMAGE = IMAGE;
IMAGESET = IMAGESET;
MAIN_FRAME = MAIN_FRAME;
MEDIA = MEDIA;
OBJECT = OBJECT;
OBJECT_SUBREQUEST = OBJECT_SUBREQUEST;
PING = PING;
SCRIPT = SCRIPT;
STYLESHEET = STYLESHEET;
SUB_FRAME = SUB_FRAME;
WEBSOCKET = WEBSOCKET;
XMLHTTPREQUEST = XMLHTTPREQUEST;
INLINE_FONT = INLINE_FONT;
INLINE_SCRIPT = INLINE_SCRIPT;
OTHER = OTHER;
FRAME_ANY = FRAME_ANY;
FONT_ANY = FONT_ANY;
INLINE_ANY = INLINE_ANY;
PING_ANY = PING_ANY;
SCRIPT_ANY = SCRIPT_ANY;
METHOD_NONE = METHOD_NONE;
METHOD_CONNECT = METHOD_CONNECT;
METHOD_DELETE = METHOD_DELETE;
METHOD_GET = METHOD_GET;
METHOD_HEAD = METHOD_HEAD;
METHOD_OPTIONS = METHOD_OPTIONS;
METHOD_PATCH = METHOD_PATCH;
METHOD_POST = METHOD_POST;
METHOD_PUT = METHOD_PUT;
static BEACON = BEACON;
static CSP_REPORT = CSP_REPORT;
static FONT = FONT;
static IMAGE = IMAGE;
static IMAGESET = IMAGESET;
static MAIN_FRAME = MAIN_FRAME;
static MEDIA = MEDIA;
static OBJECT = OBJECT;
static OBJECT_SUBREQUEST = OBJECT_SUBREQUEST;
static PING = PING;
static SCRIPT = SCRIPT;
static STYLESHEET = STYLESHEET;
static SUB_FRAME = SUB_FRAME;
static WEBSOCKET = WEBSOCKET;
static XMLHTTPREQUEST = XMLHTTPREQUEST;
static INLINE_FONT = INLINE_FONT;
static INLINE_SCRIPT = INLINE_SCRIPT;
static OTHER = OTHER;
static FRAME_ANY = FRAME_ANY;
static FONT_ANY = FONT_ANY;
static INLINE_ANY = INLINE_ANY;
static PING_ANY = PING_ANY;
static SCRIPT_ANY = SCRIPT_ANY;
static METHOD_NONE = METHOD_NONE;
static METHOD_CONNECT = METHOD_CONNECT;
static METHOD_DELETE = METHOD_DELETE;
static METHOD_GET = METHOD_GET;
static METHOD_HEAD = METHOD_HEAD;
static METHOD_OPTIONS = METHOD_OPTIONS;
static METHOD_PATCH = METHOD_PATCH;
static METHOD_POST = METHOD_POST;
static METHOD_PUT = METHOD_PUT;
}; };
/******************************************************************************/ /******************************************************************************/
FilteringContext.prototype.BEACON = FilteringContext.BEACON = BEACON;
FilteringContext.prototype.CSP_REPORT = FilteringContext.CSP_REPORT = CSP_REPORT;
FilteringContext.prototype.FONT = FilteringContext.FONT = FONT;
FilteringContext.prototype.IMAGE = FilteringContext.IMAGE = IMAGE;
FilteringContext.prototype.IMAGESET = FilteringContext.IMAGESET = IMAGESET;
FilteringContext.prototype.MAIN_FRAME = FilteringContext.MAIN_FRAME = MAIN_FRAME;
FilteringContext.prototype.MEDIA = FilteringContext.MEDIA = MEDIA;
FilteringContext.prototype.OBJECT = FilteringContext.OBJECT = OBJECT;
FilteringContext.prototype.OBJECT_SUBREQUEST = FilteringContext.OBJECT_SUBREQUEST = OBJECT_SUBREQUEST;
FilteringContext.prototype.PING = FilteringContext.PING = PING;
FilteringContext.prototype.SCRIPT = FilteringContext.SCRIPT = SCRIPT;
FilteringContext.prototype.STYLESHEET = FilteringContext.STYLESHEET = STYLESHEET;
FilteringContext.prototype.SUB_FRAME = FilteringContext.SUB_FRAME = SUB_FRAME;
FilteringContext.prototype.WEBSOCKET = FilteringContext.WEBSOCKET = WEBSOCKET;
FilteringContext.prototype.XMLHTTPREQUEST = FilteringContext.XMLHTTPREQUEST = XMLHTTPREQUEST;
FilteringContext.prototype.INLINE_FONT = FilteringContext.INLINE_FONT = INLINE_FONT;
FilteringContext.prototype.INLINE_SCRIPT = FilteringContext.INLINE_SCRIPT = INLINE_SCRIPT;
FilteringContext.prototype.OTHER = FilteringContext.OTHER = OTHER;
FilteringContext.prototype.FRAME_ANY = FilteringContext.FRAME_ANY = FRAME_ANY;
FilteringContext.prototype.FONT_ANY = FilteringContext.FONT_ANY = FONT_ANY;
FilteringContext.prototype.INLINE_ANY = FilteringContext.INLINE_ANY = INLINE_ANY;
FilteringContext.prototype.PING_ANY = FilteringContext.PING_ANY = PING_ANY;
FilteringContext.prototype.SCRIPT_ANY = FilteringContext.SCRIPT_ANY = SCRIPT_ANY;
FilteringContext.prototype.METHOD_NONE = FilteringContext.METHOD_NONE = METHOD_NONE;
FilteringContext.prototype.METHOD_CONNECT = FilteringContext.METHOD_CONNECT = METHOD_CONNECT;
FilteringContext.prototype.METHOD_DELETE = FilteringContext.METHOD_DELETE = METHOD_DELETE;
FilteringContext.prototype.METHOD_GET = FilteringContext.METHOD_GET = METHOD_GET;
FilteringContext.prototype.METHOD_HEAD = FilteringContext.METHOD_HEAD = METHOD_HEAD;
FilteringContext.prototype.METHOD_OPTIONS = FilteringContext.METHOD_OPTIONS = METHOD_OPTIONS;
FilteringContext.prototype.METHOD_PATCH = FilteringContext.METHOD_PATCH = METHOD_PATCH;
FilteringContext.prototype.METHOD_POST = FilteringContext.METHOD_POST = METHOD_POST;
FilteringContext.prototype.METHOD_PUT = FilteringContext.METHOD_PUT = METHOD_PUT;
/******************************************************************************/

View File

@ -172,6 +172,7 @@ export const NODE_TYPE_NET_OPTION_NAME_IMAGE = iota++;
export const NODE_TYPE_NET_OPTION_NAME_IMPORTANT = iota++; export const NODE_TYPE_NET_OPTION_NAME_IMPORTANT = iota++;
export const NODE_TYPE_NET_OPTION_NAME_INLINEFONT = iota++; export const NODE_TYPE_NET_OPTION_NAME_INLINEFONT = iota++;
export const NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT = iota++; export const NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT = iota++;
export const NODE_TYPE_NET_OPTION_NAME_IPADDRESS = iota++;
export const NODE_TYPE_NET_OPTION_NAME_MATCHCASE = iota++; export const NODE_TYPE_NET_OPTION_NAME_MATCHCASE = iota++;
export const NODE_TYPE_NET_OPTION_NAME_MEDIA = iota++; export const NODE_TYPE_NET_OPTION_NAME_MEDIA = iota++;
export const NODE_TYPE_NET_OPTION_NAME_METHOD = iota++; export const NODE_TYPE_NET_OPTION_NAME_METHOD = iota++;
@ -249,6 +250,7 @@ export const nodeTypeFromOptionName = new Map([
[ 'important', NODE_TYPE_NET_OPTION_NAME_IMPORTANT ], [ 'important', NODE_TYPE_NET_OPTION_NAME_IMPORTANT ],
[ 'inline-font', NODE_TYPE_NET_OPTION_NAME_INLINEFONT ], [ 'inline-font', NODE_TYPE_NET_OPTION_NAME_INLINEFONT ],
[ 'inline-script', NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT ], [ 'inline-script', NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT ],
[ 'ipaddress', NODE_TYPE_NET_OPTION_NAME_IPADDRESS ],
[ 'match-case', NODE_TYPE_NET_OPTION_NAME_MATCHCASE ], [ 'match-case', NODE_TYPE_NET_OPTION_NAME_MATCHCASE ],
[ 'media', NODE_TYPE_NET_OPTION_NAME_MEDIA ], [ 'media', NODE_TYPE_NET_OPTION_NAME_MEDIA ],
[ 'method', NODE_TYPE_NET_OPTION_NAME_METHOD ], [ 'method', NODE_TYPE_NET_OPTION_NAME_METHOD ],
@ -1401,6 +1403,14 @@ export class AstFilterParser {
modifierType = type; modifierType = type;
unredirectableTypeCount += 1; unredirectableTypeCount += 1;
break; break;
case NODE_TYPE_NET_OPTION_NAME_IPADDRESS: {
const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_IPADDRESS);
if ( /^\/.+\/$/.test(value) ) {
try { void new RegExp(value); }
catch(_) { realBad = true; }
}
break;
}
case NODE_TYPE_NET_OPTION_NAME_MATCHCASE: case NODE_TYPE_NET_OPTION_NAME_MATCHCASE:
realBad = this.isRegexPattern() === false; realBad = this.isRegexPattern() === false;
break; break;
@ -3104,6 +3114,7 @@ export const netOptionTokenDescriptors = new Map([
[ 'important', { blockOnly: true } ], [ 'important', { blockOnly: true } ],
[ 'inline-font', { canNegate: true } ], [ 'inline-font', { canNegate: true } ],
[ 'inline-script', { canNegate: true } ], [ 'inline-script', { canNegate: true } ],
[ 'ipaddress', { mustAssign: true } ],
[ 'match-case', { } ], [ 'match-case', { } ],
[ 'media', { canNegate: true } ], [ 'media', { canNegate: true } ],
[ 'method', { mustAssign: true } ], [ 'method', { mustAssign: true } ],
@ -4324,6 +4335,7 @@ export const utils = (( ) => {
[ 'env_safari', 'safari' ], [ 'env_safari', 'safari' ],
[ 'cap_html_filtering', 'html_filtering' ], [ 'cap_html_filtering', 'html_filtering' ],
[ 'cap_user_stylesheet', 'user_stylesheet' ], [ 'cap_user_stylesheet', 'user_stylesheet' ],
[ 'cap_ipaddress', 'ipaddress' ],
[ 'false', 'false' ], [ 'false', 'false' ],
// Hoping ABP-only list maintainers can at least make use of it to // Hoping ABP-only list maintainers can at least make use of it to
// help non-ABP content blockers better deal with filters benefiting // help non-ABP content blockers better deal with filters benefiting
@ -4358,8 +4370,11 @@ export const utils = (( ) => {
static evaluateExprToken(token, env = []) { static evaluateExprToken(token, env = []) {
const not = token.charCodeAt(0) === 0x21 /* ! */; const not = token.charCodeAt(0) === 0x21 /* ! */;
if ( not ) { token = token.slice(1); } if ( not ) { token = token.slice(1); }
const state = preparserTokens.get(token); let state = preparserTokens.get(token);
if ( state === undefined ) { return; } if ( state === undefined ) {
if ( token.startsWith('cap_') === false ) { return; }
state = 'false';
}
return state === 'false' && not || env.includes(state) !== not; return state === 'false' && not || env.includes(state) !== not;
} }

View File

@ -242,6 +242,7 @@ let $requestTypeValue = 0;
let $requestURL = ''; let $requestURL = '';
let $requestURLRaw = ''; let $requestURLRaw = '';
let $requestHostname = ''; let $requestHostname = '';
let $requestAddress = '';
let $docHostname = ''; let $docHostname = '';
let $docDomain = ''; let $docDomain = '';
let $tokenBeg = 0; let $tokenBeg = 0;
@ -702,6 +703,8 @@ const dnrAddRuleWarning = (rule, msg) => {
FilterNotType FilterNotType
FilterStrictParty FilterStrictParty
FilterModifier FilterModifier
FilterOnHeaders
FilterIPAddress
Collection: Collection:
FilterCollection FilterCollection
@ -1234,7 +1237,7 @@ class FilterRegex {
return [ return [
FilterRegex.fid, FilterRegex.fid,
details.pattern, details.pattern,
details.patternMatchCase ? 1 : 0 details.optionValues.has('match-case') ? 1 : 0,
]; ];
} }
@ -2075,7 +2078,7 @@ const compileToDomainOpt = (...args) => {
class FilterDenyAllow extends FilterToDomainMissSet { class FilterDenyAllow extends FilterToDomainMissSet {
static compile(details) { static compile(details) {
return super.compile(details.denyallowOpt, 0b01); return super.compile(details.optionValues.get('denyallow'), 0b01);
} }
static logData(idata, details) { static logData(idata, details) {
@ -2937,12 +2940,12 @@ class FilterOnHeaders {
} }
static compile(details) { static compile(details) {
return [ FilterOnHeaders.fid, details.headerOpt ]; return [ FilterOnHeaders.fid, details.optionValues.get('header') ];
} }
static fromCompiled(args) { static fromCompiled(args) {
return filterDataAlloc( return filterDataAlloc(
args[0], // fid args[0], // fid
filterRefAdd({ filterRefAdd({
headerOpt: args[1], headerOpt: args[1],
$parsed: null, $parsed: null,
@ -2963,6 +2966,41 @@ class FilterOnHeaders {
registerFilterClass(FilterOnHeaders); registerFilterClass(FilterOnHeaders);
/******************************************************************************/
class FilterIPAddress {
static match(idata) {
const details = filterRefs[filterData[idata+1]];
if ( details.isRegex === false ) {
return $requestAddress === details.pattern;
}
if ( details.$re === undefined ) {
details.$re = new RegExp(details.pattern.slice(1, -1));
}
return details.$re.test($requestAddress);
}
static compile(details) {
return [ FilterIPAddress.fid, details.optionValues.get('ipaddress') ];
}
static fromCompiled(args) {
const pattern = args[1];
const details = {
pattern,
isRegex: pattern.startsWith('/') && pattern.endsWith('/'),
};
return filterDataAlloc(args[0], filterRefAdd(details));
}
static logData(idata, details) {
const irefs = filterData[idata+1];
details.options.push(`ipaddress=${LogData.requote(filterRefs[irefs].pattern)}`);
}
}
registerFilterClass(FilterIPAddress);
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
@ -3146,8 +3184,7 @@ class FilterCompiler {
return Object.assign(this, other); return Object.assign(this, other);
} }
this.reToken = /[%0-9A-Za-z]+/g; this.reToken = /[%0-9A-Za-z]+/g;
this.fromDomainOptList = []; this.optionValues = new Map();
this.toDomainOptList = [];
this.tokenIdToNormalizedType = new Map([ this.tokenIdToNormalizedType = new Map([
[ sfp.NODE_TYPE_NET_OPTION_NAME_CNAME, bitFromType('cname') ], [ sfp.NODE_TYPE_NET_OPTION_NAME_CNAME, bitFromType('cname') ],
[ sfp.NODE_TYPE_NET_OPTION_NAME_CSS, bitFromType('stylesheet') ], [ sfp.NODE_TYPE_NET_OPTION_NAME_CSS, bitFromType('stylesheet') ],
@ -3304,13 +3341,9 @@ class FilterCompiler {
this.modifyType = undefined; this.modifyType = undefined;
this.modifyValue = undefined; this.modifyValue = undefined;
this.pattern = ''; this.pattern = '';
this.patternMatchCase = false;
this.party = ANYPARTY_REALM; this.party = ANYPARTY_REALM;
this.optionUnitBits = 0; this.optionUnitBits = 0;
this.fromDomainOpt = ''; this.optionValues.clear();
this.toDomainOpt = '';
this.denyallowOpt = '';
this.headerOpt = undefined;
this.isPureHostname = false; this.isPureHostname = false;
this.isGeneric = false; this.isGeneric = false;
this.isRegex = false; this.isRegex = false;
@ -3322,8 +3355,7 @@ class FilterCompiler {
this.notTypeBits = 0; this.notTypeBits = 0;
this.methodBits = 0; this.methodBits = 0;
this.notMethodBits = 0; this.notMethodBits = 0;
this.wildcardPos = -1; this.responseHeadersRealm = false;
this.caretPos = -1;
return this; return this;
} }
@ -3421,26 +3453,36 @@ class FilterCompiler {
case sfp.NODE_TYPE_NET_OPTION_NAME_CSP: case sfp.NODE_TYPE_NET_OPTION_NAME_CSP:
if ( this.processCspOption(parser.getNetOptionValue(id)) === false ) { return false; } if ( this.processCspOption(parser.getNetOptionValue(id)) === false ) { return false; }
break; break;
case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW: case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW: {
this.denyallowOpt = this.processHostnameList( const value = this.processHostnameList(
parser.getNetFilterDenyallowOptionIterator(), parser.getNetFilterDenyallowOptionIterator()
); );
if ( this.denyallowOpt === '' ) { return false; } if ( value === '' ) { return false; }
this.optionValues.set('denyallow', value);
this.optionUnitBits |= DENYALLOW_BIT; this.optionUnitBits |= DENYALLOW_BIT;
break; break;
case sfp.NODE_TYPE_NET_OPTION_NAME_FROM: }
this.fromDomainOpt = this.processHostnameList( case sfp.NODE_TYPE_NET_OPTION_NAME_FROM: {
parser.getNetFilterFromOptionIterator(), const iter = parser.getNetFilterFromOptionIterator();
this.fromDomainOptList const list = [];
); const value = this.processHostnameList(iter, list);
if ( this.fromDomainOpt === '' ) { return false; } if ( value === '' ) { return false; }
this.optionValues.set('from', value);
this.optionValues.set('fromList', list);
this.optionUnitBits |= FROM_BIT; this.optionUnitBits |= FROM_BIT;
break; break;
}
case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: { case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: {
this.headerOpt = parser.getNetOptionValue(id) || ''; this.optionValues.set('header', parser.getNetOptionValue(id) || '');
this.optionUnitBits |= HEADER_BIT; this.optionUnitBits |= HEADER_BIT;
this.responseHeadersRealm = true;
break; break;
} }
case sfp.NODE_TYPE_NET_OPTION_NAME_IPADDRESS:
this.optionValues.set('ipaddress', parser.getNetOptionValue(id) || '');
this.optionUnitBits |= IPADDRESS_BIT;
this.responseHeadersRealm = true;
break;
case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD: case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD:
this.processMethodOption(parser.getNetOptionValue(id)); this.processMethodOption(parser.getNetOptionValue(id));
this.optionUnitBits |= METHOD_BIT; this.optionUnitBits |= METHOD_BIT;
@ -3465,14 +3507,16 @@ class FilterCompiler {
this.optionUnitBits |= MODIFY_BIT; this.optionUnitBits |= MODIFY_BIT;
break; break;
} }
case sfp.NODE_TYPE_NET_OPTION_NAME_TO: case sfp.NODE_TYPE_NET_OPTION_NAME_TO: {
this.toDomainOpt = this.processHostnameList( const iter = parser.getNetFilterToOptionIterator();
parser.getNetFilterToOptionIterator(), const list = [];
this.toDomainOptList const value = this.processHostnameList(iter, list);
); if ( value === '' ) { return false; }
if ( this.toDomainOpt === '' ) { return false; } this.optionValues.set('to', value);
this.optionValues.set('toList', list);
this.optionUnitBits |= TO_BIT; this.optionUnitBits |= TO_BIT;
break; break;
}
default: default:
break; break;
} }
@ -3558,6 +3602,7 @@ class FilterCompiler {
case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW: case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW:
case sfp.NODE_TYPE_NET_OPTION_NAME_FROM: case sfp.NODE_TYPE_NET_OPTION_NAME_FROM:
case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER:
case sfp.NODE_TYPE_NET_OPTION_NAME_IPADDRESS:
case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD: case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD:
case sfp.NODE_TYPE_NET_OPTION_NAME_PERMISSIONS: case sfp.NODE_TYPE_NET_OPTION_NAME_PERMISSIONS:
case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT: case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT:
@ -3591,7 +3636,7 @@ class FilterCompiler {
this.action = BLOCKIMPORTANT_REALM; this.action = BLOCKIMPORTANT_REALM;
break; break;
case sfp.NODE_TYPE_NET_OPTION_NAME_MATCHCASE: case sfp.NODE_TYPE_NET_OPTION_NAME_MATCHCASE:
this.patternMatchCase = true; this.optionValues.set('match-case', true);
break; break;
case sfp.NODE_TYPE_NET_OPTION_NAME_MP4: { case sfp.NODE_TYPE_NET_OPTION_NAME_MP4: {
const id = this.action === ALLOW_REALM const id = this.action === ALLOW_REALM
@ -3661,11 +3706,6 @@ class FilterCompiler {
return this.FILTER_OK; return this.FILTER_OK;
} }
if ( this.isGeneric ) {
this.wildcardPos = this.pattern.indexOf('*');
this.caretPos = this.pattern.indexOf('^');
}
if ( this.pattern.length > 1024 ) { if ( this.pattern.length > 1024 ) {
return this.FILTER_UNSUPPORTED; return this.FILTER_UNSUPPORTED;
} }
@ -3793,7 +3833,7 @@ class FilterCompiler {
isJustOrigin() { isJustOrigin() {
if ( this.optionUnitBits !== FROM_BIT ) { return false; } if ( this.optionUnitBits !== FROM_BIT ) { return false; }
if ( this.isRegex ) { return false; } if ( this.isRegex ) { return false; }
if ( /[/~]/.test(this.fromDomainOpt) ) { return false; } if ( /[/~]/.test(this.optionValues.get('from')) ) { return false; }
if ( this.pattern === '*' ) { return true; } if ( this.pattern === '*' ) { return true; }
if ( this.anchor !== 0b010 ) { return false; } if ( this.anchor !== 0b010 ) { return false; }
if ( /^(?:http[s*]?:(?:\/\/)?)$/.test(this.pattern) ) { return true; } if ( /^(?:http[s*]?:(?:\/\/)?)$/.test(this.pattern) ) { return true; }
@ -3870,7 +3910,7 @@ class FilterCompiler {
} else /* 'http:' */ { } else /* 'http:' */ {
this.tokenHash = ANY_HTTP_TOKEN_HASH; this.tokenHash = ANY_HTTP_TOKEN_HASH;
} }
for ( const hn of this.fromDomainOptList ) { for ( const hn of this.optionValues.get('fromList') ) {
this.compileToAtomicFilter(hn, writer); this.compileToAtomicFilter(hn, writer);
} }
return; return;
@ -3911,31 +3951,36 @@ class FilterCompiler {
} }
// Origin // Origin
if ( this.fromDomainOpt !== '' ) { if ( this.optionValues.has('from') ) {
compileFromDomainOpt( compileFromDomainOpt(
this.fromDomainOptList, this.optionValues.get('fromList'),
units.length !== 0 && patternClass.isSlow === true, units.length !== 0 && patternClass.isSlow === true,
units units
); );
} }
// Destination // Destination
if ( this.toDomainOpt !== '' ) { if ( this.optionValues.has('to') ) {
compileToDomainOpt( compileToDomainOpt(
this.toDomainOptList, this.optionValues.get('toList'),
units.length !== 0 && patternClass.isSlow === true, units.length !== 0 && patternClass.isSlow === true,
units units
); );
} }
// Deny-allow // Deny-allow
if ( this.denyallowOpt !== '' ) { if ( this.optionValues.has('denyallow') ) {
units.push(FilterDenyAllow.compile(this)); units.push(FilterDenyAllow.compile(this));
} }
// Header // Header
if ( this.headerOpt !== undefined ) { if ( this.responseHeadersRealm ) {
units.push(FilterOnHeaders.compile(this)); if ( this.optionValues.has('ipaddress') ) {
units.push(FilterIPAddress.compile(this));
}
if ( this.optionValues.has('header') ) {
units.push(FilterOnHeaders.compile(this));
}
this.action |= HEADERS_REALM; this.action |= HEADERS_REALM;
} }
@ -3977,12 +4022,13 @@ class FilterCompiler {
units.push(FilterPatternGeneric.compile(this)); units.push(FilterPatternGeneric.compile(this));
return FilterPatternGeneric; return FilterPatternGeneric;
} }
if ( this.wildcardPos === -1 ) { if ( this.pattern.includes('*') === false ) {
if ( this.caretPos === -1 ) { const caretPos = this.pattern.indexOf('^');
if ( caretPos === -1 ) {
units.push(FilterPatternPlain.compile(this)); units.push(FilterPatternPlain.compile(this));
return FilterPatternPlain; return FilterPatternPlain;
} }
if ( this.caretPos === (this.pattern.length - 1) ) { if ( caretPos === (this.pattern.length - 1) ) {
this.pattern = this.pattern.slice(0, -1); this.pattern = this.pattern.slice(0, -1);
units.push(FilterPatternPlain.compile(this)); units.push(FilterPatternPlain.compile(this));
units.push(FilterTrailingSeparator.compile()); units.push(FilterTrailingSeparator.compile());
@ -4027,15 +4073,16 @@ class FilterCompiler {
} }
// These are to quickly test whether a filter is composite // These are to quickly test whether a filter is composite
const FROM_BIT = 0b000000001; const FROM_BIT = 0b0000000001;
const TO_BIT = 0b000000010; const TO_BIT = 0b0000000010;
const DENYALLOW_BIT = 0b000000100; const DENYALLOW_BIT = 0b0000000100;
const HEADER_BIT = 0b000001000; const HEADER_BIT = 0b0000001000;
const STRICT_PARTY_BIT = 0b000010000; const STRICT_PARTY_BIT = 0b0000010000;
const MODIFY_BIT = 0b000100000; const MODIFY_BIT = 0b0000100000;
const NOT_TYPE_BIT = 0b001000000; const NOT_TYPE_BIT = 0b0001000000;
const IMPORTANT_BIT = 0b010000000; const IMPORTANT_BIT = 0b0010000000;
const METHOD_BIT = 0b100000000; const METHOD_BIT = 0b0100000000;
const IPADDRESS_BIT = 0b1000000000;
FilterCompiler.prototype.FILTER_OK = 0; FilterCompiler.prototype.FILTER_OK = 0;
FilterCompiler.prototype.FILTER_INVALID = 1; FilterCompiler.prototype.FILTER_INVALID = 1;
@ -4751,6 +4798,7 @@ StaticNetFilteringEngine.prototype.matchAndFetchModifiers = function(
$requestHostname = fctxt.getHostname(); $requestHostname = fctxt.getHostname();
$requestMethodBit = fctxt.method || 0; $requestMethodBit = fctxt.method || 0;
$requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset;
$requestAddress = fctxt.ipaddress || '';
const modifierType = modifierTypeFromName.get(modifierName); const modifierType = modifierTypeFromName.get(modifierName);
const modifierBits = modifierBitsFromType.get(modifierType); const modifierBits = modifierBitsFromType.get(modifierType);
@ -5048,6 +5096,7 @@ StaticNetFilteringEngine.prototype.matchRequestReverse = function(type, url) {
$requestURLRaw = url; $requestURLRaw = url;
$requestMethodBit = 0; $requestMethodBit = 0;
$requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset;
$requestAddress = '';
$isBlockImportant = false; $isBlockImportant = false;
this.$filterUnit = 0; this.$filterUnit = 0;
@ -5116,6 +5165,7 @@ StaticNetFilteringEngine.prototype.matchRequest = function(fctxt, modifiers = 0)
$requestHostname = fctxt.getHostname(); $requestHostname = fctxt.getHostname();
$requestMethodBit = fctxt.method || 0; $requestMethodBit = fctxt.method || 0;
$requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset;
$requestAddress = fctxt.ipaddress || '';
$isBlockImportant = false; $isBlockImportant = false;
// Evaluate block realm before allow realm, and allow realm before // Evaluate block realm before allow realm, and allow realm before
@ -5151,6 +5201,7 @@ StaticNetFilteringEngine.prototype.matchHeaders = function(fctxt, headers) {
$requestHostname = fctxt.getHostname(); $requestHostname = fctxt.getHostname();
$requestMethodBit = fctxt.method || 0; $requestMethodBit = fctxt.method || 0;
$requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset;
$requestAddress = fctxt.ipaddress || '';
$httpHeaders.init(headers); $httpHeaders.init(headers);
let r = 0; let r = 0;