DEV Community

137Foundry
137Foundry

Posted on

How to Set Up a Pre-Commit Hook That Catches Hardcoded Secrets

A hardcoded API key or a committed .env file is one of the oldest security mistakes in software, and it hasn't gone away just because tooling has improved. If anything, AI coding assistants trained on public repositories sometimes reproduce patterns, like a placeholder key formatted just like a real one, or a config example that looks like it should be gitignored but isn't, that make this specific mistake slightly more likely to slip through. Here's a step-by-step way to catch it before it ever reaches a commit.

Step 1: Pick a secret-scanning tool that runs locally

Several well-maintained tools scan a diff for patterns that look like credentials, API keys, and tokens before a commit completes. Widely used open-source options like Gitleaks and detect-secrets both support running as a local pre-commit hook, rather than relying solely on a server-side scan after the push already happened, catching the mistake at the earliest possible point, before the secret has left your machine at all.

Step 2: Install it as an actual git hook, not just a CI step

A secret scanner running only in CI still means the secret was committed and pushed before anyone caught it, which is a meaningfully worse outcome than catching it locally, since a pushed secret needs to be rotated even after the offending commit is removed from history. Configuring the scanner through a framework like pre-commit, which makes hook installation consistent and shareable across a whole team, closes this gap at the source rather than depending on each contributor to remember to set it up manually.

Step 3: Tune the pattern list to your actual providers

Off-the-shelf secret scanners ship with pattern detection for common providers like cloud platforms and popular APIs, but your team likely uses at least a few services the default pattern list doesn't cover. Spend the time to add custom patterns for your specific internal services and less common third-party providers, since a scanner that only catches the well-known formats gives a false sense of complete coverage.

Step 4: Set the hook to fail loudly, not warn quietly

A pre-commit hook that only prints a warning and lets the commit proceed anyway is easy to ignore under deadline pressure, which defeats the purpose. Configure the hook to actually block the commit when it detects a likely secret, requiring an explicit override flag if a match turns out to be a false positive, so the default behavior is safe rather than permissive.

a chalkboard covered in handwritten formulas and diagrams
Photo by Vitaly Gariev on Pexels

Step 5: Add a corresponding CI-level check as a backstop

Not every contributor will have the local hook installed correctly, especially on a larger team or with external contributors, so a server-side scan in CI as a second layer catches anything that slipped past a missing or misconfigured local hook. This isn't redundant, it's defense in depth against the specific failure mode of a hook that exists but isn't actually running for one contributor.

Step 6: Handle historical secrets separately from new ones

A pre-commit hook only protects future commits. If your repository's history already contains committed secrets, those need to be rotated and, depending on your risk tolerance, purged from git history using a dedicated history-rewriting tool like git filter-repo. Running a one-time full-history scan when you first set up the hook surfaces this gap rather than leaving it undiscovered, and the OWASP guidance on secrets management is a reasonable reference for how credential handling fits into a broader application security posture beyond just this one control.

Step 7: Document the override process clearly

False positives happen, particularly with pattern-based detection catching something that looks like a key but isn't. Document a clear, quick process for reviewing and overriding a false positive, since a hook that's frequently annoying to work around gets disabled entirely by a frustrated team faster than one with a smooth, fast override path.

"The teams that get burned by a leaked key almost never had zero protection. They had a scanner that existed on paper but wasn't actually blocking the commit, or wasn't installed on the one contributor's machine where the leak happened." - Dennis Traina, founder of 137Foundry

Step 8: Extend the same discipline to configuration files and examples

Example configuration files, meant to show the shape of a config without real values, are a common place for a placeholder that looks close enough to a real credential to trip a scanner, or worse, to actually contain a real value that was meant to be replaced before committing. Treating example config files with the same scrutiny as application code, rather than assuming they're inherently safe, closes a specific gap AI-generated example configs sometimes introduce.

Why this matters more now, not less

It's tempting to assume secret-scanning tooling is a solved problem that doesn't need active attention. The specific risk from AI-assisted development is subtle: an assistant generating a plausible-looking code example, config snippet, or test fixture occasionally reproduces a pattern that looks enough like a real credential format to be genuinely risky if a placeholder value happens to resemble an actual key from a real account. A properly configured pre-commit hook closes this gap regardless of whether the code came from a human or an assistant.

For the broader picture of where secrets and credential handling fit alongside authentication, billing, and migrations as high-risk categories worth explicit guardrails, 137Foundry's guide on setting up AI coding assistant guardrails covers the full picture.

Getting it set up right the first time

Setting up secret scanning correctly, with the right pattern coverage for your specific stack and a process the team actually follows, is a small investment that prevents a genuinely expensive class of incident. https://137foundry.com works with engineering teams on exactly this kind of practical security tooling, tuned to the specific providers and workflows a team actually uses rather than a generic default configuration.

What to do the moment a real secret does leak

Even with a well-configured hook, mistakes happen, someone commits from a machine without the hook installed, or force-pushes around it under deadline pressure. Having a documented incident response for this specific scenario matters as much as the prevention tooling itself: rotate the credential immediately, treat the old value as compromised regardless of whether you can confirm it was actually used maliciously, and only then worry about cleaning the leaked value out of git history. Rotation first, history cleanup second, is the order that actually protects you, since a leaked credential is exploitable the moment it's pushed, independent of whether it still exists somewhere in your commit history.

Measuring whether the setup is actually working

A secret-scanning setup that's never triggered a false positive in six months of active development is worth a second look, since it either means your team writes exceptionally careful code or, more likely, means the scanner's coverage has a gap nobody's noticed yet. Periodically testing the hook with a deliberately fake credential pattern, then confirming it gets caught, is a cheap way to verify the tooling is actually still working rather than silently failing to run.

Top comments (0)