<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: KeyVault Edge</title>
    <description>The latest articles on DEV Community by KeyVault Edge (@keyvault_edge).</description>
    <link>https://dev.to/keyvault_edge</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4163606%2F7c31fed2-8c8c-4c5c-93b1-c696963ab1f4.png</url>
      <title>DEV Community: KeyVault Edge</title>
      <link>https://dev.to/keyvault_edge</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/keyvault_edge"/>
    <language>en</language>
    <item>
      <title>How to audit your codebase for exposed API keys (free tools)</title>
      <dc:creator>KeyVault Edge</dc:creator>
      <pubDate>Mon, 05 Oct 2026 11:13:20 +0000</pubDate>
      <link>https://dev.to/keyvault_edge/how-to-audit-your-codebase-for-exposed-api-keys-free-tools-27po</link>
      <guid>https://dev.to/keyvault_edge/how-to-audit-your-codebase-for-exposed-api-keys-free-tools-27po</guid>
      <description>&lt;p&gt;Accidental credential commits are often found weeks or months after they happen, long after anyone who was watching had time to use them. This guide walks through a systematic audit of your repositories, git history, Docker images and CI pipelines using free, open-source tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you're scanning and why
&lt;/h2&gt;

&lt;p&gt;A credential audit is not just scanning your current working tree. Secrets committed to git are &lt;strong&gt;permanent in history&lt;/strong&gt; unless you rewrite it. Deleting a file does not remove the secret: the commit with the original file is still reachable via &lt;code&gt;git log&lt;/code&gt; and &lt;code&gt;git show&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A proper audit covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full git history&lt;/strong&gt;: every commit, including deleted files and reverted changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All branches and tags&lt;/strong&gt;: feature branches often hold secrets that never reached main&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker image layers&lt;/strong&gt;: build-time &lt;code&gt;ARG&lt;/code&gt; and &lt;code&gt;ENV&lt;/code&gt; instructions that baked secrets in&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD configs&lt;/strong&gt;: &lt;code&gt;.github/workflows&lt;/code&gt;, &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;, and environment variables echoed into logs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock files&lt;/strong&gt;: rarely, npm/yarn lockfiles contain registry auth tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build artifacts&lt;/strong&gt;: compiled frontends that embedded env vars at build time&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Scanning git history with Gitleaks
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/gitleaks/gitleaks" rel="noopener noreferrer"&gt;Gitleaks&lt;/a&gt; (MIT) is one of the most widely used open-source secret scanners, with built-in rules for OpenAI, AWS, Stripe, GitHub, Twilio and most major providers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install (macOS)&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;gitleaks

&lt;span class="c"&gt;# Install (Linux)&lt;/span&gt;
curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://github.com/gitleaks/gitleaks/releases/latest/download/gitleaks_linux_x64.tar.gz | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xz&lt;/span&gt;
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;gitleaks /usr/local/bin/

&lt;span class="c"&gt;# Scan full git history of the current repo&lt;/span&gt;
gitleaks detect &lt;span class="nt"&gt;--source&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--verbose&lt;/span&gt;

&lt;span class="c"&gt;# Write a JSON report&lt;/span&gt;
gitleaks detect &lt;span class="nt"&gt;--source&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--report-path&lt;/span&gt; gitleaks-report.json &lt;span class="nt"&gt;--report-format&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;detect&lt;/code&gt; scans every commit. On large repos it can take a few minutes; &lt;code&gt;--log-opts&lt;/code&gt; accepts any &lt;code&gt;git log&lt;/code&gt; options, e.g. &lt;code&gt;--log-opts="--since=2025-01-01"&lt;/code&gt; to scan recent history only.&lt;/p&gt;

&lt;p&gt;Add it as a pre-commit hook so new secrets never land:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .pre-commit-config.yaml&lt;/span&gt;
&lt;span class="na"&gt;repos&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/gitleaks/gitleaks&lt;/span&gt;
    &lt;span class="na"&gt;rev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v8.21.0&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gitleaks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pre-commit &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deep scanning with TruffleHog
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/trufflesecurity/trufflehog" rel="noopener noreferrer"&gt;TruffleHog&lt;/a&gt; (AGPL-3.0) goes further by &lt;em&gt;verifying&lt;/em&gt; detected credentials against their upstream APIs: it tells you not just that a string looks like an OpenAI key, but whether that key is currently valid.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Scan local git history, verified findings only&lt;/span&gt;
trufflehog git file://. &lt;span class="nt"&gt;--only-verified&lt;/span&gt;

&lt;span class="c"&gt;# Scan a public GitHub repo&lt;/span&gt;
trufflehog github &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/yourusername/yourrepo

&lt;span class="c"&gt;# Scan every repo in an org&lt;/span&gt;
trufflehog github &lt;span class="nt"&gt;--org&lt;/span&gt; yourorgname &lt;span class="nt"&gt;--only-verified&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--only-verified&lt;/code&gt; is great for triage: it filters out false positives and surfaces credentials that are exploitable right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scanning Docker images
&lt;/h2&gt;

&lt;p&gt;Images built with &lt;code&gt;ARG&lt;/code&gt; or &lt;code&gt;ENV&lt;/code&gt; instructions that reference real keys bake those keys into a layer permanently. A later instruction that overwrites the variable doesn't help: the earlier layer is still in the image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Scan a local or registry image&lt;/span&gt;
trufflehog docker &lt;span class="nt"&gt;--image&lt;/span&gt; yourimage:latest

&lt;span class="c"&gt;# Quick manual check of every layer&lt;/span&gt;
docker save yourimage:latest | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xO&lt;/span&gt; | strings | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'sk-proj|AKIA|ghp_'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The classic mistake:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Bakes the real key into an image layer: don't&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; OPENAI_API_KEY&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; OPENAI_API_KEY=$OPENAI_API_KEY&lt;/span&gt;
&lt;span class="c"&gt;# Inject at runtime instead (docker run -e, or your orchestrator's secrets)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Auditing CI/CD pipelines
&lt;/h2&gt;

&lt;p&gt;Build logs are often public, especially in open source. Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;env:&lt;/code&gt; blocks with literal values (&lt;code&gt;OPENAI_KEY: sk-proj-...&lt;/code&gt;). References like &lt;code&gt;${{ secrets.X }}&lt;/code&gt; are fine.&lt;/li&gt;
&lt;li&gt;Steps that print the environment: &lt;code&gt;run: echo $OPENAI_API_KEY&lt;/code&gt;, &lt;code&gt;env&lt;/code&gt;, &lt;code&gt;printenv&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Test scripts that hardcode keys "for easier local testing".&lt;/li&gt;
&lt;li&gt;Cached artifacts that include environment dumps from crash reporters.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  GitHub's built-in scanning
&lt;/h2&gt;

&lt;p&gt;GitHub secret scanning runs on public repositories and can be enabled for private ones on paid plans. For partner patterns (OpenAI, Stripe, AWS and many more) GitHub also notifies the provider, which may revoke the key.&lt;/p&gt;

&lt;p&gt;Turn on &lt;strong&gt;push protection&lt;/strong&gt; under &lt;code&gt;Settings → Code security → Secret scanning&lt;/code&gt; to block commits containing known secret patterns before they land.&lt;/p&gt;

&lt;h2&gt;
  
  
  When you find something
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Rotate the credential immediately.&lt;/strong&gt; Don't wait for the history rewrite; assume it's compromised.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check for unauthorised usage&lt;/strong&gt; in the provider's logs (OpenAI usage page, AWS CloudTrail, Stripe logs).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove it from history&lt;/strong&gt; with &lt;code&gt;git filter-repo&lt;/code&gt; (not &lt;code&gt;filter-branch&lt;/code&gt;), force-push everywhere, and have contributors re-clone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask GitHub support to clear cached views&lt;/strong&gt; of the old commits; rewriting history doesn't purge them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assume forks still have it.&lt;/strong&gt; You can't rewrite other people's forks, which is why step 1 comes first.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Making the next leak cheaper
&lt;/h2&gt;

&lt;p&gt;Scanning finds the leaks you can see. The other half is making a leaked string less valuable: scoped keys, spending limits, and short-lived or origin-bound tokens.&lt;/p&gt;

&lt;p&gt;That second half is what I'm building with &lt;a href="https://keyvaultedge.com" rel="noopener noreferrer"&gt;KeyVault Edge&lt;/a&gt;: your code ships a token that the edge proxy only exchanges for the real key on requests from origins you allow. It's one layer, not a replacement for rotation and scanning.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://keyvaultedge.com/resources/audit-codebase-for-secrets" rel="noopener noreferrer"&gt;keyvaultedge.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>git</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
