Better align syntax of `header=` option to that of `queryprune=`

The header value is no longer implicitly a regex-based literal, but
a plain string against which the header name is compared. The value can
be set to a regex literal by bracing the header value with the usual
forward slashes, `/.../`.

Examples:

    *$1p,strict3p,script,header=via:1.1 google
    *$1p,strict3p,script,header=via:/1\.1\s+google/

The first form will cause a strict comparison with the value of the header
named `via` against the string `1.1 google`.

The second form will cause a regex-based test with the value of the header
named `via` against the regex `/1\.1\s+google/`.

The header value can be prepended with `~` to reverse the comparison:

    *$1p,strict3p,script,header=via:~1.1 google

The header value is optional and may be ommitted to test only for the
presence of a specific header:

    *$1p,strict3p,script,header=via
This commit is contained in:
Raymond Hill 2020-11-30 09:09:37 -05:00
parent ed64039912
commit 5db8d05975
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
2 changed files with 62 additions and 35 deletions

View File

@ -1161,8 +1161,9 @@ const Parser = class {
BITFlavorError | BITFlavorUnsupported | BITFlavorIgnore
);
}
static parseQueryPruneValue(arg) {
let s = arg;
let s = arg.trim();
if ( s === '*' ) { return { all: true }; }
const out = { };
out.not = s.charCodeAt(0) === 0x7E /* '~' */;
@ -1196,6 +1197,29 @@ const Parser = class {
out.name = s;
return out;
}
static parseHeaderValue(arg) {
let s = arg.trim();
const out = { };
let pos = s.indexOf(':');
if ( pos === -1 ) { pos = s.length; }
out.name = s.slice(0, pos);
out.bad = out.name === '';
s = s.slice(pos + 1);
out.not = s.charCodeAt(0) === 0x7E /* '~' */;
if ( out.not ) { s = s.slice(1); }
out.value = s;
const match = /^\/(.+)\/(i)?$/.exec(s);
if ( match !== null ) {
try {
out.re = new RegExp(match[1], match[2] || '');
}
catch(ex) {
out.bad = true;
}
}
return out;
}
};
/******************************************************************************/
@ -2520,10 +2544,27 @@ const NetOptionsIterator = class {
// `header`: can't be used with any modifier type
{
const i = this.tokenPos[OPTTokenHeader];
if ( i !== -1 && hasBits(allBits, OPTModifierType) ) {
optSlices[i] = OPTTokenInvalid;
if ( this.interactive ) {
this.parser.errorSlices(optSlices[i+1], optSlices[i+5]);
if ( i !== -1 ) {
if ( hasBits(allBits, OPTModifierType) ) {
optSlices[i] = OPTTokenInvalid;
if ( this.interactive ) {
this.parser.errorSlices(optSlices[i+1], optSlices[i+5]);
}
} else {
const val = this.parser.strFromSlices(
optSlices[i+4],
optSlices[i+5] - 3
);
const r = Parser.parseHeaderValue(val);
if ( r.bad ) {
optSlices[i] = OPTTokenInvalid;
if ( this.interactive ) {
this.parser.errorSlices(
optSlices[i+4],
optSlices[i+5]
);
}
}
}
}
}

View File

@ -19,8 +19,6 @@
Home: https://github.com/gorhill/uBlock
*/
/* jshint bitwise: false */
'use strict';
/******************************************************************************/
@ -2389,32 +2387,22 @@ registerFilterClass(FilterStrictParty);
const FilterOnHeaders = class {
constructor(headerOpt) {
this.headerOpt = headerOpt;
if ( headerOpt !== '' ) {
let pos = headerOpt.indexOf(':');
if ( pos === -1 ) { pos = headerOpt.length; }
this.name = headerOpt.slice(0, pos);
this.value = headerOpt.slice(pos + 1);
this.not = this.value.charCodeAt(0) === 0x21 /* '!' */;
if ( this.not ) { this.value.slice(1); }
} else {
this.name = this.value = '';
this.not = false;
}
this.reValue = null;
this.parsed = undefined;
}
match() {
if ( this.name === '' ) { return true; }
const value = $httpHeaders.lookup(this.name);
if ( value === undefined ) { return false; }
if ( this.value === '' ) { return true; }
if ( this.reValue === null ) {
let reText = this.value;
if ( reText.startsWith('|') ) { reText = '^' + reText.slice(1); }
if ( reText.endsWith('|') ) { reText = reText.slice(0, -1) + '$'; }
this.reValue = new RegExp(reText, 'i');
if ( this.parsed === undefined ) {
this.parsed =
vAPI.StaticFilteringParser.parseHeaderValue(this.headerOpt);
}
return this.reValue.test(value) !== this.not;
const { bad, name, not, re, value } = this.parsed;
if ( bad ) { return false; }
const headerValue = $httpHeaders.lookup(name);
if ( headerValue === undefined ) { return false; }
if ( value === '' ) { return true; }
return re === undefined
? (headerValue === value) !== not
: re.test(headerValue) !== not;
}
logData(details) {
@ -4231,12 +4219,10 @@ FilterContainer.prototype.matchHeaders = function(fctxt, headers) {
let r = 0;
if ( this.realmMatchString(Headers | BlockImportant, typeValue, partyBits) ) {
r = 1;
}
if ( r !== 1 && this.realmMatchString(Headers | BlockAction, typeValue, partyBits) ) {
r = 1;
if ( r === 1 && this.realmMatchString(Headers | AllowAction, typeValue, partyBits) ) {
r = 2;
}
} else if ( this.realmMatchString(Headers | BlockAction, typeValue, partyBits) ) {
r = this.realmMatchString(Headers | AllowAction, typeValue, partyBits)
? 2
: 1;
}
$httpHeaders.reset();