Skip to content
>_ITDITDWeb Security Platform

Incidents

CriticalCVSS10.0#API key leak#RCE#CVE monitoring#AI coding

AI-written code leaked an API key and ran up fraudulent charges — the real cause was an unpatched CVSS 10.0

A pattern indie developers really do hit: an app shipped with AI's help has its API key stolen and racks up fraudulent charges via unfamiliar bulk jobs. The detour of blaming the wrong culprit, the real cause — a months-old public CVSS 10.0 RCE — and how to stop it recurring, told defensively without attack steps.

Published 2026-06-07 6 min read

A case that security-unfamiliar indie developers commonly hit, with every identifying detail removed and turned into a lesson. No attack reproduction here — the goal is defense, so the same mistake doesn't happen to you.

Case file — INCIDENT FILE
Class
API key leak / fraudulent billing
Severity
Critical (a published CVSS 10.0 abused)
Root cause
A pre-auth RCE in the web framework left unpatched for months
Leak scope
Every secret in the environment (the whole keyring)
Execution scope
Stayed inside an unprivileged container (host root unharmed)
Permanent fix
①upgrade to patched version ②rotate all keys ③machine CVE monitoring
10.0
CVSS / worst class
months
how long unpatched
all env
assumed leak scope
monitoring
the real prevention

“Stopped the charges” ≠ “handled”

Stopping the bill is first aid. Closing the leak path is a separate operation. You're only done when you've done both.

What happened (timeline)

  1. Day 0 — shipped & running

    An app built with AI's help was shipped and running in production.
  2. One day — the bill spikes

    The cloud-AI bill suddenly jumps. Bulk jobs on a model that would never be used, doing work that wasn't theirs.
  3. Investigation — third-party abuse

    "Our app ran wild" doesn't explain it. A third party was running it with a stolen key.
  4. The chase — real investigation begins

    "So where did the key leak from?" — that was the real investigation.

Why it wasn't caught at first (the detours)

In the order the mistakes were made — because the mistakes are the lesson.

1

Jumped to a culprit

First assumption: "our own fallback code went haywire." But the data (what was actually processed) made clear it couldn't be that.
→ Before deciding "my fault" or "their fault," look at the data first.
2

A clean grep nearly brought relief

All code, git history, linked repos searched → no plaintext keys anywhere. Nearly gave up as "untraceable." Staring at files, the runtime leak was missed.
3

Hiding the symptom felt like fixing it

Masking the symptom at the proxy nearly felt like done. But masking leaves the RCE alive. The hole stays open until the root is fixed.

A clean grep is not the all-clear

Even with no key in any file, a vulnerability can pull environment variables out of a running process. Leaks happen at runtime (RCE, HTTP headers), not just in files. See What is RCE.

The real cause: a neglected, published CVSS 10.0

A strange signature in old access logs matched threat intel instantly. The web framework version range in use had a published pre-auth RCE (CVSS 10.0) that was already being exploited in the wild — and it had run unpatched for months.

  • This wasn't a passive bug — it was an attacker executing code on the server and exfiltrating the environment.
  • The saving grace: execution stayed inside an unprivileged container (host root wasn't taken). The breach review found no persistent backdoor, miner, or C2 — the confirmed impact was secret theft.
  • Because the internal DB was reachable, the response assumed the DB contents had leaked too.

Lesson: when something behaves strangely, suspect a known CVE first. Don't assume it's your own bug.

The count was wrong too — judge by the running version

Spreading out, "8 more vulnerable dependencies" was reported — also wrong. They'd been counted by the lower bound in package.json (the ^ caret range). The truly dangerous ones were only the 2 pinned dependencies left behind.

Check your own running versions

See the versions actually resolved, in the lockfile or the running container.

# npm: see what's actually installed
npm ls next react react-dom
 
# inside a running container
docker exec <container> npm ls <package>

The numbers here are the truth — not the ^ in package.json.

What was done first (reproducible steps)

1

Revoke the abused key immediately

First aid — only part of the job.
2

Upgrade the framework to the patched version

Actually close the RCE. After upgrading, confirm the leak signature disappears in the logs.
3

Rotate every secret

Assume-leaked. Order by "abusable from anywhere first": object-storage keys → session-signing key → API keys → DB credentials.
4

Breach review

Check for persistence, rogue cron, outbound C2 (none here).
5

Account-side defense

Change the password, enable MFA, check for unfamiliar API keys.

The real prevention: let machines watch

Boiled down, this incident was "a human overlooked a published CVSS 10.0." Manual patrols always miss things. Machines don't.

Dependency scanning you can start today

Free to start — one step in CI.

# OSV scanner (Google) checks your lockfile
osv-scanner --lockfile=pnpm-lock.yaml
 
# On GitHub, enable Dependabot (repo Settings → Security)

ITD itself, per this lesson, monitors its own dependencies for CVEs — we practice what we recommend.

Lessons

Common mistakes

  • Calling it done at "stopped the charges"
  • Relaxing because grep is clean
  • Masking the symptom at a proxy and feeling fixed
  • Counting vulnerabilities by the package.json floor
  • Rotating only the one confirmed-abused key

Correct defense

  • Treat first aid and "close the leak path" as two separate jobs, do both
  • Suspect runtime leaks (RCE, headers) too
  • Update the root (the framework)
  • Judge by the version actually running
  • Replace the whole env if it leaks

In one line: the runaway bill was the tip of the iceberg. The real cause was a neglected CVSS 10.0 RCE — and the truth came not from one lucky guess, but from being wrong, colliding, and grinding the errors away.

FAQ

QIf an API key leaks, is it enough to revoke just the one key that was abused?
A

No. When the leak path is at runtime (an RCE or a header leak), assume every secret in the environment leaked at once, and rotate them all. The key you saw abused is just the tip of the iceberg.

QIf grep across my code and git finds no keys, am I safe?
A

Not necessarily. Even with no key in any file, a framework vulnerability can exfiltrate environment variables straight from the running process. Leaks happen at runtime too, not just in files.

QWhat stops this from happening again, most reliably?
A

Machine-monitoring your dependencies for CVEs. The root cause here was a human overlooking a published CVSS 10.0 for months; Dependabot or osv-scanner closes that gap structurally.