diff --git a/sources/package-lock.json b/sources/package-lock.json index 04e209c..f24ad98 100644 --- a/sources/package-lock.json +++ b/sources/package-lock.json @@ -21,7 +21,9 @@ "@octokit/rest": "20.1.0", "@octokit/webhooks-types": "7.5.0", "semver": "7.6.0", - "string-argv": "0.3.2" + "string-argv": "0.3.2", + "typed-rest-client": "1.8.11", + "unhomoglyph": "1.0.6" }, "devDependencies": { "@types/jest": "29.5.12", @@ -33,8 +35,10 @@ "eslint-plugin-github": "4.10.2", "eslint-plugin-jest": "27.9.0", "eslint-plugin-prettier": "5.1.3", + "glob-parent": "6.0.2", "jest": "29.7.0", "js-yaml": "4.1.0", + "nock": "13.5.4", "npm-run-all": "4.1.5", "patch-package": "8.0.0", "prettier": "3.2.5", @@ -3419,14 +3423,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3774,17 +3782,19 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dev": true, + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { @@ -3974,6 +3984,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-get-iterator": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", @@ -4828,7 +4857,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4879,16 +4907,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5015,7 +5046,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -5065,12 +5095,11 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5080,7 +5109,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -5092,7 +5120,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -5119,7 +5146,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -6315,6 +6341,12 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -6757,6 +6789,20 @@ "tslib": "^2.0.3" } }, + "node_modules/nock": { + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.4.tgz", + "integrity": "sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, "node_modules/node-fetch": { "version": "2.6.8", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz", @@ -6996,7 +7042,6 @@ "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7532,6 +7577,15 @@ "node": ">= 6" } }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7557,6 +7611,20 @@ } ] }, + "node_modules/qs": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz", + "integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -7872,15 +7940,16 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "dev": true, + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7931,14 +8000,17 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8624,6 +8696,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-rest-client": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", + "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, "node_modules/typescript": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", @@ -8652,6 +8734,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, "node_modules/undici": { "version": "5.28.4", "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", @@ -8668,6 +8755,11 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "node_modules/unhomoglyph": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/unhomoglyph/-/unhomoglyph-1.0.6.tgz", + "integrity": "sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg==" + }, "node_modules/universal-user-agent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", @@ -11601,14 +11693,15 @@ "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==" }, "call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" } }, "callsites": { @@ -11857,14 +11950,13 @@ "dev": true }, "define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dev": true, + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "requires": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" } }, "define-properties": { @@ -12015,6 +12107,19 @@ "which-typed-array": "^1.1.13" } }, + "es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "requires": { + "get-intrinsic": "^1.2.4" + } + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, "es-get-iterator": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", @@ -12649,8 +12754,7 @@ "function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "function.prototype.name": { "version": "1.1.6", @@ -12683,11 +12787,11 @@ "dev": true }, "get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "requires": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", @@ -12774,7 +12878,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, "requires": { "get-intrinsic": "^1.1.3" } @@ -12812,25 +12915,22 @@ "dev": true }, "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "requires": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" } }, "has-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, "has-tostringtag": { "version": "1.0.0", @@ -12845,7 +12945,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, "requires": { "function-bind": "^1.1.2" } @@ -13714,6 +13813,12 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, "json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -14086,6 +14191,17 @@ "tslib": "^2.0.3" } }, + "nock": { + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.4.tgz", + "integrity": "sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + } + }, "node-fetch": { "version": "2.6.8", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz", @@ -14267,8 +14383,7 @@ "object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" }, "object-is": { "version": "1.1.5", @@ -14644,6 +14759,12 @@ "sisteransi": "^1.0.5" } }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -14656,6 +14777,14 @@ "integrity": "sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w==", "dev": true }, + "qs": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz", + "integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==", + "requires": { + "side-channel": "^1.0.6" + } + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -14870,15 +14999,16 @@ } }, "set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "dev": true, + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "requires": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" } }, "set-function-name": { @@ -14914,14 +15044,14 @@ "dev": true }, "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" } }, "signal-exit": { @@ -15418,6 +15548,16 @@ "is-typed-array": "^1.1.9" } }, + "typed-rest-client": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", + "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", + "requires": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, "typescript": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", @@ -15436,6 +15576,11 @@ "which-boxed-primitive": "^1.0.2" } }, + "underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, "undici": { "version": "5.28.4", "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", @@ -15449,6 +15594,11 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "unhomoglyph": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/unhomoglyph/-/unhomoglyph-1.0.6.tgz", + "integrity": "sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg==" + }, "universal-user-agent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", diff --git a/sources/package.json b/sources/package.json index 3985d5d..e5e4b70 100644 --- a/sources/package.json +++ b/sources/package.json @@ -12,6 +12,7 @@ "compile-dependency-submission-post": "ncc build src/dependency-submission/post.ts --out dist/dependency-submission/post --source-map --no-source-map-register", "compile-setup-gradle-main": "ncc build src/setup-gradle/main.ts --out dist/setup-gradle/main --source-map --no-source-map-register", "compile-setup-gradle-post": "ncc build src/setup-gradle/post.ts --out dist/setup-gradle/post --source-map --no-source-map-register", + "compile-wrapper-validation-main": "ncc build src/wrapper-validation/main.ts --out dist/wrapper-validation/main --source-map --no-source-map-register", "compile": "npm-run-all --parallel compile-*", "check": "npm-run-all --parallel prettier-check lint", "format": "npm-run-all --parallel prettier-write lint", @@ -42,8 +43,11 @@ "@octokit/rest": "20.1.0", "@octokit/webhooks-types": "7.5.0", "semver": "7.6.0", - "string-argv": "0.3.2" - }, + "string-argv": "0.3.2", + + "typed-rest-client": "1.8.11", + "unhomoglyph": "1.0.6" +}, "devDependencies": { "@types/jest": "29.5.12", "@types/node": "20.12.4", @@ -60,6 +64,9 @@ "patch-package": "8.0.0", "prettier": "3.2.5", "ts-jest": "29.1.2", - "typescript": "5.4.3" + "typescript": "5.4.3", + + "glob-parent": "6.0.2", + "nock": "13.5.4" } } diff --git a/sources/src/wrapper-validation/checksums.ts b/sources/src/wrapper-validation/checksums.ts new file mode 100644 index 0000000..4220b0c --- /dev/null +++ b/sources/src/wrapper-validation/checksums.ts @@ -0,0 +1,55 @@ +import * as httpm from 'typed-rest-client/HttpClient' + +import fileWrapperChecksums from './wrapper-checksums.json' + +const httpc = new httpm.HttpClient('gradle/wrapper-validation-action', undefined, {allowRetries: true, maxRetries: 3}) + +function getKnownValidChecksums(): Map> { + const versionsMap = new Map>() + for (const entry of fileWrapperChecksums) { + const checksum = entry.checksum + + let versionNames = versionsMap.get(checksum) + if (versionNames === undefined) { + versionNames = new Set() + versionsMap.set(checksum, versionNames) + } + + versionNames.add(entry.version) + } + + return versionsMap +} + +/** + * Known checksums from previously published Wrapper versions. + * + * Maps from the checksum to the names of the Gradle versions whose wrapper has this checksum. + */ +export const KNOWN_VALID_CHECKSUMS = getKnownValidChecksums() + +export async function fetchValidChecksums(allowSnapshots: boolean): Promise> { + const all = await httpGetJsonArray('https://services.gradle.org/versions/all') + const withChecksum = all.filter( + entry => typeof entry === 'object' && entry != null && entry.hasOwnProperty('wrapperChecksumUrl') + ) + const allowed = withChecksum.filter( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (entry: any) => allowSnapshots || !entry.snapshot + ) + const checksumUrls = allowed.map( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (entry: any) => entry.wrapperChecksumUrl as string + ) + const checksums = await Promise.all(checksumUrls.map(async (url: string) => httpGetText(url))) + return new Set(checksums) +} + +async function httpGetJsonArray(url: string): Promise { + return JSON.parse(await httpGetText(url)) +} + +async function httpGetText(url: string): Promise { + const response = await httpc.get(url) + return await response.readBody() +} diff --git a/sources/src/wrapper-validation/find.ts b/sources/src/wrapper-validation/find.ts new file mode 100644 index 0000000..de1a89e --- /dev/null +++ b/sources/src/wrapper-validation/find.ts @@ -0,0 +1,27 @@ +import * as util from 'util' +import * as path from 'path' +import * as fs from 'fs' +import unhomoglyph from 'unhomoglyph' + +const readdir = util.promisify(fs.readdir) + +export async function findWrapperJars(baseDir: string): Promise { + const files = await recursivelyListFiles(baseDir) + return files + .filter(file => unhomoglyph(file).endsWith('gradle-wrapper.jar')) + .map(wrapperJar => path.relative(baseDir, wrapperJar)) + .sort((a, b) => a.localeCompare(b)) +} + +async function recursivelyListFiles(baseDir: string): Promise { + const childrenNames = await readdir(baseDir) + const childrenPaths = await Promise.all( + childrenNames.map(async childName => { + const childPath = path.resolve(baseDir, childName) + return fs.lstatSync(childPath).isDirectory() + ? recursivelyListFiles(childPath) + : new Promise(resolve => resolve([childPath])) + }) + ) + return Array.prototype.concat(...childrenPaths) +} diff --git a/sources/src/wrapper-validation/hash.ts b/sources/src/wrapper-validation/hash.ts new file mode 100644 index 0000000..90339e1 --- /dev/null +++ b/sources/src/wrapper-validation/hash.ts @@ -0,0 +1,18 @@ +import * as crypto from 'crypto' +import * as fs from 'fs' + +export async function sha256File(path: string): Promise { + return new Promise((resolve, reject) => { + const hash = crypto.createHash('sha256') + const stream = fs.createReadStream(path) + stream.on('data', data => hash.update(data)) + stream.on('end', () => { + stream.destroy() + resolve(hash.digest('hex')) + }) + stream.on('error', error => { + stream.destroy() + reject(error) + }) + }) +} diff --git a/sources/src/wrapper-validation/main.ts b/sources/src/wrapper-validation/main.ts new file mode 100644 index 0000000..9fb3070 --- /dev/null +++ b/sources/src/wrapper-validation/main.ts @@ -0,0 +1,38 @@ +import * as path from 'path' +import * as core from '@actions/core' + +import * as validate from './validate' + +export async function run(): Promise { + try { + const result = await validate.findInvalidWrapperJars( + path.resolve('.'), + +core.getInput('min-wrapper-count'), + core.getInput('allow-snapshots') === 'true', + core.getInput('allow-checksums').split(',') + ) + if (result.isValid()) { + core.info(result.toDisplayString()) + } else { + core.setFailed( + `Gradle Wrapper Validation Failed!\n See https://github.com/gradle/wrapper-validation-action#reporting-failures\n${result.toDisplayString()}` + ) + if (result.invalid.length > 0) { + core.setOutput('failed-wrapper', `${result.invalid.map(w => w.path).join('|')}`) + } + } + } catch (error) { + if (error instanceof AggregateError) { + core.setFailed(`Multiple errors returned`) + for (const err of error.errors) { + core.error(`Error ${error.errors.indexOf(err)}: ${err.message}`) + } + } else if (error instanceof Error) { + core.setFailed(error.message) + } else { + core.setFailed(`Unknown object was thrown: ${error}`) + } + } +} + +run() diff --git a/sources/src/wrapper-validation/validate.ts b/sources/src/wrapper-validation/validate.ts new file mode 100644 index 0000000..add8e83 --- /dev/null +++ b/sources/src/wrapper-validation/validate.ts @@ -0,0 +1,98 @@ +import * as find from './find' +import * as checksums from './checksums' +import * as hash from './hash' +import {resolve} from 'path' + +export async function findInvalidWrapperJars( + gitRepoRoot: string, + minWrapperCount: number, + allowSnapshots: boolean, + allowedChecksums: string[], + knownValidChecksums: Map> = checksums.KNOWN_VALID_CHECKSUMS +): Promise { + const wrapperJars = await find.findWrapperJars(gitRepoRoot) + const result = new ValidationResult([], []) + if (wrapperJars.length < minWrapperCount) { + result.errors.push( + `Expected to find at least ${minWrapperCount} Gradle Wrapper JARs but got only ${wrapperJars.length}` + ) + } + if (wrapperJars.length > 0) { + const notYetValidatedWrappers = [] + for (const wrapperJar of wrapperJars) { + const sha = await hash.sha256File(resolve(gitRepoRoot, wrapperJar)) + if (allowedChecksums.includes(sha) || knownValidChecksums.has(sha)) { + result.valid.push(new WrapperJar(wrapperJar, sha)) + } else { + notYetValidatedWrappers.push(new WrapperJar(wrapperJar, sha)) + } + } + + // Otherwise fall back to fetching checksums from Gradle API and compare against them + if (notYetValidatedWrappers.length > 0) { + result.fetchedChecksums = true + const fetchedValidChecksums = await checksums.fetchValidChecksums(allowSnapshots) + + for (const wrapperJar of notYetValidatedWrappers) { + if (!fetchedValidChecksums.has(wrapperJar.checksum)) { + result.invalid.push(wrapperJar) + } else { + result.valid.push(wrapperJar) + } + } + } + } + return result +} + +export class ValidationResult { + valid: WrapperJar[] + invalid: WrapperJar[] + fetchedChecksums = false + errors: string[] = [] + + constructor(valid: WrapperJar[], invalid: WrapperJar[]) { + this.valid = valid + this.invalid = invalid + } + + isValid(): boolean { + return this.invalid.length === 0 && this.errors.length === 0 + } + + toDisplayString(): string { + let displayString = '' + if (this.invalid.length > 0) { + displayString += `✗ Found unknown Gradle Wrapper JAR files:\n${ValidationResult.toDisplayList( + this.invalid + )}` + } + if (this.errors.length > 0) { + if (displayString.length > 0) displayString += '\n' + displayString += `✗ Other validation errors:\n ${this.errors.join(`\n `)}` + } + if (this.valid.length > 0) { + if (displayString.length > 0) displayString += '\n' + displayString += `✓ Found known Gradle Wrapper JAR files:\n${ValidationResult.toDisplayList(this.valid)}` + } + return displayString + } + + private static toDisplayList(wrapperJars: WrapperJar[]): string { + return ` ${wrapperJars.map(wj => wj.toDisplayString()).join(`\n `)}` + } +} + +export class WrapperJar { + path: string + checksum: string + + constructor(path: string, checksum: string) { + this.path = path + this.checksum = checksum + } + + toDisplayString(): string { + return `${this.checksum} ${this.path}` + } +} diff --git a/sources/src/wrapper-validation/wrapper-checksums.json b/sources/src/wrapper-validation/wrapper-checksums.json new file mode 100644 index 0000000..f159020 --- /dev/null +++ b/sources/src/wrapper-validation/wrapper-checksums.json @@ -0,0 +1,1014 @@ +[ + { + "version": "8.7", + "checksum": "cb0da6751c2b753a16ac168bb354870ebb1e162e9083f116729cec9c781156b8" + }, + { + "version": "8.7-rc-4", + "checksum": "cb0da6751c2b753a16ac168bb354870ebb1e162e9083f116729cec9c781156b8" + }, + { + "version": "8.7-rc-3", + "checksum": "cb0da6751c2b753a16ac168bb354870ebb1e162e9083f116729cec9c781156b8" + }, + { + "version": "8.7-rc-2", + "checksum": "cb0da6751c2b753a16ac168bb354870ebb1e162e9083f116729cec9c781156b8" + }, + { + "version": "8.7-rc-1", + "checksum": "cb0da6751c2b753a16ac168bb354870ebb1e162e9083f116729cec9c781156b8" + }, + { + "version": "7.6.4", + "checksum": "14dfa961b6704bb3decdea06502781edaa796a82e6da41cd2e1962b14fbe21a3" + }, + { + "version": "8.6", + "checksum": "d3b261c2820e9e3d8d639ed084900f11f4a86050a8f83342ade7b6bc9b0d2bdd" + }, + { + "version": "8.6-rc-4", + "checksum": "d3b261c2820e9e3d8d639ed084900f11f4a86050a8f83342ade7b6bc9b0d2bdd" + }, + { + "version": "8.6-rc-3", + "checksum": "d3b261c2820e9e3d8d639ed084900f11f4a86050a8f83342ade7b6bc9b0d2bdd" + }, + { + "version": "8.6-rc-2", + "checksum": "d3b261c2820e9e3d8d639ed084900f11f4a86050a8f83342ade7b6bc9b0d2bdd" + }, + { + "version": "8.6-rc-1", + "checksum": "d3b261c2820e9e3d8d639ed084900f11f4a86050a8f83342ade7b6bc9b0d2bdd" + }, + { + "version": "8.6-milestone-1", + "checksum": "d3b261c2820e9e3d8d639ed084900f11f4a86050a8f83342ade7b6bc9b0d2bdd" + }, + { + "version": "8.5", + "checksum": "d3b261c2820e9e3d8d639ed084900f11f4a86050a8f83342ade7b6bc9b0d2bdd" + }, + { + "version": "8.5-rc-4", + "checksum": "d3b261c2820e9e3d8d639ed084900f11f4a86050a8f83342ade7b6bc9b0d2bdd" + }, + { + "version": "8.5-rc-3", + "checksum": "d3b261c2820e9e3d8d639ed084900f11f4a86050a8f83342ade7b6bc9b0d2bdd" + }, + { + "version": "8.5-rc-2", + "checksum": "d3b261c2820e9e3d8d639ed084900f11f4a86050a8f83342ade7b6bc9b0d2bdd" + }, + { + "version": "8.5-rc-1", + "checksum": "d3b261c2820e9e3d8d639ed084900f11f4a86050a8f83342ade7b6bc9b0d2bdd" + }, + { + "version": "8.4", + "checksum": "0336f591bc0ec9aa0c9988929b93ecc916b3c1d52aed202c7381db144aa0ef15" + }, + { + "version": "7.6.3", + "checksum": "14dfa961b6704bb3decdea06502781edaa796a82e6da41cd2e1962b14fbe21a3" + }, + { + "version": "8.4-rc-3", + "checksum": "0336f591bc0ec9aa0c9988929b93ecc916b3c1d52aed202c7381db144aa0ef15" + }, + { + "version": "8.4-rc-2", + "checksum": "0336f591bc0ec9aa0c9988929b93ecc916b3c1d52aed202c7381db144aa0ef15" + }, + { + "version": "8.4-rc-1", + "checksum": "0336f591bc0ec9aa0c9988929b93ecc916b3c1d52aed202c7381db144aa0ef15" + }, + { + "version": "8.3", + "checksum": "0336f591bc0ec9aa0c9988929b93ecc916b3c1d52aed202c7381db144aa0ef15" + }, + { + "version": "8.3-rc-4", + "checksum": "0336f591bc0ec9aa0c9988929b93ecc916b3c1d52aed202c7381db144aa0ef15" + }, + { + "version": "8.3-rc-3", + "checksum": "0336f591bc0ec9aa0c9988929b93ecc916b3c1d52aed202c7381db144aa0ef15" + }, + { + "version": "8.3-rc-2", + "checksum": "0336f591bc0ec9aa0c9988929b93ecc916b3c1d52aed202c7381db144aa0ef15" + }, + { + "version": "8.3-rc-1", + "checksum": "0336f591bc0ec9aa0c9988929b93ecc916b3c1d52aed202c7381db144aa0ef15" + }, + { + "version": "8.2.1", + "checksum": "a8451eeda314d0568b5340498b36edf147a8f0d692c5ff58082d477abe9146e4" + }, + { + "version": "8.2", + "checksum": "a8451eeda314d0568b5340498b36edf147a8f0d692c5ff58082d477abe9146e4" + }, + { + "version": "7.6.2", + "checksum": "14dfa961b6704bb3decdea06502781edaa796a82e6da41cd2e1962b14fbe21a3" + }, + { + "version": "8.2-rc-3", + "checksum": "a8451eeda314d0568b5340498b36edf147a8f0d692c5ff58082d477abe9146e4" + }, + { + "version": "8.2-rc-2", + "checksum": "5c9a1a6f50b4f8c0264b1ac69013bef9f8363733275fafa56c70c84be3276bb8" + }, + { + "version": "8.2-rc-1", + "checksum": "55e949185c26ba3ddcd2c6a4217d043bfa0ce3cc002bbbb52b709a181a513e81" + }, + { + "version": "8.2-milestone-1", + "checksum": "55e949185c26ba3ddcd2c6a4217d043bfa0ce3cc002bbbb52b709a181a513e81" + }, + { + "version": "8.1.1", + "checksum": "ed2c26eba7cfb93cc2b7785d05e534f07b5b48b5e7fc941921cd098628abca58" + }, + { + "version": "8.1", + "checksum": "ed2c26eba7cfb93cc2b7785d05e534f07b5b48b5e7fc941921cd098628abca58" + }, + { + "version": "8.1-rc-4", + "checksum": "ed2c26eba7cfb93cc2b7785d05e534f07b5b48b5e7fc941921cd098628abca58" + }, + { + "version": "8.1-rc-3", + "checksum": "ed2c26eba7cfb93cc2b7785d05e534f07b5b48b5e7fc941921cd098628abca58" + }, + { + "version": "8.1-rc-2", + "checksum": "ed2c26eba7cfb93cc2b7785d05e534f07b5b48b5e7fc941921cd098628abca58" + }, + { + "version": "8.1-rc-1", + "checksum": "ed2c26eba7cfb93cc2b7785d05e534f07b5b48b5e7fc941921cd098628abca58" + }, + { + "version": "8.0.2", + "checksum": "91941f522fbfd4431cf57e445fc3d5200c85f957bda2de5251353cf11174f4b5" + }, + { + "version": "7.6.1", + "checksum": "c5a643cf80162e665cc228f7b16f343fef868e47d3a4836f62e18b7e17ac018a" + }, + { + "version": "6.9.4", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "8.0.1", + "checksum": "91941f522fbfd4431cf57e445fc3d5200c85f957bda2de5251353cf11174f4b5" + }, + { + "version": "8.0", + "checksum": "91941f522fbfd4431cf57e445fc3d5200c85f957bda2de5251353cf11174f4b5" + }, + { + "version": "8.0-rc-5", + "checksum": "91941f522fbfd4431cf57e445fc3d5200c85f957bda2de5251353cf11174f4b5" + }, + { + "version": "8.0-rc-4", + "checksum": "91941f522fbfd4431cf57e445fc3d5200c85f957bda2de5251353cf11174f4b5" + }, + { + "version": "8.0-rc-3", + "checksum": "91941f522fbfd4431cf57e445fc3d5200c85f957bda2de5251353cf11174f4b5" + }, + { + "version": "8.0-rc-2", + "checksum": "91941f522fbfd4431cf57e445fc3d5200c85f957bda2de5251353cf11174f4b5" + }, + { + "version": "8.0-rc-1", + "checksum": "91941f522fbfd4431cf57e445fc3d5200c85f957bda2de5251353cf11174f4b5" + }, + { + "version": "8.0-milestone-6", + "checksum": "91941f522fbfd4431cf57e445fc3d5200c85f957bda2de5251353cf11174f4b5" + }, + { + "version": "8.0-milestone-5", + "checksum": "91941f522fbfd4431cf57e445fc3d5200c85f957bda2de5251353cf11174f4b5" + }, + { + "version": "8.0-milestone-4", + "checksum": "577b2de036000db2e0f04f2ec842a4f1e648c8b6f9c87f29a8d896acb1732538" + }, + { + "version": "7.6", + "checksum": "c5a643cf80162e665cc228f7b16f343fef868e47d3a4836f62e18b7e17ac018a" + }, + { + "version": "7.6-rc-4", + "checksum": "c5a643cf80162e665cc228f7b16f343fef868e47d3a4836f62e18b7e17ac018a" + }, + { + "version": "7.6-rc-3", + "checksum": "c5a643cf80162e665cc228f7b16f343fef868e47d3a4836f62e18b7e17ac018a" + }, + { + "version": "7.6-rc-2", + "checksum": "c5a643cf80162e665cc228f7b16f343fef868e47d3a4836f62e18b7e17ac018a" + }, + { + "version": "8.0-milestone-3", + "checksum": "c5a643cf80162e665cc228f7b16f343fef868e47d3a4836f62e18b7e17ac018a" + }, + { + "version": "7.6-rc-1", + "checksum": "c5a643cf80162e665cc228f7b16f343fef868e47d3a4836f62e18b7e17ac018a" + }, + { + "version": "6.9.3", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "8.0-milestone-2", + "checksum": "c5a643cf80162e665cc228f7b16f343fef868e47d3a4836f62e18b7e17ac018a" + }, + { + "version": "8.0-milestone-1", + "checksum": "c5a643cf80162e665cc228f7b16f343fef868e47d3a4836f62e18b7e17ac018a" + }, + { + "version": "7.6-milestone-1", + "checksum": "c5a643cf80162e665cc228f7b16f343fef868e47d3a4836f62e18b7e17ac018a" + }, + { + "version": "7.5.1", + "checksum": "91a239400bb638f36a1795d8fdf7939d532cdc7d794d1119b7261aac158b1e60" + }, + { + "version": "7.5", + "checksum": "91a239400bb638f36a1795d8fdf7939d532cdc7d794d1119b7261aac158b1e60" + }, + { + "version": "7.5-rc-5", + "checksum": "91a239400bb638f36a1795d8fdf7939d532cdc7d794d1119b7261aac158b1e60" + }, + { + "version": "7.5-rc-4", + "checksum": "91a239400bb638f36a1795d8fdf7939d532cdc7d794d1119b7261aac158b1e60" + }, + { + "version": "7.5-rc-3", + "checksum": "91a239400bb638f36a1795d8fdf7939d532cdc7d794d1119b7261aac158b1e60" + }, + { + "version": "7.5-rc-2", + "checksum": "91a239400bb638f36a1795d8fdf7939d532cdc7d794d1119b7261aac158b1e60" + }, + { + "version": "7.5-rc-1", + "checksum": "91a239400bb638f36a1795d8fdf7939d532cdc7d794d1119b7261aac158b1e60" + }, + { + "version": "7.4.2", + "checksum": "575098db54a998ff1c6770b352c3b16766c09848bee7555dab09afc34e8cf590" + }, + { + "version": "7.4.1", + "checksum": "575098db54a998ff1c6770b352c3b16766c09848bee7555dab09afc34e8cf590" + }, + { + "version": "7.4", + "checksum": "575098db54a998ff1c6770b352c3b16766c09848bee7555dab09afc34e8cf590" + }, + { + "version": "7.4-rc-2", + "checksum": "575098db54a998ff1c6770b352c3b16766c09848bee7555dab09afc34e8cf590" + }, + { + "version": "7.4-rc-1", + "checksum": "575098db54a998ff1c6770b352c3b16766c09848bee7555dab09afc34e8cf590" + }, + { + "version": "7.3.3", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.3.3-rc-1", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "6.9.2", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "7.3.2", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.3.1", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.3", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.3-rc-5", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.3-rc-4", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.3-rc-3", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.3-rc-2", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.3-rc-1", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "6.9.1", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "7.2", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.2-rc-3", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.2-rc-2", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.2-rc-1", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.1.1", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.1", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.1-rc-2", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.1-rc-1", + "checksum": "33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89" + }, + { + "version": "7.0.2", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "7.0.1", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.9", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.9-rc-2", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.9-rc-1", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "7.0", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "7.0-rc-2", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "7.0-rc-1", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "7.0-milestone-3", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "7.0-milestone-2", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.8.3", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.8.2", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "7.0-milestone-1", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.8.1", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.8", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.8-rc-5", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.8-rc-4", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.8-rc-3", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.8-rc-1", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.7.1", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.8-milestone-3", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.8-milestone-2", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.7", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.8-milestone-1", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.7-rc-5", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.7-rc-4", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.7-rc-3", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.7-rc-2", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.7-rc-1", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.6.1", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.6", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.6-rc-6", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.6-rc-5", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.6-rc-4", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.6-rc-3", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.6-rc-2", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.6-rc-1", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.6-milestone-3", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.5.1", + "checksum": "70239e6ca1f0d5e3b2808ef6d82390cf9ad58d3a3a0d271677a51d1b89475857" + }, + { + "version": "6.6-milestone-2", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.6-milestone-1", + "checksum": "e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637" + }, + { + "version": "6.5", + "checksum": "70239e6ca1f0d5e3b2808ef6d82390cf9ad58d3a3a0d271677a51d1b89475857" + }, + { + "version": "6.5-rc-1", + "checksum": "70239e6ca1f0d5e3b2808ef6d82390cf9ad58d3a3a0d271677a51d1b89475857" + }, + { + "version": "6.4.1", + "checksum": "70239e6ca1f0d5e3b2808ef6d82390cf9ad58d3a3a0d271677a51d1b89475857" + }, + { + "version": "6.5-milestone-2", + "checksum": "70239e6ca1f0d5e3b2808ef6d82390cf9ad58d3a3a0d271677a51d1b89475857" + }, + { + "version": "6.5-milestone-1", + "checksum": "70239e6ca1f0d5e3b2808ef6d82390cf9ad58d3a3a0d271677a51d1b89475857" + }, + { + "version": "6.4", + "checksum": "70239e6ca1f0d5e3b2808ef6d82390cf9ad58d3a3a0d271677a51d1b89475857" + }, + { + "version": "6.4-rc-4", + "checksum": "70239e6ca1f0d5e3b2808ef6d82390cf9ad58d3a3a0d271677a51d1b89475857" + }, + { + "version": "6.4-rc-3", + "checksum": "70239e6ca1f0d5e3b2808ef6d82390cf9ad58d3a3a0d271677a51d1b89475857" + }, + { + "version": "6.4-rc-2", + "checksum": "70239e6ca1f0d5e3b2808ef6d82390cf9ad58d3a3a0d271677a51d1b89475857" + }, + { + "version": "6.4-rc-1", + "checksum": "70239e6ca1f0d5e3b2808ef6d82390cf9ad58d3a3a0d271677a51d1b89475857" + }, + { + "version": "6.3", + "checksum": "1cef53de8dc192036e7b0cc47584449b0cf570a00d560bfaa6c9eabe06e1fc06" + }, + { + "version": "6.3-rc-4", + "checksum": "1cef53de8dc192036e7b0cc47584449b0cf570a00d560bfaa6c9eabe06e1fc06" + }, + { + "version": "6.3-rc-3", + "checksum": "1cef53de8dc192036e7b0cc47584449b0cf570a00d560bfaa6c9eabe06e1fc06" + }, + { + "version": "6.3-rc-2", + "checksum": "1cef53de8dc192036e7b0cc47584449b0cf570a00d560bfaa6c9eabe06e1fc06" + }, + { + "version": "6.3-rc-1", + "checksum": "1cef53de8dc192036e7b0cc47584449b0cf570a00d560bfaa6c9eabe06e1fc06" + }, + { + "version": "6.2.2", + "checksum": "96f793a18e056c23ffeec67c1f3bb8eccff5a4a407fc9ceac183527e7eedf4b6" + }, + { + "version": "6.2.1", + "checksum": "96f793a18e056c23ffeec67c1f3bb8eccff5a4a407fc9ceac183527e7eedf4b6" + }, + { + "version": "6.2", + "checksum": "96f793a18e056c23ffeec67c1f3bb8eccff5a4a407fc9ceac183527e7eedf4b6" + }, + { + "version": "6.2-rc-3", + "checksum": "96f793a18e056c23ffeec67c1f3bb8eccff5a4a407fc9ceac183527e7eedf4b6" + }, + { + "version": "6.2-rc-2", + "checksum": "96f793a18e056c23ffeec67c1f3bb8eccff5a4a407fc9ceac183527e7eedf4b6" + }, + { + "version": "6.2-rc-1", + "checksum": "96f793a18e056c23ffeec67c1f3bb8eccff5a4a407fc9ceac183527e7eedf4b6" + }, + { + "version": "6.1.1", + "checksum": "96f793a18e056c23ffeec67c1f3bb8eccff5a4a407fc9ceac183527e7eedf4b6" + }, + { + "version": "6.1", + "checksum": "96f793a18e056c23ffeec67c1f3bb8eccff5a4a407fc9ceac183527e7eedf4b6" + }, + { + "version": "6.1-rc-3", + "checksum": "96f793a18e056c23ffeec67c1f3bb8eccff5a4a407fc9ceac183527e7eedf4b6" + }, + { + "version": "6.1-rc-2", + "checksum": "96f793a18e056c23ffeec67c1f3bb8eccff5a4a407fc9ceac183527e7eedf4b6" + }, + { + "version": "6.1-rc-1", + "checksum": "96f793a18e056c23ffeec67c1f3bb8eccff5a4a407fc9ceac183527e7eedf4b6" + }, + { + "version": "6.1-milestone-3", + "checksum": "3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce" + }, + { + "version": "6.1-milestone-2", + "checksum": "3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce" + }, + { + "version": "6.1-milestone-1", + "checksum": "3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce" + }, + { + "version": "6.0.1", + "checksum": "28b330c20a9a73881dfe9702df78d4d78bf72368e8906c70080ab6932462fe9e" + }, + { + "version": "6.0", + "checksum": "28b330c20a9a73881dfe9702df78d4d78bf72368e8906c70080ab6932462fe9e" + }, + { + "version": "6.0-rc-3", + "checksum": "28b330c20a9a73881dfe9702df78d4d78bf72368e8906c70080ab6932462fe9e" + }, + { + "version": "5.6.4", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "6.0-rc-2", + "checksum": "28b330c20a9a73881dfe9702df78d4d78bf72368e8906c70080ab6932462fe9e" + }, + { + "version": "6.0-rc-1", + "checksum": "28b330c20a9a73881dfe9702df78d4d78bf72368e8906c70080ab6932462fe9e" + }, + { + "version": "5.6.3", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.6.2", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.6.1", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.6", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.6-rc-2", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.6-rc-1", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.5.1", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.5", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.5-rc-4", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.5-rc-3", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.5-rc-2", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.5-rc-1", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.4.1", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.4", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.4-rc-1", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.3.1", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.3", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.3-rc-3", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.3-rc-2", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.3-rc-1", + "checksum": "3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f" + }, + { + "version": "5.2.1", + "checksum": "76b12da7f4a7cdd025e5996811a2e49bf5df0fb62d72554ab555c0e434b63aae" + }, + { + "version": "5.2", + "checksum": "76b12da7f4a7cdd025e5996811a2e49bf5df0fb62d72554ab555c0e434b63aae" + }, + { + "version": "5.2-rc-1", + "checksum": "76b12da7f4a7cdd025e5996811a2e49bf5df0fb62d72554ab555c0e434b63aae" + }, + { + "version": "5.1.1", + "checksum": "76b12da7f4a7cdd025e5996811a2e49bf5df0fb62d72554ab555c0e434b63aae" + }, + { + "version": "5.1", + "checksum": "76b12da7f4a7cdd025e5996811a2e49bf5df0fb62d72554ab555c0e434b63aae" + }, + { + "version": "5.1-rc-3", + "checksum": "76b12da7f4a7cdd025e5996811a2e49bf5df0fb62d72554ab555c0e434b63aae" + }, + { + "version": "5.1-rc-2", + "checksum": "76b12da7f4a7cdd025e5996811a2e49bf5df0fb62d72554ab555c0e434b63aae" + }, + { + "version": "5.1-rc-1", + "checksum": "76b12da7f4a7cdd025e5996811a2e49bf5df0fb62d72554ab555c0e434b63aae" + }, + { + "version": "4.10.3", + "checksum": "660ab018b8e319e9ae779fdb1b7ac47d0321bde953bf0eb4545f14952cfdcaa3" + }, + { + "version": "5.1-milestone-1", + "checksum": "8ff6bee43c55efc0cce9e1147860a76fc970398fbef587e64b6e7a5a7e0291df" + }, + { + "version": "5.0", + "checksum": "f1a597a1f2b23089deec11d5b924d074f9e4ed810f2093be7021ded01c8073ad" + }, + { + "version": "4.10.2", + "checksum": "ad63ba21fb91e490e0f6fd0ca7d4049241f0f68a454b0b3075c041c4554e611c" + }, + { + "version": "4.10.1", + "checksum": "d8a69ca8efe271d8de080c42a2ea4b08fc9e85c41aa2d163255c70d9da239db0" + }, + { + "version": "4.10", + "checksum": "778e7f46bd67eaea2de5bcbdbb40878c6614656014ba59a72ce8648eaf43a925" + }, + { + "version": "4.9", + "checksum": "e55e7e47a79e04c26363805b31e2f40b7a9cc89ea12113be7de750a3b2cede85" + }, + { + "version": "4.8.1", + "checksum": "080e30657661539701b66827b96eb0043191e0a7a73090e8a57bd6735e5af5c5" + }, + { + "version": "4.8", + "checksum": "4160d5a6d8d6efc6af336582bbbba8194e4d7a742835f7b0fd3964cbd419c994" + }, + { + "version": "4.7", + "checksum": "7d6fe0a055f133226409de2457fb0e887d1f6b096f36c8d5fd76fb7a9357cd45" + }, + { + "version": "4.6", + "checksum": "381dff8aa434499aa93bc25572b049c8c586a67faff2c02f375e4f23e17e49de" + }, + { + "version": "4.5.1", + "checksum": "3bf04e39ed259ff0a1217a875199a11775855d2a29207b98318ac79178249de8" + }, + { + "version": "4.5", + "checksum": "d283a04caee0e97b666b09e50ab394834934bee6225c2e9cd4650afdeac43828" + }, + { + "version": "4.4.1", + "checksum": "4e318d74d06aa7b998091345c397a3c7c4b291b59da31e6f9c772a596711acac" + }, + { + "version": "4.4", + "checksum": "88b5b31f390a268ab3773df580d83fd1e388f49c2b685f78a16600577bd72fe2" + }, + { + "version": "4.3.1", + "checksum": "383f4efa709b52632a520708e8a07353961970941ab3867ab8ac182132ce1c54" + }, + { + "version": "4.3", + "checksum": "ba496e7e0e03ffa432eaf715bb1466fac2ffc8491a71e7164a5438c48c79d8ea" + }, + { + "version": "4.2.1", + "checksum": "c536e519e65a4bb787e071c6a90d23bd219c9d409a2123db649d3684acbf3ae7" + }, + { + "version": "4.2", + "checksum": "53aa048fef3c06a8442c6a44df5edd2e8c791ee883e42ded6189c7eed112095d" + }, + { + "version": "4.1", + "checksum": "f4d953f31fbf6c38a8c330d19171c8ba6e0d1ff59d4d5c5c2d3ed821c9f3d5a3" + }, + { + "version": "3.2.1", + "checksum": "020ef0245a07b33ca48b12f59415e7e5083cf701ef02690464a8cc2ab3984608" + }, + { + "version": "3.2", + "checksum": "d2d3abae74e89cc4200f48d4a08a7e5960363c33ee62272ef5ffbb39f4c7f83e" + }, + { + "version": "3.1", + "checksum": "0f49043be582d7a39b671f924c66bd9337b92fa88ff5951225acc60560053067" + }, + { + "version": "3.0", + "checksum": "42d7a2f636983aa09d21dfeb6e90d21d7a8dad905351390643ce60cc82c8f8a9" + }, + { + "version": "2.14.1", + "checksum": "8e47da0b2656354d059609cae9d44b196d4f9b14512e688ffee4e0eb7e723ae9" + }, + { + "version": "2.14", + "checksum": "b5ca811c057b3eb4164c78f4155d667c6092ff98ba91a4c90d29e127426f37a7" + }, + { + "version": "2.13", + "checksum": "0c3c576e28b44eddcab6b8b4854f484363dfbcd047657d41654e839835da2c53" + }, + { + "version": "2.12", + "checksum": "4894520b03c007bf38e983bf933320c483a9790010d997029fa8985dc6128559" + }, + { + "version": "2.11", + "checksum": "0bc7b16a0a3fa52af674de44d1fea48abc4dee3431f3d4829cd9ea329836e596" + }, + { + "version": "2.10", + "checksum": "16caeaf66d57a0d1d2087fef6a97efa62de8da69afa5b908f40db35afc4342da" + }, + { + "version": "2.9", + "checksum": "b92386e36a96da6be89e91f71087d1394a26c0450231ba0b22e28ee1ee8fa14b" + }, + { + "version": "2.8", + "checksum": "198159fcd7d29533c0d37423d66c44729982d5280c9e2c7c5f4b7bc6a9317f6b" + }, + { + "version": "2.7", + "checksum": "d7e1975ccf2dc079d4f0b1010febdad466506d1565c5aa8017c88ebc5e471604" + }, + { + "version": "2.6", + "checksum": "695089a2b306f55f0bd63140fbcc5ead8c383819018188ce484cd5a055bec6e4" + }, + { + "version": "2.5", + "checksum": "718d7b25ea60b357fc4cb2212ce10b3f03dfd0e6fe5f23f565b15553ec46bb7e" + }, + { + "version": "2.4", + "checksum": "98420079ffe3e24b1013180d9b9bc2e2ee6a9d867ee232004b75a961d9c18e27" + }, + { + "version": "2.3", + "checksum": "b18a1114ebe81fb7502d40ad9a4f86cef82fff244a865ad45533b4d5e7ff0cc8" + }, + { + "version": "2.2.1", + "checksum": "5f73d431fd1c5dcc2cf11555b8e486c43249c1099f678ccc6088b05be600a2e1" + }, + { + "version": "2.2", + "checksum": "fa9b4294d47cf8db7039cb9b2435de3dd1accb0d3d67926705775a0579dfa397" + }, + { + "version": "2.1", + "checksum": "5e27c39c2336c25748f279d8b105162d14b1a39eb7839d0b658432282d0ce79f" + }, + { + "version": "2.0", + "checksum": "80a33ca14e3bca3116bc8749550397f739f126190c82bb6399fdc8d10f49661f" + }, + { + "version": "1.12", + "checksum": "dea5ceba47b58df0b7f69a65b24357527c1927ccc72b6d4ed90658d39e461b29" + }, + { + "version": "1.11", + "checksum": "a14b54dd3790f5ce1dc08ebbf4b5bcc05f76c4554b43accb84696c970f29aba0" + }, + { + "version": "1.10", + "checksum": "6a6c15e222a0458aa33985b87f67954f4222410b43b1e26866197d0a77d93cbc" + }, + { + "version": "1.9", + "checksum": "134337ea7c13221f9d1a1c14288a5cf8af9f6060167b903b724b115cf5a0cf73" + }, + { + "version": "1.8", + "checksum": "13f23a24252ddca0a0fabef212e3c854f5895b081c09d015c91587a5df9bf9f7" + }, + { + "version": "1.7", + "checksum": "7af529cc3331d38b3d8f8344ddd9b2d3744542b55b68318abd8bb1a6f3812a1c" + }, + { + "version": "1.6", + "checksum": "66dbcc9f0bc33789ea0bdb4d49c8ea037047bda5647ef696c47bdca65f785159" + }, + { + "version": "1.5", + "checksum": "9598ffdf7ee26949d8b861ece267c70c802f21f7fc52596693834792d155195a" + }, + { + "version": "1.4", + "checksum": "c95985b7b5684e133c5d45044fd90faaf6c8f7cd2493d61a11c2b8c5b71ef514" + }, + { + "version": "1.3", + "checksum": "95513eccca99e1ae1aeadc4f69cabd0e7fb64821d3f26c46a489df844c8a7353" + }, + { + "version": "1.2", + "checksum": "5c91fa893665f3051eae14578fac2df14e737423387e75ffbeccd35f335a3d8b" + }, + { + "version": "1.1", + "checksum": "22c56a9780daeee00e5bf31621f991b68e73eff6fe8afca628a1fe2c50c6038e" + }, + { + "version": "1.0", + "checksum": "87e50531ca7aab675f5bb65755ef78328afd64cf0877e37ad876047a8a014055" + } +] diff --git a/sources/test/jest/wrapper-validation/checksums.test.ts b/sources/test/jest/wrapper-validation/checksums.test.ts new file mode 100644 index 0000000..ce5f750 --- /dev/null +++ b/sources/test/jest/wrapper-validation/checksums.test.ts @@ -0,0 +1,55 @@ +import * as checksums from '../../../src/wrapper-validation/checksums' +import nock from 'nock' +import {afterEach, describe, expect, test, jest} from '@jest/globals' + +jest.setTimeout(30000) + +test('has loaded hardcoded wrapper jars checksums', async () => { + // Sanity check that generated checksums file is not empty and was properly imported + expect(checksums.KNOWN_VALID_CHECKSUMS.size).toBeGreaterThan(10) + // Verify that checksums of arbitrary versions are contained + expect( + checksums.KNOWN_VALID_CHECKSUMS.get( + '660ab018b8e319e9ae779fdb1b7ac47d0321bde953bf0eb4545f14952cfdcaa3' + ) + ).toEqual(new Set(['4.10.3'])) + expect( + checksums.KNOWN_VALID_CHECKSUMS.get( + '28b330c20a9a73881dfe9702df78d4d78bf72368e8906c70080ab6932462fe9e' + ) + ).toEqual(new Set(['6.0-rc-1', '6.0-rc-2', '6.0-rc-3', '6.0', '6.0.1'])) +}) + +test('fetches wrapper jars checksums', async () => { + const validChecksums = await checksums.fetchValidChecksums(false) + expect(validChecksums.size).toBeGreaterThan(10) + // Verify that checksum of arbitrary version is contained + expect( + validChecksums.has( + // Checksum for version 6.0 + '28b330c20a9a73881dfe9702df78d4d78bf72368e8906c70080ab6932462fe9e' + ) + ).toBe(true) +}) + +describe('retry', () => { + afterEach(() => { + nock.cleanAll() + }) + + describe('for /versions/all API', () => { + test('retry three times', async () => { + nock('https://services.gradle.org', {allowUnmocked: true}) + .get('/versions/all') + .times(3) + .replyWithError({ + message: 'connect ECONNREFUSED 104.18.191.9:443', + code: 'ECONNREFUSED' + }) + + const validChecksums = await checksums.fetchValidChecksums(false) + expect(validChecksums.size).toBeGreaterThan(10) + nock.isDone() + }) + }) +}) diff --git a/sources/test/jest/wrapper-validation/data/invalid/gradle-wrapper.jar b/sources/test/jest/wrapper-validation/data/invalid/gradle-wrapper.jar new file mode 100644 index 0000000..e69de29 diff --git a/sources/test/jest/wrapper-validation/data/invalid/gradlе-wrapper.jar b/sources/test/jest/wrapper-validation/data/invalid/gradlе-wrapper.jar new file mode 100644 index 0000000..e69de29 diff --git a/sources/test/jest/wrapper-validation/data/valid/gradle-wrapper.jar b/sources/test/jest/wrapper-validation/data/valid/gradle-wrapper.jar new file mode 100644 index 0000000..6d183c6 Binary files /dev/null and b/sources/test/jest/wrapper-validation/data/valid/gradle-wrapper.jar differ diff --git a/sources/test/jest/wrapper-validation/find.test.ts b/sources/test/jest/wrapper-validation/find.test.ts new file mode 100644 index 0000000..fe4c375 --- /dev/null +++ b/sources/test/jest/wrapper-validation/find.test.ts @@ -0,0 +1,12 @@ +import * as path from 'path' +import * as find from '../../../src/wrapper-validation/find' +import {expect, test} from '@jest/globals' + +test('finds test data wrapper jars', async () => { + const repoRoot = path.resolve('./test/jest/wrapper-validation') + const wrapperJars = await find.findWrapperJars(repoRoot) + expect(wrapperJars.length).toBe(3) + expect(wrapperJars).toContain('data/valid/gradle-wrapper.jar') + expect(wrapperJars).toContain('data/invalid/gradle-wrapper.jar') + expect(wrapperJars).toContain('data/invalid/gradlе-wrapper.jar') // homoglyph +}) diff --git a/sources/test/jest/wrapper-validation/hash.test.ts b/sources/test/jest/wrapper-validation/hash.test.ts new file mode 100644 index 0000000..929f75a --- /dev/null +++ b/sources/test/jest/wrapper-validation/hash.test.ts @@ -0,0 +1,12 @@ +import * as path from 'path' +import * as hash from '../../../src/wrapper-validation/hash' +import {expect, test} from '@jest/globals' + +test('can sha256 files', async () => { + const sha = await hash.sha256File( + path.resolve('test/jest/wrapper-validation/data/invalid/gradle-wrapper.jar') + ) + expect(sha).toEqual( + 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' + ) +}) diff --git a/sources/test/jest/wrapper-validation/validate.test.ts b/sources/test/jest/wrapper-validation/validate.test.ts new file mode 100644 index 0000000..5aacd10 --- /dev/null +++ b/sources/test/jest/wrapper-validation/validate.test.ts @@ -0,0 +1,98 @@ +import * as path from 'path' +import * as validate from '../../../src/wrapper-validation/validate' +import {expect, test, jest} from '@jest/globals' + +jest.setTimeout(30000) + +const baseDir = path.resolve('./test/jest/wrapper-validation') + +test('succeeds if all found wrapper jars are valid', async () => { + const result = await validate.findInvalidWrapperJars(baseDir, 3, false, [ + 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' + ]) + + expect(result.isValid()).toBe(true) + // Only hardcoded and explicitly allowed checksums should have been used + expect(result.fetchedChecksums).toBe(false) + + expect(result.toDisplayString()).toBe( + '✓ Found known Gradle Wrapper JAR files:\n' + + ' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 data/invalid/gradle-wrapper.jar\n' + + ' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 data/invalid/gradlе-wrapper.jar\n' + // homoglyph + ' 3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce data/valid/gradle-wrapper.jar' + ) +}) + +test('succeeds if all found wrapper jars are valid (and checksums are fetched from Gradle API)', async () => { + const knownValidChecksums = new Map>() + const result = await validate.findInvalidWrapperJars( + baseDir, + 1, + false, + ['e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'], + knownValidChecksums + ) + + expect(result.isValid()).toBe(true) + // Should have fetched checksums because no known checksums were provided + expect(result.fetchedChecksums).toBe(true) + + expect(result.toDisplayString()).toBe( + '✓ Found known Gradle Wrapper JAR files:\n' + + ' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 data/invalid/gradle-wrapper.jar\n' + + ' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 data/invalid/gradlе-wrapper.jar\n' + // homoglyph + ' 3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce data/valid/gradle-wrapper.jar' + ) +}) + +test('fails if invalid wrapper jars are found', async () => { + const result = await validate.findInvalidWrapperJars(baseDir, 3, false, []) + + expect(result.isValid()).toBe(false) + + expect(result.valid).toEqual([ + new validate.WrapperJar( + 'data/valid/gradle-wrapper.jar', + '3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce' + ) + ]) + + expect(result.invalid).toEqual([ + new validate.WrapperJar( + 'data/invalid/gradle-wrapper.jar', + 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' + ), + new validate.WrapperJar( + 'data/invalid/gradlе-wrapper.jar', // homoglyph + 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' + ) + ]) + + expect(result.toDisplayString()).toBe( + '✗ Found unknown Gradle Wrapper JAR files:\n' + + ' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 data/invalid/gradle-wrapper.jar\n' + + ' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 data/invalid/gradlе-wrapper.jar\n' + // homoglyph + '✓ Found known Gradle Wrapper JAR files:\n' + + ' 3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce data/valid/gradle-wrapper.jar' + ) +}) + +test('fails if not enough wrapper jars are found', async () => { + const result = await validate.findInvalidWrapperJars(baseDir, 4, false, []) + + expect(result.isValid()).toBe(false) + + expect(result.errors).toEqual([ + 'Expected to find at least 4 Gradle Wrapper JARs but got only 3' + ]) + + expect(result.toDisplayString()).toBe( + '✗ Found unknown Gradle Wrapper JAR files:\n' + + ' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 data/invalid/gradle-wrapper.jar\n' + + ' e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 data/invalid/gradlе-wrapper.jar\n' + // homoglyph + '✗ Other validation errors:\n' + + ' Expected to find at least 4 Gradle Wrapper JARs but got only 3\n' + + '✓ Found known Gradle Wrapper JAR files:\n' + + ' 3888c76faa032ea8394b8a54e04ce2227ab1f4be64f65d450f8509fe112d38ce data/valid/gradle-wrapper.jar' + ) +}) diff --git a/sources/tsconfig.json b/sources/tsconfig.json index 583b426..7346688 100644 --- a/sources/tsconfig.json +++ b/sources/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { /* Basic Options */ - "incremental": false, /* Enable incremental compilation */ - "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ + "incremental": false, /* Enable incremental compilation */ + "target": "es2021", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ @@ -45,9 +45,10 @@ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + "resolveJsonModule": true, /* Enable importing JSON files as module; used for importing wrapper checksums JSON */ /* Source Map Options */ // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ diff --git a/wrapper-validation/action.yml b/wrapper-validation/action.yml new file mode 100644 index 0000000..117fb54 --- /dev/null +++ b/wrapper-validation/action.yml @@ -0,0 +1,29 @@ +name: 'Gradle Wrapper Validation' +description: 'Validates Gradle Wrapper JAR Files' +author: 'Gradle' + +inputs: + min-wrapper-count: + description: 'Minimum number expected gradle-wrapper.jar files found in the repository. Non-negative number. Higher number is useful in monorepos where each project might have their own wrapper.' + required: false + default: '1' + allow-snapshots: + description: 'Allow Gradle snapshot versions during checksum verification. Boolean, true or false.' + required: false + default: 'false' + allow-checksums: + description: 'Accept arbitrary user-defined checksums as valid. Comma separated list of SHA256 checksums (lowercase hex).' + required: false + default: '' + +outputs: + failed-wrapper: + description: 'The path of the Gradle Wrapper(s) JAR that failed validation. Path is a platform-dependent relative path to git repository root. Multiple paths are separated by a | character.' + +runs: + using: 'node20' + main: '../dist/wrapper-validation/main/index.js' + +branding: + icon: 'shield' + color: gray-dark