DEV Community

SULTAN SALAUDDIN ANSARI
SULTAN SALAUDDIN ANSARI

Posted on

I Built fix-commit: A Git Pre-Commit Tool That Doesn't Just Find Secrets — It Fixes Them

I Built fix-commit: A Git Pre-Commit Tool That Doesn't Just Find Secrets — It Fixes Them

Hardcoded secrets are one of those problems that every developer knows about, but they are still incredibly easy to introduce.

It can be as simple as:

const API_KEY = "sk-xxxxxxxxxxxxxxxx";
Enter fullscreen mode Exit fullscreen mode

You test your application.

Everything works.

Then you run:

git add .
git commit -m "add API integration"
git push
Enter fullscreen mode Exit fullscreen mode

And now that credential may be sitting inside your Git history.

Even if you delete the line later, the secret may still exist in previous commits.

That's why I built fix-commit.

A lightweight Git pre-commit security tool designed to detect hardcoded secrets, help remediate them, and verify the result before the commit is created.

The Problem

Traditional secret scanners are extremely useful.

They can tell you:

"A secret was found."

But I wanted to explore a slightly different workflow:

Detect
   ↓
Understand
   ↓
Remediate
   ↓
Verify
   ↓
Commit
Enter fullscreen mode Exit fullscreen mode

The goal isn't just to identify a problem.

The goal is to help the developer fix the problem before it reaches Git history.


What is fix-commit?

fix-commit is a Node.js-based security tool that integrates with Git's pre-commit workflow.

It can:

  • 🔍 Detect potential secrets
  • 🛡️ Scan staged files before commits
  • 🔄 Help migrate secrets into environment variables
  • 🧬 Fingerprint previously handled secrets
  • ♻️ Detect duplicate or reintroduced secrets
  • 🚫 Block commits containing potential credentials
  • 🧹 Filter common false positives
  • 📝 Support JavaScript, TypeScript, and Python

The project is currently open source and licensed under MIT.


Example

Suppose a developer writes:

const STRIPE_SECRET_KEY = "sk_test_xxxxxxxxx";
Enter fullscreen mode Exit fullscreen mode

Then they try:

git commit -m "add payment integration"
Enter fullscreen mode Exit fullscreen mode

Instead of allowing the credential to silently enter Git history, fix-commit can detect it:

fix-commit

Scanning staged files...

Potential secret detected

File: src/payment.js
Line: 4
Type: Stripe secret key

Commit blocked.
Enter fullscreen mode Exit fullscreen mode

That's the core idea.

Catch it before the commit.


But Detection Isn't Enough

This is the part I wanted to focus on.

Imagine the scanner finds:

const API_KEY = "sk-xxxxxxxx";
Enter fullscreen mode Exit fullscreen mode

A traditional scanner might simply tell you:

Secret detected.
Enter fullscreen mode Exit fullscreen mode

Now the developer still has to figure out:

  • Where should the secret go?
  • How should the source code change?
  • Should .env be created?
  • Is .env ignored by Git?
  • What should other developers use?
  • How do we verify the migration?

fix-commit explores automating part of that workflow.

For example:

Before

const API_KEY = "sk-xxxxxxxx";
Enter fullscreen mode Exit fullscreen mode

After

const API_KEY = process.env.API_KEY;
Enter fullscreen mode Exit fullscreen mode

The actual credential can then live in:

.env
Enter fullscreen mode Exit fullscreen mode

while the project can provide:

.env.example
Enter fullscreen mode Exit fullscreen mode

for developers.

And .gitignore can protect the real environment file.


Secret Fingerprinting

Another problem appears after you start fixing secrets.

What happens if the same credential appears in multiple places?

For example:

src/api.js
src/config.js
tests/config.test.js
Enter fullscreen mode Exit fullscreen mode

All containing the same credential.

Instead of storing the actual secret value, fix-commit can use a cryptographic fingerprint.

Conceptually:

Secret
   ↓
Cryptographic fingerprint
   ↓
Registry
Enter fullscreen mode Exit fullscreen mode

The registry can then help identify:

Previously detected secret
Duplicate secret
Reintroduced secret
Secret resurrection
Enter fullscreen mode Exit fullscreen mode

without storing the original credential itself.

This is important because a security tool shouldn't create another sensitive database containing everyone's secrets.


Duplicate Secret Detection

Suppose the same credential appears in:

src/api.js
src/config.js
tests/config.test.js
Enter fullscreen mode Exit fullscreen mode

The tool can surface the duplication:

⚠ Duplicate secret detected

src/api.js
src/config.js
tests/config.test.js
Enter fullscreen mode Exit fullscreen mode

This isn't just about security.

It can also help developers understand where credentials are being unnecessarily duplicated.


False Positives Matter

Security tools have another problem:

If they complain about everything, developers stop listening.

A scanner that reports every UUID, example string, documentation URL, or lock-file value as a secret quickly becomes annoying.

fix-commit therefore includes filtering for common non-secret values such as:

  • Lock files
  • Test fixtures
  • Documentation examples
  • Placeholder values
  • UUIDs
  • Dates
  • Image data
  • Documentation URLs

The objective is to balance:

Detection accuracy
        +
Developer experience
Enter fullscreen mode Exit fullscreen mode

Supported Languages

The current source-code support includes:

Language Extensions
JavaScript .js, .jsx, .mjs, .cjs
TypeScript .ts, .tsx
Python .py

The scanner architecture separates language-specific transformations from the core scanning engine, making it easier to add more languages later.


Getting Started

You can run the tool directly with npx:

npx fix-commit init
Enter fullscreen mode Exit fullscreen mode

Then scan the repository:

npx fix-commit scan --all
Enter fullscreen mode Exit fullscreen mode

If automatic migration is supported:

npx fix-commit migrate --all
Enter fullscreen mode Exit fullscreen mode

For non-interactive execution:

npx fix-commit migrate --all --yes
Enter fullscreen mode Exit fullscreen mode

After initialization, developers can continue using Git normally:

git add .
git commit -m "update application configuration"
Enter fullscreen mode Exit fullscreen mode

The pre-commit integration handles the security check.


Project Architecture

I structured the project around independent components:

src/
├── cli/
├── scanner/
├── migration/
├── lifecycle/
├── git/
└── config/
Enter fullscreen mode Exit fullscreen mode

This separation is intentional.

The scanner shouldn't need to know everything about Git.

The migration system shouldn't need to implement secret detection.

The lifecycle system shouldn't need to understand CLI argument parsing.

Keeping these concerns separate makes the project easier to extend.


The Workflow

At a high level:

             Developer
                 │
                 ▼
            git commit
                 │
                 ▼
        ┌─────────────────┐
        │   fix-commit    │
        │  pre-commit     │
        └────────┬────────┘
                 │
                 ▼
          Scan staged files
                 │
          ┌──────┴──────┐
          │             │
        Safe         Secret found
          │             │
          │             ▼
          │        Remediation
          │             │
          │             ▼
          │          Verify
          │             │
          └──────┬──────┘
                 ▼
              Commit
Enter fullscreen mode Exit fullscreen mode

The important part is that the security check happens before the commit becomes part of Git history.


What If a Secret Was Already Pushed?

This is extremely important.

fix-commit is designed primarily for preventing new exposure.

It cannot magically make an already-exposed credential safe.

If a real credential has already been pushed, the appropriate response generally includes:

  1. Revoke the exposed credential.
  2. Generate a replacement.
  3. Move the new credential into secure configuration.
  4. Remove the old credential from repository history where appropriate.
  5. Review other systems where the credential may have been exposed.

Simply deleting the line from the latest version isn't enough.


Why I Built It

There are already many excellent secret-scanning tools.

I didn't want to build another tool whose only job was:

FOUND SECRET
Enter fullscreen mode Exit fullscreen mode

I wanted to explore a more developer-oriented approach:

FOUND SECRET
      ↓
WHAT SHOULD I DO?
      ↓
HELP FIX IT
      ↓
VERIFY THE FIX
      ↓
CONTINUE DEVELOPMENT
Enter fullscreen mode Exit fullscreen mode

That distinction is what inspired the project.


Current Roadmap

There is still a lot I'd like to improve.

Core

  • More secret detectors
  • Better staged-file scanning
  • Stronger false-positive filtering
  • Improved Git integration

Remediation

  • Safer .env migration
  • Better source transformations
  • .gitignore management
  • Migration verification
  • Recovery and backup improvements

Secret Lifecycle

  • Fingerprint registry improvements
  • Duplicate detection
  • Secret resurrection detection
  • Credential lifecycle reporting

Future

  • Additional programming languages
  • CI/CD integration
  • GitHub integration
  • Interactive CLI
  • Optional web interface

Try It

The project is open source:

GitHub: ansarisultan/fix-commit

npm: fix-commit

Install it with:

npm install fix-commit
Enter fullscreen mode Exit fullscreen mode

Or try it without installing globally:

npx fix-commit init
Enter fullscreen mode Exit fullscreen mode

If you find the project useful, I'd love to hear what you think about the approach.

Especially if you work with:

  • Git hooks
  • Developer security
  • Secret management
  • CLI tooling
  • Node.js
  • DevSecOps

There is still plenty to improve, and feedback around the detection/remediation workflow would be particularly valuable.


Final Thought

A secret scanner shouldn't only answer:

"Did I leak a secret?"

A developer also needs:

"How do I fix it safely?"

That's the idea behind fix-commit.

Detect → Remediate → Verify → Commit.

And that's what I'm building toward.

Top comments (0)