gitleaks-scanning
Configures and runs gitleaks - Go-based secret scanner with `gitleaks git` (scan local git via `git log -p`), `gitleaks dir` (filesystem), `gitleaks stdin` (pipe); 100+ built-in rules + custom rules in `.gitleaks.toml` ([[rules]] with regex / entropy / keywords / tags); allowlist via [[rules.allowlists]] (commits / paths / stopwords); pre-commit hook + GitHub Action integration; baseline file for legacy debt. Use when the team needs OSS secret scanning at commit time + CI gate.
Install with skills.sh (any agent)
npx skills add testland/qa --skill gitleaks-scanninggitleaks-scanning
Command-API note (v8.19.0+): Per gl-gh (opens in new window), "v8.19.0 deprecated detect and protect commands, though they remain available." Current scanning modes:
| Command | Use |
|---|---|
gitleaks git <repo> | Scan local git repository using git log -p |
gitleaks dir <path> | Scan directories and files (no git history) |
gitleaks stdin | Stream data via pipe |
Use git for git-aware scans (fastest, history-rewinding); dir for non-git (e.g., extracted CI artifact); stdin for diff-piping.
When to use
How to use
Step 1 - Install
Per gl-gh (opens in new window):
# Homebrew
brew install gitleaks
# Docker
docker pull zricethezav/gitleaks:latest
docker run -v ${PWD}:/path zricethezav/gitleaks:latest git /path
# From source
git clone https://github.com/gitleaks/gitleaks.git
cd gitleaks
make buildStep 2 - Basic scans
# Scan current git repo (full history)
gitleaks git
# Scan a specific dir (no git)
gitleaks dir ./services/
# Stream + scan (e.g., scan a PR diff)
git diff main..HEAD | gitleaks stdin
# Output formats
gitleaks git --report-format json --report-path leaks.json
gitleaks git --report-format sarif --report-path leaks.sarif
gitleaks git --report-format csv --report-path leaks.csvFor PR-time scanning (faster - only check what changed):
gitleaks git --log-opts="origin/main..HEAD"Step 3 - .gitleaks.toml config
Per gl-gh (opens in new window) config structure:
[[rules]]
id = "rule-identifier"
description = "rule description"
regex = '''regex-pattern'''
secretGroup = 3
entropy = 3.5
keywords = ["auth", "password"]
tags = ["tag1", "tag2"]
[[rules.allowlists]]
description = "ignore specific matches"
commits = ["commit-hash"]
paths = ['''file-path-regex''']
stopwords = ['''false-positive-term''']Built-in rules cover AWS, GCP, Azure, GitHub, GitLab, Stripe, Twilio, Slack, npm, PyPI, etc. Use gitleaks <command> --no-banner to discover the full default rule list.
Step 4 - Custom rule example
Org-internal secret formats (internal API-key prefixes, etc.) go in [[rules]] blocks. Set [extend] useDefault = true to keep the built-in rules; without it, custom rules replace the defaults entirely. Full example (custom rule + per-rule and top-level allowlists) in references/custom-rules-and-triage.md.
Step 5 - False-positive triage (MANDATORY)
Suppress genuine false-positives with [[rules.allowlists]] (paths / commits), the top-level [[allowlists]], --baseline-path for legacy debt, or an inline # gitleaks:allow comment. Every allowlist entry needs a Re-review-date; audit them quarterly. The suppression priority table, the baseline workflow, and the justification template are in references/custom-rules-and-triage.md.
Step 6 - Pre-commit hook integration
Per gl-gh (opens in new window):
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.24.2
hooks:
- id: gitleaksPre-commit prevents commits containing secrets from being created. Faster local feedback than CI-only scanning.
Step 7 - CI integration
Per gl-gh (opens in new window):
# .github/workflows/gitleaks.yml
name: gitleaks
on: [pull_request, push]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with: { fetch-depth: 0 } # full history needed for git scan
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} # for organizationsfetch-depth: 0 is critical - without full git history, git-aware scan only sees the latest commit.
Step 8 - Rotation when a secret is found
Finding a leaked secret in git history is not enough - git history is permanent (mirrored in clones, GitHub forks, archives). The secret IS exposed. Workflow:
For automated rotation workflow, see secrets-rotation-runner.
Worked example
A team enables gitleaks on a 4-year-old private repo.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
fetch-depth: 1 in CI | git-aware scan misses history; only catches new leaks | Always fetch-depth: 0 (Step 7) |
Allowlist without Re-review-date | Permanent debt | Mandatory template (Step 5) |
| Rely only on pre-commit (no CI) | Bypass --no-verify; CI is the catch-net | Both pre-commit AND CI (Steps 6 - 7) |
| Skip baseline; legacy findings block all PRs | Team disables gitleaks | --baseline-path (Step 5) |
| Find leak; assume git-history scrub fixes it | Leaked secret IS exposed; assume compromise | Rotate immediately (Step 8) |
Limitations
References
gitleaks custom rules and false-positive triage
View source (opens in new window)gitleaks custom rules and false-positive triage
Custom org-internal rules and the mandatory allowlist / baseline / justification workflow. Referenced from Step 4 and Step 5 of the gitleaks-scanning skill.
Custom rule example
# .gitleaks.toml
[extend]
useDefault = true
[[rules]]
id = "internal-api-key"
description = "Internal API key (acme- prefix)"
regex = '''(?i)acme[_-]?api[_-]?key[_-]?[a-zA-Z0-9]{32}'''
secretGroup = 1
keywords = ["acme"]
tags = ["acme-internal"]
[[rules.allowlists]]
description = "Test fixtures"
paths = ['''tests/fixtures/.*\.json$''']
[[allowlists]]
description = "Project-wide allowlist (legacy commits)"
commits = ["abc1234", "def5678"]
paths = ['''vendor/.*''', '''third_party/.*''']The [extend] useDefault = true keeps built-in rules; without it, your custom rules replace the defaults entirely.
False-positive triage (MANDATORY)
Suppression mechanisms in priority order:
| Mechanism | Where | When to use |
|---|---|---|
[[rules.allowlists]] paths | .gitleaks.toml | Per-rule path exclusion (test fixtures, vendor) |
[[rules.allowlists]] commits | .gitleaks.toml | Per-rule commit exclusion (historical false positive) |
[[allowlists]] paths | .gitleaks.toml (top-level) | All-rule path exclusion |
--baseline-path | CI flag | Legacy debt: only fail on NEW findings vs baseline |
Inline # gitleaks:allow comment | Code | Single-line suppression |
Baseline workflow (per gl-gh (opens in new window)):
# Create baseline
gitleaks git --report-path gitleaks-baseline.json
# Apply baseline (only new findings fail)
gitleaks git --baseline-path gitleaks-baseline.json --report-path findings.jsonJustification template (mandatory in .gitleaks.toml):
[[rules.allowlists]]
description = """
Reason: tests/fixtures/* contains intentional dummy AWS credentials
for SDK initialization tests; never used against real AWS.
Approved-by: alice@example.com
Re-review-date: 2026-09-15 (re-evaluate when SDK supports mock-mode injection)
"""
paths = ['''tests/fixtures/.*\.json$''']Cadence: every quarter, audit .gitleaks.toml allowlist entries; expired re-review-date entries removed.
Related skills
kingfisher-scanning
Configures and runs Kingfisher for secret scanning with access mapping: discovers leaked credentials AND maps them to the IAM identities and cloud resources they expose (S3 buckets, RDS instances, etc.); Intel Hyperscan regex engine makes it the fastest option for large monorepos; 950 built-in rules (largest of the OSS scanners); multi-target (local files / Git history / GitHub / GitLab / AWS S3 / Docker images); live API validation plus offline checksum verification; suppression via `--skip-regex` / `--skip-word` / `--baseline-file` / inline `kingfisher:ignore`. Use when cloud-blast-radius context matters or scan time on a large repo is blocking. Front-loads access-mapping and Hyperscan speed to differentiate from trufflehog-scanning, which also does multi-target scanning and live validation but offers no IAM access mapping.
secrets-baseline-manager
Builds and maintains a unified secrets baseline/allowlist across gitleaks (.gitleaksignore + --baseline-path), TruffleHog (--results=verified filter + trufflehog:ignore), and Kingfisher (--baseline-file + --exclude/--skip-* flags); adopts legacy findings without blocking PRs; enforces a waiver lifecycle (expires + approved_by + reason) stored in .secrets-waivers.yaml; prevents baseline rot via quarterly audit + expiry enforcement. Use when onboarding secrets scanning onto a repo that already has historical findings, or when per-scanner ignore configs have drifted out of sync and need consolidating into one governed allowlist.
secrets-rotation-runner
Build-an-X for the secret-rotation workflow after detection - detect via gitleaks/trufflehog/kingfisher → identify provider via verifier → rotate via provider API (AWS IAM / GitHub PAT / Stripe / GCP / Azure / Twilio / Slack / etc.) → invalidate old secret → audit log via observability stack → post-mortem cross-ref. Use when a secret is detected in code (or proactively for periodic rotation) - assume git-history scrub does NOT prevent compromise.
trufflehog-scanning
Configures and runs TruffleHog v3 - secret scanner with **live verification** (validates discovered secrets against provider APIs to confirm actual exposure vs entropy false positive); supports per-source subcommands (`git`, `github`, `gitlab`, `filesystem`, `s3`, `docker`, `gcs`, `postman`); `--results=verified` filter for high-precision output; `--exclude-detectors=TYPE` for noise reduction; exits 183 on findings via `--fail`. Use when the team needs verified secret findings (low false-positive rate) or scans across cloud + repo + container surfaces.