diff --git a/.changeset/fix-3588-npm-audit-clean.md b/.changeset/fix-3588-npm-audit-clean.md new file mode 100644 index 000000000..feaffc6d6 --- /dev/null +++ b/.changeset/fix-3588-npm-audit-clean.md @@ -0,0 +1,5 @@ +--- +type: Security +pr: 3588 +--- +**`npm audit --omit=dev` is clean** — bumped lockfile-pinned transitive versions of `fast-uri`, `@anthropic-ai/sdk`, `hono`, `ip-address`, and `express-rate-limit` (pulled in through `@anthropic-ai/claude-agent-sdk` and `@modelcontextprotocol/sdk`) to patched releases. Same pass applied to `sdk/package-lock.json` (was clean for production already; the test now locks it in). Resolves #3588. diff --git a/package-lock.json b/package-lock.json index ad5726444..544809ac1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,35 +28,35 @@ } }, "node_modules/@anthropic-ai/claude-agent-sdk": { - "version": "0.2.119", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.2.119.tgz", - "integrity": "sha512-6AvthpsaOTlkn514brSGOcCSLHDXODnU+ExN1O3CJCjxr5RBcmzR057C9EIM0G7IchnXsRfMZgRO1QKsjTXdbA==", + "version": "0.2.141", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.2.141.tgz", + "integrity": "sha512-AIBacMWGcZIUcXlUoObqjwJ6pmJI3BayAqPAFXuvSq3DHJXdiuZVs7l/zTB5l3nRhRv5cqSrI2XbiDeHgZWizw==", "license": "SEE LICENSE IN README.md", "dependencies": { - "@anthropic-ai/sdk": "^0.81.0", + "@anthropic-ai/sdk": "^0.93.0", "@modelcontextprotocol/sdk": "^1.29.0" }, "engines": { "node": ">=18.0.0" }, "optionalDependencies": { - "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.2.119", - "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.119", - "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.119", - "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.119", - "@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.119", - "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.119", - "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.119", - "@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.119" + "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.2.141", + "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.141", + "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.141", + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.141", + "@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.141", + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.141", + "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.141", + "@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.141" }, "peerDependencies": { "zod": "^4.0.0" } }, "node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": { - "version": "0.2.119", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.2.119.tgz", - "integrity": "sha512-kxnG37SZqUata2Jcp/YQ0n9Y7o/sinE/8LdG4ltM1gePh+z+0Mfa4vBUUTEBMBFth9PTovKoesIuVuyFpvO/Cw==", + "version": "0.2.141", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.2.141.tgz", + "integrity": "sha512-9HZ0ot6+FwOfQ1aeMqQLH4IJGMm/DcP08SysDxscVjBm6l2JjqleHohxi3zid0DurfGweqT+4x9GScJffwg55g==", "cpu": [ "arm64" ], @@ -67,9 +67,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": { - "version": "0.2.119", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.2.119.tgz", - "integrity": "sha512-9Aj8g3ELsmZuOFg17TCkikeg/Wt2ucVT8hOOPQUatzLd7BKhydrHLA0RP42nBpWECO1B/n/mPdQ4iS/LS3s2Fg==", + "version": "0.2.141", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.2.141.tgz", + "integrity": "sha512-4iAdarJaQ+2R58s6QJswZCzUdz2WQmL5lYG7Y+FLzWbRSROFfcH0QYpmOqSaPXd2KRQhIJwEacqecDZd/Q1XKQ==", "cpu": [ "x64" ], @@ -80,12 +80,15 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": { - "version": "0.2.119", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.2.119.tgz", - "integrity": "sha512-v3o464XkiYehp/OKidQQirxdVb+aGSvdJvHF2zH9p33W8M/NC21zwwh4dhwDnKsyrtBIgkt2CcMwzIl30r0OtA==", + "version": "0.2.141", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.2.141.tgz", + "integrity": "sha512-Jdf0ZEwJzOP8sE6rPqdJN+SxMb0/L8sxJg4twCv/7S+Qzk0hJtls+wxSi+0Tjh6EEMaNxJqEGc7S3fx99Wi99Q==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -93,12 +96,15 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": { - "version": "0.2.119", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.2.119.tgz", - "integrity": "sha512-IPGWgtz+gGnD7fxKAvSf913EUT/lYBTBE8EZ7lh3+x5ZP2859LWLmrCm053Lf3nMWo/CWikZsVPwkDVwpz6tIQ==", + "version": "0.2.141", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.2.141.tgz", + "integrity": "sha512-6H1AJ/AVaWNnV22kubUPkOTRzZFH0+qP9k7WlhriHMN9gtgZcVAsITMddDeGjQsQJMCAdhXFd6sgi7TM1LdeOQ==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -106,12 +112,15 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": { - "version": "0.2.119", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.2.119.tgz", - "integrity": "sha512-9ePt4ZN+hsqDw4AgS4KtcWIGKfL9Oq28kwkrTER/QAcSrVKxiLonp81cCLzg7Ok/IUJu4Cfd71GZbFv/WE54zw==", + "version": "0.2.141", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.2.141.tgz", + "integrity": "sha512-DVjp72f3HmrRYpbneWZZWIqkUht5kTZXS7wXGFiwzLz6eNYEgjjh+GcsnhIi8UOwZUtNiKUrjZnoP38ovFqV8A==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -119,12 +128,15 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": { - "version": "0.2.119", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.2.119.tgz", - "integrity": "sha512-QYxFNAe4FFridPkKhGlNcNBJ0TaIygWYyvfI9g4kX0i+RVbresUWuZVkWY06ioJ0fXoixFJ+HNQBMB7dLrIp8Q==", + "version": "0.2.141", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.2.141.tgz", + "integrity": "sha512-fTI1YuM4cxOa4nSgsyMAdB5ELizkWp+w5Ispo4JnnYtcczMAL4D9GBNjWPW0sUzKvjsJOUVim68SmWLWhUOpXQ==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -132,9 +144,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": { - "version": "0.2.119", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.2.119.tgz", - "integrity": "sha512-p/TjcKQvkCYtXGPlR+mdyNwqCmvRcQL34Wtq0yUZ+iqmI/eyCe59IJ3AZrE0EZoqmiAevEYzatPIt9sncC9uxw==", + "version": "0.2.141", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.2.141.tgz", + "integrity": "sha512-Wm10J6kfbufbPGFELokiJ/7Y5Oqug4Uag3HXFsV8g7TWCpaItx/oqVaJoiGptuAtXQB7xGLQVTuk082wER+Y5w==", "cpu": [ "arm64" ], @@ -145,9 +157,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": { - "version": "0.2.119", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.2.119.tgz", - "integrity": "sha512-k98Ju0wtktm6FhqTE/cXlVr6K4kGqBolVjEGzeKkW6ZILc7124euwNapAvkQCwMAavAxS/ZnO3jdKMtHtwTVTA==", + "version": "0.2.141", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.2.141.tgz", + "integrity": "sha512-IXuP29YJuWbR5Q6xOHrjFVGG54V2s1FC61UVNwEN5fpxL09MwPnbwtQL6fqgzt/U1MP7vWAwpXZriYAklkH/mg==", "cpu": [ "x64" ], @@ -158,9 +170,9 @@ ] }, "node_modules/@anthropic-ai/sdk": { - "version": "0.81.0", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.81.0.tgz", - "integrity": "sha512-D4K5PvEV6wPiRtVlVsJHIUhHAmOZ6IT/I9rKlTf84gR7GyyAurPJK7z9BOf/AZqC5d1DhYQGJNKRmV+q8dGhgw==", + "version": "0.93.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.93.0.tgz", + "integrity": "sha512-q9vaSZQVFx6B/gPxetGYfLXSJD5v0sOmh0OpZDq7yCrTSA+Rscvrtyol7JJTW40wEpQB4U1B4JXzxQitbQ3CAA==", "license": "MIT", "dependencies": { "json-schema-to-ts": "^3.1.1" @@ -893,12 +905,12 @@ } }, "node_modules/express-rate-limit": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.4.1.tgz", - "integrity": "sha512-NGVYwQSAyEQgzxX1iCM978PP9AdO/hW93gMcF6ZwQCm+rFvLsBH6w4xcXWTcliS8La5EPRN3p9wzItqBwJrfNw==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", + "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", "license": "MIT", "dependencies": { - "ip-address": "10.1.0" + "ip-address": "^10.2.0" }, "engines": { "node": ">= 16" @@ -946,9 +958,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "funding": [ { "type": "github", @@ -1155,9 +1167,9 @@ } }, "node_modules/hono": { - "version": "4.12.15", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.15.tgz", - "integrity": "sha512-qM0jDhFEaCBb4TxoW7f53Qrpv9RBiayUHo0S52JudprkhvpjIrGoU1mnnr29Fvd1U335ZFPZQY1wlkqgfGXyLg==", + "version": "4.12.18", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.18.tgz", + "integrity": "sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ==", "license": "MIT", "engines": { "node": ">=16.9.0" @@ -1213,9 +1225,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "license": "MIT", "engines": { "node": ">= 12" diff --git a/sdk/package-lock.json b/sdk/package-lock.json index cf1ca2662..07056e4f2 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -1590,9 +1590,9 @@ } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "dev": true, "funding": [ { @@ -2308,9 +2308,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", - "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", + "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/tests/bug-3588-npm-audit-clean.test.cjs b/tests/bug-3588-npm-audit-clean.test.cjs new file mode 100644 index 000000000..20cd82a99 --- /dev/null +++ b/tests/bug-3588-npm-audit-clean.test.cjs @@ -0,0 +1,90 @@ +'use strict'; + +/** + * Regression test for #3588 — production dependency tree must not carry + * high or moderate npm-audit advisories. + * + * Strategy: run `npm audit --omit=dev --json` against both the root + * workspace and the embedded SDK package and assert that the metadata + * vulnerability counts are zero across info/low/moderate/high/critical. + * + * The test is intentionally strict — any advisory of any severity (other + * than 'low' if the maintainer accepts it; that branch is left explicit + * here) blocks CI. If a future advisory lands without an upstream patch, + * either bump the patched transitive (preferred), or annotate the + * acceptance below with a justification AND a link to the upstream tracker. + * + * Skips automatically when `node_modules/` is absent (a fresh checkout + * before `npm install`) so the test does not falsely report on developer + * machines mid-setup. + */ + +const { test, describe } = require('node:test'); +const assert = require('node:assert/strict'); +const path = require('node:path'); +const fs = require('node:fs'); +const { execFileSync } = require('node:child_process'); + +const ROOT = path.resolve(__dirname, '..'); +const SDK = path.join(ROOT, 'sdk'); + +function auditProductionVulns(cwd) { + if (!fs.existsSync(path.join(cwd, 'node_modules'))) { + return null; // signal "skip" to caller + } + const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + let out; + try { + out = execFileSync( + npmCmd, + ['audit', '--omit=dev', '--json'], + { cwd, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 60_000 } + ); + } catch (e) { + // `npm audit` exits non-zero when advisories are present; the JSON is + // still on stdout in that case. Recover and let the assertion classify. + if (e && typeof e.stdout !== 'undefined') { + out = Buffer.isBuffer(e.stdout) ? e.stdout.toString('utf-8') : String(e.stdout); + } else { + throw e; + } + } + const parsed = JSON.parse(out); + // `null` is reserved for the "node_modules missing → skip" signal above. + // Any other unexpected JSON shape is a real failure of the audit harness + // (npm changed its output format, audit aborted before metadata, etc.) — + // throw so the test fails loudly instead of skipping silently. + if (parsed && parsed.metadata && parsed.metadata.vulnerabilities) { + return parsed.metadata.vulnerabilities; + } + throw new Error(`Unexpected npm audit JSON shape in ${cwd}: missing metadata.vulnerabilities`); +} + +describe('#3588: npm audit --omit=dev reports zero advisories', () => { + test('root workspace production tree has no advisories', { timeout: 90_000 }, (t) => { + const vulns = auditProductionVulns(ROOT); + if (vulns === null) { + t.skip('node_modules/ not present — run `npm install` before this test'); + return; + } + assert.strictEqual(vulns.critical, 0, `expected 0 critical; got ${vulns.critical}`); + assert.strictEqual(vulns.high, 0, `expected 0 high; got ${vulns.high}`); + assert.strictEqual(vulns.moderate, 0, `expected 0 moderate; got ${vulns.moderate}`); + // Low advisories are not explicitly forbidden by the #3588 acceptance + // criterion but the issue listed only high/moderate as actual findings — + // tighten if any future low advisory is introduced. + assert.strictEqual(vulns.low, 0, `expected 0 low; got ${vulns.low}`); + }); + + test('sdk/ production tree has no advisories', { timeout: 90_000 }, (t) => { + const vulns = auditProductionVulns(SDK); + if (vulns === null) { + t.skip('sdk/node_modules/ not present — run `npm ci` inside sdk/ before this test'); + return; + } + assert.strictEqual(vulns.critical, 0, `expected 0 critical; got ${vulns.critical}`); + assert.strictEqual(vulns.high, 0, `expected 0 high; got ${vulns.high}`); + assert.strictEqual(vulns.moderate, 0, `expected 0 moderate; got ${vulns.moderate}`); + assert.strictEqual(vulns.low, 0, `expected 0 low; got ${vulns.low}`); + }); +});