The Ultimate Gitleaks Guide

The Ultimate Gitleaks Guide

By Pashalis Laoutaris Category: Developer Tools 51 min read

A Comprehensive Educational Tool for Developers, DevOps Engineers, and Security Professionals

⚠️ Important Update (Mid-2026): Zach Rice, the original creator of Gitleaks, has launched a successor tool called Betterleaks. Gitleaks remains stable and will continue to receive security patches, but active feature development has shifted to Betterleaks. See Section 19 at the end of this guide for full details and a migration guide.

Table of Contents

  1. Introduction to Gitleaks
  2. Understanding Git Security Risks
  3. How Gitleaks Works Under the Hood
  4. Installation Guide
  5. Getting Started: Your First Scan
  6. Command Line Interface Deep Dive
  7. Configuration and Customization
  8. Real-World Usage Scenarios
  9. Integration with Development Workflows
  10. CI/CD Pipeline Integration
  11. Troubleshooting and Debugging
  12. Performance Optimization
  13. Security Best Practices
  14. Advanced Topics and Enterprise Features
  15. Comparison with Other Tools
  16. Case Studies and Examples
  17. Maintenance and Updates
  18. Resources and Community
  19. Betterleaks: The Next Generation

1. Introduction to Gitleaks

What is Gitleaks?

Gitleaks is a powerful, open-source static analysis tool designed to detect and prevent secrets (API keys, passwords, tokens, certificates) from being exposed in Git repositories. Developed in Go, it provides fast, accurate scanning capabilities that integrate seamlessly into modern development workflows.

Current Status (Mid-2026)

The latest stable version of Gitleaks is v8.30.1 (released March 21, 2026), built with Go 1.24. It is considered feature complete by its original author, Zach Rice. Future releases will focus on security patches only. For new features and continued active development, see Section 19: Betterleaks of this guide.

Why Gitleaks Matters

The Problem:

  • 1 in 4 repositories contains exposed secrets (GitHub Secret Scanner Report 2025)
  • Average cost of a data breach: $4.88 million (IBM Security Report 2025)
  • Stolen credentials remain the #1 initial access vector for breaches (Verizon DBIR 2025)
  • AWS keys pushed to public repos can be scraped and weaponized by automated bots within minutes

The Solution: Gitleaks provides proactive security by scanning your entire Git history, current files, and incoming changes for sensitive information before it becomes a vulnerability.

Key Features Overview

FeatureBeginner BenefitExpert Benefit
Historical ScanningChecks all past commits automaticallyDeep forensic analysis of repository history
Real-time DetectionPrevents secrets in new commitsIntegrates with advanced CI/CD pipelines
Custom RulesPre-built patterns work out-of-the-boxHighly customizable for specific environments
Multiple Output FormatsEasy-to-read reportsMachine-readable JSON/SARIF for automation
Archive ScanningFinds secrets inside zip/tar filesConfigurable recursion depth
Automatic DecodingCatches base64/hex-encoded secretsConfigurable decode depth
Composite RulesN/A for beginnersProximity-aware multi-rule matching (v8.28+)

Who Should Use Gitleaks?

  • Developers: Prevent accidental secret commits
  • DevOps Engineers: Automate security in CI/CD pipelines
  • Security Teams: Audit and monitor repository security
  • Open Source Maintainers: Protect community projects
  • Compliance Officers: Meet security standards and regulations

2. Understanding Git Security Risks

Common Types of Exposed Secrets

API Keys and Tokens

# Examples of commonly leaked secrets:
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
STRIPE_SECRET_KEY=sk_test_BQokikJOvBiI2HlWgH4olfQ2
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Database Credentials

# Database connection strings
DATABASE_URL=postgres://user:password123@localhost:5432/mydb
MONGO_URI=mongodb://admin:secret@cluster.mongodb.net/database

Private Keys

# SSH and SSL certificates
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----

Real-World Impact Examples

Case Study: Travis CI (2021)

  • Issue: Exposed AWS credentials in build logs
  • Impact: Unauthorized access to customer data
  • Lesson: Even temporary exposure can lead to breaches

Case Study: Uber (2016)

  • Issue: AWS keys stored in private GitHub repository
  • Impact: $148 million fine, 57 million user records compromised
  • Lesson: Private repositories are not immune to breaches

Git-Specific Vulnerabilities

The History Problem

# Even if you remove secrets, they remain in history
git log --oneline --all
abc1234 Remove API keys  # <- Keys still in git history!
def5678 Add API integration

Branch and Tag Exposure

# Secrets might exist in forgotten branches
git branch -a
  main
  feature/old-implementation  # <- Might contain secrets
  remotes/origin/dev

3. How Gitleaks Works Under the Hood

Architecture Overview

Git RepositoryGitleaks EngineSecurity Report
• Commits• Pattern Match• Findings
• Branches• Rule Engine• Severity
• Tags• File Analysis• Remediation
• Staged Files• Archive Scan

Flow Direction: Git Repository → Gitleaks Engine → Security Report

Scanning Process Deep Dive

Phase 1: Repository Analysis

# Gitleaks analyzes multiple sources:
1. Git Object Database (.git/objects/)
2. Working Directory Files
3. Staged Changes (git index)
4. Commit History (git log -p)
5. All Branches and Tags
6. Archive files (zip, tar.gz) — when --max-archive-depth is set

Phase 2: Pattern Matching

// Simplified pattern matching logic
type Rule struct {
    Pattern     string `json:"pattern"`
    Description string `json:"description"`
    Entropy     float64 `json:"entropy"`
    Keywords    []string `json:"keywords"`
}

Phase 3: Context Analysis

  • Entropy Analysis: Detects high-entropy strings (randomness)
  • Keyword Detection: Looks for context clues like “password”, “secret”
  • File Path Analysis: Considers file types and locations
  • Commit Message Scanning: Checks for accidental disclosures
  • Composite Rules (v8.28+): Multi-rule proximity matching for higher precision

Detection Algorithms

Regex Pattern Matching

# AWS Access Key ID Pattern
(?i)aws[_-]?access[_-]?key[_-]?id["'`\s]*[=:]["'`\s]*[A-Z0-9]{20}

# Generic API Key Pattern
(?i)api[_-]?key["'`\s]*[=:]["'`\s]*[A-Za-z0-9]{16,}

Entropy-Based Detection

# High entropy strings often indicate secrets
def calculate_entropy(string):
    probability = [float(string.count(c)) / len(string) for c in set(string)]
    entropy = -sum([p * math.log(p) / math.log(2.0) for p in probability])
    return entropy

# Strings with entropy > 4.5 are flagged as potential secrets

4. Installation Guide

System Requirements

ComponentRequirement
Operating SystemLinux (Ubuntu 18.04+), macOS (10.15+), Windows (WSL2)
ArchitectureAMD64, ARM64, 386
MemoryMinimum 512MB, Recommended 2GB+
Go Version1.24+ (only for source builds)

Installation Methods

Linux/macOS:

# Download latest release (v8.30.1 as of mid-2026)
curl -sSfL https://raw.githubusercontent.com/gitleaks/gitleaks/master/scripts/install.sh | sh -s -- -b /usr/local/bin

# Or manual download
GITLEAKS_VERSION="8.30.1"
OS="linux"  # or "darwin" for macOS
ARCH="amd64"  # or "arm64"

curl -L "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_${OS}_${ARCH}.tar.gz" -o gitleaks.tar.gz
tar -xzf gitleaks.tar.gz
sudo mv gitleaks /usr/local/bin/

Windows (PowerShell):

# Using Chocolatey
choco install gitleaks

# Or manual download
$version = "8.30.1"
$url = "https://github.com/gitleaks/gitleaks/releases/download/v$version/gitleaks_${version}_windows_amd64.zip"
Invoke-WebRequest -Uri $url -OutFile "gitleaks.zip"
Expand-Archive -Path "gitleaks.zip" -DestinationPath "."

Method 2: Package Managers

macOS - Homebrew:

brew install gitleaks

Ubuntu/Debian:

# Add repository
echo "deb [trusted=yes] https://apt.fury.io/gitleaks/ /" | sudo tee -a /etc/apt/sources.list.d/gitleaks.list
sudo apt update
sudo apt install gitleaks

Arch Linux:

yay -S gitleaks

Method 3: Docker

# Pull official image
docker pull zricethezav/gitleaks:latest

# Create alias for easy use
echo 'alias gitleaks="docker run --rm -v $(pwd):/repo zricethezav/gitleaks:latest"' >> ~/.bashrc
source ~/.bashrc

Method 4: From Source

# Prerequisites: Go 1.24+
git clone https://github.com/gitleaks/gitleaks.git
cd gitleaks
make build
sudo mv gitleaks /usr/local/bin/

Verification and Testing

# Verify installation
gitleaks version

# Expected output:
# 8.30.1

# Test basic functionality
echo "api_key = 'sk_test_123456789abcdef'" > test_file.txt
gitleaks dir --source . --verbose

Note: Earlier versions of this command used gitleaks detect --source . --no-git --verbose. As of v8.19.0+, the filesystem-only scan moved to its own dir subcommand — see Section 5 for the full command-naming change.

Troubleshooting Installation

Common Issues and Solutions

Issue: Command not found
# Solution: Add to PATH
export PATH=$PATH:/usr/local/bin
echo 'export PATH=$PATH:/usr/local/bin' >> ~/.bashrc
Issue: Permission denied
# Solution: Fix permissions
chmod +x /usr/local/bin/gitleaks
Issue: SSL certificate problems
# Solution: Update certificates
sudo apt-get update && sudo apt-get install ca-certificates

5. Getting Started: Your First Scan

Updated CLI — Important Command Changes (v8.19+)

Note: The detect and protect commands were deprecated in v8.19.0. They still work but are hidden from --help. The modern equivalents are below — the rest of this guide uses the current syntax throughout.

Old Command (deprecated, still works)New Command
gitleaks detectgitleaks git
gitleaks detect --no-gitgitleaks dir
gitleaks protect --stagedgit diff --cached | gitleaks stdin

Basic Scanning Commands

Scan a Git Repository

# Scan current repository (full git history)
gitleaks git

# Scan with verbose output
gitleaks git --verbose

# Scan specific directory/repo
gitleaks git --source /path/to/repo

Scan a Directory (no git context)

# Scan filesystem only — current file state, no history
gitleaks dir --source /path/to/files
gitleaks dir --source .

Scan from stdin

# Scan staged changes (replaces 'protect --staged')
git diff --cached | gitleaks stdin

# Scan a file via stdin
cat config.js | gitleaks stdin

Understanding the Output

Clean Scan Result:

$ gitleaks git
INFO[0000] No leaks found

Secrets Found:

{
  "Description": "Generic API Key",
  "StartLine": 12,
  "EndLine": 12,
  "StartColumn": 15,
  "EndColumn": 47,
  "Match": "sk_test_4eC39HqLyjWDarjtT1zdp7dc",
  "Secret": "sk_test_4eC39HqLyjWDarjtT1zdp7dc",
  "File": "config/stripe.js",
  "SymlinkFile": "",
  "Commit": "a1b2c3d4e5f6",
  "Entropy": 4.2,
  "Author": "developer@example.com",
  "Email": "developer@example.com",
  "Date": "2026-01-15T10:30:00Z",
  "Message": "Add Stripe integration",
  "Tags": [],
  "RuleID": "stripe-access-token",
  "Fingerprint": "a1b2c3d4e5f6:config/stripe.js:stripe-access-token:12"
}

Note: The Fingerprint field (used for .gitleaksignore, covered in Section 7) was added in a later release and may not appear in output from very old Gitleaks versions.

Creating Your First Test Repository

# Create test repository with intentional secrets
mkdir gitleaks-demo
cd gitleaks-demo
git init

# Add various types of secrets
cat << 'EOF' > config.js
const config = {
  aws: {
    accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
    secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
  },
  stripe: {
    secret: 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
  },
  database: {
    url: 'mongodb://admin:password123@localhost:27017/app'
  }
}
EOF

# Create .env file
cat << 'EOF' > .env
API_KEY=super_secret_api_key_12345
JWT_SECRET=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
DATABASE_PASSWORD=my_secure_password_789
EOF

# Add SSH key
cat << 'EOF' > id_rsa
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAyXgUkQGHyrlRQ1Z9cYRtRbKtKKg0v2zLZY8zzUyZzZ...
-----END RSA PRIVATE KEY-----
EOF

git add .
git commit -m "Initial commit with secrets"

# Now scan
gitleaks git

Interpreting Results for Beginners

Understanding Severity Levels

  • High: Confirmed secrets (API keys, passwords)
  • Medium: Likely secrets (high entropy strings with keywords)
  • Low: Potential secrets (patterns that might be false positives)

Common False Positives

# These might be flagged but are usually safe:
test_api_key = "fake_key_for_testing"      # Test data
PASSWORD_HASH = "bcrypt_hashed_value"       # Already hashed
API_ENDPOINT = "https://api.example.com"    # URLs, not keys

Your First Security Fix

# 1. Identify the secret
gitleaks git --report-format json --report-path findings.json

# 2. Remove from current files
sed -i 's/sk_test_4eC39HqLyjWDarjtT1zdp7dc/process.env.STRIPE_KEY/g' config.js

# 3. Add to environment variables
echo "STRIPE_KEY=sk_test_4eC39HqLyjWDarjtT1zdp7dc" >> .env.example
echo ".env" >> .gitignore

# 4. Clean git history using git-filter-repo (preferred over filter-branch)
# git-filter-repo is the officially recommended replacement for the
# deprecated 'git filter-branch' — faster, safer, and actively maintained.
pip install git-filter-repo
git filter-repo --path config.js --invert-paths

# 5. Verify fix
gitleaks git

Note: The original version of this guide recommended git filter-branch --force --index-filter ... --prune-empty --tag-name-filter cat -- --all. The Git project itself now discourages filter-branch for performance and safety reasons. git-filter-repo is the current best practice and is what’s used above.


6. Command Line Interface Deep Dive

Current Command Structure

gitleaks [command]

Available Commands:
  completion  Generate autocompletion script
  dir         Scan directories or files for secrets
  git         Scan git repositories for secrets
  help        Help about any command
  stdin       Detect secrets from stdin
  version     Display gitleaks version

gitleaks git (replaces detect)

Primary scanning command for git repositories.

# Basic usage
gitleaks git [flags] [path]

# Common flags
--source string          # Path to git repo (default: current directory)
--config string          # Custom configuration file
--report-format string   # Output format: json, csv, junit, sarif, template
--report-path string     # Output file path
--verbose               # Enable verbose logging
--redact               # Hide secret values in output
--exit-code int        # Exit code when leaks found (default: 1)
--log-opts string      # Options passed to git log -p
--baseline-path string # Ignore previously known findings
--max-decode-depth int  # Recursive decoding depth (hex, base64, URL) (default: 0)
--max-archive-depth int # Recursive archive scanning depth (default: 0)
--enable-rule strings   # Enable only specific rules by ID
--no-banner             # Suppress the banner

Examples:

# Scan remote repository
gitleaks git --source https://github.com/user/repo.git

# Generate JSON report
gitleaks git --report-format json --report-path security-report.json

# Scan without git (filesystem only) — use the 'dir' command instead
gitleaks dir --source /path/to/files

# Redact secrets in output
gitleaks git --redact --verbose

# Scan a specific commit range
gitleaks git --log-opts="--since='2026-01-01' --until='2026-01-31'"

# Scan specific branches
gitleaks git --log-opts="origin/main..origin/develop"

# Scan with custom git log options
gitleaks git --log-opts="--author='developer@example.com' --since='1 week ago'"

# Generate SARIF report for GitHub Advanced Security
gitleaks git --report-format sarif --report-path results.sarif

# Scan with automatic base64/hex decoding (2 levels deep)
gitleaks git --max-decode-depth 2

# Scan archives inside the repo (zip, tar.gz)
gitleaks git --max-archive-depth 3

# Only alert on new findings since last scan
gitleaks git --baseline-path previous-report.json --report-format json --report-path new-report.json

gitleaks dir (replaces detect --no-git)

Scans directories and files without git context — only the current state on disk, not history. Useful for non-git folders or for the fastest possible scan when history doesn’t matter.

gitleaks dir --source /path/to/directory
gitleaks dir --source . --report-format json --report-path findings.json

gitleaks stdin (replaces protect --staged)

Scans content piped from stdin. This is the modern way to wire Gitleaks into pre-commit hooks, replacing the old protect command.

# Modern pre-commit approach
git diff --cached | gitleaks stdin --no-banner

# Scan a specific file
cat sensitive_file.txt | gitleaks stdin

Git Hook Integration (legacy protect syntax, still works but deprecated):

# Old style — still functions but hidden from --help
echo '#!/bin/sh\ngitleaks protect --staged --verbose' > .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

# Test the hook
echo "api_key = 'secret123'" > test.txt
git add test.txt
git commit -m "Test commit"  # Will be blocked by gitleaks

See Section 9 for the full, current pre-commit hook setup using stdin.

Output Formats and Reporting

JSON Format (Default):

gitleaks git --report-format json --report-path findings.json

CSV Format:

gitleaks git --report-format csv --report-path findings.csv

JUnit XML (for CI integration):

gitleaks git --report-format junit --report-path test-results.xml

SARIF (Security Analysis Results Interchange Format):

gitleaks git --report-format sarif --report-path results.sarif

Custom template:

gitleaks git --report-format template --report-template my-template.tmpl --report-path report.html

Environment Variables

# Configuration via environment variables
export GITLEAKS_CONFIG="/path/to/custom/config.toml"
# Or supply config content directly:
export GITLEAKS_CONFIG_TOML="$(cat gitleaks.toml)"

export GITLEAKS_LOG_LEVEL="debug"

# GitHub integration
export GITHUB_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxx"
gitleaks git --source https://github.com/private/repo.git

Note: The GITLEAKS_VERBOSE environment variable from older versions has been removed; use the --verbose flag instead.

Debug and Logging Options

# Enable debug logging
gitleaks git --verbose --log-level debug

# Capture all logs to file
gitleaks git --verbose 2>&1 | tee gitleaks-debug.log

# Performance profiling
gitleaks git --verbose --log-opts="--stat" --source large-repo/

7. Configuration and Customization

Default Configuration

Gitleaks ships with 160+ built-in rules covering:

# Built-in rule categories (v8.30.x):
- AWS credentials (access keys, session tokens, secret keys)
- Google Cloud Platform keys
- Azure credentials
- GitHub tokens (classic, fine-grained)
- GitLab tokens
- Slack tokens and webhooks
- Stripe live and test keys
- Generic API keys
- Database connection strings
- SSH private keys
- JWT tokens
- Airtable Personal Access Tokens (added v8.30.0)
- Looker client ID and secret (added v8.30.0)
- And 140+ more patterns

Custom Configuration File

Basic Configuration Structure

# gitleaks.toml
title = "Custom Gitleaks Configuration"

[[rules]]
id = "custom-api-key"
description = "Custom API Key Pattern"
regex = '''(?i)custom[_-]?api[_-]?key["'`\s]*[=:]["'`\s]*[A-Za-z0-9]{32}'''
keywords = ["custom_api", "custom-key"]

[[rules]]
id = "database-password"
description = "Database Password"
regex = '''(?i)db[_-]?pass(word)?["'`\s]*[=:]["'`\s]*[A-Za-z0-9@#$%^&*!]{8,}'''
keywords = ["db_pass", "database_password"]

Extending the Default Configuration

# gitleaks.toml
title = "Extended Gitleaks Configuration"

[extend]
# Extend the built-in default ruleset (do not use useDefault and path together)
useDefault = true

[[rules]]
id = "mycompany-api-token"
description = "MyCompany Internal API Token"
regex = '''(?i)mycompany[_-]?token["'`\s]*[=:]["'`\s]*[A-Za-z0-9]{40}'''
keywords = ["mycompany", "internal_token"]
secretGroup = 0
entropy = 4.0

Composite Rules (v8.28+ Feature)

Composite rules allow you to require multiple patterns to match in proximity before flagging a finding. This dramatically reduces false positives — for example, only flagging an AWS access key if a matching secret key also appears nearby.

[[rules]]
id = "aws-access-key-with-secret"
description = "AWS Access Key paired with Secret"
regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}'''
keywords = ["AKIA", "ASIA", "AROA"]
secretGroup = 0

  # Require the secret key to be within 5 lines of the access key
  [[rules.required]]
  id = "aws-secret-access-key"
  withinLines = 5

Advanced Rule Configuration

[[rules]]
id = "stripe-secret-key"
description = "Stripe Secret Key"
regex = '''sk_(test|live)_[A-Za-z0-9]{24}'''
keywords = ["stripe", "sk_"]
secretGroup = 0  # Which regex group contains the secret
entropy = 3.5    # Minimum entropy threshold
allowlist = [    # Patterns to ignore
  "sk_test_example",
  "sk_test_fake"
]

[allowlist]
description = "Global allowlist"
regexes = [
  '''fake_key_for_testing''',
  '''example\.com''',
  '''localhost'''
]
paths = [
  "test/",
  "tests/",
  "spec/",
  ".git/"
]
commits = [
  "a1b2c3d4e5f6",  # Ignore specific commits
  "abcdef123456"
]
stopwords = [
  "test",
  "fake",
  "example",
  "dummy"
]

Rule Customization Examples

Organization-Specific API Keys

[[rules]]
id = "mycompany-api-token"
description = "MyCompany Internal API Token"
regex = '''(?i)mycompany[_-]?token["'`\s]*[=:]["'`\s]*[A-Za-z0-9]{40}'''
keywords = ["mycompany", "internal_token"]
secretGroup = 0
entropy = 4.0

# Additional context matching
[[rules]]
id = "internal-service-key"
description = "Internal Service Authentication Key"
regex = '''(?i)(auth|service)[_-]?key["'`\s]*[=:]["'`\s]*([A-Za-z0-9+/]{64}={0,2})'''
keywords = ["auth_key", "service_key", "internal"]
secretGroup = 2

Database Credentials Examples

[[rules]]
id = "postgresql-connection"
description = "PostgreSQL Connection String"
regex = '''postgresql://([^:]+):([^@]+)@([^/]+)/(.+)'''
keywords = ["postgresql", "postgres", "psql"]
secretGroup = 2  # Password group

[[rules]]
id = "mongodb-uri"
description = "MongoDB Connection URI"
regex = '''mongodb://([^:]+):([^@]+)@([^/]+)/(.+)'''
keywords = ["mongodb", "mongo"]
secretGroup = 2

Allowlist Configuration

File-Based Allowlisting

[allowlist]
description = "Files and paths to ignore"
paths = [
  "docs/",
  "examples/",
  "test/fixtures/",
  "vendor/",
  "node_modules/",
  ".git/",
  "*.md",
  "*.txt"
]

Pattern-Based Allowlisting

[allowlist]
description = "Patterns that are safe to ignore"
regexes = [
  # Test data
  '''(?i)test[_-]?(key|token|secret)''',
  '''(?i)(fake|dummy|example)[_-]?(key|token)''',

  # Common false positives
  '''[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}''', # UUIDs
  '''https?://[^/]+/[^/]+''',  # URLs
  '''localhost:[0-9]+''',      # Local addresses
]

Commit-Based Allowlisting

[allowlist]
description = "Known safe commits"
commits = [
  "abc123def456",  # Initial test data commit
  "789xyz012abc"   # Documentation update with examples
]
stopwords = [
  "test",
  "fake",
  "example",
  "dummy"
]

.gitleaksignore File

You can suppress specific findings by adding their fingerprints to a .gitleaksignore file at the root of your repo. The fingerprint is included in every finding’s output (see the Fingerprint field in Section 5).

# .gitleaksignore
a1b2c3d4e5f6:config/test-fixtures.js:generic-api-key:42
789xyz012abc:docs/examples.md:stripe-access-token:15

You can also inline-suppress on a per-line basis directly in code:

const EXAMPLE_KEY = "sk_test_fake123"; // gitleaks:allow

Add // gitleaks:allow (or # gitleaks:allow for Python/Bash/YAML) at the end of a line to suppress findings for that specific line — useful for one-off documented exceptions without editing the global allowlist.

Configuration Best Practices

Layered Configuration Strategy

# 1. Base configuration (organization-wide)
gitleaks.base.toml

# 2. Project-specific overrides
gitleaks.toml

# 3. Local development
gitleaks.local.toml (gitignored)

Configuration Validation

# Test configuration against known samples
gitleaks dir --config gitleaks.toml --source test-samples/

# Validate configuration syntax
gitleaks dir --config gitleaks.toml --source /dev/null

Performance Tuning

# Optimize for large repositories
[performance]
max_target_megabytes = 4    # Skip large files
timeout = "30s"             # Per-file timeout
workers = 4                 # Parallel processing

8. Real-World Usage Scenarios

Scenario 1: New Project Security Audit

Situation: You’ve inherited a legacy codebase and need to audit it for secrets.

Step-by-Step Process:

# 1. Clone and analyze repository structure
git clone https://github.com/company/legacy-app.git
cd legacy-app

# Check repository size and history
git log --oneline | wc -l    # Number of commits
git count-objects -vH        # Repository size
git branch -a                # All branches
git tag                      # All tags

# 2. Run comprehensive scan
gitleaks git --verbose --report-format json --report-path audit-report.json

# 3. Analyze findings by severity
jq '.[] | select(.Entropy > 4.5)' audit-report.json | jq -s 'length'  # High entropy secrets
jq '.[] | group_by(.RuleID) | map({rule: .[0].RuleID, count: length})' audit-report.json

# 4. Generate summary report
cat << 'EOF' > generate-summary.sh
#!/bin/bash
echo "# Security Audit Report - $(date)"
echo ""
echo "## Repository Stats"
echo "- Commits: $(git log --oneline | wc -l)"
echo "- Branches: $(git branch -a | wc -l)"
echo "- Contributors: $(git log --format='%an' | sort -u | wc -l)"
echo ""
echo "## Secrets Found"
jq -r '.[] | "- \(.RuleID): \(.File):\(.StartLine)"' audit-report.json
EOF

chmod +x generate-summary.sh
./generate-summary.sh > audit-summary.md

Scenario 2: Pre-Deployment Security Check

Situation: Implementing security gates before production deployments.

# 1. Create deployment security script
cat << 'EOF' > deploy-security-check.sh
#!/bin/bash
set -e

echo "🔍 Running pre-deployment security checks..."

# Scan current branch
echo "Scanning current branch for secrets..."
if ! gitleaks git --redact --exit-code 0; then
    echo "❌ Secrets detected! Deployment blocked."
    echo "Please remove secrets before deploying."
    exit 1
fi

# Scan only changes since last deployment
LAST_DEPLOY=$(git tag --list "deploy-*" --sort=-version:refname | head -1)
if [ -n "$LAST_DEPLOY" ]; then
    echo "Scanning changes since $LAST_DEPLOY..."
    git diff --name-only $LAST_DEPLOY..HEAD | xargs -r -I{} gitleaks dir --source {}
fi

# Check for common security issues
echo "Checking for common security issues..."
grep -r "TODO.*security\|FIXME.*security\|XXX.*security" . --exclude-dir=.git || true

echo "✅ Security checks passed! Safe to deploy."
EOF

chmod +x deploy-security-check.sh

# 2. Integrate with deployment pipeline
# Add to CI/CD pipeline or run manually before deployment
./deploy-security-check.sh

Scenario 3: Developer Onboarding

Situation: Setting up new developers with proper secret management practices.

# 1. Create developer setup script
cat << 'EOF' > setup-developer-environment.sh
#!/bin/bash

echo "🚀 Setting up secure development environment..."

# Install gitleaks
if ! command -v gitleaks &> /dev/null; then
    echo "Installing gitleaks..."
    curl -sSfL https://raw.githubusercontent.com/gitleaks/gitleaks/master/scripts/install.sh | sh -s -- -b /usr/local/bin
fi

# Setup pre-commit hook using the modern stdin approach
echo "Setting up pre-commit hook..."
cat << 'HOOK' > .git/hooks/pre-commit
#!/bin/sh
echo "🔍 Scanning for secrets before commit..."
git diff --cached | gitleaks stdin --no-banner
if [ $? -ne 0 ]; then
    echo ""
    echo "❌ Commit blocked: Secrets detected!"
    echo "Please remove secrets and try again."
    echo ""
    echo "Need help? Check the security guidelines:"
    echo "https://company.wiki/security/secret-management"
    exit 1
fi
echo "✅ No secrets detected. Commit allowed."
HOOK

chmod +x .git/hooks/pre-commit

# Create template environment file
echo "Creating environment template..."
cat << 'TEMPLATE' > .env.example
# Copy this file to .env and fill in your values
# Never commit the .env file to git!

DATABASE_URL=your_database_url_here
API_KEY=your_api_key_here
JWT_SECRET=your_jwt_secret_here
TEMPLATE

# Ensure .env is gitignored
if ! grep -q "^\.env$" .gitignore; then
    echo ".env" >> .gitignore
fi

echo "✅ Developer environment setup complete!"
echo ""
echo "📝 Next steps:"
echo "1. Copy .env.example to .env"
echo "2. Fill in your local environment values"
echo "3. Read security guidelines: https://company.wiki/security"
EOF

chmod +x setup-developer-environment.sh

Scenario 4: Open Source Project Maintenance

Situation: Maintaining security in a public open-source project.

# 1. Create contributor security guidelines

cat << 'EOF' > SECURITY.md

# Security Guidelines

## Reporting Secrets

If you discover secrets in this repository:

1. **DO NOT** create a public issue
2. Email security@project.org immediately
3. Include the commit hash and file location

## For Contributors

### Before Contributing

```bash
# Install and run gitleaks
gitleaks git --source .
```

### Pre-commit Setup

```bash
# Automatic secret detection
echo '#!/bin/sh\ngit diff --cached | gitleaks stdin' > .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
```

## Safe Practices

- Use environment variables for secrets
- Never commit `.env` files
- Use placeholders in documentation
- Regular security audits with `gitleaks git`
  EOF
# 2. Setup GitHub Actions for automated scanning
mkdir -p .github/workflows
cat << 'EOF' > .github/workflows/security.yml
name: Security Scan

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  gitleaks:
    name: Secrets Detection
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Run Gitleaks
        id: gitleaks
        uses: gitleaks/gitleaks-action@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
      - name: Upload SARIF file
        if: failure()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: results.sarif
EOF

# 3. Community security monitoring
cat << 'EOF' > scripts/community-security-check.sh
#!/bin/bash
# Regular security audit for open source maintainers

echo "🔍 Community Security Audit - $(date)"

# Check all branches for secrets
for branch in $(git branch -r | grep -v HEAD); do
    echo "Scanning $branch..."
    git checkout $branch 2>/dev/null
    gitleaks git --report-format json --report-path "scan-$(basename $branch).json"
done

# Return to main branch
git checkout main

# Aggregate results
echo "📊 Generating community security report..."
jq -s 'map(.[]) | group_by(.RuleID) | map({rule: .[0].RuleID, occurrences: length, branches: map(.Branch) | unique})' scan-*.json > community-security-report.json

rm scan-*.json

echo "✅ Community security audit complete!"
EOF

chmod +x scripts/community-security-check.sh

Note: The GitHub Action above uses gitleaks-action@v3, not the older v2. See Section 10 for why this matters — v2 stops working September 16, 2026.

Scenario 5: Enterprise Multi-Repository Audit

Situation: Security team needs to audit hundreds of repositories across the organization.

# 1. Create enterprise audit script
cat << 'EOF' > enterprise-audit.sh
#!/bin/bash

# Enterprise-wide repository security audit
GITHUB_ORG="your-org"
OUTPUT_DIR="audit-results-$(date +%Y%m%d)"
mkdir -p "$OUTPUT_DIR"

echo "🏢 Starting enterprise security audit..."

# Get all repositories
gh repo list "$GITHUB_ORG" --limit 1000 --json name,url > repositories.json

# Function to audit a single repository
audit_repository() {
    local repo_name="$1"
    local repo_url="$2"
    local audit_dir="$OUTPUT_DIR/$repo_name"

    echo "Auditing $repo_name..."

    # Clone repository
    git clone --depth 50 "$repo_url" "$audit_dir" 2>/dev/null || {
        echo "Failed to clone $repo_name" >> "$OUTPUT_DIR/failed-clones.log"
        return 1
    }

    cd "$audit_dir"

    # Run gitleaks scan
    gitleaks git --report-format json --report-path "../${repo_name}-findings.json" --redact

    # Collect metadata
    cat << METADATA > "../${repo_name}-metadata.json"
{
    "repository": "$repo_name",
    "scan_date": "$(date -Iseconds)",
    "commit_count": $(git rev-list --count HEAD),
    "contributor_count": $(git log --format='%an' | sort -u | wc -l),
    "last_commit": "$(git log -1 --format='%H %aI %s')",
    "branches": $(git branch -r | wc -l),
    "size_mb": $(du -sm . | cut -f1)
}
METADATA

    cd ..
    rm -rf "$audit_dir"
}

export -f audit_repository
export OUTPUT_DIR

# Parallel processing of repositories
jq -r '.[] | "\(.name) \(.url)"' repositories.json | \
    parallel --colsep ' ' -j 5 audit_repository {1} {2}

# Generate summary report
echo "📈 Generating enterprise summary..."
cat << 'SUMMARY' > generate-enterprise-summary.py
#!/usr/bin/env python3
import json
import glob
from collections import defaultdict
from datetime import datetime

findings_files = glob.glob(f"{OUTPUT_DIR}/*-findings.json")
metadata_files = glob.glob(f"{OUTPUT_DIR}/*-metadata.json")

# Aggregate findings
total_secrets = 0
secrets_by_type = defaultdict(int)
repos_with_secrets = 0
high_risk_repos = []

for findings_file in findings_files:
    try:
        with open(findings_file) as f:
            findings = json.load(f)
            if findings:
                repos_with_secrets += 1
                total_secrets += len(findings)

                high_entropy_secrets = [f for f in findings if f.get('Entropy', 0) > 4.5]
                if high_entropy_secrets:
                    repo_name = findings_file.split('/')[-1].replace('-findings.json', '')
                    high_risk_repos.append({
                        'repo': repo_name,
                        'high_entropy_count': len(high_entropy_secrets)
                    })

                for finding in findings:
                    secrets_by_type[finding['RuleID']] += 1
    except:
        continue

# Generate report
report = {
    'scan_date': datetime.now().isoformat(),
    'summary': {
        'total_repositories_scanned': len(metadata_files),
        'repositories_with_secrets': repos_with_secrets,
        'total_secrets_found': total_secrets,
        'high_risk_repositories': len(high_risk_repos)
    },
    'secrets_by_type': dict(secrets_by_type),
    'high_risk_repositories': sorted(high_risk_repos, key=lambda x: x['high_entropy_count'], reverse=True)[:10]
}

with open(f'{OUTPUT_DIR}/enterprise-summary.json', 'w') as f:
    json.dump(report, f, indent=2)

print("✅ Enterprise audit complete!")
print(f"📁 Results in {OUTPUT_DIR}/")
print(f"📊 {report['summary']['repositories_with_secrets']} of {report['summary']['total_repositories_scanned']} repositories have secrets")
SUMMARY

python3 generate-enterprise-summary.py
EOF

chmod +x enterprise-audit.sh

9. Integration with Development Workflows

The simplest and most maintainable way to run Gitleaks as a pre-commit hook today is via the pre-commit framework, which Gitleaks officially supports:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.30.1
    hooks:
      - id: gitleaks # uses the native binary
      # - id: gitleaks-docker  # alternative: uses the Docker image instead

Install and activate:

pip install pre-commit
pre-commit install
# Auto-update to the latest version later
pre-commit autoupdate

To skip the check in an emergency (use sparingly):

SKIP=gitleaks git commit -m "skip gitleaks check"

IDE Integration

Visual Studio Code Extension

// .vscode/settings.json
{
  "gitleaks.enable": true,
  "gitleaks.configPath": "./gitleaks.toml",
  "gitleaks.scanOnSave": true,
  "gitleaks.showInline": true
}

Vim/Neovim Integration

-- init.lua or .vimrc equivalent
vim.api.nvim_create_autocmd("BufWritePost", {
  pattern = "*",
  callback = function()
    local file = vim.fn.expand("%:p")
    local cmd = string.format("gitleaks dir --source %s", file)
    local result = vim.fn.system(cmd)
    if vim.v.shell_error ~= 0 then
      vim.api.nvim_err_writeln("Gitleaks: Secrets detected!")
      print(result)
    end
  end
})

Note: The original Neovim snippet called gitleaks protect --staged --source %s, mixing a stdin-style hook with a file argument. The corrected version above uses gitleaks dir to scan the saved file directly, which is the right tool for a “scan this one file on save” use case.

Git Hooks Integration

Pre-commit Hook (Comprehensive, Manual Version)

#!/bin/sh
# .git/hooks/pre-commit

set -e

echo "🔍 Running pre-commit security checks..."

# Check for gitleaks installation
if ! command -v gitleaks &> /dev/null; then
    echo "❌ Gitleaks not found. Please install it first."
    echo "Visit: https://github.com/gitleaks/gitleaks#installation"
    exit 1
fi

# Run gitleaks on staged files via stdin (replaces 'protect --staged')
echo "Scanning staged files for secrets..."
if ! git diff --cached | gitleaks stdin --no-banner; then
    echo ""
    echo "❌ COMMIT BLOCKED: Secrets detected in staged files!"
    echo ""
    echo "🔧 How to fix:"
    echo "1. Remove the secrets from your code"
    echo "2. Use environment variables instead"
    echo "3. Add secrets to .env (make sure .env is in .gitignore)"
    echo "4. Consider using a secret management service"
    echo ""
    echo "📚 Learn more: https://gitleaks.io/docs/"
    exit 1
fi

# Additional checks for common issues
echo "Checking for common security issues..."

# Check if .env files are accidentally staged
if git diff --cached --name-only | grep -E "^\.env$|^\.env\..*$" | grep -v "\.env\.example$\|\.env\.template$"; then
    echo "❌ COMMIT BLOCKED: .env files should not be committed!"
    echo "Run: git reset HEAD .env"
    exit 1
fi

# Check for TODO/FIXME security comments
if git diff --cached | grep -E "TODO.*security|FIXME.*security|XXX.*security" >/dev/null; then
    echo "⚠️  Warning: Security-related TODO/FIXME comments found"
    echo "Please address these before production deployment"
fi

echo "✅ Pre-commit security checks passed!"

Note: If you’d rather not maintain this script by hand, the pre-commit framework hook shown above handles the Gitleaks portion automatically and is easier to keep updated — but the custom script here is still useful if you want the extra .env and TODO/FIXME checks bundled in.

Pre-push Hook

#!/bin/sh
# .git/hooks/pre-push

protected_branch='main'
current_branch=$(git symbolic-ref HEAD | sed 's!refs\/heads\/!!')

if [ $protected_branch = $current_branch ]; then
    echo "🔍 Running comprehensive security scan before pushing to main..."

    # Full repository scan
    if ! gitleaks git --verbose; then
        echo "❌ PUSH BLOCKED: Secrets found in repository history!"
        echo "Please clean the repository history before pushing to main."
        exit 1
    fi

    echo "✅ Security scan passed for main branch push!"
fi

Development Environment Setup

Docker Development Container

# .devcontainer/Dockerfile
FROM mcr.microsoft.com/devcontainers/base:ubuntu

# Install gitleaks
RUN curl -sSfL https://raw.githubusercontent.com/gitleaks/gitleaks/master/scripts/install.sh | sh -s -- -b /usr/local/bin

# Install other security tools
RUN apt-get update && apt-get install -y \
    git \
    jq \
    parallel \
    && rm -rf /var/lib/apt/lists/*

# Setup git hooks automatically
COPY setup-hooks.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/setup-hooks.sh

# Configure git to use hooks
RUN git config --global init.templatedir ~/.git-template
RUN mkdir -p ~/.git-template/hooks
RUN setup-hooks.sh ~/.git-template/hooks
// .devcontainer/devcontainer.json
{
  "name": "Secure Development Environment",
  "build": {
    "dockerfile": "Dockerfile"
  },
  "postCreateCommand": "setup-hooks.sh .git/hooks",
  "customizations": {
    "vscode": {
      "extensions": ["gitleaks.gitleaks", "ms-vscode.vscode-json"]
    }
  }
}

Team Workflow Integration

Pull Request Template

<!-- .github/pull_request_template.md -->

## Security Checklist

Please confirm that you have completed the following:

- [ ] Ran `gitleaks git` and resolved all findings
- [ ] No secrets or credentials are being committed
- [ ] Environment variables are used for configuration
- [ ] `.env` files are properly gitignored
- [ ] No hardcoded URLs, passwords, or API keys

## Changes Made

Brief description of changes...

## Security Impact

- [ ] No security impact
- [ ] Adds new dependencies (list them)
- [ ] Modifies authentication/authorization
- [ ] Changes data handling

## Testing

- [ ] Verified secrets detection is working
- [ ] Tested with local environment variables
- [ ] Confirmed no sensitive data in logs

Code Review Guidelines

# scripts/review-security.sh
#!/bin/bash
# Security-focused code review helper

echo "🔍 Security Review Helper"
echo "========================"

# Check for recent secret-related changes
echo "Recent files with potential secrets:"
git log --since="1 week ago" --name-only --pretty=format: | \
    sort -u | \
    grep -E "\.(env|config|yml|yaml|json)$" | \
    head -10

echo ""
echo "Files to review carefully:"

# Find files that might contain secrets
find . -type f \( -name "*.env*" -o -name "*.config*" -o -name "*.json" -o -name "*.yml" -o -name "*.yaml" \) \
    ! -path "./.git/*" ! -path "./node_modules/*" ! -path "./vendor/*" | \
    head -10

echo ""
echo "Running gitleaks on current branch:"
gitleaks git --verbose --redact

echo ""
echo "✅ Security review helper complete!"

10. CI/CD Pipeline Integration

Important: gitleaks-action@v2 will stop working on September 16, 2026, when Node 20 is removed from GitHub-hosted runners. Migrate to gitleaks-action@v3 now — it requires GitHub Actions runner v2.327.1 or later (the default on all current hosted runners). Every example below uses v3.

GitHub Actions

Basic Integration

# .github/workflows/security.yml
name: Security Scan

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: "0 2 * * 1" # Weekly scan on Mondays at 2 AM

jobs:
  gitleaks:
    name: Secrets Detection
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Run Gitleaks
        uses: gitleaks/gitleaks-action@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
        with:
          config-path: .gitleaks.toml

      - name: Upload SARIF file
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: results.sarif

      - name: Comment PR with results
        if: github.event_name == 'pull_request' && failure()
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '🚨 **Security Alert**: Secrets detected in this PR. Please remove them before merging.'
            })

Note: actions/upload-artifact@v3 and actions/github-script@v6, used in the original version of this guide, were deprecated by GitHub in early 2025; v4 and v7 respectively are the current versions and are used throughout this section.

Advanced Multi-Job Workflow

# .github/workflows/advanced-security.yml
name: Advanced Security Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  secrets-detection:
    name: Secrets Scan
    runs-on: ubuntu-latest
    outputs:
      secrets-found: ${{ steps.gitleaks.outputs.exitcode }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Run Gitleaks
        id: gitleaks
        uses: gitleaks/gitleaks-action@v3
        continue-on-error: true

      - name: Process Results
        id: process
        run: |
          if [ -f results.sarif ]; then
            SECRETS_COUNT=$(jq '.runs[0].results | length' results.sarif)
            echo "secrets-count=$SECRETS_COUNT" >> $GITHUB_OUTPUT
            
            if [ "$SECRETS_COUNT" -gt 0 ]; then
              echo "::error::Found $SECRETS_COUNT secrets in the repository"
            fi
          fi

      - name: Upload results
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: gitleaks-results
          path: results.sarif

  security-report:
    name: Generate Security Report
    runs-on: ubuntu-latest
    needs: secrets-detection
    if: always()
    steps:
      - uses: actions/checkout@v4

      - name: Download scan results
        uses: actions/download-artifact@v4
        with:
          name: gitleaks-results

      - name: Generate Report
        run: |
          cat << 'EOF' > generate-report.py
          import json
          import os
          from datetime import datetime

          # Load SARIF results
          try:
              with open('results.sarif') as f:
                  sarif = json.load(f)
              
              results = sarif['runs'][0]['results']
              
              # Generate markdown report
              report = f"""# Security Scan Report

          **Scan Date:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}
          **Repository:** {os.environ.get('GITHUB_REPOSITORY', 'Unknown')}
          **Commit:** {os.environ.get('GITHUB_SHA', 'Unknown')[:8]}

          ## Summary

          - **Secrets Found:** {len(results)}
          - **Status:** {'❌ Failed' if results else '✅ Passed'}

          ## Findings

          """
              
              if results:
                  report += "| File | Line | Rule | Severity |\n|------|------|------|----------|\n"
                  for result in results:
                      location = result['locations'][0]['physicalLocation']
                      file_path = location['artifactLocation']['uri']
                      line = location['region']['startLine']
                      rule_id = result['ruleId']
                      severity = result['level']
                      report += f"| {file_path} | {line} | {rule_id} | {severity} |\n"
              else:
                  report += "No secrets detected! 🎉\n"
              
              with open('security-report.md', 'w') as f:
                  f.write(report)
                  
          except FileNotFoundError:
              with open('security-report.md', 'w') as f:
                  f.write("# Security Scan Report\n\nNo scan results found.\n")
          EOF

          python3 generate-report.py

      - name: Comment PR with Report
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const report = fs.readFileSync('security-report.md', 'utf8');

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: report
            });

  block-deployment:
    name: Block Deployment if Secrets Found
    runs-on: ubuntu-latest
    needs: secrets-detection
    if: needs.secrets-detection.outputs.secrets-found != '0'
    steps:
      - name: Block Deployment
        run: |
          echo "::error::Deployment blocked due to secrets detection"
          echo "Please remove all secrets before deploying to production"
          exit 1

GitLab CI

# .gitlab-ci.yml
stages:
  - security
  - build
  - deploy

variables:
  GITLEAKS_VERSION: "8.30.1"

gitleaks:
  stage: security
  image: alpine:latest
  before_script:
    - apk add --no-cache git curl
    - curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_amd64.tar.gz" -o gitleaks.tar.gz
    - tar -xzf gitleaks.tar.gz
    - chmod +x gitleaks
  script:
    - ./gitleaks git --report-format sarif --report-path gitleaks-report.sarif --verbose
  artifacts:
    paths:
      - gitleaks-report.sarif
    when: always
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

# Only proceed to build if no secrets found
build:
  stage: build
  needs:
    - job: gitleaks
      artifacts: false
  script:
    - echo "Building application..."
    -  # Your build commands here
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Note: The original config used gitleaks detect --report-format json with artifacts: reports: junit:. The command has been updated to gitleaks git, and the report format switched to sarif (with a matching gitleaks-report.sarif path) since SARIF integrates more cleanly with GitLab’s native vulnerability reporting UI. If you need JUnit output for a test-results dashboard instead, --report-format junit still works identically to before.

Azure DevOps

# azure-pipelines.yml
trigger:
  branches:
    include:
      - main
      - develop

pr:
  branches:
    include:
      - main

stages:
  - stage: Security
    displayName: "Security Scanning"
    jobs:
      - job: Gitleaks
        displayName: "Secrets Detection"
        pool:
          vmImage: "ubuntu-latest"
        steps:
          - checkout: self
            fetchDepth: 0

          - task: PowerShell@2
            displayName: "Install Gitleaks"
            inputs:
              targetType: "inline"
              script: |
                $version = "8.30.1"
                $url = "https://github.com/gitleaks/gitleaks/releases/download/v$version/gitleaks_${version}_linux_amd64.tar.gz"
                Invoke-WebRequest -Uri $url -OutFile "gitleaks.tar.gz"
                tar -xzf gitleaks.tar.gz
                chmod +x gitleaks
                sudo mv gitleaks /usr/local/bin/

          - task: PowerShell@2
            displayName: "Run Gitleaks Scan"
            inputs:
              targetType: "inline"
              script: |
                gitleaks git --report-format junit --report-path gitleaks-results.xml --verbose
                if ($LASTEXITCODE -ne 0) {
                  Write-Host "##vso[task.logissue type=error]Secrets detected in repository!"
                  exit 1
                }

          - task: PublishTestResults@2
            condition: always()
            inputs:
              testResultsFormat: "JUnit"
              testResultsFiles: "gitleaks-results.xml"
              testRunTitle: "Gitleaks Security Scan"

  - stage: Build
    displayName: "Build Application"
    dependsOn: Security
    condition: succeeded()
    jobs:
      - job: Build
        displayName: "Build Job"
        steps:
          - script: echo "Building application..."

Jenkins Pipeline

// Jenkinsfile
pipeline {
    agent any

    environment {
        GITLEAKS_VERSION = '8.30.1'
    }

    stages {
        stage('Security Scan') {
            steps {
                script {
                    // Install Gitleaks
                    sh '''
                        if ! command -v gitleaks &> /dev/null; then
                            curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_amd64.tar.gz" -o gitleaks.tar.gz
                            tar -xzf gitleaks.tar.gz
                            chmod +x gitleaks
                            sudo mv gitleaks /usr/local/bin/ || mv gitleaks $HOME/bin/
                        fi
                    '''

                    // Run security scan
                    def scanResult = sh(
                        script: 'gitleaks git --report-format json --report-path gitleaks-report.json --exit-code 0',
                        returnStatus: true
                    )

                    // Process results
                    if (scanResult != 0) {
                        def report = readJSON file: 'gitleaks-report.json'
                        def secretCount = report.size()

                        currentBuild.result = 'FAILURE'
                        error("Security scan failed: ${secretCount} secrets detected!")
                    }
                }
            }
            post {
                always {
                    archiveArtifacts artifacts: 'gitleaks-report.json', allowEmptyArchive: true

                    script {
                        if (fileExists('gitleaks-report.json')) {
                            def report = readJSON file: 'gitleaks-report.json'
                            if (report.size() > 0) {
                                slackSend(
                                    color: 'danger',
                                    message: "🚨 Security Alert: ${report.size()} secrets detected in ${env.JOB_NAME} - ${env.BUILD_NUMBER}"
                                )
                            }
                        }
                    }
                }
            }
        }

        stage('Build') {
            when {
                expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
            }
            steps {
                echo 'Building application...'
                // Your build steps here
            }
        }

        stage('Deploy') {
            when {
                allOf {
                    branch 'main'
                    expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
                }
            }
            steps {
                echo 'Deploying application...'
                // Your deployment steps here
            }
        }
    }
}

Container-Based CI

# Dockerfile.gitleaks-ci
FROM alpine:latest

RUN apk add --no-cache \
    git \
    curl \
    jq \
    bash

ARG GITLEAKS_VERSION=8.30.1
RUN curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_amd64.tar.gz" -o gitleaks.tar.gz && \
    tar -xzf gitleaks.tar.gz && \
    chmod +x gitleaks && \
    mv gitleaks /usr/local/bin/ && \
    rm gitleaks.tar.gz

COPY security-scan.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/security-scan.sh

ENTRYPOINT ["/usr/local/bin/security-scan.sh"]
#!/bin/bash
# security-scan.sh

set -e

echo "🔍 Starting containerized security scan..."

# Configuration
REPORT_FORMAT=${REPORT_FORMAT:-"json"}
REPORT_PATH=${REPORT_PATH:-"gitleaks-report.json"}
CONFIG_PATH=${CONFIG_PATH:-""}
REDACT=${REDACT:-"true"}
VERBOSE=${VERBOSE:-"true"}

# Build gitleaks command
GITLEAKS_CMD="gitleaks git --report-format $REPORT_FORMAT --report-path $REPORT_PATH"

if [ "$VERBOSE" = "true" ]; then
    GITLEAKS_CMD="$GITLEAKS_CMD --verbose"
fi

if [ "$REDACT" = "true" ]; then
    GITLEAKS_CMD="$GITLEAKS_CMD --redact"
fi

if [ -n "$CONFIG_PATH" ] && [ -f "$CONFIG_PATH" ]; then
    GITLEAKS_CMD="$GITLEAKS_CMD --config $CONFIG_PATH"
fi

# Run scan
echo "Running: $GITLEAKS_CMD"
eval $GITLEAKS_CMD

# Process results
if [ -f "$REPORT_PATH" ]; then
    SECRETS_COUNT=$(jq '. | length' "$REPORT_PATH")
    echo "📊 Scan completed: $SECRETS_COUNT secrets found"

    if [ "$SECRETS_COUNT" -gt 0 ]; then
        echo "❌ Security scan failed!"
        exit 1
    fi
fi

echo "✅ Security scan passed!"

11. Troubleshooting and Debugging

Common Troubleshooting Issues and Solutions

Issue 1: “gitleaks: command not found”

Symptoms:

$ gitleaks git
bash: gitleaks: command not found

Diagnosis:

# Check if gitleaks is installed
which gitleaks
echo $PATH

Solutions:

# Solution 1: Install gitleaks
curl -sSfL https://raw.githubusercontent.com/gitleaks/gitleaks/master/scripts/install.sh | sh -s -- -b /usr/local/bin

# Solution 2: Add to PATH
export PATH=$PATH:/usr/local/bin
echo 'export PATH=$PATH:/usr/local/bin' >> ~/.bashrc

# Solution 3: Use full path
/usr/local/bin/gitleaks git

# Solution 4: Use Docker
alias gitleaks="docker run --rm -v \$(pwd):/repo zricethezav/gitleaks:latest"

Issue 2: Permission Denied Errors

Symptoms:

$ gitleaks git
FATA[0000] could not open git repo: permission denied

Diagnosis:

# Check repository permissions
ls -la .git/
ls -la .

# Check if running in correct directory
pwd
git status

Solutions:

# Solution 1: Fix permissions
sudo chown -R $(whoami):$(whoami) .git/
chmod -R 755 .git/

# Solution 2: Run as different user
sudo -u git gitleaks git --source /path/to/repo

# Solution 3: Use 'dir' command for filesystem-only scanning
gitleaks dir --source .

Issue 3: Large Repository Performance Issues

Symptoms:

$ gitleaks git
# Hangs or takes extremely long time
# High memory usage
# Process killed by OOM

Diagnosis:

# Check repository size
git count-objects -vH

# Check commit count
git rev-list --count HEAD

# Check memory usage
free -h
htop

Solutions:

# Solution 1: Limit scan depth
gitleaks git --log-opts="--max-count=1000"

# Solution 2: Scan specific time period
gitleaks git --log-opts="--since='2026-01-01'"

# Solution 3: Increase system resources
export GOMEMLIMIT=4GiB
ulimit -v 8388608  # 8GB virtual memory limit

# Solution 4: Scan in chunks
git log --format="%H" --max-count=100 | while read commit; do
    echo "Scanning commit $commit"
    gitleaks git --log-opts="$commit^..$commit"
done

# Solution 5: Use shallow clone
git clone --depth 50 <repo-url>
cd <repo>
gitleaks git

# Solution 6: Use a baseline to only scan/report new changes
gitleaks git --baseline-path baseline.json

Issue 4: False Positives

Cause: Scanning a repository with a very long and complex history. Solution: Limit the scan depth. Use the --log-opts flag to scan only recent history. For example, --log-opts="--since='1 year ago'" or --log-opts="--max-count=1000".

Cause: Scanning very large files. Solution: Skip large files by setting a size limit. Use the --max-target-megabytes flag to exclude files larger than a certain size.

Cause: Insufficient system resources. Solution: Increase memory available to Gitleaks, especially when running in a container. For Docker, you can adjust memory limits.

Cause: Legitimate test/example data that matches a secret pattern. Solution: Add the fingerprint to .gitleaksignore, add an allowlist entry to gitleaks.toml, or use an inline // gitleaks:allow comment — see Section 7 for all three approaches.

[allowlist]
regexes = [
  '''(?i)test[_-]?(key|token|secret)''',
  '''(?i)(fake|dummy|example)[_-]?(key|token)''',
]
stopwords = ["test", "fake", "example", "dummy"]

Issue 5: False Negatives

Symptoms:

  • A known secret is not being detected by Gitleaks.

Diagnosis:

  • Check the Gitleaks version to ensure you have the latest rule set.
  • Review the custom configuration file (gitleaks.toml) to see if the rule is disabled or if an allowlist is excluding it.
  • Test the specific regex for the secret against the file content using a tool like Regex101 to ensure it matches, or use the official Gitleaks Playground to test your rule live.

Solutions:

  • Update Gitleaks: Ensure you are using the latest version (v8.30.1 as of mid-2026).
  • Refine Custom Rules: If you are using custom rules, ensure the regex is correct and that there are no overly broad allowlist rules that might be causing the issue.
  • Check for gitleaks:allow comments: Look for comments in the code that might be telling Gitleaks to ignore a specific line.
  • Check .gitleaksignore: A fingerprint may have been added there for a finding that’s actually still relevant.

Issue 6: “detect/protect not found” (Deprecated Commands)

If you’re following old scripts or documentation that use gitleaks detect or gitleaks protect --staged, note that they still work (they’re hidden from --help as of v8.19.0+) but should be updated to the current equivalents used throughout this guide:

# Old → New
gitleaks detect gitleaks git
gitleaks detect --no-git -s . gitleaks dir -s .
gitleaks protect --staged git diff --cached | gitleaks stdin

Debugging with Verbose Mode

For any complex issue, the --verbose or -v flag is your best friend. It provides detailed output on which files are being scanned and which rules are being applied.

# Run a scan with verbose logging
gitleaks git --verbose

# For even more detail, set the log level to debug
gitleaks git --log-level debug

This will give you insight into the entire process, helping you pinpoint where the issue might be.

Getting Help from the Community

If you’re stuck, the Gitleaks community is a great resource. You can find help on:

  • GitHub Discussions: The official place for questions, ideas, and showing off your use cases.
  • GitHub Issues: For reporting bugs or requesting new features.

When asking for help, be sure to provide:

  • The Gitleaks version you are using (gitleaks version).
  • Your operating system.
  • The command you are running.
  • The relevant parts of your configuration file.
  • The verbose or debug log output.

12. Performance Optimization

When scanning large repositories or running Gitleaks in resource-constrained environments, performance can become a critical factor. Here are some strategies to optimize your Gitleaks scans.

Optimizing Scans for Large Repositories

Large repositories can be slow to scan due to the sheer volume of commits and files. Here’s how to speed things up:

  • Shallow Clones: When cloning a repository for scanning, use a shallow clone to limit the history.

    git clone --depth 1000 <repository_url>
  • Limit Scan Depth: Use the --log-opts flag to restrict the scan to a recent number of commits.

    gitleaks git --log-opts="--max-count=500"
  • Scan Specific Timeframes: If you only need to audit recent activity, specify a date range.

    gitleaks git --log-opts="--since='6 months ago'"
  • Use a Baseline: Once you’ve done a full initial scan, use --baseline-path on subsequent runs so Gitleaks only needs to report new findings rather than re-presenting everything each time.

    gitleaks git --baseline-path baseline.json

Efficient Branch Management

A large number of branches can also slow down scans. It’s good practice to regularly prune stale branches.

Configuration Tweaks for Performance

Your Gitleaks configuration can be tuned for better performance:

# In your gitleaks.toml
[performance]
# Skip files larger than 5MB
max_target_megabytes = 5

# Increase the number of processing workers (for multi-core systems)
workers = 8

Filesystem Scanning

For very large repositories where Git history is not the primary concern, run Gitleaks with the dir command, which only scans the current state of the files and skips walking through git history entirely — often dramatically faster.

gitleaks dir --source .

Using Git LFS for Large Files

If your repository contains large binary files, using Git LFS can improve performance by keeping those large files out of the main Git repository.


13. Security Best Practices

Beyond just running Gitleaks, here are some best practices to build a robust secret management strategy.

Shift-Left Security

The “shift-left” approach involves integrating security early in the development lifecycle.

  • Pre-commit Hooks: Prevent secrets from ever entering the repository, using the pre-commit framework (Section 9) or a manual hook.
  • CI/CD Integration: Automate secret scanning on every push and pull request with gitleaks-action@v3 (Section 10).

Defense in Depth

Relying on a single tool is not enough. A layered security approach is more effective:

  • Secret Management Systems: Use tools like HashiCorp Vault, AWS Secrets Manager, or Google Secret Manager to store and manage secrets.
  • Regular Audits: Schedule regular, full-history scans of all your repositories.
  • Code Reviews: Encourage a security-conscious culture where developers look for potential leaks during code reviews.
  • GitHub Native Scanning: GitHub’s built-in secret scanning catches post-push leaks from partner providers automatically — run Gitleaks alongside it for custom patterns, pre-commit coverage, and non-GitHub repositories.

Incident Response Plan

Have a clear plan for what to do when a secret is leaked:

  1. Revoke the Secret: Immediately invalidate the leaked credential with the provider (AWS, GitHub, Stripe, etc.) — this is the single most important step and should happen before anything else.
  2. Remove from History: Use git-filter-repo (the current recommended tool — see Section 5) or BFG Repo-Cleaner to remove the secret from the entire Git history.
  3. Force Push: Coordinate with your team and force-push the cleaned history, since rewriting history requires everyone to re-sync their local clones.
  4. Investigate the Impact: Check provider access logs to determine if the secret was used maliciously.
  5. Post-mortem: Understand how the leak happened and improve your processes to prevent it from happening again.

Remember: Rotation alone is not enough if the repository is or was ever public. Automated bots scrape newly pushed secrets within minutes. The secret needs to be both rotated and removed from history — and Step 1 (revoke) should never wait on Step 2 (clean history), since cleaning history takes longer and the credential is compromised the moment it’s pushed.

Build Security Awareness

Train your team on the importance of not hardcoding secrets and how to use approved secret management tools.


14. Advanced Topics and Enterprise Features

For large organizations, Gitleaks offers several advanced and enterprise-grade features.

Baselines

You can use a baseline report to only show new leaks since the last scan. This is useful for integrating Gitleaks into an existing project with a large number of findings.

# Create an initial baseline report
gitleaks git --report-format json --report-path baseline.json

# On subsequent scans, only report new leaks
gitleaks git --baseline-path baseline.json

Gitleaks in Enterprise Environments

For enterprise-wide adoption, consider the following:

  • Centralized Configuration: Maintain a central repository for your gitleaks.toml file that all projects can inherit from using [extend].
  • Custom Integrations: Use the JSON output format to integrate Gitleaks findings into your existing security dashboards or SIEM systems.
  • Enterprise Support: Gitleaks itself does not offer a paid enterprise support tier from the original maintainer; organizations wanting a fully managed/supported offering should evaluate GitGuardian (Section 15) or watch Betterleaks’ roadmap (Section 19), which has the backing of Aikido Security plus corporate co-maintainers.

Advanced Detection Capabilities

Gitleaks has some advanced detection capabilities that can be enabled with flags:

  • Archive Scanning (v8.27+): Scan inside zip and tar.gz files.

    gitleaks git --max-archive-depth=3
  • Automatic Decoding (v8.27+): Decode base64, hex, and percent-encoded text to find hidden secrets.

    gitleaks git --max-decode-depth=5
  • Composite Rules (v8.28+): Require multiple patterns to match in proximity before flagging a finding, dramatically reducing false positives. See Section 7 for full configuration examples.


15. Comparison with Other Tools

Gitleaks is a powerful tool, but it’s important to understand how it compares to other secret scanning tools in the ecosystem — including its own successor.

Gitleaks vs. Betterleaks

With the launch of Betterleaks by Gitleaks’ original author in early 2026, this is the most relevant comparison for anyone choosing a tool today:

  • Gitleaks: Stable, feature-complete, and the most widely integrated secret scanner in the open-source ecosystem (pre-commit, GitHub Actions, GitLab CI, IDE plugins). Now receives security patches only — no new features. v8.30.1 is current. All existing CLI flags and gitleaks.toml configs remain valid for the foreseeable future.
  • Betterleaks: The next-generation successor, built by the same author. Uses BPE tokenization instead of Shannon entropy (98.6% recall vs. 70.4% on the CredData benchmark). Adds CEL-based live secret validation, parallelized git history scanning, and is purpose-built for AI agent workflows. Designed as a drop-in replacement — existing Gitleaks configs work without modification.

See Section 19 for the full breakdown and a migration guide.

Gitleaks vs. TruffleHog

  • Gitleaks: Known for its speed and efficiency, as it’s written in Go. It has extensive customization options and is excellent for CI/CD integration.
  • TruffleHog: Also very popular, it uses both regex and entropy checks to find secrets, and supports scanning beyond git — including Slack, S3 buckets, and Docker images. While traditionally seen as slightly slower, it provides deep and thorough multi-source analysis.

Gitleaks vs. detect-secrets

  • detect-secrets: Developed by Yelp, this tool is highly modular with a plugin-based architecture, allowing for a high degree of customization.

Gitleaks vs. GitGuardian

  • GitGuardian: A commercial solution that offers a more comprehensive platform with features like real-time monitoring, a global dashboard, and incident response workflows. It’s a good choice for enterprises that need a fully managed solution and don’t mind the per-developer licensing cost.
ToolKey StrengthBest For
GitleaksSpeed, stability, customization, and CI/CD integration.Teams that want a fast, flexible, mature, open-source solution today.
BetterleaksHigher detection recall, live credential validation.Teams wanting the active successor from the same original author.
TruffleHogDeep multi-source analysis (git, Slack, S3, Docker, etc.)Thorough security audits across data sources beyond just git.
detect-secretsModular and highly customizable with plugins.Organizations that need to build a highly tailored scanning solution.
GitGuardianAll-in-one enterprise platform with advanced features.Companies looking for a fully supported, commercial, managed solution.

16. Case Studies and Examples

Case Study: Preventing an AWS Key Leak

Scenario: A developer accidentally includes an AWS access key in a debug statement.

Without Gitleaks: The code is committed and pushed to a public repository. Within minutes, automated scanners find the key, and the AWS account is compromised, leading to a costly data breach.

With Gitleaks:

  1. The developer tries to commit the code.
  2. The pre-commit hook runs git diff --cached | gitleaks stdin and detects the AWS key.
  3. The commit is blocked with a clear error message.
  4. The developer removes the key, uses an environment variable instead, and successfully commits the code.
  5. A potential disaster is averted with zero impact.

Case Study: Auditing a Legacy Monorepo

Scenario: A company acquires a new business and needs to audit a large, legacy monorepo with years of commit history.

Process:

  1. The security team clones the repository.
  2. They run a full Gitleaks scan with the command gitleaks git --report-format json --report-path audit.json.
  3. The initial scan reveals over 200 potential secrets.
  4. They use the JSON report to categorize the findings by rule and severity.
  5. They create a plan to revoke and rotate the critical secrets first, then work through the rest.
  6. They create a baseline (cp audit.json baseline.json) so future scans only surface genuinely new findings.
  7. They implement Gitleaks in the CI/CD pipeline (using gitleaks-action@v3) to prevent any new secrets from being added.

17. Maintenance and Updates

To keep Gitleaks effective, it’s important to perform regular maintenance and stay up to date.

Keeping Gitleaks Updated

  • For Binary Installations: Periodically re-run the installation script to get the latest version.

    curl -sSfL https://raw.githubusercontent.com/gitleaks/gitleaks/master/scripts/install.sh | sh -s -- -b /usr/local/bin
  • For Homebrew: Run brew upgrade gitleaks.

  • For Docker: Pull the latest image: docker pull zricethezav/gitleaks:latest.

  • For Gitleaks-Action: Make sure you’re pinned to gitleaks-action@v3 (not @v2, which stops working September 16, 2026 — see Section 10).

  • For the pre-commit framework: Run pre-commit autoupdate to bump the pinned rev automatically.

Updating Rules

The default ruleset was actively updated with each release through v8.30.0 (which added Airtable and Looker token detection). Since Gitleaks is now feature-complete, expect future rule updates to be infrequent and security-driven rather than expansive. If you need the ruleset to keep pace with new secret formats from cloud providers, Betterleaks (Section 19) is where that active development is now happening — its rules are largely compatible with Gitleaks configs.

Reviewing Allowlists

As your codebase evolves, your allowlists may become outdated. It’s a good practice to periodically review your allowlist rules to ensure they are still relevant and not hiding real leaks.


18. Resources and Community

The Gitleaks community is active and welcoming. Here are some resources to help you on your journey.

Official Resources

Community and Support

Contributing to Gitleaks

Gitleaks is an open-source project, and contributions are welcome — particularly for security patches, bug fixes, and documentation improvements, since the project is now feature-complete. You can contribute by:

  • Reporting bugs.
  • Improving documentation.
  • Submitting pull requests with security fixes.

New feature contributions and significant rule-set expansions are now generally directed toward Betterleaks instead — see below.


19. Betterleaks: The Next Generation

Background

In early February 2026, Zach Rice — the original creator of Gitleaks — joined Aikido Security as Head of Secrets Scanning. He then launched Betterleaks on February 3, 2026, as the official spiritual successor to Gitleaks.

Rice has been transparent about the reason: he no longer holds full administrative control over the Gitleaks repository and name. Rather than work around governance friction indefinitely, he started fresh. As he put it: “Betterleaks is the successor to Gitleaks. We’re dropping the ‘git’ and slapping ‘better’ on it because that’s what it is, better.”

He has confirmed that Gitleaks will remain stable and continue to receive security patches, so existing users are not forced to migrate. This guide treats Gitleaks (Sections 1–18) as the stable, current-day default and Betterleaks as the option for teams who want the newest detection technology and active development.

What’s New in Betterleaks

1. Token Efficiency Scanning (replaces Shannon Entropy)

The most significant technical change. Instead of measuring how “random” a string looks character-by-character (Shannon entropy — the method Gitleaks uses, described in Section 3), Betterleaks uses Byte Pair Encoding (BPE) tokenization — the same compression technique used by GPT-4 and other large language models.

The logic: natural language compresses efficiently into long tokens (high token efficiency). Secrets and random strings compress poorly into many short tokens (low token efficiency). BPE tokenization turns this into a far more reliable signal than raw entropy.

Benchmark results on the CredData dataset:

MethodRecallPrecision
Shannon Entropy (Gitleaks)70.4%21.1%
BPE Token Efficiency (Betterleaks)98.6%57.3%

This 28-percentage-point recall improvement means dramatically fewer real secrets slip through undetected, while precision more than doubles — meaning far fewer false positives to triage as well.

2. CEL-Based Secrets Validation

Betterleaks can fire live HTTP requests against detected credentials to verify they are still active. Validation logic is written in Common Expression Language (CEL), giving rule authors fine-grained programmatic control over what constitutes a confirmed secret — for example, distinguishing a still-active AWS key from one that was already rotated and revoked.

3. Drop-in Replacement

Betterleaks is fully backwards-compatible with Gitleaks. Existing config files, allowlists, and CLI flags all work without modification, which is why the migration guide below is so short.

4. Pure Go — No CGO or Hyperscan Dependencies

Simpler installation with no native library requirements, which also simplifies cross-platform builds and container images.

5. Parallelized Git Scanning

Processes multiple commits simultaneously instead of walking history linearly, the way Gitleaks does (see Section 3, Phase 1). Benchmarks show significantly faster scans on large repositories with deep history.

6. AI Agent-Ready Design

Betterleaks includes tight output controls and flag-based output suppression, making it easy for AI coding agents (like Claude Code or Cursor) to integrate as a subprocess without excessive token overhead — a use case that didn’t really exist when Gitleaks’ CLI was originally designed.

Installation

# Using Go
go install github.com/betterleaks/betterleaks@latest

# Using Homebrew
brew install betterleaks

# Using Docker
docker pull ghcr.io/betterleaks/betterleaks:latest

Migration from Gitleaks

Since Betterleaks is a drop-in replacement, migration is minimal:

# Step 1: Install Betterleaks
brew install betterleaks

# Step 2: Your existing gitleaks.toml works as-is — no changes needed

# Step 3: Replace 'gitleaks' with 'betterleaks' in your scripts/hooks

# Old
gitleaks git --report-format sarif --report-path results.sarif

# New
betterleaks git --report-format sarif --report-path results.sarif

Update your pre-commit config:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/betterleaks/betterleaks
    rev: v1.1.0 # Check GitHub for latest
    hooks:
      - id: betterleaks

The same applies to every example in Sections 8–14 of this guide — anywhere you see gitleaks git, gitleaks dir, or gitleaks stdin, the Betterleaks equivalent is simply betterleaks in place of gitleaks, with identical flags.

Governance and Licensing

Betterleaks is open-source under the MIT license and is maintained by four core contributors: Zach Rice (project lead), plus contributors from the Royal Bank of Canada, Red Hat, and Amazon. Aikido Security acts as a sponsor, not an owner, and Rice retains project ownership — a deliberate structural difference from how Gitleaks’ governance evolved.

Roadmap

Features planned for Betterleaks v2 include:

  • Additional data sources beyond Git repositories and files (echoing what TruffleHog already offers today, per Section 15)
  • LLM-assisted analysis for better secret classification (distinguishing real keys from test fixtures automatically)
  • More detection filters
  • Automatic secret revocation via provider APIs
  • Permissions mapping
  • Further performance optimizations

Should You Migrate?

Stick with Gitleaks if:

  • Your pipelines are stable and working well today
  • You need maximum ecosystem maturity — Gitleaks has years of integrations, IDE plugins, and Stack Overflow answers that Betterleaks doesn’t have yet
  • You have no pressing need for improved recall or live credential validation

Switch to Betterleaks if:

  • You want the best possible detection accuracy (98.6% vs. 70.4% recall)
  • You want live credential validation to separate genuinely active leaks from stale, already-rotated ones
  • You want to follow where the original author’s active development is going
  • You’re setting up a new pipeline from scratch and have no existing Gitleaks investment to preserve

Either way, if you’re currently using neither tool, start somewhere — even Gitleaks at 70.4% recall stops the vast majority of real secrets from reaching production, and migrating later is, by design, nearly frictionless.


This guide was last updated June 2026. For the latest information, consult the official repositories at github.com/gitleaks/gitleaks and github.com/betterleaks/betterleaks._


Back to All Posts
Share this post:
Share on Twitter
Share on LinkedIn
Share on Reddit
Share on Facebook
Copy Link
Copied!