Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Gitleaks: scan your Git repositories for exposed secrets

By updated

Improve this page

Gitleaks logo

Gitleaks detects the secrets exposed in your Git repositories, files and archives. Fast and highly configurable, it is the reference tool for pre-commit hooks and CI/CD pipelines. With more than 220 built-in rules (222 in the default configuration of v8.30.1), it identifies AWS keys, GitHub, Stripe and Slack tokens, and many more.

What you will learn

  • Understand how Gitleaks detects secrets with regex rules
  • Scan different sources: Git repositories, files, archives, standard input
  • Configure custom rules and exclusions
  • Integrate Gitleaks into your CI/CD pipelines (GitHub Actions, GitLab CI)
  • Use the advanced features: decoding, archives, baseline

The problem of secrets in code

A secret pushed to a public repository does not stay confidential long enough for you to notice. Crawling bots continuously watch GitHub's public events and test the credentials they find, often before the author of the commit has even finished their day. GitHub has industrialised the response with secret scanning and automatic revocation partnerships with cloud providers, which says a lot about how often this happens.

Secrets end up exposed for several reasons:

SituationExampleRisk
Committed config file.env missing from .gitignorePlaintext API keys in the Git history
Hardcoded passwordpassword = "admin123" in the codeAccess to the database
Token in the logsAuthorization: Bearer sk_live_...Account compromise
Base64-encoded secretYXdzX3NlY3JldF9hY2Nlc3Nfa2V5...False sense of security
Git historySecret deleted but present in an old commitStill reachable through git log

The problem: even if you delete a secret, it stays in the Git history. And malicious scanners analyse the whole history, not just the main branch.

How Gitleaks solves this problem

Gitleaks uses an approach based on regex rules and keywords to detect secrets. Unlike other tools, it does not check whether secrets are active: it focuses on speed and configurability.

How it works, in three steps

The classification step is the one that determines the quality of the results, and it relies on two stacked mechanisms. Gitleaks first looks for keywords that are cheap to spot (AKIA, sk_live_, ghp_), then applies the complete regular expression only to the lines that passed this first filter. This pre-filtering explains the tool's speed, and it is also why a custom rule without a keywords field noticeably slows down a large repository.

  1. Discovery: Gitleaks walks through the sources (Git, files, archives, stdin)
  2. Classification: each line is compared with the 220+ built-in rules
  3. Report: the secrets found are displayed with their exact location

Gitleaks strengths

Two rows of this table are real differentiators compared with other scanners; the others are about convenience. Automatic decoding catches base64-encoded secrets that most tools let through, and archive scanning covers build artifacts shipped as .tar.gz where configuration files linger. On the other hand, TOML configurability has a downside: without a team convention, every repository ends up with its own .gitleaks.toml and allowlists that drift apart.

FeatureDetail
SpeedWritten in Go, very fast
ConfigurabilityCustomisable TOML rules
DecodingBase64, hex, URL encoding (v8.26+)
ArchivesZIP, tar.gz, 7z... (v8.27+)
Composite rulesMulti-part secrets (v8.28+)
IntegrationPre-commit, GitHub Actions, GitLab CI

What Gitleaks detects

Coverage is broad on recognisable token formats, that is, those with a fixed prefix and a constant length. It is much weaker on secrets with no identifiable shape: a database password or a home-made internal key will only be detected by the generic rule generic-api-key, which relies on entropy and generates many false positives. That is precisely the case where you need to write your own rules.

Gitleaks ships more than 220 rules to detect:

  • Cloud providers: AWS, GCP, Azure, DigitalOcean
  • Payments: Stripe, PayPal, Square
  • Authentication: GitHub, GitLab, Slack, Discord
  • Databases: PostgreSQL, MySQL, MongoDB in URLs
  • Private keys: RSA, DSA, ECDSA, OpenSSH
  • AI/ML: OpenAI, Anthropic, HuggingFace, Perplexity

Installation

Gitleaks is a static Go binary with no dependency, which makes the four methods below equivalent in their result. The choice criterion is traceability: on a development workstation, a version manager such as asdf or mise lets the whole team align on the same version. In CI, the container image pinned by digest is preferable, otherwise an upstream version change can break a pipeline overnight on newly added rules.

Download the binary from the GitHub releases. The project publishes a checksums file next to each archive: fetch both and verify the checksum before extracting anything.

Terminal window
VERSION="8.30.1"
BASE="https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}"
# Archive + checksums file published by the project
curl -fsSLO "${BASE}/gitleaks_${VERSION}_linux_x64.tar.gz"
curl -fsSLO "${BASE}/gitleaks_${VERSION}_checksums.txt"
# Checksum verification: must print "... : OK"
sha256sum --check --ignore-missing gitleaks_${VERSION}_checksums.txt
# Extract and install only if the check passed
tar xzf gitleaks_${VERSION}_linux_x64.tar.gz gitleaks
sudo install -m 0755 gitleaks /usr/local/bin/gitleaks
# Verification
gitleaks version
# 8.30.1

Main commands

Since version 8.19.0, Gitleaks uses three main commands: git, dir and stdin. The old detect and protect commands are deprecated but still functional.

Scanning a Git repository

The git command scans the full history of a Git repository, including all branches.

Terminal window
# Scan the repository in the current directory
gitleaks git
# Scan a specific repository
gitleaks git /path/to/my-repo
# Show the details (verbose mode)
gitleaks git -v
# Hide the banner
gitleaks git --no-banner
# Mask the value of secrets in the output (use it in CI)
gitleaks git -v --redact

Sample output, captured with Gitleaks 8.30.1 on a test repository containing a GitHub token, value masked by --redact:

Finding: token = REDACTED
Secret: REDACTED
RuleID: github-pat
Entropy: 4.771928
File: config/app.cfg
Line: 1
Commit: 343f04f811221ca91b4f3a52eee9355b6cef8e5c
Author: dev
Email: dev@example.com
Date: 2026-09-30T19:41:32Z
Fingerprint: 343f04f811221ca91b4f3a52eee9355b6cef8e5c:config/app.cfg:github-pat:1

This output tells you:

  • The secret type: github-pat, a GitHub personal access token
  • The location: file config/app.cfg, line 1
  • The commit: hash, author, date
  • The fingerprint (Fingerprint): the identifier to reuse in a .gitleaksignore

Without --redact, the full value of the secret is displayed in plaintext: in a CI log, kept and readable by the whole team, that is a second leak. To test your hooks, do not use a fake AWS key: measured on this version, an isolated AKIA key is no longer detected, whereas a ghp_ token or a PEM private key still are.

Filtering by commit range

--log-opts passes its value as is to git log, without validation by Gitleaks. A misspelled option therefore makes git fail, not the scanner, and the error message you get is Git's. It is the main lever to make a scan bearable on an old repository: limiting the range divides the scan time accordingly, at the cost of a blind spot on the history that is not covered. Keep this reduction for pull request CI, and keep a scheduled full scan alongside it.

Terminal window
# Scan only the last 10 commits
gitleaks git --log-opts="--max-count=10"
# Scan a range of commits
gitleaks git --log-opts="HEAD~20..HEAD"
# Scan only a specific branch
gitleaks git --log-opts="--all develop..main"

Scanning before a commit (pre-commit)

These two options do not cover the same scope. --staged analyses what is in the Git index, that is, exactly what the commit is going to record. --pre-commit relies on a git diff and serves the project's official hook, which actually combines both. One point to watch: a secret already present in an earlier commit will be seen by neither of them; only gitleaks git on the full history will find it.

Terminal window
# Scan the staged files
gitleaks git --staged
# Scan with git diff (pre-commit mode)
gitleaks git --pre-commit

Scanning files or directories

dir ignores Git entirely: no history, no commit, no author in the report, only the path and the line. It is the command to use on a build artifact, an exported configuration folder or a directory extracted from a container image. Two behaviours to know: the .git/ directory is excluded from the walk, but hidden files and files listed in .gitignore are scanned, which often surfaces a local .env that gitleaks git would never see. The file and directory aliases designate the same command.

Terminal window
# Scan a directory
gitleaks dir /path/to/folder
# Scan a specific file
gitleaks dir config.env
# Scan several paths
gitleaks dir file1.log file2.env /etc/config/

Scanning standard input (stdin)

stdin opens the scan to anything that produces text, and that is where the leaks nobody looks for live: application logs, kubectl describe outputs, environment variable exports. The report obviously cannot point to a file or a commit, only to the line number in the stream. Be careful, however, with the command that feeds the pipe: kubectl logs on a chatty pod can send several gigabytes.

Terminal window
# Scan a file through a pipe
cat config.env | gitleaks stdin
# Scan the output of a command
kubectl logs deployment/my-app | gitleaks stdin
# Scan a file decompressed on the fly
gunzip -c logs.gz | gitleaks stdin

Advanced features

The four features that follow serve different needs but share one logic: they widen or narrow the scan scope. Decoding and archive scanning reveal secrets invisible to a naive scan; the baseline and the output formats make the results usable day to day without drowning teams in alerts that are already known.

Automatic decoding (v8.26+)

Gitleaks can automatically decode secrets encoded in base64, hexadecimal or URL encoding. By default, decoding is enabled with a depth of 5.

Terminal window
# Limit recursive decoding to depth 2
gitleaks dir /path --max-decode-depth=2
# Disable decoding
gitleaks dir /path --max-decode-depth=0

Example: a base64-encoded Stripe token will be detected:

# File containing: c2tfbGl2ZV80ZUMzOUhxTHlqV0Rhcmp0VDF6ZHA3ZGMK
Finding: sk_live_4eC39HqLyjWDarjtT1zdp7dc
Secret: sk_live_4eC39HqLyjWDarjtT1zdp7dc
RuleID: stripe-access-token
Tags: [decoded:base64 decode-depth:1]

The decoded:base64 and decode-depth:1 tags indicate that the secret was found after decoding.

Archive scanning (v8.27+)

Archive scanning is disabled by default (--max-archive-depth is 0), which surprises many users who believe their .tar.gz files are covered. You have to enable it explicitly. Raise the depth sparingly: each additional level means an extraction to disk, and a booby-trapped archive containing thousands of nested files turns the scan into a saturation of the runner's temporary space.

Terminal window
# Scan an archive with depth 1
gitleaks dir archive.tar.gz --max-archive-depth=1
# Scan nested archives (depth 3)
gitleaks dir archives/ --max-archive-depth=3

Supported formats: zip, tar, tar.gz, tar.bz2, tar.xz, 7z, rar, and more.

The path in the results shows the location inside the archive:

File: nested.tar.gz!inner/files.tar!config/.env

This means: .env is in files.tar, which is in nested.tar.gz.

Baseline: ignoring known secrets

The baseline lets you ignore secrets already identified in later scans. Useful for projects whose history contains secrets that have already been dealt with.

  1. Create the baseline by generating an initial report

    Terminal window
    gitleaks git --report-path baseline.json
  2. Use the baseline for the following scans

    Terminal window
    gitleaks git --baseline-path baseline.json --report-path findings.json

    Only new secrets will be reported.

Output formats

The choice of format depends on who reads the report. SARIF is the only one that integrates natively with the GitHub and GitLab security dashboards, JUnit makes leaks appear as failed tests in the interface of most CI systems, and JSON is the format to keep since it holds the fingerprint values you can reuse in a .gitleaksignore. The template format is reserved for internal reports that must follow an in-house template.

Terminal window
# JSON (default when --report-path is used)
gitleaks git -f json -r report.json
# CSV
gitleaks git -f csv -r report.csv
# SARIF (for GitHub Code Scanning)
gitleaks git -f sarif -r report.sarif
# JUnit (for CI/CD)
gitleaks git -f junit -r report.xml
# Custom template
gitleaks git -f template --report-template custom.tmpl -r report.txt
# JSON output to stdout
gitleaks git -f json -r -

Configuration

Gitleaks is configured through a .gitleaks.toml file versioned with the code. Two mistakes are costly here. The first is writing a configuration without useDefault = true, which silently disables the 222 built-in rules. The second is piling up exclusions until the scan returns nothing, which gives you a green pipeline and no security. Treat this file as production code: mandatory review, and a written justification for each exclusion.

Order of precedence

This order explains the most frequent failure in CI: a correctly written .gitleaks.toml that is ignored, because a GITLEAKS_CONFIG variable lingers in the runner's environment and takes over. Note point 4 as well: the file is looked for in the scanned directory, not in the current directory. Scanning /opt/app from your $HOME therefore does not load the .gitleaks.toml in your $HOME. When in doubt, --log-level=debug shows the configuration actually used.

  1. --config or -c option
  2. GITLEAKS_CONFIG environment variable (file path)
  3. GITLEAKS_CONFIG_TOML environment variable (TOML content)
  4. .gitleaks.toml file in the scanned directory

If no configuration is found, Gitleaks uses the default rules.

Extending the default configuration

The reflex to adopt is useDefault = true. Without this line, your file replaces the built-in rule set entirely instead of complementing it, and you end up with a scanner that only looks for your own patterns. disabledRules then lets you surgically remove a rule that is too noisy, generic-api-key being by far the first candidate. Disable it only after writing your own rules, otherwise you lose the only detection that covers secrets with no known format.

.gitleaks.toml
# Configuration title
title = "Custom configuration"
# Extend the default configuration
[extend]
useDefault = true
# Disable some default rules
disabledRules = ["generic-api-key"]

Creating a custom rule

Two fields drive the result. keywords is not decorative: it is the pre-filter that avoids applying the regex to every line of the repository, and forgetting it can multiply the scan time. entropy sets a disorder threshold below which a match is rejected, which discards test values such as int_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa. Finally, the regex follows Go's RE2 syntax, which supports neither lookbehind nor lookahead: an expression copied from PCRE can be rejected on load.

.gitleaks.toml
[[rules]]
# Unique identifier
id = "internal-api-key"
# Description
description = "Company internal API key"
# Regular expression (Go syntax)
regex = '''int_[a-zA-Z0-9]{32}'''
# Keywords for pre-filtering (optimisation)
keywords = ["int_", "internal_api"]
# Minimum entropy (optional)
entropy = 3.5
# Tags for reporting
tags = ["internal", "api-key"]

Allowlists: excluding false positives

An allowlist is an accepted risk, not a fix: everything it covers stops being checked, including real secrets that might later land in the same place. Prefer the narrowest criteria possible. Excluding tests/ wholesale is convenient but broad; targeting a specific fixtures file or a known value pattern, through regexes and stopwords, keeps the rest of the directory under watch. Document each entry with description: that is the field you reread six months later when wondering why a rule is silent.

.gitleaks.toml
# Global allowlist (applies to all rules)
[[allowlists]]
description = "Test files and documentation"
paths = [
'''tests/''',
'''fixtures/''',
'''\.md$''',
]
# Allowlist for a specific rule
[[rules]]
id = "aws-access-token"
[[rules.allowlists]]
description = "AWS test keys"
regexes = ['''AKIAIOSFODNN7EXAMPLE''']
stopwords = ["example", "test", "demo"]

Shared allowlists (v8.25+)

targetRules solves a concrete problem: before v8.25, excluding the same test directory for ten rules meant duplicating ten allowlist blocks, with the drift that follows when one is forgotten. The scope stays explicit, only the named rules are affected, which avoids the side effect of a global allowlist that also blinds the rules added later.

.gitleaks.toml
[[allowlists]]
# Apply to specific rules
targetRules = ["aws-access-token", "generic-api-key"]
description = "Test files"
paths = ['''tests/.*\.json$''']

Composite rules (v8.28+)

Composite rules address the case where one half of the secret is worthless on its own. An AWS identifier AKIA... alone allows nothing; combined with its secret key a few lines below, it gives full access. The withinLines parameter defines the accepted proximity window. A welcome side effect: by requiring both parts, this kind of rule strongly reduces false positives on public identifiers quoted in documentation.

.gitleaks.toml
[[rules]]
id = "aws-keypair"
description = "AWS key/secret pair"
regex = '''AKIA[0-9A-Z]{16}'''
# Required rule: the secret must be close by
[[rules.required]]
id = "aws-secret-pattern"
withinLines = 5 # The secret must be within the next 5 lines

Ignoring a specific secret in the code

This annotation lives in the code, which makes it visible in code review, unlike an allowlist buried in a configuration file. That is its main benefit. It is also its main danger: anyone can stick it on a real leak to get a pipeline through. On a sensitive repository, the --ignore-gitleaks-allow option lets you run an audit scan that neutralises all these annotations and reveals what they hide.

# This key is an example for the documentation
AWS_KEY = "AKIAIOSFODNN7EXAMPLE" # gitleaks:allow

The .gitleaksignore file

The fingerprint combines the commit, the file, the rule identifier and the line number. This precision is a strength, each exception is surgical, but also a weakness: rewriting the history or moving the line changes the fingerprint, and the exception silently stops applying. Always take these values from a JSON report rather than composing them by hand, and remember that an entry here never exempts you from revoking the secret concerned.

.gitleaksignore
# Format: fingerprint (from the JSON report)
fbc14303ffbf8fb1c2c1914e8dda7d0121633aca:config.env:aws-access-token:4
77b2a3e56973785a52ba4ae4b8dac61d4bac016f:keys:private-key:6

CI/CD integration

The three integrations below do not replace each other, they complement each other. The pre-commit hook is fast and local but can be bypassed with a --no-verify; the CI cannot be bypassed but only acts after the push. A healthy chain combines both: the hook for developer comfort, the CI for the guarantee.

Pre-commit hook

Block secrets before they are committed. The Gitleaks repository publishes three hook identifiers: gitleaks compiles the binary in Go, gitleaks-docker goes through the container image, gitleaks-system reuses the binary already installed on the workstation. The first is the simplest, the third the fastest if your team already manages the version through asdf or mise.

.pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.30.1
hooks:
- id: gitleaks
  1. Install pre-commit

    Terminal window
    pip install pre-commit
  2. Create the configuration file

    Terminal window
    cat > .pre-commit-config.yaml << 'EOF'
    repos:
    - repo: https://github.com/gitleaks/gitleaks
    rev: v8.30.1
    hooks:
    - id: gitleaks
    EOF
  3. Install the hooks

    Terminal window
    pre-commit install
  4. Test

    Terminal window
    git add . && git commit -m "test"
    # If a secret is detected, the commit will be blocked

To disable the hook temporarily:

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

GitHub Actions

Use the official Gitleaks action. Three points of this workflow deserve your attention. fetch-depth: 0 is mandatory: without it, the checkout fetches only one commit and the history scan sees nothing. The actions are pinned by SHA rather than by tag, because a Git tag can be moved to another commit by the maintainer or by an attacker who took over the repository. Finally, permissions: {} at the workflow level removes all default permissions, and each job gets back only what it needs: contents: read to clone, pull-requests: write because the action comments on pull requests.

.github/workflows/gitleaks.yml
name: Gitleaks
on:
push:
branches: [main]
pull_request:
permissions: {}
jobs:
gitleaks:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0 # Important: fetch the whole history
persist-credentials: false
- uses: gitleaks/gitleaks-action@e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e # v3.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Only for repositories owned by an organisation
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}

persist-credentials: false prevents actions/checkout from leaving the authentication token in the runner's .git/config file, where any later step of the job could read it back and exfiltrate it.

GitLab CI

Integrate Gitleaks into your merge requests. Two details determine whether the job works. First, the path to scan is a positional argument of the git command, not an option: the old --source flag has disappeared and now returns unknown flag. Second, GIT_DEPTH: 0 is essential, because GitLab clones with a limited depth by default, and a history scan on a truncated clone proves nothing.

.gitlab-ci.yml
stages:
- security
gitleaks:
stage: security
image: ghcr.io/gitleaks/gitleaks:v8.30.1@sha256:c00b6bd0aeb3071cbcb79009cb16a60dd9e0a7c60e2be9ab65d25e6bc8abbb7f
variables:
GIT_DEPTH: 0 # full history, otherwise the scan is blind
script:
- gitleaks git "$CI_PROJECT_DIR" --verbose
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
allow_failure: false

The exit code is 1 by default when a leak is found, so --exit-code is only useful to change that value. Combined with allow_failure: false, the pipeline does stop on detection.

To generate a SARIF report compatible with the GitLab Security Dashboard, add the report format and declare the artifact. Keep the paths key in addition to reports: without it, the file cannot be downloaded from the interface, which complicates analysis when the dashboard is not available in your GitLab edition.

.gitlab-ci.yml
gitleaks:
stage: security
image: ghcr.io/gitleaks/gitleaks:v8.30.1@sha256:c00b6bd0aeb3071cbcb79009cb16a60dd9e0a7c60e2be9ab65d25e6bc8abbb7f
variables:
GIT_DEPTH: 0
script:
- gitleaks git "$CI_PROJECT_DIR" -f sarif -r gl-sast-report.sarif
artifacts:
reports:
sast: gl-sast-report.sarif
paths:
- gl-sast-report.sarif

Troubleshooting

One point before reading this table: "no results" is not good news in itself. Before concluding that a repository is clean, check that the scan actually saw something: the scanned ~N bytes line of the output must show a consistent volume for the size of the repository. A 0 bytes almost always signals a truncated clone in CI or a wrong starting path, not the absence of secrets.

SymptomLikely causeSolution
No result on a test AWS keyv8.30.1 no longer detects an isolated AKIA keyTest with a ghp_ token or a PEM private key
No result in CI, scanned ~0 bytesTruncated clone or wrong starting pathfetch-depth: 0 or GIT_DEPTH: 0, and check the scanned path
Many false positivesTest secrets, examplesUse .gitleaksignore or allowlists
Very slow scanLarge Git historyUse --log-opts="--max-count=100"
"no such file" errorRemote URL not supportedClone the repo locally first
Encoded secret not detectedDecoding disabledCheck --max-decode-depth
Archive not scannedArchive scanning disabledEnable --max-archive-depth=1

Debug logs

The debug level answers the most useful question when behaviour is unexpected: which configuration was loaded and which rules are active. The trace level goes down to the detail of every match tested and quickly floods a terminal; keep it for analysing a custom rule that does not fire, redirecting the output to a file.

Terminal window
# Debug level
gitleaks git --log-level=debug
# Trace level (very verbose)
gitleaks git --log-level=trace

Slow file to scan

A scan that drags is almost always caused by a few specific files, not by general slowness: large archives, single-line minified files, datasets committed by mistake. Since v8.26, Gitleaks reports them in the debug logs, which lets you address the cause rather than lowering the scan coverage.

Terminal window
gitleaks git --log-level=debug 2>&1 | grep "slow"

Add these files to an allowlist or use --max-target-megabytes:

Terminal window
# Ignore files > 10 MB
gitleaks git --max-target-megabytes=10

Best practices

Detecting a secret always costs more than preventing it from existing. The three areas below therefore follow this order of value: prevention first, detection next, remediation as a last resort because at that stage the secret is already compromised.

Prevention: keep secrets out of Git

These four measures reinforce one another, but only one really changes the game: the secrets manager. As long as a secret exists as a string in a file on the development workstation, it will end up committed by accident. The other three measures reduce the probability; the fourth removes the cause.

  1. Pre-commit hooks: block commits that contain secrets
  2. .gitignore files: exclude .env, *.pem, credentials.json
  3. Secrets manager: use OpenBao, Vault, AWS Secrets Manager or Passbolt
  4. Environment variables: inject secrets at runtime

Detection: scan regularly

The pull request scan and the periodic scan do not look for the same thing. The first works on a diff and must stay fast so as not to slow teams down. The second goes over the full history again and catches what yesterday's rules could not detect: every version upgrade of Gitleaks adds rules, so a repository declared clean last year is not necessarily clean today.

  1. CI/CD: scan every pull request and every push
  2. Periodic scans: audit the whole organisation every week
  3. New repositories: add Gitleaks from day one

Remediation: act on findings

The order of this list is an order of priority, not a set of options. Rotation comes before everything else, including before cleaning the history: as long as the secret remains valid, rewriting the commits protects you from nothing, since clones may have been made in the meantime. For step 3, git filter-branch is deprecated by the Git project itself in favour of git-filter-repo, faster and less error-prone; BFG Repo-Cleaner remains a valid alternative.

  1. Immediate rotation: revoke and recreate the exposed secret
  2. Access audit: check the usage logs of the compromised secret
  3. History cleanup: use git filter-repo or BFG Repo-Cleaner
  4. Post-mortem: understand how the secret was exposed

Comparison with TruffleHog

Gitleaks and TruffleHog are the two most popular open source tools for secret detection:

CriterionGitleaksTruffleHog
SpeedVery fastSlower (verification)
Automatic verificationNoYes (800+ APIs)
Supported sources3 (Git, dir, stdin)18 (Git, Docker, S3, Jenkins...)
Number of rules220+800+
ConfigurationFlexible TOMLSimple YAML
DecodingYes: base64, hex, URLYes: base64, UTF-16
ArchivesYes: ZIP, tar...Yes, through --archive-max-depth
Composite rulesYes (v8.28+)No

Recommendation:

  • Gitleaks: ideal for pre-commit hooks and fast CI/CD scans
  • TruffleHog: preferable for security audits with verification of active secrets

A clarification on the last two rows of the table, since both projects evolve quickly: TruffleHog also has decoders (base64, UTF-16, escapes) and can go down into archives through its --archive-max-depth and --archive-max-size options. The real differentiator lies elsewhere: Gitleaks favours speed and fine-grained control of rules, TruffleHog the active verification of the credentials it finds against the APIs concerned.

You can use both: Gitleaks in pre-commit for speed, TruffleHog in a periodic scan for verification.


Key points

  1. Three commands: git (history), dir (files), stdin (pipeline)
  2. 220+ built-in rules: AWS, Stripe, GitHub, Slack, and much more
  3. Automatic decoding: base64, hex and URL encoding, active by default at depth 5
  4. Archive scanning: disabled by default, enable it with --max-archive-depth
  5. Baseline: ignore the secrets already dealt with
  6. Pre-commit: block secrets before they reach Git
  7. Immediate rotation: an exposed secret is a compromised secret
  8. In CI: action pinned by SHA, permissions: {}, full history, image pinned by digest

Knowledge check

Check that you have the essentials of this guide. The questions only cover what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

6 questions
6 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Resources

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn