← All news
security5 min read

How to scan a Node.js project for hardcoded secrets and API keys

By Seaworthy · May 21, 2026

A computer monitor displaying cybersecurity-themed code and interface elements, suggesting a hacker workspace or digital security context

Photo by Markus Spiske on Unsplash

Why this keeps happening

Every developer knows not to hardcode an API key. And yet it happens constantly, because the friction of setting up environment variables at the start of a project feels disproportionate to the task at hand. You're just prototyping. You'll clean it up later. You forget. You push. Three days later a bot has found it in your public repo and is running up your AWS bill.

The problem isn't ignorance. It's workflow. Scanning for hardcoded secrets needs to be part of how you work, not a one-off thing you do when you remember.

What you're actually looking for

"Secrets" is a broad term. In practice, you're hunting for:

  • API keys and tokens (OpenAI, Stripe, GitHub, AWS, etc.)
  • Passwords and connection strings with credentials embedded
  • Private keys and certificates (anything that looks like -----BEGIN)
  • JWTs hardcoded as defaults
  • Anything that follows the pattern const API_KEY = "sk-..." or process.env.X || "fallback_secret"

That last one is sneaky. The || "fallback_secret" pattern is everywhere in tutorial code, and it often gets copied into production without anyone noticing the fallback is a real secret.

Tools that actually work

git-secrets

AWS's git-secrets hooks into git itself and blocks commits containing patterns that look like AWS credentials. Setup:

brew install git-secrets   # macOS
git secrets --install      # installs hooks into current repo
git secrets --register-aws # adds AWS-specific patterns

The limitation is that it only knows about AWS patterns out of the box. You can add custom patterns, but it takes effort, and it runs at commit time, not retroactively.

truffleHog

truffleHog scans git history, not just your working tree. This matters because deleting a file doesn't remove it from history.

npx trufflehog git file://. --only-verified

The --only-verified flag attempts to check whether found credentials are still active. It won't catch everything, but it cuts down on noise significantly. Run this before open-sourcing any private repo.

detect-secrets

Yelp's detect-secrets takes a different approach: it creates a baseline file of all "secrets" (or things that look like secrets) in your repo, then future scans only alert on new ones.

pip install detect-secrets
detect-secrets scan > .secrets.baseline
detect-secrets audit .secrets.baseline

This is useful when you've inherited a codebase and can't immediately fix every finding. You acknowledge the existing ones, then block anything new.

Grep: the underrated option

For a quick check on a specific file or pattern, nothing beats knowing what to grep for:

grep -rn --include="*.js" --include="*.ts" \
  -E "(api_key|apikey|secret|password|token)\s*=\s*['\"][^'\"]{8,}" \
  ./src

This won't catch everything, but it'll surface the obvious ones fast. Combine it with a search for environment variable fallbacks:

grep -rn "process\.env\.[A-Z_]+\s*\|\|" ./src

Any hit that has a non-empty string on the right of || deserves a look.

The git history problem

This deserves its own section because developers consistently underestimate it. If you committed a secret six months ago and then deleted it, the secret is still in your history. Anyone who clones your repo has it.

To check your history:

npx trufflehog git file://. --since-commit HEAD~100

Adjust HEAD~100 as needed. If you find something real, you have two options: rotate the credential immediately (always do this), and optionally rewrite history with git filter-repo if the repo is private and you control all forks. If the repo is public, assume the credential is compromised and rotation is your only real fix.

Shifting left: pre-commit hooks

The best time to catch a secret is before it ever reaches a commit. husky plus a scanner is the standard setup:

npm install --save-dev husky
npx husky install
npx husky add .husky/pre-commit "npx trufflehog filesystem . --fail"

This adds maybe two seconds to every commit and will save you from at least one embarrassing incident per year. Whether that's worth the occasional false positive is a judgment call, but for any project with real credentials in play, it's worth it.

What static analysis tools add

Command-line scanners are good at pattern matching. What they miss is context: a secret embedded in a config object that also controls unrelated behaviour, a JWT that's constructed from parts spread across multiple files, or an API client where the key is passed through three layers of abstraction before it's used.

This is where static analysis that understands code structure rather than just text patterns earns its keep. Tools in this space can follow the data flow from where a value is defined to where it's used, rather than just checking whether a string looks like a secret.

Seaworthy's data exposure checks cover hardcoded credential detection as part of its broader static analysis pass across Node.js and TypeScript projects.

This article was generated by AI and summarises publicly available sources.

From the makers of Seaworthy

Seaworthy scans your repo for the issues covered in articles like this one.

Security gaps, exposed secrets, and misconfigurations — caught before you deploy. Free to run, no account needed.

$ npx seaworthycode