Skip to main content
COSMICBYTEZLABS
NewsSecurityHOWTOsToolsTraining
StudyProjectsNewsletterHire MeAbout
Subscribe

Press Enter to search or Esc to close

News
Security
HOWTOs
Tools
Training
Study
Projects
Newsletter
Hire Me
About
RSS Feed
Reading List
Subscribe

Stay in the Loop

Get the latest security alerts, tutorials, and tech insights delivered to your inbox.

Subscribe NowFree forever. No spam.
COSMICBYTEZLABS

Your trusted source for IT intelligence, cybersecurity insights, and hands-on technical guides.

2494+ Articles
161+ Guides

CONTENT

  • Latest News
  • Security Alerts
  • HOWTOs
  • Checklists
  • Projects
  • Exam Prep

RESOURCES

  • Search
  • Browse Tags
  • Newsletter Archive
  • Reading List
  • RSS Feed

COMPANY

  • About Us
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CosmicBytez Labs. All rights reserved.

System Status: Operational
  1. Home
  2. HOWTOs
  3. Detecting Leaked Secrets in Git Repos with Gitleaks
Detecting Leaked Secrets in Git Repos with Gitleaks
HOWTOIntermediate

Detecting Leaked Secrets in Git Repos with Gitleaks

Stop shipping API keys, tokens, and passwords to GitHub. Learn how to scan git history, enforce pre-commit hooks, and integrate Gitleaks into your CI/CD pipeline.

Dylan H.

Tutorials

August 24, 2026
10 min read

Prerequisites

  • Git installed and basic CLI familiarity
  • A GitHub (or GitLab/Bitbucket) repository to scan
  • Docker (optional — for containerized runs)
  • GitHub Actions access (for CI/CD integration section)

Introduction

Leaked secrets are one of the most preventable — and most common — security incidents in software development. API keys, database passwords, cloud credentials, and private tokens end up committed to version control every day. Once pushed to a remote repository, they can be indexed by search engines, scraped by bots, or discovered by threat actors within minutes.

Gitleaks is a fast, open-source SAST (Static Application Security Testing) tool purpose-built for detecting secrets in git repositories. It scans both the working tree and the full commit history, meaning it catches secrets that were committed and later deleted — the file is gone, but the secret lives on in history.

In this guide you will:

  • Install Gitleaks on Linux, macOS, and via Docker
  • Scan a repository's current state and entire commit history
  • Write a custom .gitleaks.toml config to tune detections
  • Add a pre-commit hook to block secrets before they reach the remote
  • Integrate Gitleaks into a GitHub Actions CI/CD pipeline

Prerequisites

Before you begin, make sure you have:

  • Git 2.28+ — git --version
  • A local git repository with at least one commit
  • curl or wget for downloading the binary
  • Optional: Docker for containerized scans
  • Optional: A GitHub repository and Actions access for the CI/CD section

Step 1 — Install Gitleaks

Linux (amd64)

# Download the latest release (check https://github.com/gitleaks/gitleaks/releases for current version)
GITLEAKS_VERSION="8.21.2"
curl -sSfL \
  "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
  -o /tmp/gitleaks.tar.gz
 
tar -xzf /tmp/gitleaks.tar.gz -C /tmp gitleaks
sudo mv /tmp/gitleaks /usr/local/bin/gitleaks
sudo chmod +x /usr/local/bin/gitleaks
 
# Verify
gitleaks version

macOS (Homebrew)

brew install gitleaks
gitleaks version

Docker (no install required)

# Pull the image
docker pull ghcr.io/gitleaks/gitleaks:latest
 
# Alias for convenience (add to ~/.bashrc or ~/.zshrc)
alias gitleaks='docker run --rm -v "$(pwd)":/path ghcr.io/gitleaks/gitleaks:latest'

Windows (Scoop or direct download)

# Via Scoop
scoop install gitleaks
 
# Or download the Windows zip from GitHub Releases and add to PATH

Step 2 — Your First Scan

Navigate to any git repository and run a basic scan:

cd /path/to/your/repo
 
# Scan the current working directory (staged + unstaged files)
gitleaks detect --source . --verbose
 
# Scan the entire git commit history (catches deleted secrets)
gitleaks git --source . --verbose

Understanding the output

A finding looks like this:

Finding:     ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Secret:      ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
RuleID:      github-pat
Entropy:     3.58
File:        config/settings.py
Line:        14
Commit:      a3f2c1d9...
Author:      Dev User <dev@example.com>
Date:        2025-11-03T10:22:41Z
Fingerprint: a3f2c1d9:config/settings.py:github-pat:14

Key fields:

  • RuleID — which detection rule matched (e.g., github-pat, aws-access-key)
  • Entropy — Shannon entropy of the matched string (higher = more random = more likely a real secret)
  • Fingerprint — unique ID used to allowlist false positives

Gitleaks ships with 150+ built-in rules covering AWS keys, GitHub tokens, Stripe keys, Slack webhooks, private keys, JWT secrets, and more.


Step 3 — Scan Modes and Useful Flags

# Scan only staged changes (useful in pre-commit hooks)
gitleaks detect --staged
 
# Output findings as JSON (for SIEM/reporting pipelines)
gitleaks git --source . --report-format json --report-path findings.json
 
# Redact secrets from output (safe for logs)
gitleaks git --source . --redact
 
# Scan a specific branch
gitleaks git --source . --log-opts="origin/main..HEAD"
 
# Exit with non-zero code on findings (useful in CI)
gitleaks git --source . --exit-code 1
 
# Verbose mode shows all files scanned, not just findings
gitleaks git --source . --verbose
 
# Scan only the last N commits
gitleaks git --source . --log-opts="-20"

Step 4 — Configure Gitleaks with .gitleaks.toml

The default ruleset is comprehensive but you will likely need to tune it — either to add custom rules for your stack or to allowlist known false positives.

Create .gitleaks.toml in your repository root:

# .gitleaks.toml
title = "CosmicBytez Gitleaks Config"
 
[extend]
# Extend the default ruleset rather than replacing it
useDefault = true
 
# ──────────────────────────────────────────────
# Custom rules for internal secrets patterns
# ──────────────────────────────────────────────
[[rules]]
id = "internal-api-key"
description = "Internal API key with CBZ prefix"
regex = '''CBZ-[A-Za-z0-9]{32,}'''
tags = ["internal", "api-key"]
 
  [rules.allowlist]
  # Allow keys in test fixtures
  paths = ['''tests/fixtures/.*''']
 
[[rules]]
id = "database-dsn"
description = "Database connection string with embedded credentials"
regex = '''(postgres|mysql|mongodb):\/\/[^:]+:[^@]+@'''
tags = ["database", "credentials"]
 
# ──────────────────────────────────────────────
# Global allowlist — suppress known false positives
# ──────────────────────────────────────────────
[allowlist]
description = "Global allowlist"
 
# Allowlist by file path (regex)
paths = [
  '''\.gitleaks\.toml''',
  '''package-lock\.json''',
  '''yarn\.lock''',
  '''.*\.test\.(ts|js)''',
  '''docs/.*''',
]
 
# Allowlist specific commit SHAs (one-off exceptions)
commits = [
  # "abc123def456..."  # add commit SHAs here
]
 
# Allowlist by finding fingerprint (from the Fingerprint field in output)
# Use this to silence a specific known-safe detection without blanket ignoring the rule
regexes = [
  # Example: test/mock data that looks like a secret
  '''EXAMPLE_SECRET_DO_NOT_USE''',
  '''test_secret_placeholder''',
]

Apply the config:

gitleaks git --source . --config .gitleaks.toml --verbose

Step 5 — Add a Pre-Commit Hook

A pre-commit hook runs locally before every commit, blocking secrets at the source rather than after a push.

Option A: Manual git hook

cat > .git/hooks/pre-commit << 'EOF'
#!/usr/bin/env bash
set -e
 
echo "[gitleaks] Scanning staged changes for secrets..."
 
if ! command -v gitleaks &>/dev/null; then
  echo "[gitleaks] WARNING: gitleaks not installed, skipping scan"
  exit 0
fi
 
gitleaks detect --staged --config .gitleaks.toml --redact --exit-code 1
 
if [ $? -ne 0 ]; then
  echo ""
  echo "[gitleaks] BLOCKED: Secrets detected in staged files."
  echo "           Review findings above, remove secrets, then commit again."
  echo "           Use 'git diff --cached' to inspect staged changes."
  exit 1
fi
 
echo "[gitleaks] No secrets found — commit allowed."
EOF
 
chmod +x .git/hooks/pre-commit

Test it by staging a file with a fake secret:

echo 'API_KEY=ghp_faketoken1234567890abcdefghijklmno' >> /tmp/test_secret.txt
git add /tmp/test_secret.txt
git commit -m "test"
# → Should be blocked by gitleaks
git restore --staged /tmp/test_secret.txt
rm /tmp/test_secret.txt

Option B: pre-commit framework (recommended for teams)

If your team uses the pre-commit framework, add Gitleaks as a hook in .pre-commit-config.yaml:

repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.21.2
    hooks:
      - id: gitleaks

Install and activate:

pip install pre-commit
pre-commit install
pre-commit run --all-files  # one-time scan of all files

The pre-commit framework handles hook versioning, updates, and cross-platform compatibility automatically — strongly recommended for team repositories.


Step 6 — GitHub Actions CI/CD Integration

Add Gitleaks to your CI pipeline so every pull request is scanned, and pushes to protected branches are blocked on findings.

Create .github/workflows/gitleaks.yml:

name: Secret Scanning (Gitleaks)
 
on:
  push:
    branches:
      - main
      - master
      - "release/**"
  pull_request:
    branches:
      - main
      - master
 
jobs:
  gitleaks:
    name: Detect secrets
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write   # Required for SARIF upload
 
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0      # Full history — required for git log scanning
 
      - name: Run Gitleaks
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          # Optional: use a custom config
          # GITLEAKS_CONFIG: .gitleaks.toml
          # Optional: fail the workflow on findings (default: true)
          # GITLEAKS_ENABLE_UPLOAD_ARTIFACT: true
          # GITLEAKS_ENABLE_SUMMARY: true

The official gitleaks/gitleaks-action uploads SARIF results to GitHub's Security tab so findings appear in the Code Scanning dashboard alongside other SAST alerts.

PR comment on findings

To get inline PR comments when secrets are found, add the GITHUB_TOKEN environment variable and ensure your repository has the Security tab enabled:

      - name: Run Gitleaks with PR annotations
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITLEAKS_ENABLE_COMMENTS: true

Step 7 — Handling True Positives (Rotating Leaked Secrets)

When Gitleaks finds a real secret in history, rotation is the only safe response — removing it from history is not enough. Even if you rewrite history, the secret may already be in other clones, forks, or CI caches.

Immediate response checklist:

  1. Revoke / rotate the secret immediately — treat it as compromised
  2. Check access logs — review the service's audit logs for unauthorized use since the commit timestamp
  3. Remove from history (stops future exposure, does not undo past exposure):
    # Using git-filter-repo (recommended over BFG)
    pip install git-filter-repo
    git filter-repo --path config/settings.py --invert-paths  # remove file entirely
    # OR replace the secret value across all history:
    git filter-repo --replace-text <(echo 'ghp_realtoken==>REDACTED')
  4. Force-push all branches and notify collaborators to re-clone
  5. Add the fingerprint to .gitleaks.toml allowlist after rotation to suppress historical findings:
    [allowlist]
    regexes = ["OLD_ROTATED_SECRET_VALUE"]

Verification

After configuring Gitleaks, verify everything is working:

# 1. Confirm binary works
gitleaks version
 
# 2. Full history scan — confirm no findings (or review any that appear)
gitleaks git --source . --config .gitleaks.toml --verbose
 
# 3. Test pre-commit hook
echo 'STRIPE_KEY=sk_live_fakeXXXXXXXXXXXXXXXXXXXXXXXX' > /tmp/fake_secret.env
git add /tmp/fake_secret.env
git commit -m "should fail"
# Expected: commit blocked with findings listed
 
# Clean up test
git restore --staged /tmp/fake_secret.env
rm /tmp/fake_secret.env
 
# 4. Confirm GitHub Actions workflow appears in Actions tab after pushing
git push origin main
# → Navigate to Actions → Secret Scanning (Gitleaks) → verify green or review findings

Expected output on a clean repository:

    ○
    │╲
    │ ○
    ○ ░
    ░    gitleaks

3:14PM INF 47 commits scanned.
3:14PM INF scan completed in 320ms
3:14PM INF no leaks found

Troubleshooting

"gitleaks: command not found" in CI

The GitHub Action pulls the binary automatically — this error only occurs with the manual binary approach. Check $PATH or use the full path /usr/local/bin/gitleaks.

Too many false positives

Tune your .gitleaks.toml allowlist by path, regex, or fingerprint. Start with the --verbose flag to see every file scanned, then narrow down which rules are firing on test data.

# Show which rule matched and why
gitleaks git --source . --verbose 2>&1 | grep "RuleID\|File\|Line"

Pre-commit hook not running

Check the hook file has execute permissions and is in the right location:

ls -la .git/hooks/pre-commit
# Should show: -rwxr-xr-x
chmod +x .git/hooks/pre-commit

If using the pre-commit framework, ensure it's installed for the current user:

pre-commit install --overwrite

Scan is very slow on large repositories

Limit the scan scope using --log-opts to scan only recent commits:

# Scan only commits from the last 30 days
gitleaks git --source . --log-opts="--since=30.days.ago"
 
# Or scan only commits since a known-clean tag
gitleaks git --source . --log-opts="v2.0.0..HEAD"

SARIF upload fails in GitHub Actions

Ensure the workflow has security-events: write permission and that GitHub Advanced Security (or free public repo access) is enabled for the repository.

Finding a secret I want to allowlist

Copy the Fingerprint field from the finding output and add it to .gitleaks.toml:

[allowlist]
# Format: <commit>:<file>:<rule>:<line>
stopwords = [
  "a3f2c1d9:config/settings.py:github-pat:14"
]

Summary

Gitleaks provides a practical, low-friction layer of defense against one of the most common sources of credential compromise in software development. By deploying it at three points — pre-commit (developer workstation), CI pipeline (pull request gate), and scheduled history scans (catch regressions) — you close the loop on secret leakage regardless of where in the workflow a mistake occurs.

Key takeaways:

  • Secrets in git history are secrets forever — rotation is the only remedy for a confirmed leak, not history rewriting alone
  • Custom rules in .gitleaks.toml let you enforce organization-specific secret patterns beyond the built-in 150+ rules
  • Pre-commit hooks stop secrets at the source before they ever leave a developer's machine
  • CI/CD integration with SARIF upload surfaces findings in GitHub's Security tab alongside other code scanning results
  • Allowlisting by fingerprint is safer than allowlisting by regex — it targets a specific occurrence rather than suppressing an entire pattern

Combine Gitleaks with a secrets manager (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault) and you eliminate the root cause: secrets no longer need to live in configuration files or environment files that developers might accidentally commit.

#devsecops#git#gitleaks#secrets-management#ci-cd#security-automation

Related Articles

HashiCorp Vault: Centralized Secrets Management for Modern

Deploy and configure HashiCorp Vault to securely store, rotate, and audit secrets across your infrastructure — covering installation, auth methods,...

8 min read

Writing and Deploying YARA Rules for Malware Detection

Learn to write effective YARA rules to identify malware, hunt threats, and scan endpoints for indicators of compromise — from basic syntax to real-world rule sets.

7 min read

OpenSSH Hardening with Certificate-Based Authentication

Replace static SSH keys with a short-lived certificate authority. Harden sshd_config, eliminate lateral-movement risk, and enforce zero-trust access across your Linux fleet.

11 min read
Back to all HOWTOs