From dc64cfbd97a28d3823a341c3f09594fc28e548cc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 5 Jul 2020 14:11:15 -0400 Subject: [PATCH] Fix properly reporting invalid filter options Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1134 Specifically; - `beacon`, `ping`, and `websocket` cannot be redirected; - it's ok to not specify a type when redirecting to `empty` resource; - `csp=` option can't be mixed with other types, redirec directives, and more `csp=` options. --- .../static-filtering-parser-checklist.txt | 9 ++- src/js/static-filtering-parser.js | 55 ++++++++++--------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/docs/tests/static-filtering-parser-checklist.txt b/docs/tests/static-filtering-parser-checklist.txt index c85bcd5a2..3bcc3a5d2 100644 --- a/docs/tests/static-filtering-parser-checklist.txt +++ b/docs/tests/static-filtering-parser-checklist.txt @@ -23,6 +23,8 @@ $script,redirect=noop.js *$redirect=empty *$xhr,redirect=empty +*$csp=default-src 'none' + @@ -41,5 +43,10 @@ $script,redirect=noop.js ! can't mix csp with other types or redirect directives *$csp=default-src 'none',empty *$csp=default-src 'none',redirect=empty -*$csp=default-src 'none',redirect=empty +*$redirect=empty,csp=default-src 'none' *$csp=default-src 'none',xhr +*$csp=default-src 'none',ghide +*$csp=default-src 'none',csp=script-src 'none' + +! bad regex +/(abc|def/$xhr diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index c15066291..856b4e331 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -1908,7 +1908,8 @@ const OPTDomainList = 1 << 13; const OPTType = 1 << 14; const OPTNetworkType = 1 << 15; const OPTRedirectType = 1 << 16; -const OPTNotSupported = 1 << 17; +const OPTRedirectableType = 1 << 17; +const OPTNotSupported = 1 << 18; /******************************************************************************/ @@ -1993,6 +1994,7 @@ Parser.prototype.OPTDomainList = OPTDomainList; Parser.prototype.OPTType = OPTType; Parser.prototype.OPTNetworkType = OPTNetworkType; Parser.prototype.OPTRedirectType = OPTRedirectType; +Parser.prototype.OPTRedirectableType = OPTRedirectableType; Parser.prototype.OPTNotSupported = OPTNotSupported; /******************************************************************************/ @@ -2006,41 +2008,41 @@ const netOptionTokens = new Map([ [ 'badfilter', OPTTokenBadfilter ], [ 'cname', OPTTokenCname | OPTAllowOnly | OPTType ], [ 'csp', OPTTokenCsp | OPTMustAssign | OPTAllowMayAssign ], - [ 'css', OPTTokenCss | OPTCanNegate | OPTType | OPTNetworkType ], - [ 'stylesheet', OPTTokenCss | OPTCanNegate | OPTType | OPTNetworkType ], + [ 'css', OPTTokenCss | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ], + [ 'stylesheet', OPTTokenCss | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ], [ 'denyallow', OPTTokenDenyAllow | OPTMustAssign | OPTDomainList ], [ 'doc', OPTTokenDoc | OPTType | OPTNetworkType ], [ 'document', OPTTokenDoc | OPTType | OPTNetworkType ], [ 'domain', OPTTokenDomain | OPTMustAssign | OPTDomainList ], [ 'ehide', OPTTokenEhide | OPTType ], [ 'elemhide', OPTTokenEhide | OPTType ], - [ 'empty', OPTTokenEmpty | OPTBlockOnly | OPTType | OPTNetworkType | OPTBlockOnly | OPTRedirectType ], - [ 'frame', OPTTokenFrame | OPTCanNegate | OPTType | OPTNetworkType ], - [ 'subdocument', OPTTokenFrame | OPTCanNegate | OPTType | OPTNetworkType ], - [ 'font', OPTTokenFont | OPTCanNegate | OPTType | OPTNetworkType ], + [ 'empty', OPTTokenEmpty | OPTBlockOnly | OPTRedirectType ], + [ 'frame', OPTTokenFrame | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ], + [ 'subdocument', OPTTokenFrame | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ], + [ 'font', OPTTokenFont | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ], [ 'genericblock', OPTTokenGenericblock | OPTNotSupported ], [ 'ghide', OPTTokenGhide | OPTType ], [ 'generichide', OPTTokenGhide | OPTType ], - [ 'image', OPTTokenImage | OPTCanNegate | OPTType | OPTNetworkType ], + [ 'image', OPTTokenImage | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ], [ 'important', OPTTokenImportant | OPTBlockOnly ], [ 'inline-font', OPTTokenInlineFont | OPTType ], [ 'inline-script', OPTTokenInlineScript | OPTType ], - [ 'media', OPTTokenMedia | OPTCanNegate | OPTType | OPTNetworkType ], - [ 'mp4', OPTTokenMp4 | OPTType | OPTNetworkType | OPTBlockOnly | OPTRedirectType ], - [ 'object', OPTTokenObject | OPTCanNegate | OPTType | OPTNetworkType ], + [ 'media', OPTTokenMedia | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ], + [ 'mp4', OPTTokenMp4 | OPTType | OPTNetworkType | OPTBlockOnly | OPTRedirectType | OPTRedirectableType ], + [ 'object', OPTTokenObject | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ], [ 'object-subrequest', OPTTokenObject | OPTCanNegate | OPTType | OPTNetworkType ], - [ 'other', OPTTokenOther | OPTCanNegate | OPTType | OPTNetworkType ], + [ 'other', OPTTokenOther | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ], [ 'ping', OPTTokenPing | OPTCanNegate | OPTType | OPTNetworkType ], [ 'beacon', OPTTokenPing | OPTCanNegate | OPTType | OPTNetworkType ], [ 'popunder', OPTTokenPopunder | OPTType ], [ 'popup', OPTTokenPopup | OPTType ], [ 'redirect', OPTTokenRedirect | OPTMustAssign | OPTBlockOnly | OPTRedirectType ], [ 'redirect-rule', OPTTokenRedirectRule | OPTMustAssign | OPTBlockOnly | OPTRedirectType ], - [ 'script', OPTTokenScript | OPTCanNegate | OPTType | OPTNetworkType ], + [ 'script', OPTTokenScript | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ], [ 'shide', OPTTokenShide | OPTType ], [ 'specifichide', OPTTokenShide | OPTType ], - [ 'xhr', OPTTokenXhr | OPTCanNegate| OPTType | OPTNetworkType ], - [ 'xmlhttprequest', OPTTokenXhr | OPTCanNegate | OPTType | OPTNetworkType ], + [ 'xhr', OPTTokenXhr | OPTCanNegate| OPTType | OPTNetworkType | OPTRedirectableType ], + [ 'xmlhttprequest', OPTTokenXhr | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ], [ 'webrtc', OPTTokenWebrtc | OPTNotSupported ], [ 'websocket', OPTTokenWebsocket | OPTCanNegate | OPTType | OPTNetworkType ], ]); @@ -2103,7 +2105,7 @@ const NetOptionsIterator = class { const slices = this.parser.slices; const optSlices = this.optSlices; let typeCount = 0; - let networkTypeCount = 0; + let redirectableTypeCount = 0; let redirectIndex = -1; let cspIndex = -1; let writePtr = 0; @@ -2158,8 +2160,8 @@ const NetOptionsIterator = class { // Keep count of types if ( hasBits(descriptor, OPTType) ) { typeCount += 1; - if ( hasBits(descriptor, OPTNetworkType) ) { - networkTypeCount += 1; + if ( hasBits(descriptor, OPTRedirectableType) ) { + redirectableTypeCount += 1; } } // Only one `redirect` or `csp` can be present @@ -2215,8 +2217,8 @@ const NetOptionsIterator = class { } // Invalid combinations of options // - // `csp` can't be used with any other types - if ( cspIndex !== -1 && typeCount !== 0 ) { + // `csp` can't be used with any other types or redirection + if ( cspIndex !== -1 && ( typeCount !== 0 || redirectIndex !== -1 ) ) { optSlices[cspIndex] = OPTTokenInvalid; if ( this.interactive ) { this.parser.markSlices( @@ -2226,17 +2228,18 @@ const NetOptionsIterator = class { ); } } - // `redirect` requires one single network type, EXCEPT for when we + // `redirect` requires one single redirectable type, EXCEPT for when we // redirect to `empty`, in which case it is allowed to not have any // network type specified. if ( - ( redirectIndex !== -1 ) && - ( typeCount !== 1 || networkTypeCount !== 1 ) && - ( typeCount !== 0 || networkTypeCount !== 0 || + redirectIndex !== -1 && + redirectableTypeCount !== 1 && ( + redirectableTypeCount !== 0 || + typeCount !== 0 || this.parser.raw.slice( - this.parser.slices[optSlices[redirectIndex+4]+1], + this.parser.slices[optSlices[redirectIndex+0]+1], this.parser.slices[optSlices[redirectIndex+5]+1] - ) !== 'empty' + ).endsWith('empty') === false ) ) { optSlices[redirectIndex] = OPTTokenInvalid;