Skip to content

ci(secret-scanner): make gitleaks gate for real, not continue-on-error#500

Open
hyperpolymath wants to merge 1 commit into
mainfrom
fix/gitleaks-real-gate
Open

ci(secret-scanner): make gitleaks gate for real, not continue-on-error#500
hyperpolymath wants to merge 1 commit into
mainfrom
fix/gitleaks-real-gate

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Problem

The gitleaks job in secret-scanner-reusable.yml carried continue-on-error: true:

- name: Gitleaks Secret Scan
  continue-on-error: true  # gitleaks-action tool-cache PATH injection unreliable on self-hosted runners
  uses: gitleaks/gitleaks-action@e0c47f4f… # v3.0.0

So the estate's primary secret scanner cannot fail a single PR anywhere this
reusable is consumed. The flag was a workaround for a real infra problem — the
gitleaks-action injects gitleaks into the runner tool-cache and prepends it to
PATH, which is unreliable on self-hosted runners — but the workaround throws
out the signal with the flakiness: a genuinely leaked secret now passes green.

Fix

Replace the flaky action with a pinned, checksum-verified gitleaks 8.18.4
binary
run directly, and let it gate:

- name: Install gitleaks (pinned + checksum-verified)   # sha256 ba6dbb65…
- name: Gitleaks secret scan (gating)
  run: gitleaks detect --source . --no-git --redact --no-banner --exit-code 1

A pinned direct install is deterministic on every runner — it removes the exact
unreliability the continue-on-error was papering over — so the scan can gate
for real. Comments/summary were already disabled via env vars, so the action
provided only the scan; the swap loses no functionality and drops the
now-unneeded GITHUB_TOKEN env and fetch-depth: 0. (secrets: inherit is no
longer required by this job; harmless if callers keep it.)

Verification

Exercised the exact install + scan logic locally with the pinned binary:

  • checksum of gitleaks_8.18.4_linux_x64.tar.gz matches (sha256sum -c → OK);
  • a private-key fixture → exit 1 (gate fails on a secret);
  • a clean tree → exit 0 (passes when clean);
  • actionlint clean on the changed steps.

Note: reusable-workflow changes are not self-tested in this repo — the caller
secret-scanner.yml pins the reusable at a fixed SHA, so this repo's own PR CI
runs the old reusable. The change is exercised once a consumer re-pins to a SHA
containing it.

⚠️ Cascade / triage before wide re-pinning (why this is a draft)

Running the new gate against this repo's own tree surfaces 15 findings —
all false positives
, not real secrets:

Count Rule Where
8 generic-api-key avow-protocol/**/deno.lock (integrity hashes)
3 generic-api-key docs / .md / examples.ndjson
1 generic-api-key lol/analysis/Project.toml (Julia UUIDs)
1 generic-api-key docs/audits/…a2ml
1 generic-api-key avow-lib/ffi/zig/src/example.zig

Lockfiles and UUID-bearing manifests are everywhere in the estate, so flipping
gitleaks to gating without a shared allowlist would cry wolf across many
repos
— noise that trains people to ignore the gate, the opposite of the goal.
Companion work needed before re-pinning consumers: a canonical
.gitleaks.toml (allowlist *.lock, UUID manifests, example/fixture/docs
paths) passed to callers via --config, or per-repo allowlists. Kept out of this
PR to keep it surgical.

Related follow-ups (separate PRs)

  • governance-reusable.yml: "Code quality + docs" and "Guix primary / Nix
    fallback policy" jobs currently cannot fail (no exit 1); the redundant
    trufflehog there (also continue-on-error) is better removed per this
    reusable's own "trufflehog removed as redundant" note. Deferred pending a
    decision on the existing advisory-first design stance.

The gitleaks job carried continue-on-error: true (comment: gitleaks-action
tool-cache PATH injection unreliable on self-hosted runners), so the estate's
primary secret scanner could not fail a single PR anywhere it is used.

Replace gitleaks/gitleaks-action with a pinned, checksum-verified gitleaks
8.18.4 binary run directly (gitleaks detect --source . --no-git --exit-code 1).
A direct install is deterministic on every runner, removing the exact
unreliability the continue-on-error was papering over, so the scan now gates.

Comments/summary were already disabled via env vars, so the action provided
only the scan; the swap loses no functionality and drops the now-unneeded
GITHUB_TOKEN env and fetch-depth: 0.

Verified locally with the pinned binary: checksum matches; a private-key
fixture exits 1, a clean tree exits 0; actionlint clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@hyperpolymath
hyperpolymath marked this pull request as ready for review July 17, 2026 22:02
@hyperpolymath
hyperpolymath enabled auto-merge (rebase) July 17, 2026 22:02
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