diff --git a/package-lock.json b/package-lock.json index bae416a..275e1d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dirtysouthalpha/sentinel-cli", - "version": "1.1.0", + "version": "3.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dirtysouthalpha/sentinel-cli", - "version": "1.1.0", + "version": "3.3.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.29.0", @@ -19,7 +19,6 @@ "ink": "^5.2.1", "openai": "^4.67.3", "ora": "^8.0.1", - "puppeteer": "^24.15.0", "react": "^18.3.1", "ws": "^8.21.0", "yaml": "^2.4.1" @@ -33,6 +32,7 @@ "@types/react": "^18.3.31", "@types/ws": "^8.18.1", "ink-testing-library": "^4.0.0", + "puppeteer": "^24.15.0", "rimraf": "^5.0.7", "tsup": "^8.1.0", "typescript": "^5.5.0", @@ -40,6 +40,9 @@ }, "engines": { "node": ">=20.0.0" + }, + "optionalDependencies": { + "puppeteer": "^24.15.0" } }, "node_modules/@alcalzone/ansi-tokenize": { @@ -59,6 +62,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", @@ -73,6 +77,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -748,6 +753,7 @@ "version": "2.13.2", "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.2.tgz", "integrity": "sha512-5EUZSUIc37H6aIXyWO0Z4y8NlF8NnjgmqeQgOGiswAU7pY0HOo16ho4+alIWmSfdZnjqBRawMsP3I5YqLSn6kw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "debug": "^4.4.3", @@ -1424,6 +1430,7 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, "license": "MIT" }, "node_modules/@tybys/wasm-util": { @@ -1523,6 +1530,7 @@ "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1684,6 +1692,7 @@ "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 14" @@ -1784,6 +1793,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, "license": "Python-2.0" }, "node_modules/assertion-error": { @@ -1800,6 +1810,7 @@ "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.0.1" @@ -1836,6 +1847,7 @@ "version": "2.9.1", "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", + "dev": true, "license": "Apache-2.0", "peerDependencies": { "bare-abort-controller": "*" @@ -1850,6 +1862,7 @@ "version": "4.7.2", "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.2.tgz", "integrity": "sha512-aTvMFUWkBmjzKtEQMDGGDNF8bkfpD5N1b/FCwt7A3wrU4t1o/e/85Wzkluh6JlODCjqVESYCkQCdTXqZ9G7VFg==", + "dev": true, "license": "Apache-2.0", "dependencies": { "bare-events": "^2.5.4", @@ -1874,6 +1887,7 @@ "version": "3.9.1", "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.1.tgz", "integrity": "sha512-6M5XjcnsygQNPMCMPXSK379xrJFiZ/AEMNBmFEmQW8d/789VQATvriyi5r0HYTL9TkQ26rn3kgdTG3aisbrXkQ==", + "dev": true, "license": "Apache-2.0", "engines": { "bare": ">=1.14.0" @@ -1883,6 +1897,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.1.tgz", "integrity": "sha512-ghj2DSK/2e99a1anTVPCV4m4YIYtrbXhfM7V3D7XZLOTsybnYyaJloymGqssQc8l/or0UoDyRtNQkmkEF/ysgQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { "bare-os": "^3.0.1" @@ -1892,6 +1907,7 @@ "version": "2.13.3", "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { "b4a": "^1.8.1", @@ -1919,6 +1935,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "dev": true, "license": "Apache-2.0", "peerDependencies": { "react-native-b4a": "*" @@ -1933,6 +1950,7 @@ "version": "2.4.5", "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz", "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { "bare-path": "^3.0.0" @@ -1942,6 +1960,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", + "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -2009,6 +2028,7 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, "license": "MIT", "engines": { "node": "*" @@ -2082,6 +2102,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2129,6 +2150,7 @@ "version": "14.0.0", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz", "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "mitt": "^3.0.1", @@ -2213,6 +2235,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -2227,6 +2250,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2236,6 +2260,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -2251,12 +2276,14 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, "node_modules/cliui/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2266,6 +2293,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -2280,6 +2308,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -2292,6 +2321,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -2450,6 +2480,7 @@ "version": "9.0.2", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "dev": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.1", @@ -2497,6 +2528,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 14" @@ -2523,6 +2555,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, "license": "MIT", "dependencies": { "ast-types": "^0.13.4", @@ -2565,6 +2598,7 @@ "version": "0.0.1608973", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1608973.tgz", "integrity": "sha512-Tpm17fxYzt+J7VrGdc1k8YdRqS3YV7se/M6KeemEqvUbq/n7At1rWVuXMxQgpWkdwSdIEKYbU//Bve+Shm4YNQ==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/dotenv": { @@ -2624,6 +2658,7 @@ "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, "license": "MIT", "dependencies": { "once": "^1.4.0" @@ -2633,6 +2668,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2654,6 +2690,7 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" @@ -2767,6 +2804,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2791,6 +2829,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", @@ -2812,6 +2851,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -2825,6 +2865,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -2844,6 +2885,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -2871,6 +2913,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "bare-events": "^2.7.0" @@ -2972,6 +3015,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "debug": "^4.1.1", @@ -2998,6 +3042,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, "license": "MIT" }, "node_modules/fast-uri": { @@ -3020,6 +3065,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, "license": "MIT", "dependencies": { "pend": "~1.2.0" @@ -3194,6 +3240,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -3252,6 +3299,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, "license": "MIT", "dependencies": { "pump": "^3.0.0" @@ -3267,6 +3315,7 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "dev": true, "license": "MIT", "dependencies": { "basic-ftp": "^5.0.2", @@ -3411,6 +3460,7 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.0", @@ -3424,6 +3474,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -3462,6 +3513,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -3586,6 +3638,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, "license": "MIT" }, "node_modules/is-fullwidth-code-point": { @@ -3695,6 +3748,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, "funding": [ { "type": "github", @@ -3717,6 +3771,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { @@ -4009,6 +4064,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, "license": "MIT" }, "node_modules/load-tsconfig": { @@ -4181,6 +4237,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, "license": "MIT" }, "node_modules/mlly": { @@ -4246,6 +4303,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz", "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4.0" @@ -4480,6 +4538,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "dev": true, "license": "MIT", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", @@ -4499,6 +4558,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, "license": "MIT", "dependencies": { "degenerator": "^5.0.0", @@ -4518,6 +4578,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -4530,6 +4591,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", @@ -4608,12 +4670,14 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -4736,6 +4800,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -4758,6 +4823,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -4777,6 +4843,7 @@ "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4786,12 +4853,14 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, "license": "MIT" }, "node_modules/pump": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", @@ -4802,6 +4871,7 @@ "version": "24.43.1", "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.43.1.tgz", "integrity": "sha512-/FSOViCrqRdb1HDocpsM9Z1giA71gTQPUt3SpHGVRALKAy/rJr1fLFYZW9F23qPxqVxTHQnbh/5B5opJST3kAw==", + "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -4823,6 +4893,7 @@ "version": "24.43.1", "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.43.1.tgz", "integrity": "sha512-T5ScUMAsmhdNbgDR41AGESYeS6V9MSgetkSnVhhW+gXvzC42VesKCn5ld87gAZDJ6vLHL9GkRvY9WtQWSnwFbw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.13.2", @@ -4922,6 +4993,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4940,6 +5012,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -5097,6 +5170,7 @@ "version": "7.8.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -5303,6 +5377,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6.0.0", @@ -5313,6 +5388,7 @@ "version": "2.8.9", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "dev": true, "license": "MIT", "dependencies": { "ip-address": "^10.1.1", @@ -5327,6 +5403,7 @@ "version": "8.0.5", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -5341,6 +5418,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "license": "BSD-3-Clause", "optional": true, "engines": { @@ -5408,6 +5486,7 @@ "version": "2.28.0", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", + "dev": true, "license": "MIT", "dependencies": { "events-universal": "^1.0.0", @@ -5557,6 +5636,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz", "integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==", + "dev": true, "license": "MIT", "dependencies": { "pump": "^3.0.0", @@ -5571,6 +5651,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", + "dev": true, "license": "MIT", "dependencies": { "b4a": "^1.6.4", @@ -5583,6 +5664,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "dev": true, "license": "Apache-2.0", "peerDependencies": { "react-native-b4a": "*" @@ -5597,6 +5679,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, "license": "MIT", "dependencies": { "streamx": "^2.12.5" @@ -5606,6 +5689,7 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { "b4a": "^1.6.4" @@ -5615,6 +5699,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "dev": true, "license": "Apache-2.0", "peerDependencies": { "react-native-b4a": "*" @@ -5725,6 +5810,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, "license": "0BSD" }, "node_modules/tsup": { @@ -5847,13 +5933,14 @@ "version": "2.12.2", "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz", "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==", + "dev": true, "license": "MIT" }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -6085,6 +6172,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz", "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==", + "dev": true, "license": "Apache-2.0" }, "node_modules/webidl-conversions": { @@ -6281,6 +6369,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -6305,6 +6394,7 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -6323,6 +6413,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -6332,6 +6423,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6341,12 +6433,14 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, "node_modules/yargs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6356,6 +6450,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -6370,6 +6465,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -6382,6 +6478,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", diff --git a/src/core/secrets/file-backend.ts b/src/core/secrets/file-backend.ts index 0062213..d775db5 100644 --- a/src/core/secrets/file-backend.ts +++ b/src/core/secrets/file-backend.ts @@ -30,7 +30,18 @@ import type { SecretStore } from "./store.js"; * stronger; this exists so no install is ever forced to store plaintext. */ -const DIR = join(homedir(), ".config", "sentinel"); +// SENTINEL_CONFIG_DIR relocates the vault wholesale. It exists so tests can +// sandbox it: they previously set HOME, but homedir() reads USERPROFILE on +// Windows and ignores HOME, so the isolation silently failed there and the +// suite wrote its test secrets into the user's real vault. +// +// Deliberately NOT keying off HOME. Git Bash sets HOME on Windows (often as +// /c/Users/...), which join() would resolve against the current drive and +// quietly move a real user's vault to C:\c\Users\... An explicit variable +// cannot be triggered by accident. +const DIR = process.env.SENTINEL_CONFIG_DIR + ? join(process.env.SENTINEL_CONFIG_DIR, ".config", "sentinel") + : join(homedir(), ".config", "sentinel"); const SALT_FILE = join(DIR, "master.salt"); const VAULT_FILE = join(DIR, "secrets.enc.json"); diff --git a/src/core/secrets/windows-backend.ts b/src/core/secrets/windows-backend.ts index 8453d08..c98b639 100644 --- a/src/core/secrets/windows-backend.ts +++ b/src/core/secrets/windows-backend.ts @@ -22,7 +22,11 @@ const execFileP = promisify(execFile); * there is zero native build dependency (no node-gyp, no libsecret-dev). */ -const FILE = join(homedir(), ".config", "sentinel", "secrets.dpapi.json"); +// Honours SENTINEL_CONFIG_DIR for the same reason as file-backend.ts: tests +// sandbox the vault through it, and homedir() ignores HOME on Windows so a +// HOME-based sandbox silently wrote into the user's real vault. +const CONFIG_ROOT = process.env.SENTINEL_CONFIG_DIR || homedir(); +const FILE = join(CONFIG_ROOT, ".config", "sentinel", "secrets.dpapi.json"); type BlobMap = Record; diff --git a/tests/atomic-write.test.ts b/tests/atomic-write.test.ts index a07cc48..68ec4cf 100644 --- a/tests/atomic-write.test.ts +++ b/tests/atomic-write.test.ts @@ -35,7 +35,13 @@ describe("writeAtomicFileSync", () => { expect(readFileSync(target, "utf-8")).toBe("x"); }); - it("does not corrupt the existing file when rename fails (target dir read-only)", () => { + // chmodSync(dir, 0o500) does not make a directory read-only on Windows - POSIX + // mode bits are ignored there, so the write succeeds and nothing throws. Making + // this hold on Win32 needs an ACL denial (icacls), which is far heavier than the + // behaviour under test warrants. The guarantee itself (a failed write leaves the + // original intact) is platform-independent; only this way of provoking the + // failure is POSIX-specific. + it.skipIf(process.platform === "win32")("does not corrupt the existing file when rename fails (target dir read-only)", () => { // Make the destination dir read-only so the temp write fails; the existing // file at the target must be untouched. const dir = mkdtempSync(join(tmpdir(), "sentinel-atomic-")); diff --git a/tests/sandbox.test.ts b/tests/sandbox.test.ts index cd530f1..00f70a0 100644 --- a/tests/sandbox.test.ts +++ b/tests/sandbox.test.ts @@ -1,6 +1,13 @@ import { describe, it, expect } from "vitest"; import { buildBwrapArgs, sandboxAvailable } from "../src/tools/sandbox.js"; +// bubblewrap is a Linux sandbox; buildBwrapArgs is never invoked on Windows. +// The two assertions that compare a path round-trip cannot hold there because +// resolve("/home/u/proj") yields "C:\home\u\proj" on Win32. Skipping those two +// explicitly beats asserting a POSIX-only shape on a platform this project +// treats as first-class. +const posixOnly = it.skipIf(process.platform === "win32"); + describe("buildBwrapArgs (pure argv builder)", () => { const root = "/home/u/proj"; @@ -10,7 +17,7 @@ describe("buildBwrapArgs (pure argv builder)", () => { expect(args).toContain("--die-with-parent"); }); - it("bind-mounts the project root read-write", () => { + posixOnly("bind-mounts the project root read-write", () => { const args = buildBwrapArgs({ projectRoot: root }); const i = args.indexOf("--bind"); expect(i).toBeGreaterThan(-1); @@ -43,7 +50,7 @@ describe("buildBwrapArgs (pure argv builder)", () => { expect(args[i + 1]).toBe("/home/u/proj/sub"); }); - it("adds extra read-only bind mounts", () => { + posixOnly("adds extra read-only bind mounts", () => { const args = buildBwrapArgs({ projectRoot: root, extraRoBind: ["/home/u/.cache"], diff --git a/tests/secrets-file-backend.test.ts b/tests/secrets-file-backend.test.ts index 0e13af1..2419f45 100644 --- a/tests/secrets-file-backend.test.ts +++ b/tests/secrets-file-backend.test.ts @@ -3,7 +3,10 @@ import { mkdtempSync, existsSync, readFileSync } from "fs"; import { tmpdir } from "os"; import { join } from "path"; -// Isolate the vault into a temp HOME so the real ~/.config/sentinel is untouched. +// Isolate the vault into a temp dir so the real ~/.config/sentinel is untouched. +// Uses SENTINEL_CONFIG_DIR, not HOME: homedir() ignores HOME on Windows, so the +// old HOME-based sandbox silently resolved to the real profile and this suite +// wrote its test secrets into the user's actual vault. let origHome: string | undefined; let sandbox: string; @@ -11,15 +14,16 @@ const MODULE = "../src/core/secrets/file-backend.js"; describe("encrypted-file secret backend", () => { beforeEach(() => { - origHome = process.env.HOME; + origHome = process.env.SENTINEL_CONFIG_DIR; sandbox = mkdtempSync(join(tmpdir(), "sentinel-secrets-")); - process.env.HOME = sandbox; + process.env.SENTINEL_CONFIG_DIR = sandbox; // Fresh module + master-key cache per test. vi.resetModules(); }); afterEach(() => { - if (origHome !== undefined) process.env.HOME = origHome; + if (origHome === undefined) delete process.env.SENTINEL_CONFIG_DIR; + else process.env.SENTINEL_CONFIG_DIR = origHome; }); it("round-trips a secret (set then get)", async () => { diff --git a/tests/secrets-real-keyring.test.ts b/tests/secrets-real-keyring.test.ts index f0ed401..e02b742 100644 --- a/tests/secrets-real-keyring.test.ts +++ b/tests/secrets-real-keyring.test.ts @@ -1,16 +1,41 @@ -import { describe, it, expect } from "vitest"; +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; import { randomBytes } from "crypto"; +import { mkdtempSync } from "fs"; +import { tmpdir } from "os"; +import { join } from "path"; /** - * Real (non-mocked) round-trip through whichever backend the platform picks. - * On this Linux box that's the Secret Service (secret-tool + gnome-keyring), - * so this proves the S1 plumbing genuinely stores/retrieves a key via the OS - * keyring — not just the mock used in the other secrets tests. + * Real (non-mocked) round-trip through whichever backend the platform picks: + * Secret Service on Linux, Keychain on macOS, DPAPI on Windows. This proves the + * S1 plumbing genuinely stores and retrieves a key via the OS — not just the + * mock used in the other secrets tests. + * + * Sandboxed through SENTINEL_CONFIG_DIR. The file-backed backends (DPAPI's + * sidecar, and the encrypted-file fallback) otherwise write to the user's REAL + * ~/.config/sentinel — measured on Windows 2026-08-05, this suite created + * secrets.dpapi.json in the live vault. Only the sidecar's *location* moves; + * DPAPI still does the actual encrypt/decrypt through PowerShell, so the test + * stays honest about what it claims to prove. * * Uses a unique randomized key name so it can never collide with a real * provider key, and always cleans up. */ describe("secret store: real backend round-trip (no mock)", () => { + let origConfigDir: string | undefined; + + beforeEach(() => { + origConfigDir = process.env.SENTINEL_CONFIG_DIR; + process.env.SENTINEL_CONFIG_DIR = mkdtempSync(join(tmpdir(), "sentinel-real-")); + // The backends read the path at module load, so the sandbox only takes + // effect on a fresh import. + vi.resetModules(); + }); + + afterEach(() => { + if (origConfigDir === undefined) delete process.env.SENTINEL_CONFIG_DIR; + else process.env.SENTINEL_CONFIG_DIR = origConfigDir; + }); + it("stores, retrieves, and deletes a key via the platform backend", async () => { const { getSecretStore } = await import("../src/core/secrets/store.js"); const store = await getSecretStore();