From 3bab83b13b6ffb4bd4bd53d881eaebe69b4166dd Mon Sep 17 00:00:00 2001 From: JP Lomas Date: Mon, 14 Sep 2026 11:33:53 +0100 Subject: [PATCH] fix: dependecy updates Primarily qrllib 1.2.7 Signed-off-by: JP Lomas --- package-lock.json | 167 ++++++++++++++------------ package.json | 86 ++++++------- scripts/postinstall.js | 56 +++++++-- test/commands/config.test.js | 16 ++- test/functions/grpc.test.js | 14 +++ test/functions/network-helper.test.js | 7 ++ 6 files changed, 211 insertions(+), 135 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ae4256..310d8c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,49 +10,49 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@grpc/grpc-js": "^1.6.7", - "@grpc/proto-loader": "^0.7.15", - "@oclif/command": "^1", - "@oclif/config": "^1", - "@oclif/plugin-help": "^2", - "@theqrl/explorer-helpers": "^2.7.0", - "@theqrl/qrl-proto-sha256": "^3.4.1", - "@theqrl/qrlbase.proto": "^1.1.0", - "@theqrl/validate-qrl-address": "^1.1.0", - "aes-js": "^3.1.2", - "bech32": "^1.1.3", - "bignumber.js": "^9.0.0", - "cli-ux": "^5.3.1", - "conf": "^10.2.0", - "crypto-js": "^4.2.0", - "eccrypto": "^1.1.5", - "kleur": "^3.0.3", - "moment": "^2.24.0", - "ora": "^3.4.0", - "prompts": "^2.4.2", - "qrcode-terminal": "^0.12.0", - "qrllib": "^1.1.7", - "tmp": "^0.2.7", - "util": "^0.12.1" + "@grpc/grpc-js": "1.14.4", + "@grpc/proto-loader": "0.7.15", + "@oclif/command": "1.8.36", + "@oclif/config": "1.18.17", + "@oclif/plugin-help": "2.2.3", + "@theqrl/explorer-helpers": "2.8.2", + "@theqrl/qrl-proto-sha256": "3.5.0", + "@theqrl/qrlbase.proto": "1.1.1", + "@theqrl/validate-qrl-address": "1.1.0", + "aes-js": "3.1.2", + "bech32": "1.1.4", + "bignumber.js": "9.3.1", + "cli-ux": "5.6.7", + "conf": "10.2.0", + "crypto-js": "4.2.0", + "eccrypto": "1.1.6", + "kleur": "3.0.3", + "moment": "2.30.1", + "ora": "3.4.0", + "prompts": "2.4.2", + "qrcode-terminal": "0.12.0", + "qrllib": "1.2.7", + "tmp": "0.2.7", + "util": "0.12.5" }, "bin": { "qrl-cli": "bin/run" }, "devDependencies": { - "@babel/eslint-parser": "^7.25.8", - "@oclif/dev-cli": "^1", - "@oclif/test": "^1", - "@semantic-release/git": "^10.0.1", - "@yao-pkg/pkg": "^6.21.0", - "chai": "^4", - "eslint": "^8.57.1", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-import": "^2.31.0", - "globby": "^10", - "mocha": "^11.7.6", - "nyc": "^18.0.0", - "semantic-release": "^25.0.8" + "@babel/eslint-parser": "7.29.7", + "@oclif/dev-cli": "1.26.10", + "@oclif/test": "1.2.9", + "@semantic-release/git": "10.0.1", + "@yao-pkg/pkg": "6.22.0", + "chai": "4.5.0", + "eslint": "8.57.1", + "eslint-config-airbnb-base": "15.0.0", + "eslint-config-prettier": "9.1.2", + "eslint-plugin-import": "2.32.0", + "globby": "10.0.2", + "mocha": "11.8.0", + "nyc": "18.0.0", + "semantic-release": "25.0.9" }, "engines": { "node": ">=16.0.0" @@ -138,6 +138,7 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -1903,6 +1904,7 @@ "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", @@ -2983,9 +2985,9 @@ "license": "ISC" }, "node_modules/@yao-pkg/pkg": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@yao-pkg/pkg/-/pkg-6.21.0.tgz", - "integrity": "sha512-dZl2C7rdwwEI4tv7WW+Cvnl+2K8OqHKUXfNQRq3mZCTC4degoFx1jA4d5wOn9d8lHrUy58xuEt1eJD0pTddW5w==", + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/@yao-pkg/pkg/-/pkg-6.22.0.tgz", + "integrity": "sha512-u+ZgwLsvEFB+Q1rA+IGymgxnm+anl1qGJWsTH6hDHyy2cCiOvMteDNK7NgqFYxN/zdithtORIlCsYSyhAXAgQA==", "dev": true, "license": "MIT", "dependencies": { @@ -2994,7 +2996,7 @@ "@babel/traverse": "^7.23.0", "@babel/types": "^7.23.0", "@roberts_lando/vfs": "^0.3.3", - "@yao-pkg/pkg-fetch": "3.6.4", + "@yao-pkg/pkg-fetch": "3.6.5", "esbuild": "^0.28.1", "into-stream": "^9.1.0", "multistream": "^4.1.0", @@ -3017,9 +3019,9 @@ } }, "node_modules/@yao-pkg/pkg-fetch": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/@yao-pkg/pkg-fetch/-/pkg-fetch-3.6.4.tgz", - "integrity": "sha512-2JXvS9HbMudLlzEjSaJ7bLNnF/WlTv7iaTAJp2Tk8pBsgwCyL9p4rIN8cztHlyZF7qVzr1EaBjd7DQooFk0azQ==", + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/@yao-pkg/pkg-fetch/-/pkg-fetch-3.6.5.tgz", + "integrity": "sha512-Sd1Hff7imsF2rcZ2GLQuIzVm2fc3Za+nE4KSWPTwIYegN/r90UFg3bq0MLSugbWQaVht72zzxV0MiKE0wvT1zA==", "dev": true, "license": "MIT", "dependencies": { @@ -3075,9 +3077,9 @@ } }, "node_modules/@yao-pkg/pkg-fetch/node_modules/tar-stream": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", - "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.1.tgz", + "integrity": "sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3546,9 +3548,9 @@ } }, "node_modules/b4a": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", - "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.9.0.tgz", + "integrity": "sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==", "dev": true, "license": "Apache-2.0", "peerDependencies": { @@ -3571,9 +3573,9 @@ } }, "node_modules/bare-events": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", - "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", + "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", "dev": true, "license": "Apache-2.0", "peerDependencies": { @@ -3586,9 +3588,9 @@ } }, "node_modules/bare-fs": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.4.tgz", - "integrity": "sha512-y1kC+ffIx/tPLdTE693uNjHfzTfr+ravR5tvWlMXe25nELbkqV400S71qHDwbkAQ1FVEZobB1NFRzFbCCcyBCQ==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.1.tgz", + "integrity": "sha512-N1nnXdHZAOSstz0XiHikGS4HGMH4CnSwhqWdGQQMqqdvp4Jybm9sE3R1WVnpWVd4SFkc8ryPDBLViNLwiEqECg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3599,7 +3601,7 @@ "fast-fifo": "^1.3.2" }, "engines": { - "bare": ">=1.16.0" + "bare": ">=1.28.0" }, "peerDependencies": { "bare-buffer": "*" @@ -3611,16 +3613,16 @@ } }, "node_modules/bare-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz", - "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.2.tgz", + "integrity": "sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==", "dev": true, "license": "Apache-2.0" }, "node_modules/bare-stream": { - "version": "2.13.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", - "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.4.tgz", + "integrity": "sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3646,9 +3648,9 @@ } }, "node_modules/bare-url": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.6.tgz", - "integrity": "sha512-iQxPClE07hETVpbRoX7JXX3v/ZQViCxe/SYCxylRLzdEx1xJAufPptfiOqR8tqiCtmbtMDANKWszzjLu1PMAZQ==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.4.tgz", + "integrity": "sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3851,6 +3853,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.11.20", "caniuse-lite": "^1.0.30001810", @@ -5634,6 +5637,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -5773,6 +5777,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -8516,6 +8521,7 @@ "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -8799,9 +8805,9 @@ "license": "MIT" }, "node_modules/mocha": { - "version": "11.7.6", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.6.tgz", - "integrity": "sha512-nS9xOGbw2I3cjCpxwZAEJ9xK9lmJ08vEkQvLtz4du9ZrF9UrjRpeJGiIgl2Z+Qs++pmB4ecDe48Fwsh+j+j7xA==", + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.8.0.tgz", + "integrity": "sha512-VyCeUdGN3A9lmCTTgG4yuvY9ixxaDk+xt2R/7/+1AP6EqNG+G9OKkzBwhVtVYoNX8YsxNSgAl8mOv3IAeOpFbw==", "dev": true, "license": "MIT", "dependencies": { @@ -11053,6 +11059,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -12780,9 +12787,9 @@ } }, "node_modules/qrllib": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/qrllib/-/qrllib-1.2.4.tgz", - "integrity": "sha512-vIvvMiEEUlnzjRpXSKEM57MFJE9+SNwLCflPRW4/fHnKLc8XM+CFdKUfEldMybC6jP69L2oyVpa6JwmLqd19NA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/qrllib/-/qrllib-1.2.7.tgz", + "integrity": "sha512-qiI2B5VVWQ4yp2FNlGJcHlJ/PSPCaToDmjqXS8u+Z2P0i8GQU1vrKamoJmn6lPwRkyEou9OMtQr1/+IWGL8xVg==", "license": "MIT", "engines": { "node": ">=4.2.4" @@ -13365,11 +13372,12 @@ "license": "MIT" }, "node_modules/semantic-release": { - "version": "25.0.8", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-25.0.8.tgz", - "integrity": "sha512-w/iZ0bur36rKffXZYmIUmy068eoBY3Ij1DCCddx2JwWEM5Tg+eU9ld/E9qSInVvPASyyR2Ln/XGfQ9OZrMlhtw==", + "version": "25.0.9", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-25.0.9.tgz", + "integrity": "sha512-bxve7csK0/Txr++CkfrmV+X1r4jqiSOw2WsSad9E2S68R+ZfLBwDn8IceM8WfiOmKQIHgsQc1cNA8Dzg7U75pg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/error": "^4.0.0", @@ -14479,9 +14487,9 @@ } }, "node_modules/streamx": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", - "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", + "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", "dev": true, "license": "MIT", "dependencies": { @@ -15057,6 +15065,7 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, diff --git a/package.json b/package.json index 2e50446..4f0c63d 100644 --- a/package.json +++ b/package.json @@ -8,46 +8,46 @@ }, "bugs": "https://github.com/theqrl/qrl-cli/issues", "dependencies": { - "@grpc/grpc-js": "^1.6.7", - "@grpc/proto-loader": "^0.7.15", - "@oclif/command": "^1", - "@oclif/config": "^1", - "@oclif/plugin-help": "^2", - "@theqrl/explorer-helpers": "^2.7.0", - "@theqrl/qrl-proto-sha256": "^3.4.1", - "@theqrl/qrlbase.proto": "^1.1.0", - "@theqrl/validate-qrl-address": "^1.1.0", - "aes-js": "^3.1.2", - "bech32": "^1.1.3", - "bignumber.js": "^9.0.0", - "cli-ux": "^5.3.1", - "conf": "^10.2.0", - "crypto-js": "^4.2.0", - "eccrypto": "^1.1.5", - "kleur": "^3.0.3", - "moment": "^2.24.0", - "ora": "^3.4.0", - "prompts": "^2.4.2", - "qrcode-terminal": "^0.12.0", - "qrllib": "^1.1.7", - "tmp": "^0.2.7", - "util": "^0.12.1" + "@grpc/grpc-js": "1.14.4", + "@grpc/proto-loader": "0.7.15", + "@oclif/command": "1.8.36", + "@oclif/config": "1.18.17", + "@oclif/plugin-help": "2.2.3", + "@theqrl/explorer-helpers": "2.8.2", + "@theqrl/qrl-proto-sha256": "3.5.0", + "@theqrl/qrlbase.proto": "1.1.1", + "@theqrl/validate-qrl-address": "1.1.0", + "aes-js": "3.1.2", + "bech32": "1.1.4", + "bignumber.js": "9.3.1", + "cli-ux": "5.6.7", + "conf": "10.2.0", + "crypto-js": "4.2.0", + "eccrypto": "1.1.6", + "kleur": "3.0.3", + "moment": "2.30.1", + "ora": "3.4.0", + "prompts": "2.4.2", + "qrcode-terminal": "0.12.0", + "qrllib": "1.2.7", + "tmp": "0.2.7", + "util": "0.12.5" }, "devDependencies": { - "@babel/eslint-parser": "^7.25.8", - "@oclif/dev-cli": "^1", - "@oclif/test": "^1", - "@semantic-release/git": "^10.0.1", - "@yao-pkg/pkg": "^6.21.0", - "chai": "^4", - "eslint": "^8.57.1", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-import": "^2.31.0", - "globby": "^10", - "mocha": "^11.7.6", - "nyc": "^18.0.0", - "semantic-release": "^25.0.8" + "@babel/eslint-parser": "7.29.7", + "@oclif/dev-cli": "1.26.10", + "@oclif/test": "1.2.9", + "@semantic-release/git": "10.0.1", + "@yao-pkg/pkg": "6.22.0", + "chai": "4.5.0", + "eslint": "8.57.1", + "eslint-config-airbnb-base": "15.0.0", + "eslint-config-prettier": "9.1.2", + "eslint-plugin-import": "2.32.0", + "globby": "10.0.2", + "mocha": "11.8.0", + "nyc": "18.0.0", + "semantic-release": "25.0.9" }, "engines": { "node": ">=16.0.0" @@ -127,10 +127,10 @@ ] }, "overrides": { - "secp256k1": "^3.8.1", - "elliptic": "^6.6.1", - "serialize-javascript": "^7.0.7", - "tmp": "^0.2.7", - "diff": "^8.0.3" + "secp256k1": "3.8.1", + "elliptic": "6.6.1", + "serialize-javascript": "7.0.7", + "tmp": "0.2.7", + "diff": "8.0.4" } } diff --git a/scripts/postinstall.js b/scripts/postinstall.js index f1de982..831bf04 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -10,12 +10,15 @@ * copied; a mismatch aborts the copy and eccrypto falls back to its * JS implementation. * - * 2. Hardens the vendored qrllib Emscripten bundles: - * - forces Module.ENVIRONMENT = 'NODE' so the random-device selection is - * explicit rather than environment-sniffed - * - replaces the Math.random() fallback arm of the /dev/urandom device - * with a throw, so a mis-detected environment fails closed instead of - * silently degrading key generation + * 2. Checks the RNG the vendored qrllib Emscripten bundles feed into key + * generation, and hardens it where the bundle allows: + * - older bundles that sniff the environment get Module.ENVIRONMENT pinned + * to 'NODE', and the Math.random() fallback arm of the /dev/urandom + * device replaced with a throw, so a mis-detected environment fails + * closed instead of silently degrading key generation + * - current bundles already wire the device to randomFillSync / + * getRandomValues with no fallback arm, so they are verified instead of + * rewritten; anything else is reported as an error */ const crypto = require('crypto'); @@ -84,11 +87,20 @@ function installEcdhNode() { } } -// The Emscripten glue in the qrllib bundles selects the /dev/urandom device -// implementation by sniffing the environment, with a silent Math.random() -// fallback if neither WebCrypto nor Node is detected. Both properties are -// unacceptable for code that feeds key generation, so patch the installed -// bundles: pin the environment and make the fallback arm throw. +// qrllib ships prebuilt Emscripten bundles, so the RNG that feeds key +// generation is whatever the bundle's glue code decided to use. +// +// Bundles from qrllib < 1.2.5 sniffed the environment for the /dev/urandom +// device and fell back to Math.random() when neither WebCrypto nor Node was +// detected. That fallback is patched out here and replaced with a throw, so a +// mis-detected environment fails closed instead of silently degrading. +// +// qrllib >= 1.2.5 is built with an Emscripten that wires the device straight to +// randomFillSync/getRandomValues and has no Math.random() arm at all, so there +// is nothing to patch. Those bundles are verified rather than rewritten: a +// Math.random() RNG arm in any form is reported as an error, because it means +// the bundle is neither a shape this script can harden nor one that is already +// safe. const QRLLIB_BUNDLES = [ 'offline-libjsqrl.js', 'offline-libjsdilithium.js', @@ -101,6 +113,14 @@ const ENV_PINNED = 'var Module=typeof Module!=="undefined"?Module:{};Module["ENV const MATH_RANDOM_ARM = 'else{random_device=(function(){return Math.random()*256|0})}'; const FAIL_CLOSED_ARM = 'else{random_device=(function(){throw new Error("qrl-cli: no secure random source available in this environment")})}'; +// Signatures of the secure entropy sources the current bundles use: the +// randomFill helper backing /dev/random and /dev/urandom, and the getRandomSeed +// export. A bundle needs at least one of them. +const SECURE_RNG_SIGNATURES = [ + 'nodeCrypto.randomFillSync(view)', + 'cryptoObj.getRandomValues(bytes)', +]; + function patchQrllibBundles() { const buildDir = path.join(__dirname, '..', 'node_modules', 'qrllib', 'build'); QRLLIB_BUNDLES.forEach((bundle) => { @@ -124,10 +144,22 @@ function patchQrllibBundles() { if (changed) { fs.writeFileSync(bundlePath, source); console.log(`✓ Hardened ${bundle}: ENVIRONMENT pinned to NODE, Math.random RNG arm removed`); - } else if (source.includes('Math.random()*256|0')) { + return; + } + + if (source.includes('Math.random')) { console.error(`ERROR: ${bundle} contains a Math.random RNG arm in an unexpected form; manual review required`); process.exitCode = 1; + return; + } + + const secure = SECURE_RNG_SIGNATURES.filter((sig) => source.includes(sig)); + if (secure.length === 0) { + console.error(`ERROR: ${bundle} exposes no recognised secure entropy source; manual review required`); + process.exitCode = 1; + return; } + console.log(`✓ Verified ${bundle}: no Math.random RNG arm, entropy from ${secure.join(' + ')}`); }); } diff --git a/test/commands/config.test.js b/test/commands/config.test.js index 77ce17e..aab246c 100644 --- a/test/commands/config.test.js +++ b/test/commands/config.test.js @@ -29,6 +29,20 @@ let childEnv // before matching on the message text. const ANSI = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, 'g') +// Where `conf` puts the store once the child has been pointed at `home`. `env-paths` builds +// that path differently per platform, and only the Linux branch reads XDG_CONFIG_HOME -- macOS +// and Windows derive it from the home directory and APPDATA -- so the layout has to be mirrored +// here rather than assumed. +function configDir(home) { + if (process.platform === 'darwin') { + return path.join(home, 'Library', 'Preferences', 'qrl-cli-nodejs') + } + if (process.platform === 'win32') { + return path.join(home, 'qrl-cli-nodejs', 'Config') + } + return path.join(home, 'qrl-cli-nodejs') +} + // Run the CLI against the throwaway config store and capture what it said. function run(args) { return new Promise(resolve => { @@ -102,7 +116,7 @@ describe('config command tests', () => { it('writes only to the redirected config directory', async () => { await run(['config', 'set', 'grpc-endpoint', '127.0.0.1:19009']) - const stored = path.join(tempHome, 'qrl-cli-nodejs', 'config.json') + const stored = path.join(configDir(tempHome), 'config.json') assert.ok(fs.existsSync(stored), `expected a config file at ${stored}`) assert.match(fs.readFileSync(stored, 'utf8'), /127\.0\.0\.1:19009/) await run(['config', 'delete', 'grpc-endpoint']) diff --git a/test/functions/grpc.test.js b/test/functions/grpc.test.js index 6872087..447aec1 100644 --- a/test/functions/grpc.test.js +++ b/test/functions/grpc.test.js @@ -55,6 +55,17 @@ const contentDigest = text => crypto.createHash('sha256').update(text).digest('h // The constructor reads process.argv to find out whether the user pinned a network on the command // line. Under mocha, argv carries the runner's own flags (`-t` is mocha's timeout), so every test // states the argv it means instead of inheriting however mocha happened to be invoked. +// `env-paths` reads os.homedir() once, when it is first required, and on macOS the config +// directory is derived from that alone -- XDG_CONFIG_HOME is a Linux-only variable. Requiring it +// happens as soon as anything pulls in `conf`, which src/functions/grpc.js does, so by the time +// the hooks below redirect HOME the cached value is already the developer's real home. Dropping +// both modules from the require cache forces the next `new Conf()` -- including the one inside +// QrlNode -- to resolve against whatever HOME is set now. +function reloadConf() { + delete require.cache[require.resolve('env-paths')] + delete require.cache[require.resolve('conf')] +} + function makeNode(ipAddress, argv = ['node', 'qrl-cli', '--grpc', ipAddress]) { const savedArgv = process.argv process.argv = argv @@ -303,6 +314,7 @@ describe('functions/grpc endpoint precedence', () => { NETWORK_ENV.forEach(key => { delete process.env[key] }) + reloadConf() // eslint-disable-next-line global-require const Conf = require('conf') @@ -326,6 +338,8 @@ describe('functions/grpc endpoint precedence', () => { process.env[key] = saved[key] } }) + // Leave the real home resolving normally for whatever runs next. + reloadConf() fs.rmSync(tempHome, {recursive: true, force: true}) }) diff --git a/test/functions/network-helper.test.js b/test/functions/network-helper.test.js index fc67886..ba889b4 100644 --- a/test/functions/network-helper.test.js +++ b/test/functions/network-helper.test.js @@ -22,6 +22,11 @@ const NETWORK_ENV = ['QRL_NETWORK', 'QRL_GRPC_ENDPOINT'] const MODULE_PATH = require.resolve('../../src/functions/network-helper') const CONF_PATH = require.resolve('conf') +// `env-paths` reads os.homedir() once, when it is first required, and on macOS the config +// directory is derived from that alone -- XDG_CONFIG_HOME is a Linux-only variable. Unless it is +// dropped from the require cache too, redirecting HOME below leaves `conf` pointing at the +// developer's real configuration. +const ENV_PATHS_PATH = require.resolve('env-paths') let saved let tempHome @@ -48,6 +53,7 @@ describe('functions/network-helper', () => { // environment is redirected. delete require.cache[MODULE_PATH] delete require.cache[CONF_PATH] + delete require.cache[ENV_PATHS_PATH] // eslint-disable-next-line global-require const Conf = require('conf') config = new Conf({projectName: 'qrl-cli'}) @@ -70,6 +76,7 @@ describe('functions/network-helper', () => { }) delete require.cache[MODULE_PATH] delete require.cache[CONF_PATH] + delete require.cache[ENV_PATHS_PATH] fs.rmSync(tempHome, {recursive: true, force: true}) })