fix(ci): unblock the JS tooling audit gate without lowering it - #46
Conversation
npm audit --audit-level=moderate runs before any Python step in both ci.yml and release.yml, so advisories published after the last green run (2026-08-19) turned every PR red at step four and left release.yml unable to reach its bump-and-publish steps. Nothing in the repo changed; the audit reads the committed lockfile against the live advisory database, so the result moved with the calendar. Bump two overrides that fix cleanly. fast-uri was already pinned to 4.1.2 as an earlier remediation and the new advisory range is 4.0.0 - 4.1.2, so the old fix had become the vulnerability; qs goes to 6.16.0. The remaining six findings are one chain: @faker-js/faker 5.5.3, hard-pinned by postman-collection, under @stoplight/http-spec, under Prism. There is no version to upgrade into — postman-collection 5.3.1 still pins 5.5.3 exactly, prism-cli 5.16.0 is the newest published, and overriding faker forward breaks postman-collection/lib/superstring and makes all 2616 contract tests uncollectable. npm audit cannot waive a single advisory; the only knob is --audit-level, and raising that to clear one high would hide the next real one too. So read the JSON report and decide in scripts/audit-js-tooling.mjs, which fails on anything at moderate or above that is not explicitly waived. A waiver carries a reason and an expiry, and the script fails both when an entry expires and when an entry stops matching any advisory, so a waiver cannot quietly outlive its justification. Refs #45 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMtJ4fwgPjiG8Zcv6bEkBi
|
Orchestrator review (read-only, verified on Defect (must fix in this PR): the description says the audit runs in two workflows, but there are three. Non-blocking:
|
…ges honest
Review found regenerate-sdk.yml still running the raw npm audit, so the
next manual regenerate dispatch would have failed on the same faker
advisory this branch waives. All three workflows now call the script.
Two smaller corrections from the same review:
A registry failure exits non-zero with an {"error": ...} body and no
vulnerabilities map, which the script read as a clean tree whose waiver had
gone stale - fail-closed, but pointing at a live waiver as the thing to
delete. Refuse an errored report explicitly instead.
Restore the em dash in the package.json description. Rewriting the file
through json.dump had replaced it with its ASCII escape sequence, a
cosmetic change that does not belong here; the diff should be the two
overrides and nothing else.
Refs #45
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMtJ4fwgPjiG8Zcv6bEkBi
|
All three addressed in Defect — fixed. I've corrected "two workflows" to three in the PR description as well — that was wrong in the same way the code was. Registry failure — fixed. Confirmed the misleading path and added an explicit refusal before the loop. Exercised it with a stubbed Still fail-closed, and it no longer tells you to delete a live waiver — Em dash — reverted. You were right about the cause: rewriting Re-verified after the changes: gate passes ( Agreed on leaving the script without an automated test at this size. |
Review on ArchCode
Closes #45
Problem and author intent
npm audit --audit-level=moderateruns as step four ofci.yml, before Python is even installed, and again as a gate insiderelease.ymlandregenerate-sdk.yml. Advisories published since this repo's last green run turned it red, so every PR fails before a single Python test executes, and the release workflow cannot reach its bump-and-publish steps.The intent is to get the gate green and keep it meaningful — not to lower it.
The failure as a system story
Actors: the committed
package-lock.json, GitHub's live advisory database, and the CI job.The audit never reads the diff. Its result is a function of today's date against a lockfile that has not changed, so the gate went red on its own:
mainlast ran green on 2026-08-19 and has had no push since, leaving a stale green badge on a repo where the next PR from anyone fails identically. Verified by diffing the lockfile againstmain(identical) and reproducing the nine findings locally against it.Because
release.ymlruns the same step, this also blocks shipping #44 and, downstream, ArchAstro/firstlanding#13724.What changed
Two overrides that fix cleanly.
fast-uri→4.1.4. Note the shape of this one:package.jsonalready pinnedfast-urito4.1.2as an earlier remediation, and the new advisory range is4.0.0 - 4.1.2. The previous fix had become the vulnerability.qs→6.16.0.That takes 9 findings to 6, and moderate to zero.
The remaining six are one chain with no upgrade path.
Everything that looks like a fix was tried and does not work:
postman-collection@5.3.1(latest) still pins@faker-js/faker: "5.5.3"exactly. Upgrading it changes nothing.@stoplight/prism-cli@5.16.0is already the newest published version. There is nothing to upgrade into.@faker-js/fakerto10.5.0does reachfound 0 vulnerabilities— and breakspostman-collection/lib/superstring, sopytest tests/contractdies during collection and all 2616 contract tests become uncollectable. That trade is strictly worse than the red gate.So the waiver is scoped, not global.
npm audithas no way to waive a single advisory — the only knob is--audit-level, and raising it tohighto clear this one would also hide the next real moderate.scripts/audit-js-tooling.mjsreadsnpm audit --jsonand fails on anything at moderate or above that is not explicitly listed, so the bar stays where it was for everything else. All three workflows that audit —ci.yml,release.yml,regenerate-sdk.yml— call it.A report that failed to build is refused rather than parsed: a registry failure also exits non-zero, with an
{"error": ...}body and no vulnerabilities map, which would otherwise read as a clean tree whose waiver had gone stale.The waiver for
GHSA-qxc2-j82w-r537records why it is acceptable, and the reasoning is about reachability, not convenience:faker.helpers.fakeexecuting a caller-supplied template. We never call it.tests/contract/conftest.pystartsprism mock <openapi.json>against a spec we generate ourselves, with no--dynamicflag — the file's own comment already says static responses are deliberate. No Postman collection is ever parsed, sopostman-collectionnever runs, and faker generates nothing.devDependencyof aprivate: truetooling package. It is not in the published wheel.A waiver cannot quietly outlive its justification. Each entry carries a
reasonand anexpiresdate, and the script fails when an entry expires and when an entry stops matching any advisory — so a stale waiver is a build failure telling you to delete it, not a silent mute.Intentionally unchanged: the
moderatethreshold, every other override, and all Python tooling.Testing
node scripts/audit-js-tooling.mjs— passes:A gate that only passes proves nothing, so all three failure paths were exercised against the real report:
HIGH GHSA-qxc2-j82w-r537 (@faker-js/faker) — Faker: helpers.fake exploitable into arbritary code executionexpiresset to 2026-01-01) → exit 1,waiver expired 2026-01-01; re-review itwaiver no longer matches any advisory; delete it from ALLOWED{"error": {"code": "ENETUNREACH"}}) → exit 1,npm audit could not produce a report: ..., and crucially not a stale-waiver messageThe point of the change is that Prism keeps working, so that is verified directly rather than assumed:
npm ci --ignore-scriptsthenuv run pytest tests/contract— 2616 passeduv run pytest tests/test_http_client.py tests/harness— 48 passeduv run ruff check— clean;uv run ruff format --check— 157 files already formattedThose are the same gates
release.ymlruns before bumping, so the release path is verified end to end on this branch.There is no new automated test file. The gate script is the check, it runs on every PR and every release through the two workflow steps this PR rewires, and its own failure modes were exercised by hand as listed above. Calling that a regression test would overstate it.
Scope and risk
CI and dev tooling only. Nothing in
src/, nothing in the published wheel. Risk: low — the worst case is the gate being wrong about a waiver, and it fails closed in every direction tested above.User impact
None. No SDK behaviour changes.
Follow-ups
🤖 Generated with Claude Code
https://claude.ai/code/session_01MMtJ4fwgPjiG8Zcv6bEkBi