fix(ci): unblock the audit gate and raise the callback secret floor - #53
Merged
Merged
Conversation
The Security job has failed on every run since 2026-08-12 19:22Z, when GHSA-jmr9-qjv8-65gv (extract-zip unvalidated symlink path traversal) was updated. No code change caused it and none can clear it: extract-zip 2.0.1 is both the latest published release and the vulnerable one, the package is unmaintained, and the advisory has no patched version — so neither a bump nor a `pnpm.overrides` entry resolves it. It reaches us only as dev tooling, via @lhci/cli > lighthouse > puppeteer-core > @puppeteer/browsers > extract-zip, where it unpacks a browser download from a trusted source during `pnpm lighthouse`. It is not in the shipped agent's dependency graph. Exempted by advisory id through pnpm's auditConfig rather than dropping the gate to --prod, so every other high in every other dev dependency still fails CI. `pnpm audit --audit-level=high` now exits 0 and still reports the advisory as ignored rather than hiding it. This mattered more than one red check: `quality` declares `needs: [security]` and contracts, e2e and perf all gate on `quality`, so a single unfixable transitive advisory was skipping the entire pipeline — including the gitleaks scan and check-no-credentials.sh, which sit after the failing step in the same job. The security gate was the thing not running. Also closes a weak-credential gap found while confirming the rest of the pipeline is sound: REVOKER_CALLBACK_SECRET had no length floor, so a three-character token was accepted as a valid bearer credential on POST /revoke, the one endpoint that submits transactions. The 20/min limiter bounds online guessing but does not end it — a four-character lowercase secret still falls inside a day. Anything under 16 characters is now normalised to unset, which leaves the endpoint closed and reports revokeCallback "unconfigured" rather than looking armed. The floor is enforced on both sides. scripts/deploy-workflow.ts already had its own 8-character check, which would have become a trap: the deploy would report success and wire the token into the live workflow, and the agent would then refuse every callback with a 503 nobody is watching for. Raised to 16 to match, and its base64 fixture corrected from 12 characters to the 44 that `openssl rand -base64 32` actually produces — a token-sized fixture would have let the floor regress without failing the test. Verified locally: 726 tests pass, lint and typecheck clean, audit exits 0, and `pnpm install --frozen-lockfile` is unaffected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why CI is red
The Security — dependencies · secrets job has failed on every run since
2026-08-12T19:22Z, when GHSA-jmr9-qjv8-65gv was updated. The 14:35Z run that day was green; the 23:10Z run — a release-please version bump with no source change — was red. No code change caused it.None can clear it either:
extract-zip@2.0.1is both the latest published release and the vulnerable onefirstPatchedVersionisnulland the package is unmaintainedpnpm.overridesentry resolves itIt reaches us only as dev tooling —
@lhci/cli > lighthouse > puppeteer-core > @puppeteer/browsers > extract-zip— where it unpacks a browser download from a trusted source duringpnpm lighthouse. It is not in the shipped agent's dependency graph.This was skipping the whole pipeline
qualitydeclaresneeds: [security], and contracts, e2e and perf all gate onquality. One unfixable transitive advisory was therefore skipping Quality, Contracts, E2E, Perf and Ready-to-ship — and the gitleaks scan andcheck-no-credentials.shtoo, since both sit after the failing step inside the same job. The security gate was the part that wasn't running.The fix
Exempted by advisory id via pnpm's
auditConfig, rather than dropping the gate to--prod— every other high in every other dev dependency still fails CI.pnpm audit --audit-level=highnow exits 0 and still prints the advisory as1 ignoredrather than hiding it. Carries a comment recording why and when to re-check.Weak-credential gap, found while confirming the rest of the pipeline
REVOKER_CALLBACK_SECREThad no length floor, so a three-character token was accepted as a valid bearer credential onPOST /revoke— the one endpoint that submits transactions. The 20/min limiter bounds online guessing but does not end it: a four-character lowercase secret still falls inside a day.Anything under 16 characters is now normalised to unset, leaving the endpoint closed and reporting
revokeCallback: "unconfigured"rather than looking armed. The 503 names which of the two faults it is.Enforced on both sides.
scripts/deploy-workflow.tsalready had its own 8-character check, which would have become a trap once the server floor landed: the deploy would report success and wire the token into the live workflow, and the agent would then refuse every callback with a 503 nobody is watching for. Raised to 16 to match, with the two sites cross-referenced in comments.Its base64 fixture was also corrected from 12 characters to the 44 that
openssl rand -base64 32actually produces — the command named in the error message has to produce something the check accepts, and a token-sized fixture would have let the floor regress without failing the test.Verification
pnpm audit --audit-level=high1 ignored)pnpm testpnpm lintpnpm typecheckpnpm install --frozen-lockfileScope note
Everything else in the audit came back clean: CodeQL 0 open alerts, no secrets in history (every
0x[a-f0-9]{64}in tracked files is a Sepolia tx hash), and the/revokeauth path is otherwise well-hardened — constant-time compare, rate limiting applied before the secret is examined, a body cap measured on received bytes rather than trustedContent-Length,ownertaken from config and never from the request, and tx-hash idempotency keys.