diff --git a/controllers/scriptStorage.js b/controllers/scriptStorage.js index 29b860051..0ddb756a4 100644 --- a/controllers/scriptStorage.js +++ b/controllers/scriptStorage.js @@ -1831,6 +1831,40 @@ exports.storeScript = function (aUser, aMeta, aBuf, aUpdate, aCallback) { aInnerCallback(null); }, + function (aInnerCallback) { + // `@connect` validations + hasInvalidKey = scriptStorageLib.invalidKey( + userName, + scriptName, + isLib, + 'connect', + findMeta(aMeta, 'UserScript.connect.value') + ); + + if (hasInvalidKey) { + aInnerCallback(hasInvalidKey, null); + return; + } + + aInnerCallback(null); + }, + function (aInnerCallback) { + // `@antifeature` validations + hasInvalidKey = scriptStorageLib.invalidKey( + userName, + scriptName, + isLib, + 'antifeature', + findMeta(aMeta, 'UserScript.antifeature.value1') + ); + + if (hasInvalidKey) { + aInnerCallback(hasInvalidKey, null); + return; + } + + aInnerCallback(null); + }, function (aInnerCallback) { // `@noframes` validations hasInvalidKey = scriptStorageLib.invalidKey( diff --git a/libs/scriptStorage.js b/libs/scriptStorage.js index 24b12dfd7..91264a49a 100644 --- a/libs/scriptStorage.js +++ b/libs/scriptStorage.js @@ -57,6 +57,47 @@ function invalidKey(aAuthorName, aScriptName, aIsLib, aKeyName, aKeyValue) { // } } break; + case 'antifeature': + if (aKeyValue) { + aKeyValue.forEach(function (aElement, aIndex, aArray) { + switch (aElement) { + case 'ads': + case 'membership': + case 'miner': + case 'referral-link': + case 'tracking': + // fallsthrough + break; + default: + hasInvalidKeys.push( + new statusError({ + message: '`@' + aKeyName + + '` with value of `' + aElement + '` is not valid or supported.', + code: 400 // Bad request + }) + ); + } + }); + + if (hasInvalidKeys.length > 0) { + // NOTE: return only first error since header limitations may throw if RFC2047'd + // TODO: May expand this later + return hasInvalidKeys[0]; + } + + } + break; + case 'connect': + if (aIsLib) { + if (aKeyValue) { + return new statusError({ + message: '`@' + aKeyName + + '` not valid in a Library.', + code: 400 // Bad request + }); + } + } + break; case 'downloadURL': if (aIsLib) { if (aKeyValue) { diff --git a/public/pegjs/blockUserScript.pegjs b/public/pegjs/blockUserScript.pegjs index d6e81e94e..d629e008f 100644 --- a/public/pegjs/blockUserScript.pegjs +++ b/public/pegjs/blockUserScript.pegjs @@ -22,6 +22,7 @@ Test the generated parser with some input for peg.js site at https://pegjs.org/o // @noframes // @grant GM_log // @grant none +// @connect example.com // @homepageURL http://example.com/foo/atHomepageURL1 // @homepage http://example.com/foo/atHomepage2 // @website http://example.com/foo/atSite3 @@ -45,10 +46,25 @@ Test the generated parser with some input for peg.js site at https://pegjs.org/o // @exclude http://example.com/foo // @exclude http://example.org/foo // @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=your.email@example.org&item_name=OpenUserJS+Author+Donation +// @antifeature ads +// @antifeature ads This script contains too many ads. +// @antifeature:ru ads Этот скрипт содержит рекламу. +// @antifeature membership +// @antifeature membership This script requires an account for full functionality. +// @antifeature miner +// @antifeature miner This script uses a lot of electricity on your behalf. +// @antifeature referral-link +// @antifeature referral-link This script makes money for the Author. +// @antifeature tracking +// @antifeature tracking This script contains a tracking of your activity. // ==/UserScript== */ +/* + * // @antifeature payment should not be supported in FOSS + */ + { var upmix = function (aKeyword) { // Keywords need to mirrored in the below rules for detection and transformation @@ -68,6 +84,8 @@ Test the generated parser with some input for peg.js site at https://pegjs.org/o case 'installURL': aKeyword = 'downloadURL'; break; + case 'domain': + aKeyword = 'connect'; } return aKeyword; @@ -91,7 +109,8 @@ line = item1Localized / items1 / - items2 + items2 / + itemz2Localized ) '\n'? { @@ -186,6 +205,7 @@ items1 = 'homepageURL' / 'homepage' / 'grant' / + 'connect' / 'exclude' / 'copyright' ) @@ -218,3 +238,43 @@ items2 = value2: value2trimmed }; } + +itemz2Localized = + keyword: + ( + 'antifeature' + ) + locale: (':' localeValue:$[a-zA-Z-]+ { + return localeValue; + })? + whitespace + value1: non_whitespace + whitespace? + value2: non_newline? + { + var keywordUpmixed = upmix(keyword); + var value2trimmed = null; + + if (value2) { + value2trimmed = value2.trim(); + } + + var obj = { + key: keywordUpmixed, + value: (value1 + (value2trimmed ? '\u0020' + value2trimmed : '')), + + value1: value1 + } + + if (value2trimmed) { + obj.value2 = value2trimmed; + } + + if (locale) { + obj.key += ':' + locale; + obj.keyword = keywordUpmixed; + obj.locale = locale; + } + + return obj; + }