Repository navigation
fix(security): upgrade secret scanning from retired trufflehog to gitleaks - #47
Conversation
…leaks
This repo's only leak scanning was an inline trufflehog job, and trufflehog
was retired estate-wide. The standards secret-scanner reusable records the
ruling in its own header:
"Trufflehog was removed as redundant; gitleaks catches what we need"
"Trufflehog removed: gitleaks provides sufficient coverage at lower cost."
So this is an UPGRADE, not a removal. A sweep is currently deleting leftover
trufflehog steps across the estate, and this repo was deliberately EXCLUDED
from it: with no other scanner present, deleting would have left the repo
with no secret scanning at all. Replacing the inline job adds gitleaks and
drops the retired scanner in one move.
What changes in practice:
- gitleaks over the whole working tree with --no-git, exiting non-zero on
a finding. The inline job scanned a base..head diff, which is narrower.
- the reusable's rust-secrets and shell-secrets jobs come along with it,
so the local rust-secrets grep is no longer needed separately.
- a pinned, checksum-verified gitleaks binary rather than an action that
injects one.
`secrets: inherit` is REQUIRED and included. Without it the gitleaks action's
inner secrets.GITHUB_TOKEN is empty and the scan silently degrades — a quiet
failure exactly of the kind this campaign exists to remove.
The workflow's `name:` is preserved verbatim, so no required status-check
context is stranded. Job-level context names DO change, because they now come
from the reusable; this repo's rulesets were checked first and require no
secret-scanning context.
Where an actions.lock exists it gains a hand-authored [] entry for this file:
gh actions-lock SKIPS reusable-workflow callers, and without the entry the
workflow is rejected as startup_failure with no log.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
|
| permissions: | ||
| actions: read | ||
| contents: read | ||
| jobs: | ||
| trufflehog: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 15 | ||
| steps: | ||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 | ||
| with: | ||
| fetch-depth: 0 # Full history for scanning | ||
| - name: TruffleHog Secret Scan | ||
| uses: trufflesecurity/trufflehog@6c05c4a00b91aa542267d8e32a8254774799d68d # v3 | ||
| with: | ||
| # The v3 action injects --fail automatically on pull_request events. | ||
| # Passing --fail here triggers "flag 'fail' cannot be repeated". | ||
| extra_args: --only-verified | ||
| # Rust-specific: Check for hardcoded crypto values | ||
| rust-secrets: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 15 | ||
| steps: | ||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 | ||
| - name: Check for hardcoded secrets in Rust | ||
| run: | | ||
| if ! find . -name Cargo.toml -not -path './target/*' -print -quit | grep -q .; then | ||
| echo 'No Cargo.toml found — skipping Rust secrets check' | ||
| exit 0 | ||
| fi | ||
| # Patterns that suggest hardcoded secrets | ||
| PATTERNS=( | ||
| 'const.*SECRET.*=.*"' | ||
| 'const.*KEY.*=.*"[a-zA-Z0-9]{16,}"' | ||
| 'const.*TOKEN.*=.*"' | ||
| 'let.*api_key.*=.*"' | ||
| 'HMAC.*"[a-fA-F0-9]{32,}"' | ||
| 'password.*=.*"[^"]+"' | ||
| ) | ||
|
|
||
| found=0 | ||
| for pattern in "${PATTERNS[@]}"; do | ||
| if grep -rn --include="*.rs" -E "$pattern" src/; then | ||
| echo "WARNING: Potential hardcoded secret found matching: $pattern" | ||
| found=1 | ||
| fi | ||
| done | ||
|
|
||
| if [ $found -eq 1 ]; then | ||
| echo "::error::Potential hardcoded secrets detected. Use environment variables instead." | ||
| exit 1 | ||
| fi | ||
| scan: | ||
| uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@bd0df9ead7faf0cdfe0e13e7966d91e28d0101d4 |
There was a problem hiding this comment.
💡 Bug: Caller permissions may under-grant the reusable scanner
The caller now grants only contents: read (the previous actions: read was dropped) and calls the reusable secret-scanner-reusable.yml. Reusable-workflow jobs cannot exceed the caller's permissions, so if the gitleaks/rust-secrets/shell-secrets jobs require more (e.g. security-events: write to upload SARIF, or actions: read), those steps will be denied or silently degrade — the exact quiet-failure mode this PR aims to eliminate. Confirm the reusable workflow's required scopes and mirror them in this caller's permissions: block.
Was this helpful? React with 👍 / 👎
|
Note Automatic reviews are paused because your trial's included automatic processing has been used for this period. Upgrade now, or comment "Gitar review" to run a review anytime. Code Review 👍 Approved with suggestions 0 resolved / 1 findingsUpgrades CI secret scanning from the retired Trufflehog tool to Gitleaks with a pinned, checksum-verified binary. Ensure the caller workflow retains sufficient permissions for the reusable scanner job.
💡 Bug: Caller permissions may under-grant the reusable scanner📄 .github/workflows/secret-scanner.yml:17-21 The caller now grants only 🤖 Prompt for agentsOptionsDisplay: compact → Showing less information. Comment with these commands to change the behavior for this request:
Important Your trial ends in 5 days — upgrade now to keep code review, CI analysis, auto-apply, custom automations, and more. Was this helpful? React with 👍 / 👎 | Gitar |
There was a problem hiding this comment.
Configure merge blocking · Maintainers can dismiss this review.
|
|




This repo's only leak scanning was an inline trufflehog job — and trufflehog was retired estate-wide. The standards secret-scanner reusable records the ruling in its own header:
This is an upgrade, not a removal — and that distinction is the point
A sweep is currently deleting leftover trufflehog steps across the estate. This repo was deliberately excluded from it. With no other scanner present, deleting would have left it with no secret scanning at all.
Estate-wide measurement, all 424 repos (364 have workflows):
What actually changes
--no-git, exiting non-zero on a finding. The inline job scanned abase..headdiff — narrower than the tree.rust-secretsandshell-secretsjobs come along with it, so the localrust-secretsgrep is no longer needed separately.Two details that would fail silently if omitted
secrets: inheritis required, and is included. Without it the gitleaks action's innersecrets.GITHUB_TOKENis empty and the scan silently degrades — a quiet failure of exactly the kind this campaign exists to remove.Where an
actions.lockexists it gains a hand-authored[]entry for this file.gh actions-lockskips reusable-workflow callers, and without the entry the workflow is rejected asstartup_failurewith no log and no check run — which reads as "the change broke CI" rather than "a lockfile entry is missing".No context is stranded
The workflow's
name:is preserved verbatim. Job-level context names do change, since they now come from the reusable — this repo's rulesets were checked first and require no secret-scanning context, so nothing is left pointing at a string that will never report.Found during the 2026-08-05 estate CI/CD census.
🤖 Generated with Claude Code