Verified11 commandsAI-assisted

Gitleaks

.md

Verified against Gitleaks 8.30.1, flags verified via `gitleaks --help` / `gitleaks detect --help` run · official docs

What it is and where it fits 🎯#

Gitleaks is the fast, regex-and-entropy-based secret scanner most teams reach for first, because it's simple to reason about and trivial to drop into a pre-commit hook. It's the classic fix for the exact failure mode this series' own DevSecOps tutorial opens with: a credential gets hardcoded, merged, and discovered days later by chance — Gitleaks catches it in seconds, locally, before the commit even lands. Where it differs from its sibling TruffleHog: Gitleaks reports pattern matches (a string that looks like an AWS key), not confirmed-live credentials — faster and simpler, at the cost of more false positives on ambiguous-looking strings. See the TruffleHog cheat sheet for the live-verification alternative.

Installation#

apt-get install gitleaks                     # Debian/Ubuntu, if the package is in your repos
brew install gitleaks                        # macOS/Linux Homebrew
# or download the binary release directly:
curl -sLo gitleaks.tar.gz \
  https://github.com/gitleaks/gitleaks/releases/latest/download/gitleaks_8.30.1_linux_x64.tar.gz
tar -xzf gitleaks.tar.gz gitleaks && sudo mv gitleaks /usr/local/bin/

gitleaks version

Note

There's no official curl install-script; pin the exact release filename from the GitHub Releases page rather than guessing the version in the tarball URL — the filename embeds the version number directly.

Scanning git history#

gitleaks git .                              # full history of the current repo
gitleaks git --log-opts="--since=2026-01-01" .   # limit the history range scanned

gitleaks git walks every commit's diff, not just the current working tree — this is the mode that catches a secret that was committed and then "removed" in a later commit, since it's still sitting in git history and fully retrievable by anyone with clone access.

Scanning a working directory (no git history)#

gitleaks dir .                              # every file on disk, as-is
gitleaks dir . --no-git                     # equivalent — treat as a plain directory even inside a git repo

Scanning stdin#

cat some_file | gitleaks stdin
git diff --staged | gitleaks stdin           # scan only what's about to be committed — the pre-commit-hook shape

Pre-commit hook usage 🧪#

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.30.1
    hooks:
      - id: gitleaks
  • Install pre-commit (pipx install pre-commit) and run pre-commit install once per clone
  • Commit .pre-commit-config.yaml so every contributor gets the hook automatically
  • Run pre-commit run --all-files once after adding it, to catch anything already in the working tree

Output and CI gating#

gitleaks git . --report-format json --report-path report.json
gitleaks git . --exit-code 1                 # default is already 1 on leaks found, 0 clean — set explicitly if scripting
gitleaks git . -v                            # verbose — print each finding, not just a summary

Sample output shape (illustrative — real rule IDs and match counts depend on what's actually in the repo):

Finding: AKIA************WXYZ Secret: AKIAIOSFODNN7EXAMPLE RuleID: aws-access-token Entropy: 3.646430 File: config/settings.py Line: 42 Commit: a1b2c3d4e5f6... Author: Jane Doe Date: 2026-03-14T10:15:00Z ○ │╲ │ ○ ○ ░ ░ gitleaks 10:15AM INF 1 commits scanned. 10:15AM WARN leaks found: 1

Reducing false positives#

gitleaks git . --baseline-path .gitleaks-baseline.json   # ignore known/accepted findings, only report new ones
gitleaks git . -i .gitleaksignore                          # ignore specific finding fingerprints by file
gitleaks git . --redact                                     # redact the actual secret value from output (default: on)

A # gitleaks:allow comment on the offending line suppresses that specific match:

API_KEY = "AKIAIOSFODNN7EXAMPLE"  # gitleaks:allow — test fixture, not a real credential

Caution

Use gitleaks:allow sparingly and only for genuinely fake/example credentials (test fixtures, docs examples). Permanently allow-listing a real secret instead of rotating and removing it defeats the entire point of the scanner — it's a suppression mechanism for false positives, not an escape hatch for real ones.

Custom detection rules#

Gitleaks ships a broad default rule set (.gitleaks.toml-shaped), but a team can extend it for internal credential formats a generic ruleset wouldn't recognize:

# .gitleaks.toml
[[rules]]
id = "internal-api-token"
description = "Internal platform API token"
regex = '''internal_[a-z0-9]{32}'''
tags = ["key", "internal"]

[allowlist]
paths = [
  '''(.*?)(test|fixture)(.*?)''',
]
gitleaks git . -c .gitleaks.toml            # use the custom config instead of the built-in default rules

Real-world scenario: GitHub Actions CI gate#

# .github/workflows/gitleaks.yml
name: Gitleaks
on: [pull_request]
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0        # full history — a shallow clone hides everything but the latest commit
      - uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Important

fetch-depth: 0 matters here specifically — GitHub Actions checks out a shallow clone by default (usually depth 1), which means gitleaks git only ever sees the single latest commit. A secret introduced two commits ago and "fixed" since would be invisible to the scan without the full history.

Common pitfalls#

  • Running gitleaks dir when you meant gitleaks gitdir never looks at history at all, so a secret removed in a later commit (but still in history) goes undetected.
  • Shallow CI checkouts — see the IMPORTANT callout above.
  • Over-allowlisting — a .gitleaksignore or config allowlist that's too broad (a wide path regex) can silently blind the scanner to real findings in that path going forward.

Exit codes#

0 no leaks found · 1 leaks found (configurable via --exit-code).

When to reach for something else#

If false-positive noise from pattern-only matching is a real adoption blocker, TruffleHog's live-verification mode (--only-verified) trades a network call per candidate for far higher confidence per finding — see its cheat sheet. Semgrep's p/secrets ruleset overlaps here too but is a much lighter-weight community set, not a dedicated secrets engine.