DEV Community

Cover image for Our Dependency Scanner Said We Were Clean. The Vulnerability Had Been Public for Two Weeks.
Rohit Bhadani
Rohit Bhadani Subscriber

Posted on

Our Dependency Scanner Said We Were Clean. The Vulnerability Had Been Public for Two Weeks.

We run a weekly dependency audit as part of our release checklist — npm audit, a Dependabot digest, the usual. Two weeks ago it came back clean for our Next.js version. Green checkmark, ship it. Last week, I read a writeup about CVE-2026-75604, a critical Next.js vulnerability — CVSS 9.0, no authentication required, actively exploited since late September — affecting exactly the version we were running.

Our scanner had said we were fine. We were not fine. The gap between those two facts is the actual story.

The wrong assumption

My first instinct was that we'd misconfigured something — maybe Dependabot wasn't actually scanning that package, or our npm audit was pointed at a stale advisory database. I went and manually checked npm's own advisory feed for Next.js. Nothing there either, as of the date our last scan ran.

Second instinct: maybe this CVE was announced after our scan and we just hadn't re-run it since. Reasonable, except the exploitation activity had reportedly started back in late September, and I was looking at this in early October. If the vulnerability was being actively exploited for over a week, why had nothing we ran turned it up?

What was actually going on

The vulnerability had been disclosed by Vercel in their own advisory before the formal CVE record was published. For about a week, the CVE ID existed and was referenced publicly, but the actual CVE record — the thing NVD, EPSS, and most automated scoring tools read — hadn't been published yet. Security researchers call this an RBP window: Reserved but Public. The GitHub Advisory Database, which is what Dependabot, npm audit, and OSV actually read from, didn't pick it up until roughly a week after that.

So for close to two weeks, there was a fully public, actively exploited, unauthenticated RCE in a framework millions of applications run — and every automated tool we had, used correctly, configured correctly, running on schedule, said we were clean. Not because the tools were broken. Because they were reading a database that hadn't caught up to a publicly known fact yet.

The bug itself, once I read the technical detail, was almost elegant in how specific it was: an encoded path traversal sequence in a route segment could walk out of the expected cache directory and reach server-reference-manifest.json — the file holding the key Next.js uses to encrypt values passed between browser and server for Server Actions. With that key, an attacker can forge those values without ever authenticating.

The fix

1. We patched immediately, obviously — but the patch wasn't the lesson, the detection gap was.

2. We added a second signal that doesn't depend on CVE record publication timing: we now watch vendor security advisories directly for our core dependencies (Next.js, our database, our auth library), not just the downstream databases that scanners read. A simple RSS/webhook watch on Vercel's, Postgres's, and our auth provider's own security pages, feeding into the same Slack channel as our automated scan results.

import feedparser

VENDOR_FEEDS = {
    "nextjs": "https://vercel.com/changelog/rss.xml",
    "postgres": "https://www.postgresql.org/support/security/feed/",
}

def check_vendor_advisories():
    alerts = []
    for name, url in VENDOR_FEEDS.items():
        feed = feedparser.parse(url)
        for entry in feed.entries[:5]:
            if any(k in entry.title.lower() for k in ("security", "vulnerability", "cve")):
                alerts.append(f"[{name}] {entry.title} — {entry.link}")
    return alerts
Enter fullscreen mode Exit fullscreen mode

Crude, but it means a vendor's own disclosure reaches a human before the downstream advisory databases catch up — which, per this CVE, can be a two-week gap.

3. We also stopped deploying a patched version straight to production on faith. Before any security patch — especially an emergency one, written under time pressure — rolls out, we now run it against a short-lived clone of production first, hitting the exact code paths the CVE described, to confirm the patch actually closes the hole and doesn't break something else in the process. I'm the founder of Krova Cloud, and this is the workflow we built the product around: spin up a disposable VM from a production snapshot, run the patched version against it, throw it away once confirmed. It takes a few minutes and means "we patched it" and "we verified the patch actually works" stop being the same unverified claim.

Lessons

  • A clean automated scan is a claim about what's in the database your scanner reads, not a claim about what's actually publicly known. Those two things can disagree for weeks at a time.
  • "Reserved but Public" is a real, named gap in the vulnerability disclosure pipeline — the CVE can be functionally public while the record your tools depend on hasn't caught up. Worth knowing the term so you recognize the pattern next time your scanner is wrong in this specific way.
  • Watching a handful of vendor security pages directly, for your genuinely critical dependencies, costs almost nothing and catches exactly this kind of gap.
  • Never treat "we deployed the patch" and "we verified the patch works against our actual setup" as the same step. They're not, and the difference matters most exactly when you're patching under pressure.

If your security process ends at "the scanner is green," it might be worth tracing, for your most critical dependency, how long a gap could exist between a vendor's own disclosure and your tooling actually flagging it.


I'm Rohit, founder of Krova Cloud — disposable VMs cloned from production snapshots, built for verifying patches and fixes actually work before they reach real infrastructure. If you want more deep debugging stories like this one, I write regularly over at debugly.dev too.

Top comments (1)

Collapse
 
manojkagitha profile image
Manoj Kumar Kagitha •

The vendor RSS watch is the practical takeaway. We onboarded Snyk scanning at work and hit the same lesson: the scanner tells you what its database knows, not what the vendor already disclosed.