Shift Left Secret Scanning: Harden CI with Gitleaks Before Credentials Leak
This article demonstrates configuring Gitleaks in GitHub Actions to scan full Git history for leaked credentials, explains why post-push CI scans cannot prevent initial exposure, and shows how to add pre-commit checks and GitLab CI integration for earlier detection.
Credential leaks via git push to public repositories are a systemic risk: GitHub's public Events API exposes PushEvent data, enabling automated scraping of new commits. GH Archive proves continuous collection of public activity is routine. GitGuardian's 2026 report detected ~29 million new leaked credentials on GitHub in 2025, confirming the scale. Deleting a file later does not erase it from Git history; cloned or cached copies persist. The first response to a real leak must be revocation and rotation of the credential, not just history rewriting.
Gitleaks Capabilities and Limits
Gitleaks (v8.24.3 used here) scans Git history, directories, or stdin using rules that combine format, keywords, and entropy thresholds. It suits CI because a non-zero exit code fails the job, surfacing findings in the familiar checks UI. It does not require production secrets or sending code to an external API. However, rule-based matching can produce false positives on examples and miss unusually formatted internal passwords; a clean scan only means no matches under the current rules and scope.
GitHub Actions Workflow Configuration
The workflow .github/workflows/secret-scan.yml runs on push, pull_request, workflow_dispatch, and a weekly schedule (cron '17 2 * * 1' UTC). Key hardening details:
Full history fetch: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 (v4.2.2) with fetch-depth: 0 and persist-credentials: false.
Verified binary install: Fixed version GITLEAKS_VERSION: '8.24.3' and SHA-256
GITLEAKS_SHA256: '9991e0b2903da4c8f6122b5c3186448b927a5da4deef1fe45271c3793f4ee29c'downloaded from the official release URL, verified with sha256sum --check --strict, then added to GITHUB_PATH.
Scan command:
gitleaks git . --log-opts="--all" --redact=100 --verbose --exit-code=1traverses all reachable history; --redact=100 masks secrets in logs; --exit-code=1 fails the step on findings.
Failure propagation: set -euo pipefail ensures download or verification failures are not masked; no continue-on-error or || true.
Least privilege: Workflow permission contents: read only; checkout does not persist Git credentials; action and binary pinned to immutable SHA/hash.
The author chose the CLI directly instead of the gitleaks/gitleaks-action wrapper to avoid its organizational license requirements.
Real-World Test Results
The workflow scanned 161 commits (~2.36 MB) in 357 ms, total job ~5 seconds on ubuntu-24.04. Log output showed 161 commits scanned and no leaks found. A local validation with a synthetic GitHub-token-shaped string confirmed: clean history passes; adding the string then deleting it still fails (history detection works); output redacted the test string. No real credentials were pushed.
Why Post-Push CI Cannot Block Initial Exposure
A diagram illustrates the sequence: local checks → push protection → public upload → CI scan & public read occur concurrently. Since CI runs after the push succeeds, it cannot retract already-public commits. To make CI failures block merges, the Secret scan job must be added as a required status check in repository Rulesets or branch protection.
Moving Checks Earlier: Pre-Commit and Pre-Push
Install the same Gitleaks version locally and run:
# Scan staged diff (includes deletions)
set -o pipefail
git diff --cached -U0 | gitleaks stdin --redact=100 --exit-code=1
# Scan committed local history
gitleaks git . --log-opts="--all" --redact=100 --exit-code=1The first suits a team-maintained pre-commit hook (catches staged changes, including deletions of old secrets). The second fits a pre-push hook (covers committed history). Git mode does not scan uncommitted changes, so both are needed. Hooks can be bypassed, so CI remains essential.
Complementary Defenses
GitHub Push Protection: Blocks pushes containing supported credential types before acceptance, but has detection gaps and bypass mechanisms; not a universal filter.
.gitignore: Ignore .env, private keys, personal configs, browser credential files. Does not protect already-tracked files, clean history, or prevent copying secrets into README.
Porting to GitLab CI, Jenkins, Others
Gitleaks' core is the CLI; any CI can run the same scan command. Three conditions must be met: fetch required history (e.g., GitLab GIT_DEPTH: "0"), install a fixed verified scanner version, and let a non-zero exit code halt subsequent merge/deploy stages. Example GitLab CI snippet (not production-validated):
secret_scan:
stage: test
variables:
GIT_DEPTH: "0"
script:
- gitleaks git . --log-opts="--all" --redact=100 --exit-code=1
allow_failure: falseLarge repos may scan commit ranges daily and run periodic full scans, but must correctly handle baseline comparisons, merge commits, and new branches to avoid missing commits.
Incident Response: Revoke First, Then Clean
Revoke/rotate the credential on its platform and update dependent services.
Check audit logs, anomalous calls, data access, and billing for abuse.
Remove the credential from code; switch to environment variables, secret managers, or short-lived identities; restrict new credential scope and TTL.
If needed, coordinate Git history/branch cleanup and add automated scanning.
Scanning finds the problem; revocation neutralizes the key. Deleting a commit does not equal safety. For existing repos, automated credential scanning is a high-value, low-cost guardrail that works continuously and catches what human review misses. Combine it with pre-commit checks, push protection, and least privilege. Add the check first, then optimize scope and speed. A green run proves the guardrail is active; the real goal is never learning of a leak from an abnormal bill.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Ops Development & AI Practice
DevSecOps engineer sharing experiences and insights on AI, Web3, and Claude code development. Aims to help solve technical challenges, improve development efficiency, and grow through community interaction. Feel free to comment and discuss.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
