Security incidents often start with a simple mistake.

A developer accidentally commits an API key, a database password, a private key, or a cloud credential. The secret gets pushed to a repository, appears in CI logs, gets replicated to forks, or becomes part of Git history.

Even when detected quickly, the cleanup can be painful:

  • Rotate credentials
  • Clean repository history
  • Investigate exposure
  • Update environments
  • Document the incident

A much better approach is to prevent secrets from being committed in the first place.

In this article, we'll configure Betterleaks and Git pre-commit hooks to automatically scan staged changes and block commits containing sensitive information.

Why Local Secret Scanning Matters

Many organizations rely on:

  • GitHub Secret Scanning
  • GitLab Secret Detection
  • CI/CD security pipelines
  • Repository scanning solutions

While these tools are important, they usually identify problems after the secret has already entered the repository.

Local scanning shifts detection further left.

Benefits

✅ Immediate feedback for developers

✅ Secrets never reach Git history

✅ Faster remediation

✅ Consistent security practices

✅ Reduced risk of accidental exposure

The earlier a problem is discovered, the cheaper it is to fix.

Step 1: Install Betterleaks

Install Betterleaks on your workstation.

macOS

brew install betterleaks 

Linux

BETTERLEAKS_VERSION="1.8.1" curl -sSfL \ "https://github.com/betterleaks/betterleaks/releases/download/v${BETTERLEAKS_VERSION}/betterleaks_${BETTERLEAKS_VERSION}_linux_x64.tar.gz" \ | tar -xz sudo mv betterleaks /usr/local/bin/ 

Verify Installation

betterleaks --version 

If a version number is displayed, you're ready to continue.

Step 2: Store Git Hooks Inside the Repository

By default, Git stores hooks inside:

.git/hooks 

The problem is that this directory is not version-controlled.

To ensure every developer uses the same hooks, create a dedicated directory:

my-project/ ├── .githooks/ │ └── pre-commit ├── .betterleaks.toml └── README.md 

Configure Git to use this directory:

git config core.hooksPath .githooks 

This command only needs to be executed once per cloned repository.

Step 3: Create the Pre-Commit Hook

Create the file:

.githooks/pre-commit 

Add the following content:

#!/usr/bin/env bash # Verify Betterleaks is installed if ! command -v betterleaks &> /dev/null; then echo "Betterleaks is not installed." echo "Please install Betterleaks before committing." exit 1 fi echo "Running Betterleaks scan..." betterleaks git . \ --staged \ --config .betterleaks.toml EXIT_CODE=$? if [ $EXIT_CODE -ne 0 ]; then echo "" echo "Secrets detected." echo "Commit blocked." exit 1 fi echo "No secrets found." exit 0 

Make the hook executable:

chmod +x .githooks/pre-commit 

Now every commit will automatically trigger a secret scan.

Step 4: Configure Betterleaks

Create the configuration file:

.betterleaks.toml 

Example configuration:

title = "Project-Wide Betterleaks Configuration" [extend] useDefault = true # Optional: # disabledRules = ["generic-api-key"] [[rules]] id = "custom-api-token" description = "Detect custom API tokens" regex = '''MYTOKEN_[A-Za-z0-9]{32}''' keywords = [ "MYTOKEN_" ] [allowlist] description = "Ignore test fixtures" paths = [ '''(^|/)testdata/''', '''(^|/)fixtures/''', '''(^|/)__mocks__/''' ] stopwords = [ "dummy_token", "example_secret", "test_api_key" ] 

This configuration:

  • Uses Betterleaks' built-in detection rules
  • Adds project-specific secret detection
  • Reduces false positives
  • Excludes known test data

Step 5: Test the Setup

Let's verify that the hook works.

Create a file containing a fake GitHub token:

echo "token: ghp_AbC123xYz456D7Ef89GhI01JkLmNoPqRsTuV" > test-secret.txt 

Stage the file:

git add test-secret.txt 

Attempt a commit:

git commit -m "testing secret detection" 

Expected output:

Running Betterleaks scan... Secrets detected. Commit blocked. 

The commit will fail, preventing the secret from entering the repository.

Handling False Positives

No detection tool is perfect.

If Betterleaks incorrectly identifies a value as a secret, you have several options.

Allow a Specific Line

example_value # betterleaks:allow 

Ignore Files or Directories

Create a:

.betterleaksignore 

file to define exclusions.

Customize Detection Rules

Adjust your rules inside:

.betterleaks.toml 

The goal is to reduce noise while maintaining effective security controls.

Emergency Bypass

In rare situations, you may need to bypass the hook:

git commit --no-verify -m "emergency commit" 

Use this carefully.

If developers regularly bypass security controls, it may indicate that the rules need refinement.

Onboarding New Developers

A simple developer onboarding process might look like:

git clone <repository> cd <repository> brew install betterleaks git config core.hooksPath .githooks 

That's it.

From that point onward, every commit is automatically checked before it is created.

Defense in Depth: Local + CI/CD Scanning

Local secret scanning should complement, not replace, existing security controls.

A layered approach typically includes:

Layer Purpose
Pre-Commit Hooks Immediate developer feedback
Pull Request Validation Team-level review
CI/CD Scanning Automated enforcement
Repository Scanning Continuous monitoring
Secret Management Solutions Secure storage and rotation

Security controls are most effective when they work together.

Conclusion

Secret leaks are among the easiest security incidents to prevent.

By integrating Betterleaks directly into the local development workflow, teams can catch sensitive information before it enters a repository.

The setup is lightweight, easy to maintain, and provides immediate value without disrupting developers.

If you're already scanning repositories and CI/CD pipelines for secrets, consider adding local detection as another layer of defense.

The best secret leak is the one that never reaches Git in the first place.

Have You Tried Local Secret Scanning?

How does your team prevent accidental secret exposure?

Do you rely on GitHub Secret Scanning, GitLab Secret Detection, gitleaks, Betterleaks, custom tooling, or a combination of approaches?

I'd be interested to hear what has worked best in your environment.