# Gitleaks Cheat Sheet

> **Tool:** Gitleaks
> **Category:** Security & Compliance
> **Verified against:** Gitleaks 8.30.1, flags verified via `gitleaks --help` / `gitleaks detect --help` run
> locally, 2026-08-29
> **Official docs:** https://github.com/gitleaks/gitleaks

## 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

```bash
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

```bash
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)

```bash
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

```bash
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 🧪

```yaml
# .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

```bash
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

```bash
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:

```python
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:

```toml
# .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)(.*?)''',
]
```

```bash
gitleaks git . -c .gitleaks.toml            # use the custom config instead of the built-in default rules
```

## Real-world scenario: GitHub Actions CI gate

```yaml
# .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 git`** — `dir` 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.
