1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-10-06 09:37:12 +02:00

Incrementally improve static filtering parser

Most notably, the `denyallow=` option now requires
the presence of a valid `domain=` option to not be
rejected.

Using `denyallow=` without narrowing down using the
`domain=` option leads to catastrophic blocking
behvior, hence the requirement for a valid `domain=`
option.
This commit is contained in:
Raymond Hill 2020-11-07 13:20:02 -05:00
parent 7da92d3ad1
commit efea83a825
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
2 changed files with 175 additions and 110 deletions

View File

@ -44,7 +44,6 @@ a*
$script,redirect=noop.js $script,redirect=noop.js
*$empty *$empty
*$xhr,empty *$xhr,empty
*$redirect=empty
*$xhr,redirect=empty *$xhr,redirect=empty
*$csp=default-src 'none' *$csp=default-src 'none'
@ -58,7 +57,7 @@ $script,redirect=noop.js
*$domain=toto.com|toto.*|~toto.com|~toto.*|tôtó.ça|tôtó.*|~tôtó.ça|[ff00::0]|1.1.1.1 *$domain=toto.com|toto.*|~toto.com|~toto.*|tôtó.ça|tôtó.*|~tôtó.ça|[ff00::0]|1.1.1.1
! valid denyallow option values ! valid denyallow option values
*$denyallow=toto.com|tôtó.ça|[ff00::0]|1.1.1.1 *$denyallow=toto.com|tôtó.ça|[ff00::0]|1.1.1.1,domain=toto.com
@ -95,10 +94,11 @@ $
! can't redirect without type (except to `empty`) ! can't redirect without type (except to `empty`)
*$redirect=noop.js *$redirect=noop.js
! can't redirect beacon, ping, websocket ! non-redirectable types
*$beacon,redirect-rule=empty *$beacon,redirect-rule=empty
*$ping,redirect-rule=empty *$ping,redirect-rule=empty
*$websocket,redirect-rule=empty *$websocket,redirect-rule=empty
*$ghide,redirect=noop.js
! can't mix csp with other types or redirect directives ! can't mix csp with other types or redirect directives
*$csp=default-src 'none',empty *$csp=default-src 'none',empty
@ -119,4 +119,7 @@ $
*$domain=.toto.com|toto.com.|[ff00::00000]|1.1.1.1111 *$domain=.toto.com|toto.com.|[ff00::00000]|1.1.1.1111
! invalid denyallow= option values ! invalid denyallow= option values
*$denyallow=.toto.com|toto.com.|toto.*|~toto.com|~toto.*|[ff00::00000]|1.1.1.1111 *$denyallow=.toto.com|toto.com.|toto.*|~toto.com|~toto.*|[ff00::00000]|1.1.1.1111,domain=toto.com
! denyallow= requires a domain= option
*$script,denyallow=toto.com

View File

@ -28,13 +28,13 @@
Roughly, this is how things work: each input string (passed to analyze()) Roughly, this is how things work: each input string (passed to analyze())
is decomposed into a minimal set of distinct slices. Each slice is a is decomposed into a minimal set of distinct slices. Each slice is a
triplet of integers consisiting of: triplet of integers consisting of:
- a bit vector describing the characters inside the slice - a bit vector describing the characters inside the slice
- an index of where in the origin string the slice starts - an index of where in the origin string the slice starts
- a length for the number of character in the slice - a length for the number of character in the slice
Slice descriptor are all flatly stored in an array of integer so as to Slice descriptors are all flatly stored in an array of integers so as to
avoid the need for a secondary data structure. Example: avoid the need for a secondary data structure. Example:
raw string: toto.com raw string: toto.com
@ -60,7 +60,7 @@
The array used to hold the slices is reused across string analysis, in The array used to hold the slices is reused across string analysis, in
order to eliminate memory churning. order to eliminate memory churning.
Above the slices, there are various span objects used to describe Beyond the slices, there are various span objects used to describe
consecutive sequences of slices and which are filled in as a result consecutive sequences of slices and which are filled in as a result
of parsing. of parsing.
@ -405,7 +405,7 @@ const Parser = class {
if ( this.findFirstOdd(0, BITHostname | BITComma | BITAsterisk) === i ) { if ( this.findFirstOdd(0, BITHostname | BITComma | BITAsterisk) === i ) {
this.flavorBits |= BITFlavorError; this.flavorBits |= BITFlavorError;
if ( this.interactive ) { if ( this.interactive ) {
this.markSlices(i, i+3, BITError); this.errorSlices(i, i+3);
} }
} else { } else {
this.splitSlot(i, len - 1); this.splitSlot(i, len - 1);
@ -639,10 +639,9 @@ const Parser = class {
this.patternIsDubious() === false && this.patternIsDubious() === false &&
this.toASCII(true) === false this.toASCII(true) === false
) { ) {
this.markSlices( this.errorSlices(
this.patternLeftAnchorSpan.i, this.patternLeftAnchorSpan.i,
this.optionsAnchorSpan.i, this.optionsAnchorSpan.i
BITError
); );
} }
this.netOptionsIterator.init(); this.netOptionsIterator.init();
@ -653,22 +652,22 @@ const Parser = class {
let beg = from; let beg = from;
// Dangling leading separator? // Dangling leading separator?
if ( hasBits(this.slices[beg], bitSeparator) ) { if ( hasBits(this.slices[beg], bitSeparator) ) {
this.markSlices(beg, beg + 3, BITError); this.errorSlices(beg, beg + 3);
beg += 3; beg += 3;
} }
while ( beg < to ) { while ( beg < to ) {
let end = this.skipUntil(beg, to, bitSeparator); let end = this.skipUntil(beg, to, bitSeparator);
if ( end < to && this.slices[end+2] !== 1 ) { if ( end < to && this.slices[end+2] !== 1 ) {
this.markSlices(end, end + 3, BITError); this.errorSlices(end, end + 3);
} }
if ( this.analyzeDomain(beg, end, optionBits) === false ) { if ( this.analyzeDomain(beg, end, optionBits) === false ) {
this.markSlices(beg, end, BITError); this.errorSlices(beg, end);
} }
beg = end + 3; beg = end + 3;
} }
// Dangling trailing separator? // Dangling trailing separator?
if ( hasBits(this.slices[to-3], bitSeparator) ) { if ( hasBits(this.slices[to-3], bitSeparator) ) {
this.markSlices(to - 3, to, BITError); this.errorSlices(to - 3, to);
} }
} }
@ -827,6 +826,10 @@ const Parser = class {
} }
} }
errorSlices(beg, end) {
this.markSlices(beg, end, BITError);
}
findFirstMatch(from, bits) { findFirstMatch(from, bits) {
let to = from; let to = from;
while ( to < this.sliceWritePtr ) { while ( to < this.sliceWritePtr ) {
@ -943,7 +946,7 @@ const Parser = class {
? this.optionsAnchorSpan.i ? this.optionsAnchorSpan.i
: this.optionsSpan.i; : this.optionsSpan.i;
} }
this.markSlices(l, r, BITError); this.errorSlices(l, r);
return true; return true;
} }
@ -1892,6 +1895,7 @@ const BITFlavorNetRightAnchor = BITFlavorNetRightURLAnchor | BITFlavorNetRigh
const BITFlavorNetHnAnchor = BITFlavorNetLeftHnAnchor | BITFlavorNetRightHnAnchor; const BITFlavorNetHnAnchor = BITFlavorNetLeftHnAnchor | BITFlavorNetRightHnAnchor;
const BITFlavorNetAnchor = BITFlavorNetLeftAnchor | BITFlavorNetRightAnchor; const BITFlavorNetAnchor = BITFlavorNetLeftAnchor | BITFlavorNetRightAnchor;
const OPTTokenMask = 0x000000ff;
const OPTTokenInvalid = 0; const OPTTokenInvalid = 0;
const OPTToken1p = 1; const OPTToken1p = 1;
const OPTToken3p = 2; const OPTToken3p = 2;
@ -1928,19 +1932,26 @@ const OPTTokenShide = 32;
const OPTTokenXhr = 33; const OPTTokenXhr = 33;
const OPTTokenWebrtc = 34; const OPTTokenWebrtc = 34;
const OPTTokenWebsocket = 35; const OPTTokenWebsocket = 35;
const OPTTokenCount = 36;
//const OPTPerOptionMask = 0x0000ff00;
const OPTCanNegate = 1 << 8; const OPTCanNegate = 1 << 8;
const OPTBlockOnly = 1 << 9; const OPTBlockOnly = 1 << 9;
const OPTAllowOnly = 1 << 10; const OPTAllowOnly = 1 << 10;
const OPTMustAssign = 1 << 11; const OPTMustAssign = 1 << 11;
const OPTAllowMayAssign = 1 << 12; const OPTAllowMayAssign = 1 << 12;
const OPTDomainList = 1 << 13; const OPTDomainList = 1 << 13;
const OPTType = 1 << 14;
const OPTNetworkType = 1 << 15; //const OPTGlobalMask = 0x0fff0000;
const OPTRedirectType = 1 << 16; const OPTNetworkType = 1 << 16;
const OPTModifiableType = 1 << 17; const OPTNonNetworkType = 1 << 17;
const OPTModifierType = 1 << 18; const OPTModifiableType = 1 << 18;
const OPTNotSupported = 1 << 19; const OPTModifierType = 1 << 19;
const OPTRedirectableType = 1 << 20;
const OPTNonRedirectableType = 1 << 21;
const OPTNonCspableType = 1 << 22;
const OPTNeedDomainOpt = 1 << 23;
const OPTNotSupported = 1 << 24;
/******************************************************************************/ /******************************************************************************/
@ -2023,9 +2034,7 @@ Parser.prototype.OPTAllowOnly = OPTAllowOnly;
Parser.prototype.OPTMustAssign = OPTMustAssign; Parser.prototype.OPTMustAssign = OPTMustAssign;
Parser.prototype.OPTAllowMayAssign = OPTAllowMayAssign; Parser.prototype.OPTAllowMayAssign = OPTAllowMayAssign;
Parser.prototype.OPTDomainList = OPTDomainList; Parser.prototype.OPTDomainList = OPTDomainList;
Parser.prototype.OPTType = OPTType;
Parser.prototype.OPTNetworkType = OPTNetworkType; Parser.prototype.OPTNetworkType = OPTNetworkType;
Parser.prototype.OPTRedirectType = OPTRedirectType;
Parser.prototype.OPTModifiableType = OPTModifiableType; Parser.prototype.OPTModifiableType = OPTModifiableType;
Parser.prototype.OPTNotSupported = OPTNotSupported; Parser.prototype.OPTNotSupported = OPTNotSupported;
@ -2036,48 +2045,48 @@ const netOptionTokenDescriptors = new Map([
[ 'first-party', OPTToken1p | OPTCanNegate ], [ 'first-party', OPTToken1p | OPTCanNegate ],
[ '3p', OPTToken3p | OPTCanNegate ], [ '3p', OPTToken3p | OPTCanNegate ],
[ 'third-party', OPTToken3p | OPTCanNegate ], [ 'third-party', OPTToken3p | OPTCanNegate ],
[ 'all', OPTTokenAll | OPTType | OPTNetworkType ], [ 'all', OPTTokenAll | OPTNetworkType | OPTNonCspableType ],
[ 'badfilter', OPTTokenBadfilter ], [ 'badfilter', OPTTokenBadfilter ],
[ 'cname', OPTTokenCname | OPTAllowOnly | OPTType ], [ 'cname', OPTTokenCname | OPTAllowOnly | OPTModifierType ],
[ 'csp', OPTTokenCsp | OPTMustAssign | OPTAllowMayAssign | OPTModifierType ], [ 'csp', OPTTokenCsp | OPTMustAssign | OPTAllowMayAssign | OPTModifierType ],
[ 'css', OPTTokenCss | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'css', OPTTokenCss | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
[ 'stylesheet', OPTTokenCss | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'stylesheet', OPTTokenCss | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
[ 'denyallow', OPTTokenDenyAllow | OPTMustAssign | OPTDomainList ], [ 'denyallow', OPTTokenDenyAllow | OPTMustAssign | OPTDomainList | OPTNeedDomainOpt | OPTNonCspableType ],
[ 'doc', OPTTokenDoc | OPTType | OPTNetworkType | OPTCanNegate | OPTModifiableType ], [ 'doc', OPTTokenDoc | OPTNetworkType | OPTCanNegate | OPTModifiableType | OPTRedirectableType ],
[ 'document', OPTTokenDoc | OPTType | OPTNetworkType | OPTCanNegate | OPTModifiableType ], [ 'document', OPTTokenDoc | OPTNetworkType | OPTCanNegate | OPTModifiableType | OPTRedirectableType ],
[ 'domain', OPTTokenDomain | OPTMustAssign | OPTDomainList ], [ 'domain', OPTTokenDomain | OPTMustAssign | OPTDomainList ],
[ 'ehide', OPTTokenEhide | OPTType ], [ 'ehide', OPTTokenEhide | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ],
[ 'elemhide', OPTTokenEhide | OPTType ], [ 'elemhide', OPTTokenEhide | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ],
[ 'empty', OPTTokenEmpty | OPTBlockOnly | OPTRedirectType | OPTModifierType ], [ 'empty', OPTTokenEmpty | OPTBlockOnly | OPTModifierType ],
[ 'frame', OPTTokenFrame | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'frame', OPTTokenFrame | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType ],
[ 'subdocument', OPTTokenFrame | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'subdocument', OPTTokenFrame | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType ],
[ 'font', OPTTokenFont | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'font', OPTTokenFont | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTNonCspableType ],
[ 'genericblock', OPTTokenGenericblock | OPTNotSupported ], [ 'genericblock', OPTTokenGenericblock | OPTNotSupported ],
[ 'ghide', OPTTokenGhide | OPTType ], [ 'ghide', OPTTokenGhide | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ],
[ 'generichide', OPTTokenGhide | OPTType ], [ 'generichide', OPTTokenGhide | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ],
[ 'image', OPTTokenImage | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'image', OPTTokenImage | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
[ 'important', OPTTokenImportant | OPTBlockOnly ], [ 'important', OPTTokenImportant | OPTBlockOnly ],
[ 'inline-font', OPTTokenInlineFont | OPTType | OPTCanNegate ], [ 'inline-font', OPTTokenInlineFont | OPTNonNetworkType | OPTCanNegate | OPTNonCspableType | OPTNonRedirectableType ],
[ 'inline-script', OPTTokenInlineScript | OPTType | OPTCanNegate ], [ 'inline-script', OPTTokenInlineScript | OPTNonNetworkType | OPTCanNegate | OPTNonCspableType | OPTNonRedirectableType ],
[ 'media', OPTTokenMedia | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'media', OPTTokenMedia | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
[ 'mp4', OPTTokenMp4 | OPTType | OPTNetworkType | OPTBlockOnly | OPTRedirectType | OPTModifierType ], [ 'mp4', OPTTokenMp4 | OPTNetworkType | OPTBlockOnly | OPTModifierType ],
[ 'object', OPTTokenObject | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'object', OPTTokenObject | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
[ 'object-subrequest', OPTTokenObject | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'object-subrequest', OPTTokenObject | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
[ 'other', OPTTokenOther | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'other', OPTTokenOther | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
[ 'ping', OPTTokenPing | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'ping', OPTTokenPing | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTNonCspableType | OPTNonRedirectableType ],
[ 'beacon', OPTTokenPing | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'beacon', OPTTokenPing | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTNonCspableType | OPTNonRedirectableType ],
[ 'popunder', OPTTokenPopunder | OPTType ], [ 'popunder', OPTTokenPopunder | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ],
[ 'popup', OPTTokenPopup | OPTType | OPTCanNegate ], [ 'popup', OPTTokenPopup | OPTNonNetworkType | OPTCanNegate | OPTNonCspableType | OPTNonRedirectableType ],
[ 'queryprune', OPTTokenQueryprune | OPTMustAssign | OPTAllowMayAssign | OPTModifierType ], [ 'queryprune', OPTTokenQueryprune | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType | OPTNonRedirectableType ],
[ 'redirect', OPTTokenRedirect | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTRedirectType ], [ 'redirect', OPTTokenRedirect | OPTMustAssign | OPTAllowMayAssign | OPTModifierType ],
[ 'redirect-rule', OPTTokenRedirectRule | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTRedirectType ], [ 'redirect-rule', OPTTokenRedirectRule | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType ],
[ 'script', OPTTokenScript | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'script', OPTTokenScript | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
[ 'shide', OPTTokenShide | OPTType ], [ 'shide', OPTTokenShide | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ],
[ 'specifichide', OPTTokenShide | OPTType ], [ 'specifichide', OPTTokenShide | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ],
[ 'xhr', OPTTokenXhr | OPTCanNegate| OPTType | OPTNetworkType | OPTModifiableType ], [ 'xhr', OPTTokenXhr | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
[ 'xmlhttprequest', OPTTokenXhr | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'xmlhttprequest', OPTTokenXhr | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
[ 'webrtc', OPTTokenWebrtc | OPTNotSupported ], [ 'webrtc', OPTTokenWebrtc | OPTNotSupported ],
[ 'websocket', OPTTokenWebsocket | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ], [ 'websocket', OPTTokenWebsocket | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTNonCspableType | OPTNonRedirectableType ],
]); ]);
Parser.prototype.netOptionTokenDescriptors = Parser.prototype.netOptionTokenDescriptors =
@ -2191,6 +2200,11 @@ const NetOptionsIterator = class {
this.optSlices = []; this.optSlices = [];
this.writePtr = 0; this.writePtr = 0;
this.readPtr = 0; this.readPtr = 0;
this.tokenPos = (( ) => {
const out = [];
for ( let i = 0; i < OPTTokenCount; i++ ) { out[i] = -1; }
return out;
})();
this.item = { this.item = {
id: OPTTokenInvalid, id: OPTTokenInvalid,
val: undefined, val: undefined,
@ -2221,13 +2235,12 @@ const NetOptionsIterator = class {
// //
// At index 0 is the option descriptor. // At index 0 is the option descriptor.
// At indices 1-5 is a slice index. // At indices 1-5 is a slice index.
this.tokenPos.fill(-1);
const lopts = this.parser.optionsSpan.i; const lopts = this.parser.optionsSpan.i;
const ropts = lopts + this.parser.optionsSpan.len; const ropts = lopts + this.parser.optionsSpan.len;
const slices = this.parser.slices; const slices = this.parser.slices;
const optSlices = this.optSlices; const optSlices = this.optSlices;
let typeCount = 0; let allBits = 0;
let modifiableTypeCount = 0;
let modifierIndex = -1;
let writePtr = 0; let writePtr = 0;
let lopt = lopts; let lopt = lopts;
while ( lopt < ropts ) { while ( lopt < ropts ) {
@ -2266,6 +2279,7 @@ const NetOptionsIterator = class {
// Validate option according to context // Validate option according to context
if ( if (
descriptor === undefined || descriptor === undefined ||
hasBits(descriptor, OPTNotSupported) ||
ltok !== lopt && hasNoBits(descriptor, OPTCanNegate) || ltok !== lopt && hasNoBits(descriptor, OPTCanNegate) ||
this.exception && hasBits(descriptor, OPTBlockOnly) || this.exception && hasBits(descriptor, OPTBlockOnly) ||
this.exception === false && hasBits(descriptor, OPTAllowOnly) || this.exception === false && hasBits(descriptor, OPTAllowOnly) ||
@ -2277,35 +2291,30 @@ const NetOptionsIterator = class {
) { ) {
descriptor = OPTTokenInvalid; descriptor = OPTTokenInvalid;
} }
// Keep count of types // Keep track of which options are present: any given option can
if ( hasBits(descriptor, OPTType) ) { // appear only once.
typeCount += 1; const tokenId = descriptor & OPTTokenMask;
const modifiable = hasBits(descriptor, OPTModifiableType); if ( tokenId !== OPTTokenInvalid ) {
if ( modifiable ) { if ( this.tokenPos[tokenId] !== -1 ) {
modifiableTypeCount += 1;
} else if ( modifierIndex !== -1 ) {
descriptor = OPTTokenInvalid; descriptor = OPTTokenInvalid;
} else {
this.tokenPos[tokenId] = writePtr;
} }
} }
// Only one modifier can be present // Only one modifier can be present
if ( hasBits(descriptor, OPTModifierType) ) {
if ( modifierIndex === -1 ) {
modifierIndex = writePtr;
} else {
descriptor = OPTTokenInvalid;
}
}
// Mark slices in case of invalid filter option
if ( if (
this.interactive && ( hasBits(descriptor, OPTModifierType) &&
descriptor === OPTTokenInvalid || hasBits(allBits, OPTModifierType)
hasBits(descriptor, OPTNotSupported)
)
) { ) {
this.parser.markSlices(lopt, i, BITError); descriptor = OPTTokenInvalid;
} }
// Store indices to raw slices -- this will be used during // Accumulate description bits
// iteration allBits |= descriptor;
// Mark slices in case of invalid filter option
if ( this.interactive && descriptor === OPTTokenInvalid ) {
this.parser.errorSlices(lopt, i);
}
// Store indices to raw slices, this will be used during iteration
optSlices[writePtr+0] = descriptor; optSlices[writePtr+0] = descriptor;
optSlices[writePtr+1] = lopt; optSlices[writePtr+1] = lopt;
optSlices[writePtr+2] = ltok; optSlices[writePtr+2] = ltok;
@ -2315,7 +2324,7 @@ const NetOptionsIterator = class {
if ( this.interactive && hasBits(descriptor, OPTDomainList) ) { if ( this.interactive && hasBits(descriptor, OPTDomainList) ) {
this.parser.analyzeDomainList( this.parser.analyzeDomainList(
lval + 3, i, BITPipe, lval + 3, i, BITPipe,
(descriptor & 0xFF) === OPTTokenDomain ? 0b1010 : 0b0000 tokenId === OPTTokenDomain ? 0b1010 : 0b0000
); );
} }
} else { } else {
@ -2329,31 +2338,84 @@ const NetOptionsIterator = class {
} }
this.writePtr = writePtr; this.writePtr = writePtr;
// Dangling comma // Dangling comma
if ( this.interactive && hasBits(this.parser.slices[ropts-3], BITComma) ) { if (
this.interactive &&
hasBits(this.parser.slices[ropts-3], BITComma)
) {
this.parser.slices[ropts-3] |= BITError; this.parser.slices[ropts-3] |= BITError;
} }
// `redirect` requires one single redirectable type, EXCEPT for when we // `denyallow=` option requires `domain=` option.
// redirect to `empty`, in which case it is allowed to not have any {
// network type specified. const i = this.tokenPos[OPTTokenDenyAllow];
if ( if ( i !== -1 && this.tokenPos[OPTTokenDomain] === -1 ) {
modifierIndex !== -1 && optSlices[i] = OPTTokenInvalid;
hasBits(optSlices[modifierIndex+0], OPTRedirectType) && if ( this.interactive ) {
modifiableTypeCount !== 1 && ( this.parser.errorSlices(optSlices[i+1], optSlices[i+5]);
modifiableTypeCount !== 0 || }
typeCount !== 0 || }
this.parser.raw.slice( }
this.parser.slices[optSlices[modifierIndex+0]+1], // `redirect=`: requires at least one single redirectable type
this.parser.slices[optSlices[modifierIndex+5]+1] {
).endsWith('empty') === false let i = this.tokenPos[OPTTokenRedirect];
) if ( i === -1 ) {
) { i = this.tokenPos[OPTTokenRedirectRule];
optSlices[modifierIndex] = OPTTokenInvalid; }
if ( this.interactive ) { if (
this.parser.markSlices( i !== -1 && (
optSlices[modifierIndex+1], hasNoBits(allBits, OPTRedirectableType) ||
optSlices[modifierIndex+5], hasBits(allBits, OPTNonRedirectableType)
BITError )
); ) {
optSlices[i] = OPTTokenInvalid;
if ( this.interactive ) {
this.parser.errorSlices(optSlices[i+1], optSlices[i+5]);
}
}
}
// `empty`: can't apply to non-redirectable types
{
let i = this.tokenPos[OPTTokenEmpty];
if ( i !== -1 && hasBits(allBits, OPTNonRedirectableType) ) {
optSlices[i] = OPTTokenInvalid;
if ( this.interactive ) {
this.parser.errorSlices(optSlices[i+1], optSlices[i+5]);
}
}
}
// `csp=`: only to "csp-able" types, which currently are only
// document types.
{
const i = this.tokenPos[OPTTokenCsp];
if ( i !== -1 && hasBits(allBits, OPTNonCspableType) ) {
optSlices[i] = OPTTokenInvalid;
if ( this.interactive ) {
this.parser.errorSlices(optSlices[i+1], optSlices[i+5]);
}
}
}
// `queryprune=`: only for network requests.
{
const i = this.tokenPos[OPTTokenQueryprune];
if ( i !== -1 && hasBits(allBits, OPTNonNetworkType) ) {
optSlices[i] = OPTTokenInvalid;
if ( this.interactive ) {
this.parser.errorSlices(optSlices[i+1], optSlices[i+5]);
}
}
}
// `cname`: can't be used with any type
{
const i = this.tokenPos[OPTTokenCname];
if (
i !== -1 && (
hasBits(allBits, OPTNetworkType) ||
hasBits(allBits, OPTNonNetworkType)
)
) {
optSlices[i] = OPTTokenInvalid;
if ( this.interactive ) {
this.parser.errorSlices(optSlices[i+1], optSlices[i+5]);
}
} }
} }
return this; return this;
@ -2367,7 +2429,7 @@ const NetOptionsIterator = class {
} }
const optSlices = this.optSlices; const optSlices = this.optSlices;
const descriptor = optSlices[i+0]; const descriptor = optSlices[i+0];
this.item.id = descriptor & 0xFF; this.item.id = descriptor & OPTTokenMask;
this.item.not = optSlices[i+2] !== optSlices[i+1]; this.item.not = optSlices[i+2] !== optSlices[i+1];
this.item.val = undefined; this.item.val = undefined;
if ( optSlices[i+4] !== optSlices[i+5] ) { if ( optSlices[i+4] !== optSlices[i+5] ) {