Skip to content

feat(binary): verify SLSA build provenance via gh attestation#16

Merged
avifenesh merged 2 commits into
mainfrom
feat/slsa-verify
Apr 26, 2026
Merged

feat(binary): verify SLSA build provenance via gh attestation#16
avifenesh merged 2 commits into
mainfrom
feat/slsa-verify

Conversation

@avifenesh
Copy link
Copy Markdown
Contributor

@avifenesh avifenesh commented Apr 26, 2026

Summary

Closes the "stolen release token" gap in the binary downloader. Current chain is TLS + SHA-256 sidecar, which a leaked release-scope token can fully defeat (upload attacker binary + matching attacker .sha256). Adds SLSA build-provenance verification as a third gate via gh attestation verify, which checks the Sigstore-signed attestation that agent-analyzer's release workflow publishes through actions/attest-build-provenance.

  • Soft by default: if gh is not on PATH, warn and continue on SHA-256 alone. Not every end user has gh yet.
  • AGENT_ANALYZER_REQUIRE_ATTESTATION=1 promotes missing-gh to a hard failure. Recommended for CI.
  • Present gh + failed verify is always hard-fail regardless of mode.

Verification chain

TLS  ->  SHA-256 sidecar  ->  SLSA build provenance (optional/required)

Implementation notes

  • New verifySlsaAttestation(filePath, opts) with injectable ghRunner + ghProbe (matches the repo's existing mock-via-injection test pattern, no monkey-patching cp).
  • Wired into downloadBinary after SHA-256, before extraction. The in-memory buffer is persisted to a scratch tmpfile just long enough for the gh call, then rmrf'd in a finally.
  • skipAttestation option added (symmetric with skipChecksum, LOCAL DEV ONLY), threaded through ensureBinary / ensureBinarySync.
  • No new runtime deps.

Test plan

  • node --test lib/binary/index.test.js - 39 passing, 2 skipped (Windows-only cases), 0 failing
  • 10 new tests cover: skipped (no gh), failed (no gh + require), verified (gh runner exit 0), failed with stderr (gh runner exit != 0), hard-fail on bad attestation even in soft mode, env var promotion, repo defaulting, isGhAvailable probe semantics
  • End-to-end test against a real agent-analyzer release (once v0.8.0 with attestations is published)

Deferred design questions

  1. Attestation removal by GitHub: if GitHub ever takes down an attestation (compromise-response), gh attestation verify will fail even for legitimate binaries that were previously verified. Current behavior = hard-fail. Alternative = cache a successful verification result locally and trust the cache for N days. Not implementing now - prefer correctness over availability for a security gate.
  2. Cache TTL: gh attestation verify caches Sigstore/Rekor lookups in the user's gh config dir; TTL is opaque and controlled by gh. We rely on it rather than managing our own.
  3. Async vs blocking on execution: current impl blocks binary execution on attestation verification (synchronous gate). Alternative = verify in parallel with extraction and kill the binary post-install if verification failed. Rejected - gate before execution is the whole point.
  4. Revocation / transparency log consultation: gh attestation verify consults Rekor by default. No action needed unless we want to tighten to "must have seen log witness" mode.

Note

Medium Risk
Adds an additional security gate to the binary download/install path that can now hard-fail installs (or warn/skip) based on gh attestation verify results and env/option flags. Risk is moderate due to new external CLI dependency behavior and failure-mode changes during installation.

Overview
Adds SLSA build provenance verification to the binary downloader by running gh attestation verify on the downloaded release asset before extraction, failing the install when attestation verification fails.

Introduces verifySlsaAttestation/isGhAvailable helpers (with injectable runners for tests), a soft-by-default skip when gh is missing, and an opt-in hard requirement via AGENT_ANALYZER_REQUIRE_ATTESTATION=1 (plus skipAttestation for local dev), and threads the new options through ensureBinary/ensureBinarySync with expanded test coverage.

Reviewed by Cursor Bugbot for commit de72b3a. Configure here.

Adds a third layer to the download verification chain:

  TLS  ->  SHA-256 sidecar  ->  SLSA build provenance (optional/required)

SHA-256 alone cannot detect the case where a release-scope token is
stolen and the attacker uploads both a malicious binary AND a matching
malicious .sha256 sidecar to the same release. The SLSA attestation
published by agent-analyzer's release workflow
(actions/attest-build-provenance, Sigstore-signed, Rekor-logged) binds
the artifact bytes to the GitHub Actions workflow run that produced
them and closes that hole.

Behavior:
- Soft by default: if `gh` CLI is absent, log a warning and continue
  with just SHA-256. Most end users do not yet have `gh` installed.
- AGENT_ANALYZER_REQUIRE_ATTESTATION=1 promotes missing-gh to a hard
  failure (recommended for CI / paranoid consumers).
- A present `gh` that reports verification failure is ALWAYS a hard
  failure; we never silently ignore an explicit bad attestation.

Implementation:
- New `verifySlsaAttestation(filePath, opts)` with injectable `ghRunner`
  and `ghProbe` (matches existing test mock style).
- Wired into `downloadBinary` after the SHA-256 gate, before extraction.
  The downloaded buffer is persisted to a scratch tmpfile just long
  enough for `gh attestation verify` to read it; the tmpfile is rmrf'd
  in a finally.
- New `skipAttestation` option (LOCAL DEV ONLY, symmetric with
  `skipChecksum`), threaded through `ensureBinary` / `ensureBinarySync`.

Tests (node:test): 10 new tests covering skipped/verified/failed states,
env-var promotion, hard-fail on bad attestation, repo defaulting.
@gemini-code-assist
Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit de72b3a. Configure here.

Comment thread lib/binary/index.js Outdated
Previously requireAttestation was dropped when sync callers crossed the
child-process boundary, silently losing the hard-fail intent for SLSA
verification when gh is missing. Tri-state forwarding: undefined lets
the child fall back to AGENT_ANALYZER_REQUIRE_ATTESTATION env var
(matching ensureBinary), boolean forwards the explicit setting.
@avifenesh avifenesh merged commit 889cfde into main Apr 26, 2026
3 checks passed
@avifenesh avifenesh deleted the feat/slsa-verify branch April 26, 2026 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant