1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-11-06 19:02:30 +01:00

Expand static parser to better detect invalid syntax

Specifically:

There can't be more than one `redirect` or `csp`
option.

There can be no type specified when there is a
`csp` option.

There must be one single network-related type when
there is a `redirect` option. Since `empty` and
`mp4` imply a `redirect` and a network-related
type, these can't be used along another `redirect`
or network-related type.

Related commit:
- 01b1ed9a98
This commit is contained in:
Raymond Hill 2020-06-10 12:15:50 -04:00
parent d784fda98b
commit 6926030b68
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2

View File

@ -566,8 +566,7 @@ const Parser = class {
} else if ( this.patternIsDubious() ) { } else if ( this.patternIsDubious() ) {
this.markSpan(this.patternSpan, BITError); this.markSpan(this.patternSpan, BITError);
} }
// Validate options this.netOptionsIterator.init();
for ( const _ of this.options() ) { void _; }
} }
analyzeDomainList(from, to, bitSeparator, canEntity) { analyzeDomainList(from, to, bitSeparator, canEntity) {
@ -977,6 +976,12 @@ const Parser = class {
/******************************************************************************/ /******************************************************************************/
const hasNoBits = (v, bits) => (v & bits) === 0;
const hasBits = (v, bits) => (v & bits) !== 0;
const hasNotAllBits = (v, bits) => (v & bits) !== bits;
/******************************************************************************/
const CATNone = 0; const CATNone = 0;
const CATStaticExtFilter = 1; const CATStaticExtFilter = 1;
const CATStaticNetFilter = 2; const CATStaticNetFilter = 2;
@ -1189,11 +1194,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 OPTNotSupported = 1 << 14; const OPTType = 1 << 14;
const OPTNetworkType = 1 << 15;
const hasNoBits = (v, bits) => (v & bits) === 0; const OPTRedirectType = 1 << 16;
const hasBits = (v, bits) => (v & bits) !== 0; const OPTNotSupported = 1 << 17;
const hasNotAllBits = (v, bits) => (v & bits) !== bits;
/******************************************************************************/ /******************************************************************************/
@ -1294,7 +1298,7 @@ const NetOptionsIterator = class {
this.value = undefined; this.value = undefined;
this.done = true; this.done = true;
} }
[Symbol.iterator]() { init() {
this.readPtr = this.writePtr = 0; this.readPtr = this.writePtr = 0;
this.done = this.parser.optionsSpan.l === 0; this.done = this.parser.optionsSpan.l === 0;
if ( this.done ) { if ( this.done ) {
@ -1317,15 +1321,21 @@ const NetOptionsIterator = class {
const ropts = lopts + this.parser.optionsSpan.l; const ropts = lopts + this.parser.optionsSpan.l;
const slices = this.parser.slices; const slices = this.parser.slices;
const optSlices = this.optSlices; const optSlices = this.optSlices;
let typeCount = 0;
let networkTypeCount = 0;
let redirectIndex = -1;
let cspIndex = -1;
let writePtr = 0; let writePtr = 0;
let lopt = lopts; let lopt = lopts;
while ( lopt < ropts ) { while ( lopt < ropts ) {
let good = true; let good = true;
let ltok = lopt; let ltok = lopt;
// Parse optional negation
if ( hasBits(slices[lopt], BITTilde) ) { if ( hasBits(slices[lopt], BITTilde) ) {
if ( slices[lopt+2] > 1 ) { good = false; } if ( slices[lopt+2] > 1 ) { good = false; }
ltok += 3; ltok += 3;
} }
// Find end of current option
let lval = 0; let lval = 0;
let i = ltok; let i = ltok;
while ( i < ropts ) { while ( i < ropts ) {
@ -1350,6 +1360,7 @@ const NetOptionsIterator = class {
const token = this.parser.raw.slice(slices[ltok+1], slices[rtok+1]); const token = this.parser.raw.slice(slices[ltok+1], slices[rtok+1]);
descriptor = netOptionTokens.get(token); descriptor = netOptionTokens.get(token);
} }
// Validate option according to context
if ( if (
descriptor === undefined || descriptor === undefined ||
ltok !== lopt && hasNoBits(descriptor, OPTCanNegate) || ltok !== lopt && hasNoBits(descriptor, OPTCanNegate) ||
@ -1363,6 +1374,28 @@ const NetOptionsIterator = class {
) { ) {
descriptor = OPTTokenInvalid; descriptor = OPTTokenInvalid;
} }
// Keep count of types
if ( hasBits(descriptor, OPTType) ) {
typeCount += 1;
if ( hasBits(descriptor, OPTNetworkType) ) {
networkTypeCount += 1;
}
}
// Only one `redirect` or `csp` can be present
if ( hasBits(descriptor, OPTRedirectType) ) {
if ( redirectIndex === -1 ) {
redirectIndex = writePtr;
} else {
descriptor = OPTTokenInvalid;
}
} else if ( (descriptor & 0xFF) === OPTTokenCsp ) {
if ( cspIndex === -1 ) {
cspIndex = writePtr;
} else {
descriptor = OPTTokenInvalid;
}
}
// Mark slices in case of invalid filter option
if ( if (
this.interactive && ( this.interactive && (
descriptor === OPTTokenInvalid || descriptor === OPTTokenInvalid ||
@ -1371,17 +1404,26 @@ const NetOptionsIterator = class {
) { ) {
this.parser.markSlices(lopt, i, BITError); this.parser.markSlices(lopt, i, BITError);
} }
// 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;
if ( lval !== 0 ) { if ( lval !== 0 ) {
optSlices[writePtr+3] = lval; optSlices[writePtr+3] = lval;
optSlices[writePtr+4] = lval+3; optSlices[writePtr+4] = lval+3;
if ( this.interactive && hasBits(descriptor, OPTDomainList) ) {
this.parser.analyzeDomainList(
lval + 3, i, BITPipe,
(descriptor & 0xFF) === OPTTokenDomain
);
}
} else { } else {
optSlices[writePtr+3] = i; optSlices[writePtr+3] = i;
optSlices[writePtr+4] = i; optSlices[writePtr+4] = i;
} }
optSlices[writePtr+5] = i; optSlices[writePtr+5] = i;
// Advance to next option
writePtr += 6; writePtr += 6;
lopt = i + 3; lopt = i + 3;
} }
@ -1390,12 +1432,30 @@ const NetOptionsIterator = class {
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;
} }
// TODO: Now that all options are parsed, find out erroneous // Invalid combinations of options
// combinations of options: //
// - redirect(-rule) requires a single discrete type // `csp` can't be used with any other types
// - csp can't be mixed with any other types if ( cspIndex !== -1 && typeCount !== 0 ) {
// - any option should appear only once optSlices[cspIndex] = OPTTokenInvalid;
// - etc. if ( this.interactive ) {
this.parser.markSlices(
optSlices[cspIndex+1],
optSlices[cspIndex+5],
BITError
);
}
}
// `redirect` requires one single network type
if ( redirectIndex !== -1 && typeCount !== 1 && networkTypeCount !== 1 ) {
optSlices[redirectIndex] = OPTTokenInvalid;
if ( this.interactive ) {
this.parser.markSlices(
optSlices[redirectIndex+1],
optSlices[redirectIndex+5],
BITError
);
}
}
return this; return this;
} }
next() { next() {
@ -1416,55 +1476,61 @@ const NetOptionsIterator = class {
parser.slices[optSlices[i+4]+1], parser.slices[optSlices[i+4]+1],
parser.slices[optSlices[i+5]+1] parser.slices[optSlices[i+5]+1]
); );
if ( parser.interactive && hasBits(descriptor, OPTDomainList) ) {
parser.analyzeDomainList(
optSlices[i+4],
optSlices[i+5],
BITPipe,
this.item.id === OPTTokenDomain
);
}
} }
this.readPtr = i + 6; this.readPtr = i + 6;
return this; return this;
} }
[Symbol.iterator]() {
return this.init();
}
}; };
const netOptionTokens = new Map([ const netOptionTokens = new Map([
[ '1p', OPTToken1p | OPTCanNegate ], [ 'first-party', OPTToken1p | OPTCanNegate ], [ '1p', OPTToken1p | OPTCanNegate ],
[ '3p', OPTToken3p | OPTCanNegate ], [ 'third-party', OPTToken3p | OPTCanNegate ], [ 'first-party', OPTToken1p | OPTCanNegate ],
[ 'all', OPTTokenAll ], [ '3p', OPTToken3p | OPTCanNegate ],
[ 'third-party', OPTToken3p | OPTCanNegate ],
[ 'all', OPTTokenAll | OPTType | OPTNetworkType ],
[ 'badfilter', OPTTokenBadfilter ], [ 'badfilter', OPTTokenBadfilter ],
[ 'cname', OPTTokenCname | OPTAllowOnly ], [ 'cname', OPTTokenCname | OPTAllowOnly | OPTType ],
[ 'csp', OPTTokenCsp | OPTMustAssign | OPTAllowMayAssign ], [ 'csp', OPTTokenCsp | OPTMustAssign | OPTAllowMayAssign ],
[ 'css', OPTTokenCss | OPTCanNegate ], [ 'stylesheet', OPTTokenCss | OPTCanNegate ], [ 'css', OPTTokenCss | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'stylesheet', OPTTokenCss | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'denyallow', OPTTokenDenyAllow | OPTMustAssign | OPTDomainList ], [ 'denyallow', OPTTokenDenyAllow | OPTMustAssign | OPTDomainList ],
[ 'doc', OPTTokenDoc ], [ 'document', OPTTokenDoc ], [ 'doc', OPTTokenDoc | OPTType | OPTNetworkType ],
[ 'document', OPTTokenDoc | OPTType | OPTNetworkType ],
[ 'domain', OPTTokenDomain | OPTMustAssign | OPTDomainList ], [ 'domain', OPTTokenDomain | OPTMustAssign | OPTDomainList ],
[ 'ehide', OPTTokenEhide ], [ 'elemhide', OPTTokenEhide ], [ 'ehide', OPTTokenEhide | OPTType ],
[ 'empty', OPTTokenEmpty | OPTBlockOnly ], [ 'elemhide', OPTTokenEhide | OPTType ],
[ 'frame', OPTTokenFrame | OPTCanNegate ], [ 'subdocument', OPTTokenFrame | OPTCanNegate ], [ 'empty', OPTTokenEmpty | OPTBlockOnly | OPTType | OPTNetworkType | OPTBlockOnly | OPTRedirectType ],
[ 'font', OPTTokenFont | OPTCanNegate ], [ 'frame', OPTTokenFrame | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'subdocument', OPTTokenFrame | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'font', OPTTokenFont | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'genericblock', OPTTokenGenericblock | OPTNotSupported ], [ 'genericblock', OPTTokenGenericblock | OPTNotSupported ],
[ 'ghide', OPTTokenGhide ], [ 'generichide', OPTTokenGhide ], [ 'ghide', OPTTokenGhide | OPTType ],
[ 'image', OPTTokenImage | OPTCanNegate ], [ 'generichide', OPTTokenGhide | OPTType ],
[ 'image', OPTTokenImage | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'important', OPTTokenImportant | OPTBlockOnly ], [ 'important', OPTTokenImportant | OPTBlockOnly ],
[ 'inline-font', OPTTokenInlineFont ], [ 'inline-font', OPTTokenInlineFont | OPTType ],
[ 'inline-script', OPTTokenInlineScript ], [ 'inline-script', OPTTokenInlineScript | OPTType ],
[ 'media', OPTTokenMedia | OPTCanNegate ], [ 'media', OPTTokenMedia | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'mp4', OPTTokenMp4 ], [ 'mp4', OPTTokenMp4 | OPTType | OPTNetworkType | OPTBlockOnly | OPTRedirectType ],
[ 'object', OPTTokenObject | OPTCanNegate ], [ 'object-subrequest', OPTTokenObject | OPTCanNegate ], [ 'object', OPTTokenObject | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'other', OPTTokenOther | OPTCanNegate ], [ 'object-subrequest', OPTTokenObject | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'ping', OPTTokenPing | OPTCanNegate ], [ 'beacon', OPTTokenPing | OPTCanNegate ], [ 'other', OPTTokenOther | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'popunder', OPTTokenPopunder ], [ 'ping', OPTTokenPing | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'popup', OPTTokenPopup ], [ 'beacon', OPTTokenPing | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'redirect', OPTTokenRedirect | OPTMustAssign | OPTBlockOnly ], [ 'popunder', OPTTokenPopunder | OPTType ],
[ 'redirect-rule', OPTTokenRedirectRule | OPTMustAssign | OPTBlockOnly ], [ 'popup', OPTTokenPopup | OPTType ],
[ 'script', OPTTokenScript | OPTCanNegate ], [ 'redirect', OPTTokenRedirect | OPTMustAssign | OPTBlockOnly | OPTRedirectType ],
[ 'shide', OPTTokenShide ], [ 'specifichide', OPTTokenShide ], [ 'redirect-rule', OPTTokenRedirectRule | OPTMustAssign | OPTBlockOnly | OPTRedirectType ],
[ 'xhr', OPTTokenXhr | OPTCanNegate ], [ 'xmlhttprequest', OPTTokenXhr | OPTCanNegate ], [ 'script', OPTTokenScript | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'shide', OPTTokenShide | OPTType ],
[ 'specifichide', OPTTokenShide | OPTType ],
[ 'xhr', OPTTokenXhr | OPTCanNegate| OPTType | OPTNetworkType ],
[ 'xmlhttprequest', OPTTokenXhr | OPTCanNegate | OPTType | OPTNetworkType ],
[ 'webrtc', OPTTokenWebrtc | OPTNotSupported ], [ 'webrtc', OPTTokenWebrtc | OPTNotSupported ],
[ 'websocket', OPTTokenWebsocket | OPTCanNegate ], [ 'websocket', OPTTokenWebsocket | OPTCanNegate | OPTType | OPTNetworkType ],
]); ]);
/******************************************************************************/ /******************************************************************************/
@ -1539,7 +1605,7 @@ const ExtOptionsIterator = class {
this.value = undefined; this.value = undefined;
this.done = true; this.done = true;
} }
[Symbol.iterator]() { init() {
const { i, l } = this.parser.optionsSpan; const { i, l } = this.parser.optionsSpan;
this.l = i; this.l = i;
this.r = i + l; this.r = i + l;
@ -1589,6 +1655,9 @@ const ExtOptionsIterator = class {
this.l = i; this.l = i;
return this; return this;
} }
[Symbol.iterator]() {
return this.init();
}
}; };
/******************************************************************************/ /******************************************************************************/