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:
parent
d784fda98b
commit
6926030b68
@ -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();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
Loading…
Reference in New Issue
Block a user