From 6b7013066d23189bafbcf7c9a596b1716994e1f0 Mon Sep 17 00:00:00 2001 From: k9ert <117085+k9ert@users.noreply.github.com> Date: Sun, 19 Apr 2026 22:01:56 +0200 Subject: [PATCH 01/28] =?UTF-8?q?Chore(deps):=20Security=20=E2=80=94=20bum?= =?UTF-8?q?p=20h11=20and=20form-data=20(#2613)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 29 +++--- package.json | 8 ++ pyinstaller/electron/package-lock.json | 126 +++++++++++++------------ pyinstaller/electron/package.json | 8 ++ pyinstaller/electron/yarn.lock | 42 ++++++--- requirements.txt | 6 +- yarn.lock | 31 +++--- 7 files changed, 152 insertions(+), 98 deletions(-) diff --git a/package-lock.json b/package-lock.json index fec9c5d49..af01d5049 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2500,14 +2500,18 @@ } }, "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35", + "safe-buffer": "^5.2.1" }, "engines": { "node": ">= 0.12" @@ -7191,7 +7195,7 @@ "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.3.2", + "form-data": "^2.5.4", "http-signature": "~1.3.6", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", @@ -9101,14 +9105,17 @@ "dev": true }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", "dev": true, "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35", + "safe-buffer": "^5.2.1" } }, "fragment-cache": { diff --git a/package.json b/package.json index 2202683e6..ade00cb8c 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,14 @@ "gulp": "^4.0.2", "tailwindcss": "^3.2.4" }, + "overrides": { + "@cypress/request": { + "form-data": "^2.5.4" + } + }, + "resolutions": { + "**/@cypress/request/form-data": "^2.5.4" + }, "scripts": { "dev": "gulp", "tailwind": "npx tailwindcss -i ./src/cryptoadvance/specter/static/style.css -o ./src/cryptoadvance/specter/static/output.css --watch", diff --git a/pyinstaller/electron/package-lock.json b/pyinstaller/electron/package-lock.json index 63b8bbe14..c5840c0ec 100644 --- a/pyinstaller/electron/package-lock.json +++ b/pyinstaller/electron/package-lock.json @@ -1427,7 +1427,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -1970,7 +1969,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -2301,7 +2299,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2311,7 +2308,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2321,7 +2317,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2334,7 +2329,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -2561,18 +2555,42 @@ } }, "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35", + "safe-buffer": "^5.2.1" }, "engines": { "node": ">= 0.12" } }, + "node_modules/form-data/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -2609,7 +2627,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, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2628,7 +2645,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -2653,7 +2669,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -2772,7 +2787,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2858,7 +2872,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2871,7 +2884,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -2886,7 +2898,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3367,7 +3378,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3386,19 +3396,21 @@ } }, "node_modules/mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { - "mime-db": "1.44.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -6149,7 +6161,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "requires": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -6563,7 +6574,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "requires": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -6832,20 +6842,17 @@ "es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" }, "es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" }, "es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "requires": { "es-errors": "^1.3.0" } @@ -6854,7 +6861,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "requires": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -7025,13 +7031,23 @@ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35", + "safe-buffer": "^5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "fs-extra": { @@ -7063,8 +7079,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==" }, "get-caller-file": { "version": "2.0.5", @@ -7076,7 +7091,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "requires": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -7094,7 +7108,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "requires": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -7185,8 +7198,7 @@ "gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" }, "got": { "version": "11.8.6", @@ -7246,14 +7258,12 @@ "has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" }, "has-tostringtag": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "requires": { "has-symbols": "^1.0.3" } @@ -7262,7 +7272,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "requires": { "function-bind": "^1.1.2" } @@ -7613,8 +7622,7 @@ "math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" }, "mime": { "version": "2.6.0", @@ -7623,16 +7631,16 @@ "dev": true }, "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.44.0" + "mime-db": "1.52.0" } }, "mimic-fn": { @@ -8150,7 +8158,7 @@ "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.3.2", + "form-data": "^2.5.4", "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", diff --git a/pyinstaller/electron/package.json b/pyinstaller/electron/package.json index d09b8f89a..a7f55e486 100644 --- a/pyinstaller/electron/package.json +++ b/pyinstaller/electron/package.json @@ -60,5 +60,13 @@ "read-last-lines": "^1.8.0", "request": "^2.88.2", "winston": "^3.13.0" + }, + "overrides": { + "request": { + "form-data": "^2.5.4" + } + }, + "resolutions": { + "**/request/form-data": "^2.5.4" } } \ No newline at end of file diff --git a/pyinstaller/electron/yarn.lock b/pyinstaller/electron/yarn.lock index 53ebc5147..0fb9bf104 100644 --- a/pyinstaller/electron/yarn.lock +++ b/pyinstaller/electron/yarn.lock @@ -709,7 +709,7 @@ colorspace@1.1.x: color "3.0.x" text-hex "1.0.x" -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -1137,6 +1137,18 @@ forever-agent@~0.6.1: resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +form-data@^2.5.4, form-data@~2.3.2: + version "2.5.5" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.5.tgz#a5f6364ad7e4e67e95b4a07e2d8c6f711c74f624" + integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.35" + safe-buffer "^5.2.1" + form-data@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" @@ -1148,15 +1160,6 @@ form-data@^4.0.5: hasown "^2.0.2" mime-types "^2.1.12" -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - fs-extra@^10.0.0, fs-extra@^10.1.0: version "10.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz" @@ -1591,7 +1594,7 @@ jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz" integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - dependencies: + optionalDependencies: graceful-fs "^4.1.6" jsonfile@^6.0.1: @@ -1711,6 +1714,11 @@ mime-db@1.44.0, mime-db@^1.28.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz" integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + mime-types@^2.1.12, mime-types@~2.1.19: version "2.1.27" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz" @@ -1718,6 +1726,13 @@ mime-types@^2.1.12, mime-types@~2.1.19: dependencies: mime-db "1.44.0" +mime-types@^2.1.35: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime@^2.5.2: version "2.6.0" resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz" @@ -2195,6 +2210,11 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0: resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-buffer@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-stable-stringify@^2.3.1: version "2.4.3" resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz" diff --git a/requirements.txt b/requirements.txt index 913302284..b5a2e857e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -300,9 +300,9 @@ gunicorn==23.0.0 \ --hash=sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d \ --hash=sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec # via -r requirements.in -h11==0.14.0 \ - --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \ - --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761 +h11==0.16.0 \ + --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ + --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 # via wsproto hidapi==0.14.0 \ --hash=sha256:01929fbbe206ebcb0bad9b8e925e16de0aa8f872bf80a263f599e519866d9900 \ diff --git a/yarn.lock b/yarn.lock index 854c9e8ac..f0d802d58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -781,7 +781,7 @@ colorette@^2.0.16: resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz" integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -1449,6 +1449,18 @@ forever-agent@~0.6.1: resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +form-data@^2.5.4, form-data@~2.3.2: + version "2.5.5" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.5.tgz#a5f6364ad7e4e67e95b4a07e2d8c6f711c74f624" + integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.35" + safe-buffer "^5.2.1" + form-data@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" @@ -1460,15 +1472,6 @@ form-data@^4.0.5: hasown "^2.0.2" mime-types "^2.1.12" -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz" @@ -2408,9 +2411,9 @@ mime-db@1.52.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@~2.1.19: +mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.19: version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" @@ -3131,9 +3134,9 @@ rxjs@^7.5.1, rxjs@^7.8.2: dependencies: tslib "^2.1.0" -safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2: +safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.1: version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-buffer@~5.1.0, safe-buffer@~5.1.1: From a793e2dce8df1c3c451b35b8b8f3664bf58c357a Mon Sep 17 00:00:00 2001 From: k9ert <117085+k9ert@users.noreply.github.com> Date: Sun, 19 Apr 2026 22:27:32 +0200 Subject: [PATCH 02/28] test: bump tested bitcoind to v27.2.0 (#2609) Co-authored-by: Claude Opus 4.7 (1M context) --- pyproject.toml | 2 +- .../specter/devices/bitcoin_core.py | 16 +++-- tests/bitcoin_SHA256SUMS | 58 ++++++++++--------- tests/test_ep_wallet_api.py | 6 +- tests/test_wallet_txlist.py | 5 +- 5 files changed, 51 insertions(+), 36 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index faf36f277..c822bb7db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ write_to = "src/cryptoadvance/specter/_version.py" norecursedirs = "tests/bitcoin* tests/elements* tests/xtestdata_testextensions" log_format = "[%(levelname)8s] %(message)s %(name)s (%(filename)s:%(lineno)s)" -addopts = "--bitcoind-version v22.0.0 --elementsd-version v0.21.0.2" +addopts = "--bitcoind-version v27.2.0 --elementsd-version v0.21.0.2" markers = [ "slow: mark test as slow.", "elm: mark test as elementsd dependent", diff --git a/src/cryptoadvance/specter/devices/bitcoin_core.py b/src/cryptoadvance/specter/devices/bitcoin_core.py index 1a6facff6..9937ba7ca 100644 --- a/src/cryptoadvance/specter/devices/bitcoin_core.py +++ b/src/cryptoadvance/specter/devices/bitcoin_core.py @@ -65,13 +65,17 @@ class BitcoinCore(Device): def taproot_available(self, rpc): try: - # currently only master branch supports tr() descriptors - # TODO: replace to 220000 core_version = rpc.getnetworkinfo().get("version", 0) - info = rpc.getblockchaininfo() - taproot_active = (core_version >= 219900) and ( - info.get("softforks", {}).get("taproot", {}).get("active", False) - ) + if core_version >= 240000: + # Core 24 moved softfork status from getblockchaininfo.softforks + # to the getdeploymentinfo RPC's deployments field. + deployments = rpc.getdeploymentinfo().get("deployments", {}) + taproot_active = deployments.get("taproot", {}).get("active", False) + else: + softforks = rpc.getblockchaininfo().get("softforks", {}) + taproot_active = (core_version >= 219900) and softforks.get( + "taproot", {} + ).get("active", False) taproot_support = self.use_descriptors(rpc) and taproot_active self.taproot_support = taproot_support return taproot_support diff --git a/tests/bitcoin_SHA256SUMS b/tests/bitcoin_SHA256SUMS index 7649492d4..d120b70cc 100644 --- a/tests/bitcoin_SHA256SUMS +++ b/tests/bitcoin_SHA256SUMS @@ -1,8 +1,8 @@ -# Trusted reference: verbatim plaintext of https://bitcoincore.org/bin/bitcoin-core-22.0/SHA256SUMS +# Trusted reference: verbatim plaintext of https://bitcoincore.org/bin/bitcoin-core-27.2/SHA256SUMS # Provenance: -# Source URL: https://bitcoincore.org/bin/bitcoin-core-22.0/SHA256SUMS -# Signature URL: https://bitcoincore.org/bin/bitcoin-core-22.0/SHA256SUMS.asc -# Verified on: 2026-04-15 +# Source URL: https://bitcoincore.org/bin/bitcoin-core-27.2/SHA256SUMS +# Signature URL: https://bitcoincore.org/bin/bitcoin-core-27.2/SHA256SUMS.asc +# Verified on: 2026-04-19 # Verified by: Good signatures from: # - fanquake (primary fingerprint E777299FC265DD04793070EB944D35F9AC3DB76A, # signing subkey CFB16E21C950F67FA95E558F2EEB9F5CC09526C1) @@ -10,26 +10,30 @@ # Builder keys sourced from: https://github.com/bitcoin-core/guix.sigs/tree/main/builder-keys # Do not edit the hash lines below — they are the on-disk trust anchor compared # against downloaded tarballs by tests/install_noded.sh::verify_binary. -9547fa03574f8bde296f707c7d9f7d89827c75c5a28f84402578a4fa92a787ec bitcoin-22.0-aarch64-linux-gnu-debug.tar.gz -ac718fed08570a81b3587587872ad85a25173afa5f9fbbd0c03ba4d1714cfa3e bitcoin-22.0-aarch64-linux-gnu.tar.gz -80071e0ecd24edfec8a1972b495b9822c79a5d33c7123bff51688638aac97cab bitcoin-22.0-arm-linux-gnueabihf-debug.tar.gz -b8713c6c5f03f5258b54e9f436e2ed6d85449aa24c2c9972f91963d413e86311 bitcoin-22.0-arm-linux-gnueabihf.tar.gz -8f70852feb39078e02182563517d17bdfc4a12904cf1bdabbae95594d9a1e473 bitcoin-22.0-codesignatures-22.0.tar.gz -d0e9d089b57048b1555efa7cd5a63a7ed042482045f6f33402b1df425bf9613b bitcoin-22.0.tar.gz -bfc04a3c4e8b613bfd9359e54da6cc60f027860e9723f9a6bfd6f13873eb811f bitcoin-22.0-powerpc64-linux-gnu-debug.tar.gz -2cca5f99007d060aca9d8c7cbd035dfe2f040dd8200b210ce32cdf858479f70d bitcoin-22.0-powerpc64-linux-gnu.tar.gz -5f0bf1491bc8825ca1506f7cf586030f06bb17a563ccde92e8c75720022704e6 bitcoin-22.0-powerpc64le-linux-gnu-debug.tar.gz -91b1e012975c5a363b5b5fcc81b5b7495e86ff703ec8262d4b9afcfec633c30d bitcoin-22.0-powerpc64le-linux-gnu.tar.gz -59b16e63aa935f50fd2813efe7f137187fcf0fff84e3205a9c6cb462a8bb160c bitcoin-22.0-riscv64-linux-gnu-debug.tar.gz -9cc3a62c469fe57e11485fdd32c916f10ce7a2899299855a2e479256ff49ff3c bitcoin-22.0-riscv64-linux-gnu.tar.gz -3b3e2680f7d9304c13bfebaf6445ada40d72324b4b3e0a07de9db807389a6c5b bitcoin-22.0-osx-signed.dmg -52449aa894a6ce5653315e1260d0ce87c1d9f490afe3c92b44285710804b11ae bitcoin-22.0-osx-unsigned.dmg -f51156774c24c0ac5cc30237fa08aa17ed04a180dfd72c3e7d20fdc3f45806dc bitcoin-22.0-osx-unsigned.tar.gz -2744d199c3343b2d94faffdfb2c94d75a630ba27301a70e47b0ad30a7e0155e9 bitcoin-22.0-osx64.tar.gz -3a4f05657c048d3e9505bdb9c4fb3658e5e3d4233b0b93c1853e080620589765 bitcoin-22.0-x86_64-linux-gnu-debug.tar.gz -59ebd25dd82a51638b7a6bb914586201e67db67b919b2a1ff08925a7936d1b16 bitcoin-22.0-x86_64-linux-gnu.tar.gz -9169989d649937c0f9ebccd3ab088501328aa319fe9e91fc7ea8e8cf0fcccede bitcoin-22.0-win64-setup.exe -f890473d6d910d478f8ff08f9356d0305d19b46cf06e4fc3b5a49b0b684fd2a7 bitcoin-22.0-win-unsigned.tar.gz -0a97ebc8ae44913e3ef9c5b1ddd2af3a4ffb0ba25b6ab1ee8173e40e60499402 bitcoin-22.0-win64-debug.zip -ecc579d006230d6ffc5a5b7b53ce8c76477d37c1c7bad69694e9c2d69f00331d bitcoin-22.0-win64-setup-unsigned.exe -9485e4b52ed6cebfe474ab4d7d0c1be6d0bb879ba7246a8239326b2230a77eb1 bitcoin-22.0-win64.zip +eb0c8518ad2e96d01208f589ecd827b5a951be6dfcac418d4b807842a6ebdf65 bitcoin-27.2-aarch64-linux-gnu-debug.tar.gz +154c9b9e6e17136edc8f20fda5d252fb339e727e4a85ef49e7d8facb9085f2d3 bitcoin-27.2-aarch64-linux-gnu.tar.gz +4a05d46dcb74d337f62b78b884df080534677897fd9e1a0ae41253547789cef8 bitcoin-27.2-arm-linux-gnueabihf-debug.tar.gz +fb00da13525b52a4aad091c6521f94a79879e5ca0956c2302289301f71e6303c bitcoin-27.2-arm-linux-gnueabihf.tar.gz +3a4795d591c0e506e88ce227b17048d347be37611ba98837a71a16c3a5f908c6 bitcoin-27.2-arm64-apple-darwin.zip +4e91003a95108f85e85f92c3af23aa6108175a1d0a0c60cb515c2d3daf8a20f4 bitcoin-27.2-arm64-apple-darwin-unsigned.tar.gz +48800ea29baca2e27f1544fc0c4d4cd096a908291c1e681bd9daa632645bce06 bitcoin-27.2-arm64-apple-darwin-unsigned.zip +8f2247f4786f3559d37189b58452c91623efc5fa6886c975fa9386f9ff3f1001 bitcoin-27.2-arm64-apple-darwin.tar.gz +33b59d57b1a3f11ca9c7993891a359fdc19e62284be3235d3951053cbbe81a60 bitcoin-27.2-codesignatures-27.2.tar.gz +5a8b0094b3c6bc7f63c7fd6e0ba2e733a3022f09c0064c96ccd6e331bd7b9f6a bitcoin-27.2.tar.gz +6a273a634a04123c48c4a4be9ddbeeba27d2acdbefecd1025cfca1f30b394759 bitcoin-27.2-powerpc64-linux-gnu-debug.tar.gz +566ed37e33ce529c75d9d0c72ff3894d0d5a311bd5f21be2de7495937348e454 bitcoin-27.2-powerpc64-linux-gnu.tar.gz +6f9824a833a0c9a2c11cabf302cf0b0f573471cf8a7f0f7ad8723db4094515d5 bitcoin-27.2-powerpc64le-linux-gnu-debug.tar.gz +efb66cc2648b3968f7186c990640589bd6fd72d93879b0cc1ff2adef9cd377b9 bitcoin-27.2-powerpc64le-linux-gnu.tar.gz +fdc90b4692a5aaf04f6626b71fe40368ec0bcf000d066b1efdab99318000a3a8 bitcoin-27.2-riscv64-linux-gnu-debug.tar.gz +0c272ec7b7bb6bdee410fb177ceaab0df0373de5aed2228ab9fb4e6128ccc793 bitcoin-27.2-riscv64-linux-gnu.tar.gz +d74feac2e7bbb7df84c8f67ca6216ef4f83eb7e9c2213a901896d7814bfafb90 bitcoin-27.2-x86_64-apple-darwin.zip +cbac742ce80dac1b57d455680b3accb8f75c20e782169185ef519b3e0534d498 bitcoin-27.2-x86_64-apple-darwin-unsigned.tar.gz +8630d96eba03ecb57066442947cc08d58367666f96d825c636015ff458f46c16 bitcoin-27.2-x86_64-apple-darwin-unsigned.zip +6ebc56ca1397615d5a6df2b5cf6727b768e3dcac320c2d5c2f321dcaabc7efa2 bitcoin-27.2-x86_64-apple-darwin.tar.gz +8eb4e52ab1b3a27640a2c1a00c526815ab8a740443f963f5e6dfc5fdb37bcc2c bitcoin-27.2-x86_64-linux-gnu-debug.tar.gz +acc223af46c178064c132b235392476f66d486453ddbd6bca6f1f8411547da78 bitcoin-27.2-x86_64-linux-gnu.tar.gz +30e9a0e4b3e1e32aceb4773615c0a9ed54443b037ef541ea3be07508377cf128 bitcoin-27.2-win64-setup.exe +723a70ca4c0f7d0cef028d6cf502f595e3684c9f30c6c3c6b88d05c26b638c35 bitcoin-27.2-win64-debug.zip +5f779dbf911e75c4ca4a57b04f972a52006ed49de3519c640fef99a596b22e0e bitcoin-27.2-win64-setup-unsigned.exe +e92e8531f50915a74790689e6143d8d712b471233c38231f60bfa40ffb820bca bitcoin-27.2-win64-unsigned.tar.gz +82e18f768aa5962b3c002d7f5d6ec9338896804f48406af4b5054c927575dbdf bitcoin-27.2-win64.zip diff --git a/tests/test_ep_wallet_api.py b/tests/test_ep_wallet_api.py index 34fafb9a1..30c95b70d 100644 --- a/tests/test_ep_wallet_api.py +++ b/tests/test_ep_wallet_api.py @@ -181,7 +181,11 @@ def test_addressinfo(caplog, client, funded_ghost_machine_wallet): assert res.data.decode().startswith( '{"error":"Request error for method getaddressinfo' ) - assert res.data.decode().endswith('Invalid address format"}\n') + # Core 22 says "Invalid address format"; Core 24+ says "Invalid checksum". + assert ( + 'Invalid address format"}\n' in res.data.decode() + or 'Invalid checksum"}\n' in res.data.decode() + ) # send post request with address, not belonging to wallet # this recreates an edge case, see https://github.com/cryptoadvance/specter-desktop/issues/2000 diff --git a/tests/test_wallet_txlist.py b/tests/test_wallet_txlist.py index cbf2b4fac..8f691785f 100644 --- a/tests/test_wallet_txlist.py +++ b/tests/test_wallet_txlist.py @@ -347,7 +347,10 @@ def test_WalletAwareTxItem(bitcoin_regtest, parent_mock, empty_data_folder): print("\n\nOutgoing-Transaction (0.2 btc)") print("=========================================") - txid_outgoing_addr = "n4MN27Lk7Yh3pwfjCiAbRXtRVjs4Uk67fG" + # bech32 destination so Core keeps change as wpkh (Core 24+ matches change + # type to destination type; legacy destinations produce legacy change that + # the wpkh-only watch descriptor in this test does not recognise). + txid_outgoing_addr = "bcrt1qvtdx75y4554ngrq6aff3xdqnvjhmct5wck95qs" print(f"address = {txid_outgoing_addr}") txid_outgoing = wrpc.sendtoaddress(txid_outgoing_addr, 0.2) print(f"balance: {wrpc.getbalances()['mine']['trusted']}") From 5a4f24295f22b5ee302cb3d776adeca94369d36c Mon Sep 17 00:00:00 2001 From: k9ert <117085+k9ert@users.noreply.github.com> Date: Mon, 20 Apr 2026 09:29:09 +0200 Subject: [PATCH 03/28] ci: cache electron binaries + retry downloads on 502 (#2612) Co-authored-by: Claude Opus 4.7 (1M context) --- .github/workflows/release.yml | 71 ++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0cf5ffcb5..039b68d23 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -267,6 +267,15 @@ jobs: name: specterd-linux path: ./release-artifacts + - name: Cache Electron binaries + uses: actions/cache@v4 + with: + path: | + ~/.cache/electron + ~/.cache/electron-builder + key: electron-linux-${{ hashFiles('pyinstaller/electron/package-lock.json') }} + restore-keys: electron-linux- + - name: Prepare Electron build run: | VERSION=${GITHUB_REF#refs/tags/} @@ -286,7 +295,17 @@ jobs: cp -R ../../src/cryptoadvance/specter/static/fonts \ ../../src/cryptoadvance/specter/static/output.css \ ../../src/cryptoadvance/specter/static/typography.css . - npm run dist -- --linux + # Retry: electron-builder downloads from github.com occasionally 502 + # (see release run 24636274855). + attempt=0 + until npm run dist -- --linux; do + attempt=$((attempt + 1)) + if [ $attempt -ge 3 ]; then + echo "electron-builder failed after $attempt attempts"; exit 1 + fi + echo "Attempt $attempt failed; retrying in $((attempt * 15))s..." + sleep $((attempt * 15)) + done cd ../.. - name: Package release @@ -311,6 +330,10 @@ jobs: runs-on: ubuntu-latest container: image: electronuserland/builder:wine + env: + # Shared HOME so `npm ci` (electron postinstall) and electron-builder + # write to the same ~/.cache/electron path — kept cacheable across runs. + HOME: /tmp/electron-home steps: - uses: actions/checkout@v4 with: @@ -319,6 +342,7 @@ jobs: - name: Install system dependencies run: | apt-get update && apt-get install -y unzip + mkdir -p $HOME - name: Download specterd Windows artifact uses: actions/download-artifact@v4 @@ -326,6 +350,15 @@ jobs: name: specterd-windows path: ./release-artifacts + - name: Cache Electron binaries + uses: actions/cache@v4 + with: + path: | + /tmp/electron-home/.cache/electron + /tmp/electron-home/.cache/electron-builder + key: electron-win-${{ hashFiles('pyinstaller/electron/package-lock.json') }} + restore-keys: electron-win- + - name: Prepare Electron build run: | VERSION=${GITHUB_REF#refs/tags/} @@ -339,15 +372,22 @@ jobs: cd ../.. - name: Build Electron app - env: - HOME: /tmp/electron-home run: | - mkdir -p $HOME cd pyinstaller/electron cp -R ../../src/cryptoadvance/specter/static/fonts \ ../../src/cryptoadvance/specter/static/output.css \ ../../src/cryptoadvance/specter/static/typography.css . - npm run dist -- --win + # Retry: electron-builder downloads from github.com occasionally 502 + # (see release run 24636274855). + attempt=0 + until npm run dist -- --win; do + attempt=$((attempt + 1)) + if [ $attempt -ge 3 ]; then + echo "electron-builder failed after $attempt attempts"; exit 1 + fi + echo "Attempt $attempt failed; retrying in $((attempt * 15))s..." + sleep $((attempt * 15)) + done cd ../.. - name: Package release @@ -401,6 +441,15 @@ jobs: name: specterd-macos-arm64 path: ./release-artifacts + - name: Cache Electron binaries + uses: actions/cache@v4 + with: + path: | + ~/Library/Caches/electron + ~/Library/Caches/electron-builder + key: electron-mac-${{ hashFiles('pyinstaller/electron/package-lock.json') }} + restore-keys: electron-mac- + - name: Import code signing certificate if: env.HAVE_APPLE_CERT == 'true' env: @@ -466,7 +515,17 @@ jobs: cp -R ../../src/cryptoadvance/specter/static/fonts \ ../../src/cryptoadvance/specter/static/output.css \ ../../src/cryptoadvance/specter/static/typography.css . - npm run dist -- --mac + # Retry: electron-builder downloads from github.com occasionally 502 + # (see release run 24636274855). + attempt=0 + until npm run dist -- --mac; do + attempt=$((attempt + 1)) + if [ $attempt -ge 3 ]; then + echo "electron-builder failed after $attempt attempts"; exit 1 + fi + echo "Attempt $attempt failed; retrying in $((attempt * 15))s..." + sleep $((attempt * 15)) + done cd ../.. env: # electron-builder reads these for notarization From 9e43afbdd03a756132d619718b9331dc535f61c4 Mon Sep 17 00:00:00 2001 From: k9ert <117085+k9ert@users.noreply.github.com> Date: Thu, 23 Apr 2026 08:46:33 +0200 Subject: [PATCH 04/28] =?UTF-8?q?Chore(deps):=20Security=20=E2=80=94=20hig?= =?UTF-8?q?h-severity=20pip=20bumps=20(incremental)=20(#2614)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.in | 10 +- requirements.txt | 364 +++++++++++++++++++++++++++-------------------- 2 files changed, 217 insertions(+), 157 deletions(-) diff --git a/requirements.in b/requirements.in index 71c717ead..b598a3184 100644 --- a/requirements.in +++ b/requirements.in @@ -2,7 +2,7 @@ certifi==2024.6.2 Click==8.1.7 Flask==2.2.5 Flask-Babel==3.1.0 -Flask-Cors==3.0.10 +Flask-Cors==6.0.0 Flask-Login==0.6.3 Flask-RESTful==0.3.10 Flask-HTTPAuth==4.8.0 @@ -14,17 +14,17 @@ six==1.16.0 stem==1.8.0 embit==0.6.1 psutil==5.9.0 -pyopenssl==24.1.0 +pyopenssl==26.0.0 flask_wtf==1.2.1 pgpy==0.6.0 -cbor2==5.4.6 +cbor2==5.9.0 mnemonic==0.21 -cryptography==42.0.7 +cryptography==46.0.6 Flask-APScheduler==1.12.4 gunicorn==23.0.0 simple-websocket==0.8.1 protobuf==4.23.3 -PyJWT==2.4.0 +PyJWT==2.12.0 pytimeparse==1.1.8 psycopg2-binary==2.9.5 aioitertools==0.11.0 diff --git a/requirements.txt b/requirements.txt index b5a2e857e..00ab63955 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,43 +20,51 @@ babel==2.15.0 \ --hash=sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb \ --hash=sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413 # via flask-babel -cbor2==5.4.6 \ - --hash=sha256:0b956f19e93ba3180c336282cd1b6665631f2d3a196a9c19b29a833bf979e7a4 \ - --hash=sha256:0bd12c54a48949d11f5ffc2fa27f5df1b4754111f5207453e5fae3512ebb3cab \ - --hash=sha256:0d2b926b024d3a1549b819bc82fdc387062bbd977b0299dd5fa5e0ea3267b98b \ - --hash=sha256:1618d16e310f7ffed141762b0ff5d8bb6b53ad449406115cc465bf04213cefcf \ - --hash=sha256:181ac494091d1f9c5bb373cd85514ce1eb967a8cf3ec298e8dfa8878aa823956 \ - --hash=sha256:1835536e76ea16e88c934aac5e369ba9f93d495b01e5fa2d93f0b4986b89146d \ - --hash=sha256:1c12c0ab78f5bc290b08a79152a8621822415836a86f8f4b50dadba371736fda \ - --hash=sha256:24144822f8d2b0156f4cda9427f071f969c18683ffed39663dc86bc0a75ae4dd \ - --hash=sha256:309fffbb7f561d67f02095d4b9657b73c9220558701c997e9bfcfbca2696e927 \ - --hash=sha256:3316f09a77af85e7772ecfdd693b0f450678a60b1aee641bac319289757e3fa0 \ - --hash=sha256:3545b16f9f0d5f34d4c99052829c3726020a07be34c99c250d0df87418f02954 \ - --hash=sha256:39452c799453f5bf33281ffc0752c620b8bfa0b7c13070b87d370257a1311976 \ - --hash=sha256:3950be57a1698086cf26d8710b4e5a637b65133c5b1f9eec23967d4089d8cfed \ - --hash=sha256:456cdff668a50a52fdb8aa6d0742511e43ed46d6a5b463dba80a5a720fa0d320 \ - --hash=sha256:4b9f3924da0e460a93b3674c7e71020dd6c9e9f17400a34e52a88c0af2dcd2aa \ - --hash=sha256:4bbbdb2e3ef274865dc3f279aae109b5d94f4654aea3c72c479fb37e4a1e7ed7 \ - --hash=sha256:4ce1a2c272ba8523a55ea2f1d66e3464e89fa0e37c9a3d786a919fe64e68dbd7 \ - --hash=sha256:56dfa030cd3d67e5b6701d3067923f2f61536a8ffb1b45be14775d1e866b59ae \ - --hash=sha256:6709d97695205cd08255363b54afa035306d5302b7b5e38308c8ff5a47e60f2a \ - --hash=sha256:6e1b5aee920b6a2f737aa12e2b54de3826b09f885a7ce402db84216343368140 \ - --hash=sha256:6f9c702bee2954fffdfa3de95a5af1a6b1c5f155e39490353d5654d83bb05bb9 \ - --hash=sha256:78304df140b9e13b93bcbb2aecee64c9aaa9f1cadbd45f043b5e7b93cc2f21a2 \ - --hash=sha256:79e048e623846d60d735bb350263e8fdd36cb6195d7f1a2b57eacd573d9c0b33 \ - --hash=sha256:7bbd3470eb685325398023e335be896b74f61b014896604ed45049a7b7b6d8ac \ - --hash=sha256:80ac8ba450c7a41c5afe5f7e503d3092442ed75393e1de162b0bf0d97edf7c7f \ - --hash=sha256:9394ca49ecdf0957924e45d09a4026482d184a465a047f60c4044eb464c43de9 \ - --hash=sha256:94f844d0e232aca061a86dd6ff191e47ba0389ddd34acb784ad9a41594dc99a4 \ - --hash=sha256:96087fa5336ebfc94465c0768cd5de0fcf9af3840d2cf0ce32f5767855f1a293 \ - --hash=sha256:b893500db0fe033e570c3adc956af6eefc57e280026bd2d86fd53da9f1e594d7 \ - --hash=sha256:c285a2cb2c04004bfead93df89d92a0cef1874ad337d0cb5ea53c2c31e97bfdb \ - --hash=sha256:d2984a488f350aee1d54fa9cb8c6a3c1f1f5b268abbc91161e47185de4d829f3 \ - --hash=sha256:d54bd840b4fe34f097b8665fc0692c7dd175349e53976be6c5de4433b970daa4 \ - --hash=sha256:db9eb582fce972f0fa429d8159b7891ff8deccb7affc4995090afc61ce0d328a \ - --hash=sha256:e5094562dfe3e5583202b93ef7ca5082c2ba5571accb2c4412d27b7d0ba8a563 \ - --hash=sha256:e73ca40dd3c7210ff776acff9869ddc9ff67bae7c425b58e5715dcf55275163f \ - --hash=sha256:ff95b33e5482313a74648ca3620c9328e9f30ecfa034df040b828e476597d352 +cbor2==5.9.0 \ + --hash=sha256:0322296b9d52f55880e300ba8ba09ecf644303b99b51138bbb1c0fb644fa7c3e \ + --hash=sha256:0485d3372fc832c5e16d4eb45fa1a20fc53e806e6c29a1d2b0d3e176cedd52b9 \ + --hash=sha256:08388ea54195738602b4c4999966bcaef6f0b17d293c9658658409d9fff96f57 \ + --hash=sha256:1d02b65f070fd726bdc310d927228975bb655d155bf059b6eb7cacefb3dca86f \ + --hash=sha256:1da96ce5d852fe3d342c1eb2c202a52d1c97edfddc9230f1be7e02674662bf26 \ + --hash=sha256:1f223dffb1bcdd2764665f04c1152943d9daa4bc124a576cd8dee1cad4264313 \ + --hash=sha256:23606d31ba1368bd1b6602e3020ee88fe9523ca80e8630faf6b2fc904fd84560 \ + --hash=sha256:2372d357d403e7912f104ff085950ffc82a5854d6d717f1ca1ce16a40a0ef5a7 \ + --hash=sha256:25bec7beb2089465382b1be72e78667fe9090598800826559c3e3008cf0db743 \ + --hash=sha256:27695cbd70c90b8de5c4a284642c2836449b14e2c2e07e3ffe0744cb7669a01b \ + --hash=sha256:2a54fbb32cb828c214f7f333a707e4aec61182e7efdc06ea5d9596d3ecee624a \ + --hash=sha256:3095dc49e75572841a9534cbfdabc2a17487ea4ee33341436abc4a7ac7245a3a \ + --hash=sha256:34a6cb15e6ab6a8eae94ad2041731cd3ef786af43a8df99f847969af5b902ee7 \ + --hash=sha256:380e534482b843e43442b87d8777a7bf9bed20cb7526f89b780c3400f617304b \ + --hash=sha256:420d2490c7836c81151b4bd591c35cffc55391e33e7e333c50fda391bcea7d31 \ + --hash=sha256:422817286c1d0ce947fb2f7eca9212b39bddd7231e8b452e2d2cc52f15332dba \ + --hash=sha256:4753a6d1bc71054d9179557bc65740860f185095ccb401d46637fff028a5b3ec \ + --hash=sha256:4aa07b392cc3d76fb31c08a46a226b58c320d1c172ff3073e864409ced7bc50f \ + --hash=sha256:4cd43d8fc374b31643b2830910f28177a606a7bc84975a62675dd3f2e320fc7b \ + --hash=sha256:5326336f633cc89dfe543c78829c16c3a6449c2c03277d1ddba99086c3323363 \ + --hash=sha256:53cfa49e0df9c639beb871d480de098eedc81eb63ff29f2dc922720d7577b676 \ + --hash=sha256:55bea0dd9a7d354e35f4e5fe58ceab393e76962713749dc3a0a64a0e5d19545e \ + --hash=sha256:5e702b02d42a5ace45425b595ffe70fe35aebaf9a3cdfdc2c758b6189c744422 \ + --hash=sha256:65f8eac3268c608533f326f0fd9010ab1b2a8a917b05edaf3853116336821669 \ + --hash=sha256:7221483fad0c63afa4244624d552abf89d7dfdbc5f5edfc56fc1ff2b4b818975 \ + --hash=sha256:7d1ddc4541e7367ac58c2470cc0df847f7137167fe4f5729e2d3cc0b993d7da4 \ + --hash=sha256:837754ece9052b3f607047e1741e5f852a538aa2b0ee3db11c82a8fa11804aa4 \ + --hash=sha256:85c7a46279ac8f226e1059275221e6b3d0e370d2bb6bd0500f9780781615bcea \ + --hash=sha256:86baf870d4c0bfc6f79de3801f3860a84ab76d9c8b0abb7f081f2c14c38d79d3 \ + --hash=sha256:971d425b3a23b75953d8853d5f9911bdeefa09d759ee3b5e6b07b5ff3cbd9073 \ + --hash=sha256:9a4907e0c3035bb8836116854ed8e56d8aef23909d601fa59706320897ec2551 \ + --hash=sha256:a9d6e4e0f988b0e766509a8071975a8ee99f930e14a524620bf38083106158d2 \ + --hash=sha256:ac684fe195c39821fca70d18afbf748f728aefbfbf88456018d299e559b8cae0 \ + --hash=sha256:ae6c706ac1d85a0b3cb3395308fd0c4d55e3202b4760773675957e93cdff45fc \ + --hash=sha256:cc5efec69055c3c470997935d95762be7e4bfd1248d88fb1a33bb7e0f45712e9 \ + --hash=sha256:d1a21c006760f95acd9509cc5a7d15d6fc82e58f721f94fa9039b4e77189a6e5 \ + --hash=sha256:d8524a8c142c3cc228e635f8a97499a6c0b18ca91382e8276565658035cdcb6d \ + --hash=sha256:dcf0f695873e5c94bd072d6af8698e72b8fb7f7a18f37e0bced1041b7111a6cf \ + --hash=sha256:f29e5c3abcc91c1aeefecde0e057bf33f1655588d3065c6560c30ceb3be6f333 \ + --hash=sha256:f797532d13469f2193e5c16e827d8df7a8c33674b19be755790b54ab231e6a73 \ + --hash=sha256:f7c9751a9611601ab326d8f5837f01379195bbf06175fb4effeb552140e7c9e8 \ + --hash=sha256:fb7afe77f8d269e42d7c4b515c6fd14f1ccc0625379fb6829b269f493d16eddd \ + --hash=sha256:fbb06f34aa645b4deca66643bba3d400d20c15312d1fe88d429be60c1ab50f27 \ + --hash=sha256:fbdcf4d74acbeb7672e6413e81cd2c1ced1a4a8cf949484ac54e9af5265c3c72 # via # -r requirements.in # hwi @@ -66,59 +74,91 @@ certifi==2024.6.2 \ # via # -r requirements.in # requests -cffi==1.16.0 \ - --hash=sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc \ - --hash=sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a \ - --hash=sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417 \ - --hash=sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab \ - --hash=sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520 \ - --hash=sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36 \ - --hash=sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743 \ - --hash=sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8 \ - --hash=sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed \ - --hash=sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684 \ - --hash=sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56 \ - --hash=sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324 \ - --hash=sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d \ - --hash=sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235 \ - --hash=sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e \ - --hash=sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088 \ - --hash=sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000 \ - --hash=sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7 \ - --hash=sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e \ - --hash=sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673 \ - --hash=sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c \ - --hash=sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe \ - --hash=sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2 \ - --hash=sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098 \ - --hash=sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8 \ - --hash=sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a \ - --hash=sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0 \ - --hash=sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b \ - --hash=sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896 \ - --hash=sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e \ - --hash=sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9 \ - --hash=sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2 \ - --hash=sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b \ - --hash=sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6 \ - --hash=sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404 \ - --hash=sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f \ - --hash=sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0 \ - --hash=sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4 \ - --hash=sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc \ - --hash=sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936 \ - --hash=sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba \ - --hash=sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872 \ - --hash=sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb \ - --hash=sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614 \ - --hash=sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1 \ - --hash=sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d \ - --hash=sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969 \ - --hash=sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b \ - --hash=sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4 \ - --hash=sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627 \ - --hash=sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956 \ - --hash=sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357 +cffi==2.0.0 \ + --hash=sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb \ + --hash=sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b \ + --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \ + --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \ + --hash=sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44 \ + --hash=sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2 \ + --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \ + --hash=sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75 \ + --hash=sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65 \ + --hash=sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e \ + --hash=sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a \ + --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \ + --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \ + --hash=sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a \ + --hash=sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe \ + --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \ + --hash=sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91 \ + --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \ + --hash=sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187 \ + --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \ + --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \ + --hash=sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94 \ + --hash=sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba \ + --hash=sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb \ + --hash=sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165 \ + --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \ + --hash=sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca \ + --hash=sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c \ + --hash=sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6 \ + --hash=sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c \ + --hash=sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0 \ + --hash=sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743 \ + --hash=sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63 \ + --hash=sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5 \ + --hash=sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5 \ + --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \ + --hash=sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d \ + --hash=sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b \ + --hash=sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93 \ + --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \ + --hash=sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27 \ + --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \ + --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \ + --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \ + --hash=sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037 \ + --hash=sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26 \ + --hash=sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322 \ + --hash=sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb \ + --hash=sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c \ + --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \ + --hash=sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4 \ + --hash=sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414 \ + --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \ + --hash=sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664 \ + --hash=sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9 \ + --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \ + --hash=sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739 \ + --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \ + --hash=sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062 \ + --hash=sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe \ + --hash=sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9 \ + --hash=sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92 \ + --hash=sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5 \ + --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \ + --hash=sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d \ + --hash=sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26 \ + --hash=sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f \ + --hash=sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495 \ + --hash=sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b \ + --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \ + --hash=sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c \ + --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \ + --hash=sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5 \ + --hash=sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18 \ + --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \ + --hash=sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3 \ + --hash=sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7 \ + --hash=sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5 \ + --hash=sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534 \ + --hash=sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49 \ + --hash=sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2 \ + --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 \ + --hash=sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453 \ + --hash=sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf # via cryptography charset-normalizer==2.0.12 \ --hash=sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597 \ @@ -138,47 +178,64 @@ cryptoadvance-spectrum==0.7.0 \ --hash=sha256:40b31d38ad40c85438bc44d9243edb9ddbaf331c4468a48ebc0f40a109b43a62 \ --hash=sha256:6cd8858fa07668536345ecab08b4143e28055d7524c9b27b8c885c5b98730879 # via -r requirements.in -cryptography==42.0.7 \ - --hash=sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55 \ - --hash=sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785 \ - --hash=sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b \ - --hash=sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886 \ - --hash=sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82 \ - --hash=sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1 \ - --hash=sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda \ - --hash=sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f \ - --hash=sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68 \ - --hash=sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60 \ - --hash=sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7 \ - --hash=sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd \ - --hash=sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582 \ - --hash=sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc \ - --hash=sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858 \ - --hash=sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b \ - --hash=sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2 \ - --hash=sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678 \ - --hash=sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13 \ - --hash=sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4 \ - --hash=sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8 \ - --hash=sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604 \ - --hash=sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477 \ - --hash=sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e \ - --hash=sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a \ - --hash=sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9 \ - --hash=sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14 \ - --hash=sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda \ - --hash=sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da \ - --hash=sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562 \ - --hash=sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2 \ - --hash=sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9 +cryptography==46.0.6 \ + --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ + --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ + --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ + --hash=sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0 \ + --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ + --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ + --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ + --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ + --hash=sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead \ + --hash=sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275 \ + --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ + --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ + --hash=sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361 \ + --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ + --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ + --hash=sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b \ + --hash=sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b \ + --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ + --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ + --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ + --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ + --hash=sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e \ + --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ + --hash=sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a \ + --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ + --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ + --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ + --hash=sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4 \ + --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ + --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ + --hash=sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca \ + --hash=sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d \ + --hash=sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a \ + --hash=sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed \ + --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ + --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ + --hash=sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8 \ + --hash=sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707 \ + --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ + --hash=sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736 \ + --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ + --hash=sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4 \ + --hash=sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013 \ + --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ + --hash=sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b \ + --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ + --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ + --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ + --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 # via # -r requirements.in # noiseprotocol # pgpy # pyopenssl -ecdsa==0.19.0 \ - --hash=sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a \ - --hash=sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8 +ecdsa==0.19.2 \ + --hash=sha256:62635b0ac1ca2e027f82122b5b81cb706edc38cd91c63dda28e4f3455a2bf930 \ + --hash=sha256:840f5dc5e375c68f36c1a7a5b9caad28f95daa65185c9253c0c08dd952bb7399 # via hwi embit==0.6.1 \ --hash=sha256:16a84c6668dc9ffc907594457a46f7142cee379646bc009a5a9b77b0d2cb4e12 @@ -206,9 +263,9 @@ flask-babel==3.1.0 \ --hash=sha256:be015772c5d7f046f3b99c508dcf618636eb93d21b713b356db79f3e79f69f39 \ --hash=sha256:deb3ee272d5adf97f5974ed09ab501243d63e7fb4a047501a00de4bd4aca4830 # via -r requirements.in -flask-cors==3.0.10 \ - --hash=sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438 \ - --hash=sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de +flask-cors==6.0.0 \ + --hash=sha256:4592c1570246bf7beee96b74bc0adbbfcb1b0318f6ba05c412e8909eceec3393 \ + --hash=sha256:6332073356452343a8ccddbfec7befdc3fdd040141fe776ec9b94c262f058657 # via -r requirements.in flask-httpauth==4.8.0 \ --hash=sha256:66568a05bc73942c65f1e2201ae746295816dc009edd84b482c44c758d75097a \ @@ -514,6 +571,10 @@ numpy==1.26.4 \ --hash=sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3 \ --hash=sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f # via pandas +packaging==26.0 \ + --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ + --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 + # via gunicorn pandas==2.2.2 \ --hash=sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863 \ --hash=sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2 \ @@ -569,10 +630,6 @@ protobuf==4.23.3 \ # via # -r requirements.in # hwi -packaging==26.0 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 - # via gunicorn psutil==5.9.0 \ --hash=sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5 \ --hash=sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a \ @@ -685,21 +742,21 @@ psycopg2-binary==2.9.5 \ pyaes==1.6.1 \ --hash=sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f # via hwi -pyasn1==0.6.0 \ - --hash=sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c \ - --hash=sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473 +pyasn1==0.6.3 \ + --hash=sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf \ + --hash=sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde # via pgpy pycparser==2.22 \ --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \ --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc # via cffi -pyjwt==2.4.0 \ - --hash=sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf \ - --hash=sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba +pyjwt==2.12.0 \ + --hash=sha256:2f62390b667cd8257de560b850bb5a883102a388829274147f1d724453f8fb02 \ + --hash=sha256:9bb459d1bdd0387967d287f5656bf7ec2b9a26645d1961628cda1764e087fd6e # via -r requirements.in -pyopenssl==24.1.0 \ - --hash=sha256:17ed5be5936449c5418d1cd269a1a9e9081bc54c17aed272b45856a3d3dc86ad \ - --hash=sha256:cabed4bfaa5df9f1a16c0ef64a0cb65318b5cd077a7eda7d6970131ca2f41a6f +pyopenssl==26.0.0 \ + --hash=sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81 \ + --hash=sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc # via -r requirements.in pyserial==3.5 \ --hash=sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb \ @@ -735,8 +792,8 @@ pytz==2024.1 \ # flask-restful # pandas requests==2.31.0 \ - --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1 \ - --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f + --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \ + --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1 # via # -r requirements.in # cryptoadvance-spectrum @@ -755,7 +812,6 @@ six==1.16.0 \ # -r requirements.in # apscheduler # ecdsa - # flask-cors # flask-restful # python-dateutil specterext-exfund==0.1.7 \ @@ -829,10 +885,13 @@ tenacity==8.3.0 \ --hash=sha256:3649f6443dbc0d9b01b9d8020a9c4ec7a1ff5f6f3c6c8a036ef371f573fe9185 \ --hash=sha256:953d4e6ad24357bceffbc9707bc74349aca9d245f68eb65419cf0c249a1949a2 # via plotly -typing-extensions==4.11.0 \ - --hash=sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0 \ - --hash=sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a - # via hwi +typing-extensions==4.15.0 \ + --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ + --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 + # via + # cryptography + # hwi + # pyopenssl tzdata==2024.1 \ --hash=sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd \ --hash=sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252 @@ -841,15 +900,16 @@ tzlocal==5.2 \ --hash=sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8 \ --hash=sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e # via apscheduler -urllib3==1.26.18 \ - --hash=sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07 \ - --hash=sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0 +urllib3==2.6.3 \ + --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ + --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 # via requests werkzeug==3.0.3 \ --hash=sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18 \ --hash=sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8 # via # flask + # flask-cors # flask-login wsproto==1.2.0 \ --hash=sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065 \ From 64139ebdb7368745fd8a907cf62bfdfa4ee3d1e3 Mon Sep 17 00:00:00 2001 From: al-munazzim Date: Mon, 11 May 2026 21:22:50 +0200 Subject: [PATCH 05/28] ci: harden elements GPG key import in install_noded.sh (#2621) Co-authored-by: Nazim --- tests/install_noded.sh | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/install_noded.sh b/tests/install_noded.sh index 99b909ffb..3eb2dd37a 100755 --- a/tests/install_noded.sh +++ b/tests/install_noded.sh @@ -317,14 +317,18 @@ function gpg_verify_sums { fi local imported=0 for fpr in "${keys[@]}"; do - # Try keys.openpgp.org first, then keyserver.ubuntu.com. - if curl -fsSL "https://keys.openpgp.org/vks/v1/by-fingerprint/${fpr}" 2>/dev/null \ - | gpg --import 2>/dev/null; then - imported=$((imported + 1)) - continue - fi - if curl -fsSL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${fpr}&options=mr" 2>/dev/null \ - | gpg --import 2>/dev/null; then + # Import from both sources. Some keyservers may return partial key + # material for old signatures (e.g. missing signing subkeys). + # Importing from both increases robustness while keeping the same + # pinned trust anchors for verification. + curl -fsSL "https://keys.openpgp.org/vks/v1/by-fingerprint/${fpr}" 2>/dev/null \ + | gpg --import 2>/dev/null || true + curl -fsSL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${fpr}&options=mr" 2>/dev/null \ + | gpg --import 2>/dev/null || true + + # Count this key as imported if it exists in the keyring after the + # multi-source import attempts. + if gpg --list-keys --with-colons "$fpr" 2>/dev/null | grep -q '^pub:'; then imported=$((imported + 1)) fi done From a1a82082ddd3a42ff2e01a77479590fa66d5083b Mon Sep 17 00:00:00 2001 From: Oren <115847146+oren-z0@users.noreply.github.com> Date: Sun, 17 May 2026 22:15:57 +0300 Subject: [PATCH 06/28] Update specterext-timelockrecovery to v0.2.3 (#2620) --- requirements.in | 2 +- requirements.txt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.in b/requirements.in index b598a3184..86990f404 100644 --- a/requirements.in +++ b/requirements.in @@ -35,7 +35,7 @@ specterext-exfund==0.1.7 specterext-faucet==0.1.2 cryptoadvance.spectrum==0.7.0 specterext-stacktrack==0.3.0 -specterext-timelockrecovery==0.2.1 +specterext-timelockrecovery==0.2.3 # workarounds diff --git a/requirements.txt b/requirements.txt index 00ab63955..704474bb5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -824,9 +824,9 @@ specterext-stacktrack==0.3.0 \ --hash=sha256:14f96f1f552f57ba017b8bc642f07343edbb1abafe09e03bbaae179d78d7ce23 \ --hash=sha256:9e2946185730aab377951e83a27d8791a34e0f031e44f15991212b6b85722ca0 # via -r requirements.in -specterext-timelockrecovery==0.2.1 \ - --hash=sha256:2fc2a3eec045377b54926ff41dab9c5f5a6f46074150aec1ff67cb1456fef911 \ - --hash=sha256:35e181971b5b69ca6706496169c4581f2fa20706c88ba24760e56e153ec51939 +specterext-timelockrecovery==0.2.3 \ + --hash=sha256:a0b22c4e010061055da4ea6ea4e08bbc38d1f7c2ec58327970caa0ce987cba95 \ + --hash=sha256:5df3deb6245a22d48f75bbb62fd87dc141a9f60407011ce9fbb0a7bcc40fe4cc # via -r requirements.in sqlalchemy==1.4.52 \ --hash=sha256:1296f2cdd6db09b98ceb3c93025f0da4835303b8ac46c15c2136e27ee4d18d94 \ From 314a8579a09c097f8691056d3de452e55e5aea27 Mon Sep 17 00:00:00 2001 From: k9ert <117085+k9ert@users.noreply.github.com> Date: Fri, 22 May 2026 09:08:28 +0200 Subject: [PATCH 07/28] deps: bump hwi 2.4.0 -> 3.1.0 (drops Python 3.7/3.8) (#2616) --- docs/development.md | 31 ++++++ pyproject.toml | 5 +- requirements.in | 2 +- requirements.txt | 9 +- tests/conftest.py | 15 +++ tests/fixtures/jade_hardware.psbt | 1 + tests/fixtures/jade_seedqr_abandon.png | Bin 0 -> 354 bytes tests/fixtures/jade_seedqr_abandon.txt | 15 +++ tests/test_jade_hardware.py | 140 +++++++++++++++++++++++++ 9 files changed, 211 insertions(+), 7 deletions(-) create mode 100644 tests/fixtures/jade_hardware.psbt create mode 100644 tests/fixtures/jade_seedqr_abandon.png create mode 100644 tests/fixtures/jade_seedqr_abandon.txt create mode 100644 tests/test_jade_hardware.py diff --git a/docs/development.md b/docs/development.md index 251ee8f64..bb0fcc955 100644 --- a/docs/development.md +++ b/docs/development.md @@ -14,6 +14,7 @@ - [Set up virtualenv](#set-up-virtualenv) - [If `pip install` fails on `cryptography==3.4.x`](#if-pip-install-fails-on-cryptography34x) - [How to run the tests](#how-to-run-the-tests) + - [Hardware-attended Jade tests](#hardware-attended-jade-tests) - [Code-Style](#code-style) - [Developing on tests](#developing-on-tests) - [bitcoin-specific stuff](#bitcoin-specific-stuff) @@ -209,6 +210,36 @@ Print the logging output live to the terminal: pytest --capture=no --log-cli-level=DEBUG ``` +### Hardware-attended Jade tests + +`tests/test_jade_hardware.py` exercises Specter's HWI integration end-to-end against a physical Blockstream Jade. It is gated by `--run-jade-hardware` and skipped by default, so GitHub Actions ignore it without any workflow change. + +Run with `-s` so operator prompts reach the terminal: +``` +pytest --run-jade-hardware tests/test_jade_hardware.py -s +``` + +Three tests, increasing operator effort: + +| Test | What it does | Operator action | +|---|---|---| +| `test_jade_enumerate_via_specter` | `HWIBridge.enumerate()` finds the Jade and returns a fingerprint | Connect Jade, unlock | +| `test_jade_extract_xpub_via_specter` | Pulls xpub at `m/84h/0h/0h` (mainnet) | Confirm xpub export on device | +| `test_jade_sign_psbt_via_specter` | Signs a canned testnet PSBT through Specter's sign path | Boot Jade in Temporary Signer mode, scan SeedQR, confirm tx | + +The signing test uses the public **BIP-39 abandon vector** (`abandon abandon ... about`) so the PSBT fixture matches anyone's Jade once they load that seed. Setup procedure: + +1. Power-cycle the Jade so it shows the boot menu. +2. Choose **Temporary Signer** -> **Scan SeedQR**. +3. Display `tests/fixtures/jade_seedqr_abandon.png` (or `cat tests/fixtures/jade_seedqr_abandon.txt` for the ASCII version) and scan it with the Jade camera. +4. When Jade asks for the network, select **TESTNET**. +5. Press Enter at the test prompt. +6. Confirm the transaction on the Jade screen when it pops up (~99,500 sats to a testnet bech32 output, ~99,500 change auto-validated, 1,000 fee). + +Temporary Signer state is held in RAM only and wiped on power-cycle/USB-unplug — your real seed is not affected. Expected master fingerprint for the abandon vector is `73c5da0a`; the test fails fast with a clear hint if the loaded seed is wrong. + +The fixture PSBT (`tests/fixtures/jade_hardware.psbt`) was generated with embit against m/84'/1'/0'/0/0 of the abandon vector, including a synthetic `non_witness_utxo` so Jade can verify the input amount per the SegWit fee-spoof mitigation. + Get the log-output of bitcoind side by side with the test-output. For sure you will only see the logs if the test fails. ``` pytest --bitcoind-log-stdout diff --git a/pyproject.toml b/pyproject.toml index c822bb7db..df8c7953d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ readme = "README.md" license = {file = "LICENSE"} -requires-python = ">=3.7,<4.0" +requires-python = ">=3.9,<3.13" dynamic = ["dependencies", "version"] @@ -52,7 +52,8 @@ markers = [ "slow: mark test as slow.", "elm: mark test as elementsd dependent", "bottleneck: mark a test as so ressource intensive that it can create a bottleneck where the test just fails due to a lack of ressources", - "threading: test needs threading to work" + "threading: test needs threading to work", + "jade_hardware: requires a real Jade attached and an operator; opt-in only via --run-jade-hardware" ] filterwarnings = [ diff --git a/requirements.in b/requirements.in index 86990f404..35282fa9e 100644 --- a/requirements.in +++ b/requirements.in @@ -6,7 +6,7 @@ Flask-Cors==6.0.0 Flask-Login==0.6.3 Flask-RESTful==0.3.10 Flask-HTTPAuth==4.8.0 -hwi==2.4.0 +hwi==3.1.0 python-dotenv==0.21.1 requests==2.31.0 pysocks==1.7.1 diff --git a/requirements.txt b/requirements.txt index 704474bb5..3d622e9b6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -432,9 +432,9 @@ hidapi==0.14.0 \ --hash=sha256:fb4e94e45f6dddb20d59501187721e5d3b02e6cc8a59d261dd5cac739008582a \ --hash=sha256:fc9ec2321bf3b0b4953910aa87c0c8ab5f93b1f113a9d3d4f18845ce54708d13 # via hwi -hwi==2.4.0 \ - --hash=sha256:3eaa7593f1ab360569eacdd9507dab75532bb58e8cd991d8ad72f5c4fcb67997 \ - --hash=sha256:7cb7ef2a4db4bc434815374d9bad43c6425491f77828314a2d2898d3e86d3f04 +hwi==3.1.0 \ + --hash=sha256:21ba92bb06e2f805e2806c686f2c50d02db6826a363b01e44052415755504d6f \ + --hash=sha256:42e875cbb616a91638fb90679cad93edb5075bf375e92fc1709be9b2a3dfd59c # via -r requirements.in idna==3.7 \ --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ @@ -921,5 +921,6 @@ wtforms==3.1.2 \ # via flask-wtf # WARNING: The following packages were not pinned, but pip requires them to be -# pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag. +# pinned when the requirements file includes hashes and the requirement is not +# satisfied by a package already installed. Consider using the --allow-unsafe flag. # setuptools diff --git a/tests/conftest.py b/tests/conftest.py index a7368138a..6be08fab0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -89,9 +89,24 @@ def pytest_addoption(parser): default="master", help="Version of elementsd (something which works with git checkout ...)", ) + parser.addoption( + "--run-jade-hardware", + action="store_true", + default=False, + help="Run tests marked jade_hardware (real Jade attached + operator).", + ) listen() +def pytest_collection_modifyitems(config, items): + if config.getoption("--run-jade-hardware"): + return + skip = pytest.mark.skip(reason="opt-in via --run-jade-hardware") + for item in items: + if item.get_closest_marker("jade_hardware") is not None: + item.add_marker(skip) + + def pytest_generate_tests(metafunc): # ToDo: use custom compiled version of bitcoind # E.g. test again bitcoind version [currentRelease] + master-branch diff --git a/tests/fixtures/jade_hardware.psbt b/tests/fixtures/jade_hardware.psbt new file mode 100644 index 000000000..3a68aa829 --- /dev/null +++ b/tests/fixtures/jade_hardware.psbt @@ -0,0 +1 @@ +cHNidP8BAHECAAAAAYz9aGwGnHSzsZXBuDlKg9hNl6KeLvo8czy6WHaPTGwGAAAAAAD/////AqyEAQAAAAAAFgAUb6AWUAo8anN+uyYOLdyni6kjRVishAEAAAAAABYAFC80qhzwClOwVaKRoDp9RfCmmItSAAAAAAABAFMCAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wFR/////wFADQMAAAAAABYAFNDEo+8J6Ze26Z45flGP4+QaEYyhAAAAAAEBH0ANAwAAAAAAFgAU0MSj7wnpl7bpnjl+UY/j5BoRjKEiBgLnqyU3tdSelwMJquBunknzbOHJ/rvUTsjg0cygtPnDGRhzxdoKVAAAgAEAAIAAAACAAAAAAAAAAAAAACICA11J7M1U0AmeQ2did8em1GJdYR2oil30m/lReneRp3elGHPF2gpUAACAAQAAgAAAAIABAAAAAAAAAAA= diff --git a/tests/fixtures/jade_seedqr_abandon.png b/tests/fixtures/jade_seedqr_abandon.png new file mode 100644 index 0000000000000000000000000000000000000000..9ec31d505b9853c4cfeee5ef2fa10a7b45f29fc0 GIT binary patch literal 354 zcmV-o0iFJdP)-v^hyX>cot}JQToH_Y=1x(Y$2F+JyGFN=RewZflNQxie{->TR=jh1c-XRMh} zO`FFC%5_@l3f=ttQkjY1Y%QRtbd|Mv?4ly12y8#UV$iN^^9HJ!eQEU-*Q{*wmR`vV zNq9whkgdLY#ZOhKEIK@}oARYEn9{ZAHjQV^N2e^@F`maE86ncq9sFxU*#Y~9rvBwu zeFaZUvGnzC44v*cbc6d1Dl|I#q^A8KgEKvdMGtJ&wRv2F%6w~;bF}?Mtok2HuPK|? z&%>7Njo=>6?5qCw8#t ``Scan SeedQR``. + 3. Scan ``tests/fixtures/jade_seedqr_abandon.png`` (or display + ``tests/fixtures/jade_seedqr_abandon.txt`` and scan from screen). + 4. Confirm the **testnet** network on Jade. + 5. The PSBT at ``tests/fixtures/jade_hardware.psbt`` is fabricated by + Coldcard's psbt_faker against this exact seed. + +Temporary Signer state lives in RAM only; it's wiped on power-cycle. Your +real seed is not affected. +""" + +from pathlib import Path + +import pytest + +from cryptoadvance.specter.hwi_rpc import HWIBridge + + +FIXTURE_DIR = Path(__file__).parent / "fixtures" +PSBT_FIXTURE = FIXTURE_DIR / "jade_hardware.psbt" +SEEDQR_PNG = FIXTURE_DIR / "jade_seedqr_abandon.png" +SEEDQR_TXT = FIXTURE_DIR / "jade_seedqr_abandon.txt" + +ABANDON_FINGERPRINT = "73c5da0a" + + +def _enumerate_jade(bridge: HWIBridge, chain: str = "main"): + # HWIBridge.enumerate defaults chain="" which Chain.argparse passes + # through unchanged; Jade's enumerate then fails with + # "Unhandled network: ". Pass an explicit chain. + devs = bridge.enumerate(chain=chain) + return [d for d in devs if d.get("type") == "jade"] + + +def _prompt(msg: str) -> None: + print(f"\n>>> {msg}") + try: + input(">>> Press Enter when ready... ") + except EOFError: + pass + + +@pytest.mark.jade_hardware +def test_jade_enumerate_via_specter(): + """Jade is detected by Specter's HWIBridge and reports a fingerprint.""" + _prompt("Connect and unlock the Jade.") + bridge = HWIBridge(skip_hwi_initialisation=True) + jades = _enumerate_jade(bridge) + assert jades, "no Jade detected — connect, unlock, and rerun" + jade = jades[0] + assert jade.get("fingerprint"), f"Jade enumerated without fingerprint: {jade}" + assert jade.get("path"), f"Jade enumerated without path: {jade}" + + +@pytest.mark.jade_hardware +def test_jade_extract_xpub_via_specter(): + """Specter can pull an xpub at a known derivation from Jade.""" + _prompt("Unlock the Jade. You may be asked to confirm the xpub export.") + bridge = HWIBridge(skip_hwi_initialisation=True) + jades = _enumerate_jade(bridge) + assert jades, "no Jade detected" + fingerprint = jades[0].get("fingerprint") + assert fingerprint, f"Jade enumerated without fingerprint: {jades[0]}" + + # chain must be passed explicitly: HWIBridge.extract_xpub default is + # chain="" which Specter's JadeClient.__init__ rejects via _network() + # before extract_xpub's post-init override can apply. + xpub_line = bridge.extract_xpub( + derivation="m/84h/0h/0h", + device_type="jade", + fingerprint=fingerprint, + chain="main", + ) + assert xpub_line, "extract_xpub returned empty" + assert xpub_line.startswith("["), f"unexpected format: {xpub_line!r}" + assert "]" in xpub_line, f"unexpected format: {xpub_line!r}" + body = xpub_line.split("]", 1)[1].strip() + assert body.startswith(("xpub", "zpub", "ypub")), f"unexpected xpub: {body[:8]}" + + +@pytest.mark.jade_hardware +def test_jade_sign_psbt_via_specter(): + """End-to-end: Specter signs the canned abandon-vector PSBT through Jade. + + Requires Jade in Temporary Signer mode with the abandon-vector seed + (see module docstring). The fixture PSBT was generated by Coldcard's + psbt_faker against m/84'/1'/0' on testnet; xfp is 73c5da0a. + """ + assert PSBT_FIXTURE.exists(), f"missing fixture: {PSBT_FIXTURE}" + + seedqr_hint = ( + f"\n PNG: {SEEDQR_PNG}\n" + f" ASCII: cat {SEEDQR_TXT}" + ) + _prompt( + "Put Jade in Temporary Signer mode -> Scan SeedQR -> select TESTNET." + f"\n SeedQR for the BIP-39 abandon-vector lives at:{seedqr_hint}\n" + " Then confirm the transaction on device when prompted." + ) + + psbt_b64 = PSBT_FIXTURE.read_text().strip() + + bridge = HWIBridge(skip_hwi_initialisation=True) + jades = _enumerate_jade(bridge, chain="test") + assert jades, "no Jade detected" + fingerprint = jades[0].get("fingerprint") + assert fingerprint, f"Jade enumerated without fingerprint: {jades[0]}" + assert fingerprint.lower() == ABANDON_FINGERPRINT, ( + f"connected Jade fingerprint is {fingerprint}; " + f"expected {ABANDON_FINGERPRINT} (abandon-vector). " + "Are you in Temporary Signer mode with the right SeedQR?" + ) + + signed = bridge.sign_tx( + psbt=psbt_b64, + device_type="jade", + fingerprint=fingerprint, + chain="test", + ) + assert signed, "sign_tx returned empty" + assert signed != psbt_b64, "PSBT was returned unsigned" From febcf619d98a6fd1d97c15050150fd77f80d7b8c Mon Sep 17 00:00:00 2001 From: k9ert <117085+k9ert@users.noreply.github.com> Date: Mon, 29 Jun 2026 14:35:14 +0200 Subject: [PATCH 08/28] Bump Spectrum to 0.8.0 (#2641) --- requirements.in | 2 +- requirements.txt | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/requirements.in b/requirements.in index 35282fa9e..e4a6f7802 100644 --- a/requirements.in +++ b/requirements.in @@ -33,7 +33,7 @@ aioitertools==0.11.0 cryptoadvance-liquidissuer==0.2.4 specterext-exfund==0.1.7 specterext-faucet==0.1.2 -cryptoadvance.spectrum==0.7.0 +cryptoadvance.spectrum==0.8.0 specterext-stacktrack==0.3.0 specterext-timelockrecovery==0.2.3 diff --git a/requirements.txt b/requirements.txt index 3d622e9b6..1124d940f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile with Python 3.10 # by the following command: # -# pip-compile --generate-hashes requirements.in +# pip-compile --cert=None --client-cert=None --generate-hashes --index-url=None --pip-args=None requirements.in # aioitertools==0.11.0 \ --hash=sha256:04b95e3dab25b449def24d7df809411c10e62aab0cbe31a50ca4e68748c43394 \ @@ -174,9 +174,9 @@ cryptoadvance-liquidissuer==0.2.4 \ --hash=sha256:5a2c531801854c5a4a46daf184877e22f731cdb42d2cfb840785bda7371ba6fb \ --hash=sha256:9e468f3e35ecc566b3f74a2263677cf26632548abb194521dba15ad37acd1e9b # via -r requirements.in -cryptoadvance-spectrum==0.7.0 \ - --hash=sha256:40b31d38ad40c85438bc44d9243edb9ddbaf331c4468a48ebc0f40a109b43a62 \ - --hash=sha256:6cd8858fa07668536345ecab08b4143e28055d7524c9b27b8c885c5b98730879 +cryptoadvance-spectrum==0.8.0 \ + --hash=sha256:cc91eef05f6417898e735b793b91135ddd989fe223ff51aeaa4bc278eb15c3ed \ + --hash=sha256:d3a53b21ca13d8e6a014ff2c0b0e68f8d4eef2c1efbe6383ae018c15409c67c1 # via -r requirements.in cryptography==46.0.6 \ --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ @@ -825,8 +825,8 @@ specterext-stacktrack==0.3.0 \ --hash=sha256:9e2946185730aab377951e83a27d8791a34e0f031e44f15991212b6b85722ca0 # via -r requirements.in specterext-timelockrecovery==0.2.3 \ - --hash=sha256:a0b22c4e010061055da4ea6ea4e08bbc38d1f7c2ec58327970caa0ce987cba95 \ - --hash=sha256:5df3deb6245a22d48f75bbb62fd87dc141a9f60407011ce9fbb0a7bcc40fe4cc + --hash=sha256:5df3deb6245a22d48f75bbb62fd87dc141a9f60407011ce9fbb0a7bcc40fe4cc \ + --hash=sha256:a0b22c4e010061055da4ea6ea4e08bbc38d1f7c2ec58327970caa0ce987cba95 # via -r requirements.in sqlalchemy==1.4.52 \ --hash=sha256:1296f2cdd6db09b98ceb3c93025f0da4835303b8ac46c15c2136e27ee4d18d94 \ @@ -921,6 +921,5 @@ wtforms==3.1.2 \ # via flask-wtf # WARNING: The following packages were not pinned, but pip requires them to be -# pinned when the requirements file includes hashes and the requirement is not -# satisfied by a package already installed. Consider using the --allow-unsafe flag. +# pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag. # setuptools From 44c5fdeaadf06b3bfe2d4b717bf688a94f953c01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 21:13:02 +0200 Subject: [PATCH 09/28] Chore(deps): Bump jinja2 from 3.1.4 to 3.1.6 (#2670) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 1124d940f..0f6dbab6c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -352,7 +352,9 @@ greenlet==2.0.2 \ --hash=sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19 \ --hash=sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1 \ --hash=sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526 - # via -r requirements.in + # via + # -r requirements.in + # sqlalchemy gunicorn==23.0.0 \ --hash=sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d \ --hash=sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec @@ -446,9 +448,9 @@ itsdangerous==2.2.0 \ # via # flask # flask-wtf -jinja2==3.1.4 \ - --hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \ - --hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 # via # flask # flask-babel @@ -921,5 +923,6 @@ wtforms==3.1.2 \ # via flask-wtf # WARNING: The following packages were not pinned, but pip requires them to be -# pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag. +# pinned when the requirements file includes hashes and the requirement is not +# satisfied by a package already installed. Consider using the --allow-unsafe flag. # setuptools From 54b3616b405e6ddd5ccfb24f8437f0f4cef0ef20 Mon Sep 17 00:00:00 2001 From: al-munazzim Date: Tue, 4 Aug 2026 16:56:52 +0200 Subject: [PATCH 10/28] fix: restore Ledger udev hidraw fallback (#2633) Co-authored-by: Nazim Co-authored-by: k9ert <117085+k9ert@users.noreply.github.com> --- udev/20-hw1.rules | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/udev/20-hw1.rules b/udev/20-hw1.rules index 02d630bd6..99f386b88 100644 --- a/udev/20-hw1.rules +++ b/udev/20-hw1.rules @@ -1,12 +1,8 @@ -# HW.1 / Nano +# HW.1, Nano SUBSYSTEMS=="usb", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="1b7c|2b7c|3b7c|4b7c", TAG+="uaccess", TAG+="udev-acl" -# Blue -SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0000|0000|0001|0002|0003|0004|0005|0006|0007|0008|0009|000a|000b|000c|000d|000e|000f|0010|0011|0012|0013|0014|0015|0016|0017|0018|0019|001a|001b|001c|001d|001e|001f", TAG+="uaccess", TAG+="udev-acl" -# Nano S -SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0001|1000|1001|1002|1003|1004|1005|1006|1007|1008|1009|100a|100b|100c|100d|100e|100f|1010|1011|1012|1013|1014|1015|1016|1017|1018|1019|101a|101b|101c|101d|101e|101f", TAG+="uaccess", TAG+="udev-acl" -# Aramis -SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0002|2000|2001|2002|2003|2004|2005|2006|2007|2008|2009|200a|200b|200c|200d|200e|200f|2010|2011|2012|2013|2014|2015|2016|2017|2018|2019|201a|201b|201c|201d|201e|201f", TAG+="uaccess", TAG+="udev-acl" -# HW2 -SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0003|3000|3001|3002|3003|3004|3005|3006|3007|3008|3009|300a|300b|300c|300d|300e|300f|3010|3011|3012|3013|3014|3015|3016|3017|3018|3019|301a|301b|301c|301d|301e|301f", TAG+="uaccess", TAG+="udev-acl" -# Nano X -SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0004|4000|4001|4002|4003|4004|4005|4006|4007|4008|4009|400a|400b|400c|400d|400e|400f|4010|4011|4012|4013|4014|4015|4016|4017|4018|4019|401a|401b|401c|401d|401e|401f", TAG+="uaccess", TAG+="udev-acl" \ No newline at end of file + +# Blue, NanoS, Aramis, HW.2, Nano X, NanoSP, Stax, Ledger Test, +SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", TAG+="uaccess", TAG+="udev-acl" + +# Same, but with hidraw-based library (instead of libusb) +KERNEL=="hidraw*", ATTRS{idVendor}=="2c97", MODE="0666" From fe232048d42179c4eae405da01baa19c616870db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:57:20 +0200 Subject: [PATCH 11/28] Chore(deps-dev): Bump electron from 39.8.5 to 39.8.10 in /pyinstaller/electron (#2680) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyinstaller/electron/package-lock.json | 14 +++++++------- pyinstaller/electron/package.json | 2 +- pyinstaller/electron/yarn.lock | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pyinstaller/electron/package-lock.json b/pyinstaller/electron/package-lock.json index c5840c0ec..9bd574d00 100644 --- a/pyinstaller/electron/package-lock.json +++ b/pyinstaller/electron/package-lock.json @@ -19,7 +19,7 @@ "winston": "^3.13.0" }, "devDependencies": { - "electron": "^39.8.5", + "electron": "^39.8.10", "electron-builder": "^26.8.1" } }, @@ -2010,9 +2010,9 @@ } }, "node_modules/electron": { - "version": "39.8.5", - "resolved": "https://registry.npmjs.org/electron/-/electron-39.8.5.tgz", - "integrity": "sha512-q6+LiQIcTadSyvtPgLDQkCtVA9jQJXQVMrQcctfOJILh6OFMN+UJJLRkuUTy8CZDYeCIBn1ZycqsL1dAXugxZA==", + "version": "39.8.10", + "resolved": "https://registry.npmjs.org/electron/-/electron-39.8.10.tgz", + "integrity": "sha512-zbYtGPYUI7PzqLAzkk21Rk6j67WN0hxn0Mq/njErZo1d0HSf33is4f8ICI5fMLy5vYe0JtCtM5sYunNOaochSQ==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -6605,9 +6605,9 @@ } }, "electron": { - "version": "39.8.5", - "resolved": "https://registry.npmjs.org/electron/-/electron-39.8.5.tgz", - "integrity": "sha512-q6+LiQIcTadSyvtPgLDQkCtVA9jQJXQVMrQcctfOJILh6OFMN+UJJLRkuUTy8CZDYeCIBn1ZycqsL1dAXugxZA==", + "version": "39.8.10", + "resolved": "https://registry.npmjs.org/electron/-/electron-39.8.10.tgz", + "integrity": "sha512-zbYtGPYUI7PzqLAzkk21Rk6j67WN0hxn0Mq/njErZo1d0HSf33is4f8ICI5fMLy5vYe0JtCtM5sYunNOaochSQ==", "dev": true, "requires": { "@electron/get": "^2.0.0", diff --git a/pyinstaller/electron/package.json b/pyinstaller/electron/package.json index a7f55e486..8a1af0c7a 100644 --- a/pyinstaller/electron/package.json +++ b/pyinstaller/electron/package.json @@ -20,7 +20,7 @@ "author": "Specter", "license": "MIT", "devDependencies": { - "electron": "^39.8.5", + "electron": "^39.8.10", "electron-builder": "^26.8.1" }, "build": { diff --git a/pyinstaller/electron/yarn.lock b/pyinstaller/electron/yarn.lock index 0fb9bf104..915d63a2f 100644 --- a/pyinstaller/electron/yarn.lock +++ b/pyinstaller/electron/yarn.lock @@ -949,10 +949,10 @@ electron-publish@26.8.1: lazy-val "^1.0.5" mime "^2.5.2" -electron@^39.8.5: - version "39.8.5" - resolved "https://registry.yarnpkg.com/electron/-/electron-39.8.5.tgz#422d42318d993a77a960ea1a9b4bfb4822221388" - integrity sha512-q6+LiQIcTadSyvtPgLDQkCtVA9jQJXQVMrQcctfOJILh6OFMN+UJJLRkuUTy8CZDYeCIBn1ZycqsL1dAXugxZA== +electron@^39.8.10: + version "39.8.10" + resolved "https://registry.yarnpkg.com/electron/-/electron-39.8.10.tgz#998d9c5b9e7601debb70bd7355e8d4dfd6d70f43" + integrity sha512-zbYtGPYUI7PzqLAzkk21Rk6j67WN0hxn0Mq/njErZo1d0HSf33is4f8ICI5fMLy5vYe0JtCtM5sYunNOaochSQ== dependencies: "@electron/get" "^2.0.0" "@types/node" "^22.7.7" From 0b027f49fdf7d649cf782bf585d5ff30610cd756 Mon Sep 17 00:00:00 2001 From: k9ert <117085+k9ert@users.noreply.github.com> Date: Sat, 8 Aug 2026 14:12:46 +0200 Subject: [PATCH 12/28] fix: dev-server crash in the specter-desktop repo after the project-rename (#2686) Co-authored-by: Claude Opus 5 --- .github/workflows/test.yml | 25 ++++++++++ src/cryptoadvance/specter/util/reflection.py | 52 ++++++++++++-------- tests/test_util_reflection.py | 51 +++++++++++++++++-- tests/test_util_shell.py | 15 +++++- 4 files changed, 116 insertions(+), 27 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 58f6a44c5..6611f4827 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -196,6 +196,31 @@ jobs: pip3 install -r requirements.txt --require-hashes pip3 install -e ".[test]" + - name: Dev-server smoketest (from the repo-root) + # No job used to start the dev-server from the repo-root the way + # docs/development.md describes it. That's why #2526 (renaming the + # project to cryptoadvance_specter) could break it unnoticed. + run: | + source ./.env/bin/activate + export SPECTER_DATA_FOLDER=$(mktemp -d) + # --debug enables the werkzeug-reloader which forks a child-process, so + # start a new process-group we can kill as a whole further down + setsid python3 -m cryptoadvance.specter server --config DevelopmentConfig --debug > specterd.log 2>&1 & + specterd_pid=$! + started="" + for i in $(seq 1 30); do + if curl -sf http://127.0.0.1:25441/ > /dev/null; then started="yes"; break; fi + sleep 2 + done + kill -- -$specterd_pid || true + # the next step needs port 25441, so make sure it's free again + for i in $(seq 1 10); do + curl -sf http://127.0.0.1:25441/ > /dev/null || break + sleep 1 + done + cat specterd.log + if [ -z "$started" ]; then echo "The dev-server did not come up!"; exit 1; fi + - name: Extension smoketest run: | git config --global user.name "CI CD" diff --git a/src/cryptoadvance/specter/util/reflection.py b/src/cryptoadvance/specter/util/reflection.py index 708730c28..70cfca46f 100644 --- a/src/cryptoadvance/specter/util/reflection.py +++ b/src/cryptoadvance/specter/util/reflection.py @@ -5,11 +5,11 @@ import os from pathlib import Path import pkgutil from pkgutil import iter_modules +import re import sys from typing import List from .common import camelcase2snake_case from ..specter_error import SpecterError, SpecterInternalException -from .shell import grep from .reflection_fs import detect_extension_style_in_cwd, search_dirs_in_path @@ -130,6 +130,29 @@ def get_classlist_of_type_clazz_from_modulelist(clazz, modulelist, skip_missing= return class_list +def is_specter_desktop_project(cwd=".") -> bool: + """Whether cwd is the specter-desktop project itself rather than an + extension-project. Detected via the project-name in the pyproject.toml. + Hmm, a bit hackish but we don't want to depend on toml-parsing libs. + """ + try: + with open(Path(cwd, "pyproject.toml")) as pyproject_file: + for line in pyproject_file: + line = line.strip().replace(" ", "").replace("'", "").replace('"', "") + if not line.startswith("name="): + continue + # PEP 503: ".", "-" and "_" are equivalent in project-names, so + # "cryptoadvance.specter" and "cryptoadvance_specter" are the same + name = re.sub(r"[-_.]+", "-", line[len("name=") :]).lower() + if name == "cryptoadvance-specter": + return True + except FileNotFoundError: + # Expected for adhoc-style extension-projects: those have no + # pyproject.toml at all and are therefore not specter-desktop + pass + return False + + def get_subclasses_for_clazz_in_cwd(clazz, cwd=".") -> List[type]: """Returns all subclasses of class clazz located in the CWD if the cwd is not a specter-desktop dev-env-kind-of-dir or contains any .py-file @@ -140,37 +163,26 @@ def get_subclasses_for_clazz_in_cwd(clazz, cwd=".") -> List[type]: return [] # if not testing but in a folder which looks like specter-desktop/src --> No dynamic extensions - if "PYTEST_CURRENT_TEST" not in os.environ: - # Hmm, a bit hackish but if the pyproject.toml specifies cryptoadvance.specter as a name and - # we don't need to depend on toml-parsing libs, that should be ok. - try: - found, line = grep("./pyproject.toml", 'name = "cryptoadvance.specter"') - if found: - return [] - if line: - line = line.replace(" ", "").replace("'", "").replace('"', "") - if line == "name=cryptoadvance.specter": - return [] - except FileNotFoundError: - pass + if "PYTEST_CURRENT_TEST" not in os.environ and is_specter_desktop_project(cwd): + return [] - # Depending on the style we either add "." or "./src" to the searchpath + # Depending on the style we either add cwd or cwd/src to the searchpath - extension_style = detect_extension_style_in_cwd() + extension_style = detect_extension_style_in_cwd(cwd) # raise Exception(extension_style) if extension_style == "adhoc": - package_dirs.append(Path(".")) + package_dirs.append(Path(cwd)) elif extension_style == "publish-ready": - package_dirs.extend(search_dirs_in_path(Path("./src"))) + package_dirs.extend(search_dirs_in_path(Path(cwd, "src"))) elif extension_style == "specter-desktop": if "PYTEST_CURRENT_TEST" in os.environ: # I admit, ugly hack logger.info("We're in testing mode. Adding CWD to searchpath") - package_dirs.append(Path("./src")) + package_dirs.append(Path(cwd, "src")) else: raise Exception( f""" - We checked before that we're not in the specter-desktop home + We checked before that we're not in the specter-desktop home directory but now the extension-style is 'specter-desktop' ?! This should not happen! """ diff --git a/tests/test_util_reflection.py b/tests/test_util_reflection.py index 6890a6039..fb737515e 100644 --- a/tests/test_util_reflection.py +++ b/tests/test_util_reflection.py @@ -11,6 +11,7 @@ from cryptoadvance.specter.util.reflection import ( get_subclasses_for_clazz, get_subclasses_for_clazz_in_cwd, get_classlist_of_type_clazz_from_modulelist, + is_specter_desktop_project, _get_module_from_class, get_package_dir_for_subclasses_of, search_dirs_in_path, @@ -119,13 +120,53 @@ def test_get_classlist_raises_on_missing_module_by_default(): get_classlist_of_type_clazz_from_modulelist(Service, modulelist) +repo_root = Path(__file__).parent.parent +xtestdata = repo_root / "tests" / "xtestdata_testextensions" + + +def test_is_specter_desktop_project(): + """The specter-desktop project detects itself via the name in its own + pyproject.toml. If that name changes (PEP 503 allows "." "-" and "_" to be + used interchangeably), the dev-server dies on startup, see #2526.""" + assert is_specter_desktop_project(repo_root) + assert not is_specter_desktop_project(xtestdata / "ext_root_fully_qualified_1") + assert not is_specter_desktop_project(xtestdata) + + +def test_is_specter_desktop_project_pep503_names(tmp_path): + for name in [ + "cryptoadvance.specter", + "cryptoadvance_specter", + "Cryptoadvance-Specter", + ]: + (tmp_path / "pyproject.toml").write_text( + f'[project]\nname = "{name}"\nversion = "1.2.3"\n' + ) + assert is_specter_desktop_project(tmp_path), f"{name} should be detected" + + (tmp_path / "pyproject.toml").write_text( + '[project]\nname = "boatacccorp.tretboot"\n' + ) + assert not is_specter_desktop_project(tmp_path) + + +def test_get_subclasses_for_clazz_in_cwd_in_specter_desktop_project(monkeypatch): + """No dynamic extension-discovery in the specter-desktop project itself. + Regression test: this used to raise "This should not happen!" when the + project got renamed to cryptoadvance_specter, breaking + `python3 -m cryptoadvance.specter server --config DevelopmentConfig`""" + # the production code takes a shortcut for tests, so pretend we're not testing + monkeypatch.delenv("PYTEST_CURRENT_TEST", raising=False) + assert get_subclasses_for_clazz_in_cwd(Service, cwd=repo_root) == [] + + def test_get_subclasses_for_clazz_in_cwd(caplog): caplog.set_level(logging.DEBUG) - classlist: List[type] = get_subclasses_for_clazz_in_cwd( - Service, cwd="./tests/xtestdata_testextensions" - ) - # damn, this is difficult to test - # assert len(classlist) == 3 + classlist: List[type] = get_subclasses_for_clazz_in_cwd(Service, cwd=xtestdata) + # That folder is a container of extension-projects, not an extension-project + # itself, so there is nothing importable in there + assert classlist == [] + assert "Detected Extension-style: adhoc" in caplog.text def test_get_subclasses_for_class(caplog): diff --git a/tests/test_util_shell.py b/tests/test_util_shell.py index 9b08b078e..73a7d9c30 100644 --- a/tests/test_util_shell.py +++ b/tests/test_util_shell.py @@ -23,7 +23,18 @@ def test_last_lines(caplog): assert lines[-2].startswith("OUT OF OR IN CONNECTION WITH THE SOFTWARE ") -def test_grep(): +def test_grep(tmp_path): + """grep returns a (found, line)-tuple. Asserting on the tuple itself is + always truthy, so always assert on the first element!""" from cryptoadvance.specter.util.shell import grep - assert grep("./pyproject.toml", 'name = "cryptoadvance.specter"') + some_file = tmp_path / "some_file.txt" + some_file.write_text('name = "cryptoadvance_specter"\nversion = "1.2.3"\n') + + found, line = grep(str(some_file), 'name = "cryptoadvance_specter"') + assert found + assert line.strip() == 'name = "cryptoadvance_specter"' + + found, line = grep(str(some_file), "does not exist") + assert not found + assert line is None From 48fea77b8aa5ac166df0263675204f529595fa7b Mon Sep 17 00:00:00 2001 From: k9ert <117085+k9ert@users.noreply.github.com> Date: Sat, 8 Aug 2026 14:57:14 +0200 Subject: [PATCH 13/28] fix: validate active API tokens (#2685) --- .gitignore | 4 + src/cryptoadvance/specter/api/security.py | 18 +++- src/cryptoadvance/specter/user.py | 8 +- tests/test_jwt.py | 101 ++++++++++++++++++++++ tests/test_rest.py | 23 +++-- 5 files changed, 137 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index ba0a6ec05..321fc7c9b 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,7 @@ _bmad-output/ # Claude Code local workspace .claude/ + +# Local agent workspaces +/.agents/ +/.history/ diff --git a/src/cryptoadvance/specter/api/security.py b/src/cryptoadvance/specter/api/security.py index ea03e1c00..92d297e26 100644 --- a/src/cryptoadvance/specter/api/security.py +++ b/src/cryptoadvance/specter/api/security.py @@ -40,11 +40,21 @@ def verify_token(jwt_token): return abort(401) try: payload = jwt.decode(jwt_token, app.config["SECRET_KEY"], algorithms=["HS256"]) - username = payload["username"] - the_user = app.specter.user_manager.get_user_by_username(username) - if not the_user: + username = payload.get("username") + jwt_token_id = payload.get("jwt_token_id") + if ( + not isinstance(username, str) + or not username + or not isinstance(jwt_token_id, str) + or not jwt_token_id + ): return abort(401) - g.user = app.specter.user_manager.get_user_by_username(username) + the_user = app.specter.user_manager.get_user_by_username(username) + if not the_user or not the_user.verify_jwt_token_id_and_jwt_token( + jwt_token_id, jwt_token + ): + return abort(401) + g.user = the_user logger.info({"payload": payload}) logger.info(f"Rest-Request for user {username} PASSED JWT-test") return username diff --git a/src/cryptoadvance/specter/user.py b/src/cryptoadvance/specter/user.py index 28aef4d76..5462fa807 100644 --- a/src/cryptoadvance/specter/user.py +++ b/src/cryptoadvance/specter/user.py @@ -463,10 +463,10 @@ class User(UserMixin): def verify_jwt_token_id_and_jwt_token(self, jwt_token_id, jwt_token): # Verifying the JWT token ID and JWT token - if jwt_token_id in self.jwt_tokens: - if self.jwt_tokens[jwt_token_id]["jwt_token"] == jwt_token: - return True - return False + if not isinstance(self.jwt_tokens, dict) or not isinstance(jwt_token_id, str): + return False + token_info = self.jwt_tokens.get(jwt_token_id) + return isinstance(token_info, dict) and token_info.get("jwt_token") == jwt_token def get_jwt_token(self, jwt_token_id): # Getting a JWT token from the hashmap by ID diff --git a/tests/test_jwt.py b/tests/test_jwt.py index f8349fd48..fd3e1c51d 100644 --- a/tests/test_jwt.py +++ b/tests/test_jwt.py @@ -82,6 +82,99 @@ def test_token_endpoints(client, empty_data_folder, caplog): assert data["jwt_token_life"] == 360 jwt_token_id = data["jwt_token_id"] + jwt_token = data["jwt_token"] + + # API-created tokens remain registered after the user store is reloaded. + client.application.specter.user_manager.update() + + # An active, registered token authenticates successfully. The missing wallet + # is rejected by authorization after authentication has completed. + token_headers = {"Authorization": "Bearer " + jwt_token} + response = client.get( + "/api/v1alpha/wallets/missing/psbt", + follow_redirects=True, + headers=token_headers, + ) + assert response.status_code == 403 + + # A signed token must still be registered in the user's active token store. + unregistered_token = User.generate_jwt_token( + "someuser", User.generate_token_id(), "unregistered", 360 + ) + response = client.get( + "/api/v1alpha/wallets/missing/psbt", + follow_redirects=True, + headers={"Authorization": "Bearer " + unregistered_token}, + ) + assert response.status_code == 401 + + # Missing and non-string token identifiers fail closed. + expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=360) + invalid_payloads = [ + {"username": "someuser", "exp": expiry}, + {"username": "someuser", "jwt_token_id": ["invalid"], "exp": expiry}, + ] + for invalid_payload in invalid_payloads: + invalid_token = jwt.encode( + invalid_payload, + client.application.config["SECRET_KEY"], + algorithm="HS256", + ) + response = client.get( + "/api/v1alpha/wallets/missing/psbt", + follow_redirects=True, + headers={"Authorization": "Bearer " + invalid_token}, + ) + assert response.status_code == 401 + + # A different signed token cannot borrow an active token's identifier. + mismatched_token = User.generate_jwt_token( + "someuser", jwt_token_id, "mismatched", 360 + ) + response = client.get( + "/api/v1alpha/wallets/missing/psbt", + follow_redirects=True, + headers={"Authorization": "Bearer " + mismatched_token}, + ) + assert response.status_code == 401 + + # Malformed persisted records fail closed instead of raising an error. + user_details = client.application.specter.user_manager.get_user_by_username( + "someuser" + ) + stored_token_info = user_details.jwt_tokens[jwt_token_id] + user_details.jwt_tokens[jwt_token_id] = {} + user_details.save_info() + client.application.specter.user_manager.update() + response = client.get( + "/api/v1alpha/wallets/missing/psbt", + follow_redirects=True, + headers=token_headers, + ) + assert response.status_code == 401 + user_details = client.application.specter.user_manager.get_user_by_username( + "someuser" + ) + user_details.jwt_tokens[jwt_token_id] = stored_token_info + user_details.save_info() + + # A malformed persisted token container also fails closed after reload. + stored_tokens = user_details.jwt_tokens + user_details.jwt_tokens = [] + user_details.save_info() + client.application.specter.user_manager.update() + response = client.get( + "/api/v1alpha/wallets/missing/psbt", + follow_redirects=True, + headers=token_headers, + ) + assert response.status_code == 401 + user_details = client.application.specter.user_manager.get_user_by_username( + "someuser" + ) + user_details.jwt_tokens = stored_tokens + user_details.save_info() + client.application.specter.user_manager.update() # testing GET request response = client.get("/api/v1alpha/token", follow_redirects=True, headers=headers) @@ -133,6 +226,14 @@ def test_token_endpoints(client, empty_data_folder, caplog): data = json.loads(response.data) assert data["message"] == "Token deleted" + # Deletion immediately revokes the bearer token. + response = client.get( + "/api/v1alpha/wallets/missing/psbt", + follow_redirects=True, + headers=token_headers, + ) + assert response.status_code == 401 + # retry accessing a deleted token response = client.get( "/api/v1alpha/token/" + jwt_token_id, follow_redirects=True, headers=headers diff --git a/tests/test_rest.py b/tests/test_rest.py index 1f2ba2792..9dfb3ae84 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -17,6 +17,17 @@ from cryptoadvance.specter.user import User logger = logging.getLogger(__name__) +def create_registered_jwt_token(specter, username): + user = specter.user_manager.get_user_by_username(username) + jwt_token_id = User.generate_token_id() + jwt_token_life = 3600 + jwt_token = User.generate_jwt_token( + username, jwt_token_id, "test token", jwt_token_life + ) + user.add_jwt_token(jwt_token_id, jwt_token, "test token", jwt_token_life) + return jwt_token + + def almost_equal(a: Number, b: Number, precision: float = 0.01) -> bool: """ Checks if a and b are not very different. @@ -58,9 +69,7 @@ def test_rr_psbt_get(client, specter_regtest_configured, bitcoin_regtest, caplog # Admin but not authorized (admin is NOT allowed to read everything) headers = { "Authorization": "Bearer " - + User.generate_jwt_token( - "admin", "tokenid", "tokendescription", random.randrange(100, 200) - ) + + create_registered_jwt_token(specter_regtest_configured, "admin") } result = client.get( "/api/v1alpha/wallets/a_simple_wallet/psbt", @@ -76,9 +85,7 @@ def test_rr_psbt_get(client, specter_regtest_configured, bitcoin_regtest, caplog # Proper authorized (the wallet is owned by someuser) headers = { "Authorization": "Bearer " - + User.generate_jwt_token( - "someuser", "tokenid", "tokendescription", random.randrange(100, 200) - ) + + create_registered_jwt_token(specter_regtest_configured, "someuser") } result = client.get( "/api/v1alpha/wallets/a_simple_wallet/psbt", @@ -97,9 +104,7 @@ def test_rr_psbt_post(specter_regtest_configured, bitcoin_regtest, client, caplo headers = { "Authorization": "Bearer " - + User.generate_jwt_token( - "someuser", "tokenid", "tokendescription", random.randrange(100, 200) - ), + + create_registered_jwt_token(specter_regtest_configured, "someuser"), "Content-type": "application/json", } From 3d554fbc2ed13d94f5069282f19c3651c6384ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Nagy?= Date: Sat, 8 Aug 2026 19:33:01 +0300 Subject: [PATCH 14/28] Warn on ColdCard Mk3 seed-entropy advisory at device setup (#2676) Co-authored-by: k9ert <117085+k9ert@users.noreply.github.com> --- src/cryptoadvance/specter/static/output.css | 4 ++++ .../templates/device/new_device/new_device_keys.jinja | 10 ++++++++++ .../specter/templates/device/new_device_manual.jinja | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/cryptoadvance/specter/static/output.css b/src/cryptoadvance/specter/static/output.css index b669522b6..427873a33 100644 --- a/src/cryptoadvance/specter/static/output.css +++ b/src/cryptoadvance/specter/static/output.css @@ -1100,6 +1100,10 @@ input[type="number"]::-webkit-outer-spin-button, z-index: 50; } +.z-20 { + z-index: 20; +} + .z-30 { z-index: 30; } diff --git a/src/cryptoadvance/specter/templates/device/new_device/new_device_keys.jinja b/src/cryptoadvance/specter/templates/device/new_device/new_device_keys.jinja index 6b5f35a79..e0a0661db 100644 --- a/src/cryptoadvance/specter/templates/device/new_device/new_device_keys.jinja +++ b/src/cryptoadvance/specter/templates/device/new_device/new_device_keys.jinja @@ -30,6 +30,16 @@ {% endif %} + +

{{ _("Connect your ColdCard to the computer via USB and unlock it or upload a wallet export file from micro SD card.") }}

diff --git a/src/cryptoadvance/specter/templates/device/new_device_manual.jinja b/src/cryptoadvance/specter/templates/device/new_device_manual.jinja index f0357c52b..3eb1a1558 100644 --- a/src/cryptoadvance/specter/templates/device/new_device_manual.jinja +++ b/src/cryptoadvance/specter/templates/device/new_device_manual.jinja @@ -49,7 +49,7 @@

{{ _("Specify account number (advanced)") }}

-
+
{{ _("Scan QR Code") }} From 693338d3afd1afd11604e8178dedc8280e26bbd9 Mon Sep 17 00:00:00 2001 From: Bunlong Heng Date: Sat, 8 Aug 2026 13:13:31 -0400 Subject: [PATCH 15/28] fix(swan): add timeout to remote HTTP requests to prevent hang (#2687) Co-authored-by: k9ert <117085+k9ert@users.noreply.github.com> Co-authored-by: Claude Opus 5 --- .../specter/services/extension_gen.py | 4 +- src/cryptoadvance/specterext/swan/client.py | 61 +++++++++++++----- tests/test_specterext_swan_client.py | 62 +++++++++++++++++++ 3 files changed, 109 insertions(+), 18 deletions(-) diff --git a/src/cryptoadvance/specter/services/extension_gen.py b/src/cryptoadvance/specter/services/extension_gen.py index a0304d49d..e47a1a6aa 100644 --- a/src/cryptoadvance/specter/services/extension_gen.py +++ b/src/cryptoadvance/specter/services/extension_gen.py @@ -152,7 +152,7 @@ class ExtGen: shutil.copy(sourcepath, targetpath) print(f" --> Created {targetpath} (copied)") else: - r = requests.get(self.env.loader.url_for_template(sourcepath)) + r = requests.get(self.env.loader.url_for_template(sourcepath), timeout=30) open(targetpath, "wb").write(r.content) print(f" --> Created {targetpath} (via Github)") @@ -233,7 +233,7 @@ class GithubUrlLoader(BaseLoader): def get_source(self, environment, template): url = self.url_for_template(template) for attempt in range(3): - r = requests.get(url) + r = requests.get(url, timeout=30) if r.status_code == 200: return r.text, url, None if r.status_code == 429 and attempt < 2: diff --git a/src/cryptoadvance/specterext/swan/client.py b/src/cryptoadvance/specterext/swan/client.py index 70b44bbfd..c9695fbcc 100644 --- a/src/cryptoadvance/specterext/swan/client.py +++ b/src/cryptoadvance/specterext/swan/client.py @@ -137,12 +137,25 @@ class SwanClient: ).decode() auth_header["Authorization"] = f"Basic {auth_hash}" - response = requests.post( - f"{self.api_url}/oidc/token", - data=payload, - headers=auth_header, - ) - resp = json.loads(response.text) + try: + response = requests.post( + f"{self.api_url}/oidc/token", + data=payload, + headers=auth_header, + timeout=30, + ) + except requests.exceptions.RequestException as e: + logger.exception(e) + raise SwanApiException( + f"Could not reach the Swan API ({self.api_url}/oidc/token): {e}" + ) from e + try: + resp = json.loads(response.text) + except ValueError as e: + logger.error(f"{response.status_code}: {response.text}") + raise SwanApiException( + f"Swan API returned no valid json ({response.status_code}): {response.text}" + ) from e """ { "access_token": "***************", @@ -184,27 +197,43 @@ class SwanClient: "User-Agent": "Specter Desktop", "Authorization": f"Bearer {access_token}", } + request_context = f"endpoint: {self.api_url}{endpoint} | method: {method} | payload: {json.dumps(json_payload, indent=4)}" + try: if method == "GET": - response = requests.get(self.api_url + endpoint, headers=auth_header) + response = requests.get( + self.api_url + endpoint, headers=auth_header, timeout=30 + ) elif method in ["POST", "PATCH", "PUT", "DELETE"]: response = requests.request( method=method, url=self.api_url + endpoint, headers=auth_header, json=json_payload, + timeout=30, ) - if response.status_code != 200: - raise SwanApiException(f"{response.status_code}: {response.text}") - return response.json() - except Exception as e: - # TODO: tighten up expected Exceptions + else: + raise SwanApiException(f"Unsupported method: {method}") + except requests.exceptions.RequestException as e: + # Timeouts, connection errors, ... : no response to report about logger.exception(e) - logger.error( - f"endpoint: {self.api_url}{endpoint} | method: {method} | payload: {json.dumps(json_payload, indent=4)}" - ) + logger.error(request_context) + raise SwanApiException(f"Could not reach the Swan API: {e}") from e + + if response.status_code != 200: + logger.error(request_context) logger.error(f"{response.status_code}: {response.text}") - raise e + raise SwanApiException(f"{response.status_code}: {response.text}") + + try: + return response.json() + except ValueError as e: + logger.exception(e) + logger.error(request_context) + logger.error(f"{response.status_code}: {response.text}") + raise SwanApiException( + f"Swan API returned no valid json ({response.status_code}): {response.text}" + ) from e def get_autowithdrawal_addresses(self, swan_wallet_id: str) -> dict: """ diff --git a/tests/test_specterext_swan_client.py b/tests/test_specterext_swan_client.py index c4475773e..6b9a41fbb 100644 --- a/tests/test_specterext_swan_client.py +++ b/tests/test_specterext_swan_client.py @@ -6,7 +6,10 @@ from unittest.mock import MagicMock import pytest import mock from mock import Mock, patch +import requests + from cryptoadvance.specterext.swan.client import ( + SwanApiException, SwanApiRefreshTokenException, SwanClient, ) @@ -106,6 +109,65 @@ def test_expired_access_token(): sc._get_access_token() +def construct_client_with_valid_token(): + """A client which won't need to fetch an access_token first""" + return SwanClient( + "a_hostname", "forever_valid_access_token", 5000000000, "a_refresh_token" + ) + + +def test_authenticated_request_get_timeout(app_no_node): + """A timeout must surface as SwanApiException, not as an UnboundLocalError""" + sc = construct_client_with_valid_token() + with app_no_node.app_context(): + with mock.patch( + "requests.get", side_effect=requests.exceptions.Timeout("simulated timeout") + ): + with pytest.raises(SwanApiException) as exc_info: + sc.authenticated_request("/some/endpoint") + assert "simulated timeout" in str(exc_info.value) + assert isinstance(exc_info.value.__cause__, requests.exceptions.Timeout) + + +def test_authenticated_request_post_timeout(app_no_node): + """Same for the methods going through requests.request""" + sc = construct_client_with_valid_token() + with app_no_node.app_context(): + with mock.patch( + "requests.request", + side_effect=requests.exceptions.ConnectTimeout("simulated timeout"), + ): + with pytest.raises(SwanApiException) as exc_info: + sc.authenticated_request( + "/some/endpoint", method="POST", json_payload={"muuh": "meeh"} + ) + assert isinstance(exc_info.value.__cause__, requests.exceptions.ConnectTimeout) + + +def test_authenticated_request_error_status_code(app_no_node): + sc = construct_client_with_valid_token() + fake_response = Mock() + fake_response.status_code = 500 + fake_response.text = "Internal Server Error" + with app_no_node.app_context(): + with mock.patch("requests.get", return_value=fake_response): + with pytest.raises(SwanApiException, match="500: Internal Server Error"): + sc.authenticated_request("/some/endpoint") + + +def test_get_access_token_timeout(app_no_node): + """The token-endpoint is used before authenticated_request can even start""" + sc = SwanClient("a_hostname", "an_expired_access_token", 1000, "a_refresh_token") + with app_no_node.app_context(): + with mock.patch( + "requests.post", + side_effect=requests.exceptions.Timeout("simulated timeout"), + ): + with pytest.raises(SwanApiException) as exc_info: + sc.authenticated_request("/some/endpoint") + assert isinstance(exc_info.value.__cause__, requests.exceptions.Timeout) + + @patch("requests.delete") @patch("requests.request") @patch("requests.patch") From 426ed39aacf7b695a4d7be516329fb2dd3c45b6a Mon Sep 17 00:00:00 2001 From: al-munazzim Date: Sat, 8 Aug 2026 19:48:20 +0200 Subject: [PATCH 16/28] Warn that Specter auth does not encrypt data folder (#2683) Co-authored-by: Nazim --- docs/faq.md | 7 +++++++ .../specter/templates/settings/auth_settings.jinja | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/docs/faq.md b/docs/faq.md index 43211f3e9..819dce3e2 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -31,6 +31,7 @@ - [*Can this also work with external nodes like Casa, MyNode, and Raspilitz?*](#can-this-also-work-with-external-nodes-like-casa-mynode-and-raspilitz) - [*Can I use Tor?*](#can-i-use-tor) - [I forgot my password, how can I reset it?](#i-forgot-my-password-how-can-i-reset-it) + - [Does the Specter password encrypt my wallet data files?](#does-the-specter-password-encrypt-my-wallet-data-files) - [BACKING UP FUNDS](#backing-up-funds) - [*If something happens to the `~/.specter` folder, is it still possible to **restore** access to multisigs created there (assuming there is no backup of the `~/.specter` folder)?*](#if-something-happens-to-the-specter-folder-is-it-still-possible-to-restore-access-to-multisigs-created-there-assuming-there-is-no-backup-of-the-specter-folder) - [*To recover a multisig that was built on specter (eg: 2 of 3 with ColdCard), is having the seeds of all 3 signing wallets sufficient or do we need to backup more info?*](#to-recover-a-multisig-that-was-built-on-specter-eg-2-of-3-with-coldcard-is-having-the-seeds-of-all-3-signing-wallets-sufficient-or-do-we-need-to-backup-more-info) @@ -241,6 +242,12 @@ Depending on "what's written in `somethingInHere`: * If it's `usernamepassword`, you won't be able to recover the password but you can deactivate it by setting it to `none` and restart Specter. * If it's `none` (or you just set it to `none`) you can login without any password. So hurry up with setting it again within specter. +### Does the Specter password encrypt my wallet data files? + +No. Specter authentication protects access to the app/session, but it does not encrypt the whole Specter data folder at rest. The data folder contains wallet metadata files such as xpubs, addresses, wallet names, labels, and transaction metadata that may be readable by anyone or any process with filesystem access to that folder. + +If this is in your threat model, use OS-level disk encryption (for example FileVault on macOS), make sure backups and sync tools that include the Specter data folder are encrypted and access-controlled, and consider running Specter from a data folder located on an encrypted volume/container via `--specter-data-folder`. + ## BACKING UP FUNDS ### *If something happens to the `~/.specter` folder, is it still possible to **restore** access to multisigs created there (assuming there is no backup of the `~/.specter` folder)?* diff --git a/src/cryptoadvance/specter/templates/settings/auth_settings.jinja b/src/cryptoadvance/specter/templates/settings/auth_settings.jinja index 13b17784b..e9f5ffecb 100644 --- a/src/cryptoadvance/specter/templates/settings/auth_settings.jinja +++ b/src/cryptoadvance/specter/templates/settings/auth_settings.jinja @@ -22,6 +22,14 @@
{% endif %} + +