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";
You test your application.
Everything works.
Then you run:
git add .
git commit -m "add API integration"
git push
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
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";
Then they try:
git commit -m "add payment integration"
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.
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";
A traditional scanner might simply tell you:
Secret detected.
Now the developer still has to figure out:
- Where should the secret go?
- How should the source code change?
- Should
.envbe created? - Is
.envignored 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";
After
const API_KEY = process.env.API_KEY;
The actual credential can then live in:
.env
while the project can provide:
.env.example
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
All containing the same credential.
Instead of storing the actual secret value, fix-commit can use a cryptographic fingerprint.
Conceptually:
Secret
↓
Cryptographic fingerprint
↓
Registry
The registry can then help identify:
Previously detected secret
Duplicate secret
Reintroduced secret
Secret resurrection
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
The tool can surface the duplication:
⚠ Duplicate secret detected
src/api.js
src/config.js
tests/config.test.js
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
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
Then scan the repository:
npx fix-commit scan --all
If automatic migration is supported:
npx fix-commit migrate --all
For non-interactive execution:
npx fix-commit migrate --all --yes
After initialization, developers can continue using Git normally:
git add .
git commit -m "update application configuration"
The pre-commit integration handles the security check.
Project Architecture
I structured the project around independent components:
src/
├── cli/
├── scanner/
├── migration/
├── lifecycle/
├── git/
└── config/
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
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:
- Revoke the exposed credential.
- Generate a replacement.
- Move the new credential into secure configuration.
- Remove the old credential from repository history where appropriate.
- 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
I wanted to explore a more developer-oriented approach:
FOUND SECRET
↓
WHAT SHOULD I DO?
↓
HELP FIX IT
↓
VERIFY THE FIX
↓
CONTINUE DEVELOPMENT
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
.envmigration - Better source transformations
-
.gitignoremanagement - 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
Or try it without installing globally:
npx fix-commit init
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)