diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9be369e..5a0f96a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,9 @@ jobs: - name: Install (lockfile-pinned) run: npm ci + - name: Audit runtime and development dependencies + run: npm audit + - name: Build (tsc typecheck + compile) run: npm run build diff --git a/CHANGELOG/dependencies/developer-tool-audit.md b/CHANGELOG/dependencies/developer-tool-audit.md new file mode 100644 index 0000000..06e6bc4 --- /dev/null +++ b/CHANGELOG/dependencies/developer-tool-audit.md @@ -0,0 +1,36 @@ +# 2026-10-14 — Audit the tools coding agents actually execute + +A developer or coding agent could receive green CI while the test toolchain +contained five affected packages. `npm ci` printed the advisories without +failing, and `npm run check` audited only production dependencies—which this +CLI does not have. + +Update eleven locked package records: Vitest and its seven coupled packages +from 4.1.9 to 4.1.11; PostCSS 8.5.16 to 8.5.30; Nano ID 3.3.15 to 3.3.20; and +source-map-js 1.2.1 to 1.2.2. The manifest declares the patched Vitest minimum. +Other locked packages, including Vite 8.1.3, stay unchanged. No runtime +dependency, app code or test assertion changes. + +Both `npm run check` and an explicit CI step now use full `npm audit`, so +development advisories cause failure instead of an apparently clean result. +No advisory suppression, override or force resolution is introduced. + +Before: full audit exited 1 with five affected packages (two moderate, three +high), representing six advisories. After: a clean locked install and full +audit pass with zero reported findings. The existing 364 scenario tests, +compiler output and Node 20 CI provide behavior and compatibility checks. +This is evidence about the known dependency graph, not a claim that all +possible vulnerabilities are absent. + +Primary fixes: [Vitest](https://github.com/vitest-dev/vitest/security/advisories/GHSA-82fw-gwwq-j7x9), +[Nano ID negative size](https://github.com/advisories/GHSA-28wg-ghj8-5hjv) +and [zero size](https://github.com/advisories/GHSA-2v37-7h3g-55p8), +[PostCSS source maps](https://github.com/advisories/GHSA-r28c-9q8g-f849) +and [follow-up fix](https://github.com/advisories/GHSA-fxqj-rqcc-2cmp), +[source-map-js](https://github.com/advisories/GHSA-68fv-2mgg-jv7q). + +Vitest 4.1.11 is the patched supported line for the existing Node 20 tests; +it is not the globally latest release. Vitest 5.0.4 modernization remains +open and requires newer development Node/type peers plus explicit Node 20 +consumer proof. See the [migration requirements](https://vitest.dev/guide/migration/). +The CLI's Node >=20 contract and zero-runtime-dependency promise remain. diff --git a/docs/codebase/TESTING.md b/docs/codebase/TESTING.md index a4cf67b..5586ad4 100644 --- a/docs/codebase/TESTING.md +++ b/docs/codebase/TESTING.md @@ -4,6 +4,7 @@ ```bash npm test # pretest builds (tsc), then vitest run +npm run check # tests, then audit runtime AND development dependencies npx vitest run tests/gate.test.ts # one file npm run test:watch # watch mode npm run proofloop:browser-proof # the real-Chromium proof of the landing page @@ -12,19 +13,24 @@ npm run proofloop:wig-review # Web Interface Guidelines measurements, same node dist/cli.js gate # the tool's own gate: npm run build && npm test ``` -Current state at the commit that ships this document: **28 files, 263 tests, -0 failed**, measured 2026-08-14. +The 2026-10-14 repair-loop baseline has **35 files, 364 tests, 0 failed**. +The developer-tool security update reruns this unchanged suite with Vitest +4.1.11. Read the current run's count: documentation anchors generate cases, +so adding documentation can change it. `npm test` builds first — `pretest` runs `npm run build` — because two suites spawn the compiled `dist/cli.js` rather than importing TypeScript. -The previous value on this line was 198 and was already stale before the commit -that corrected it: the parent commit measures **260** on the same machine. All -three extra cases are that commit's own, because the count is data-driven — -`walkthrough.test.ts` generates one case per documentation anchor and one per -markdown file under `docs/` and `promotion/`, and that commit added one anchor -and two documents. Adding a document to this repository moves this number. Run -the suite and read it; never assume it. +`npm run check` and CI audit the full dependency graph because coding agents +and developers execute the test tools too. Previously, `--omit=dev` excluded +all installed packages in this zero-runtime-dependency project, while CI's +install printed known vulnerabilities without failing. An advisory or an +unavailable audit service now fails the audit step; it is not a clean result. + +The patched Vitest 4 line retains the existing Node 20 test environment. +Vitest 5 modernization remains separate: it requires newer development Node +and type peers, and must preserve explicit Node 20 consumer proof rather than +silently raising the CLI's `>=20` runtime floor. Chromium is a separate install for the browser proof: `npx playwright install chromium`. diff --git a/package-lock.json b/package-lock.json index db987d7..7a63467 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@types/node": "^20.11.0", "playwright": "^1.62.1", "typescript": "^5.7.2", - "vitest": "^4.1.9" + "vitest": "^4.1.11" }, "engines": { "node": ">=20" @@ -877,16 +877,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -895,13 +895,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.9", + "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -922,9 +922,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { @@ -935,13 +935,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.11", "pathe": "^2.0.3" }, "funding": { @@ -949,14 +949,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -965,9 +965,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", "funding": { @@ -975,13 +975,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", + "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -1402,9 +1402,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "dev": true, "funding": [ { @@ -1509,9 +1509,9 @@ } }, "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "version": "8.5.30", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.30.tgz", + "integrity": "sha512-zkd+e9S1AQHhmaBKye9zlFHWjOwVgqXl1q3ECYy8561K7qxR2cREf/STLskulBFd78k6AOFeA+ff1TtEzk9bAA==", "dev": true, "funding": [ { @@ -1529,9 +1529,9 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.20", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" @@ -1579,9 +1579,9 @@ "license": "ISC" }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -1754,19 +1754,19 @@ } }, "node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -1794,12 +1794,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" diff --git a/package.json b/package.json index a2a68e4..6525921 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "dev": "tsc -p tsconfig.json --watch", "demo": "npm run proofloop:doctor", "doctor": "npm run proofloop:doctor", - "check": "npm test && npm audit --omit=dev", + "check": "npm test && npm audit", "proof": "npm run proofloop:maturity", "build": "tsc -p tsconfig.json && node scripts/stamp-build-sha.mjs", "prepublishOnly": "npm run build", @@ -65,6 +65,6 @@ "@types/node": "^20.11.0", "playwright": "^1.62.1", "typescript": "^5.7.2", - "vitest": "^4.1.9" + "vitest": "^4.1.11" } }