The defenders aren't the only ones getting AI leverage. In mid-2026, GLM-5.2 — a freely-downloadable, MIT-licensed open-weight model anyone can run — scored 39% F1 finding IDOR and authorization vulnerabilities at roughly $0.17 per vulnerability found, beating flagship closed coding-agent scores. That benchmark measured one narrow bug class, not supply-chain compromise — but the economics it exposes generalize: the same cheap, headcount-free automation that lets a defender triage findings at scale is available to an adversary who wants to phish a maintainer, reverse-engineer a patch, or credential-harvest at scale, none of which require a large team anymore either. Supply-chain attacks were already faster than the defenses built to catch them. Now the people running them can scale their own capability without hiring anyone.
You can watch how that asymmetry plays out in a modern npm or PyPI compromise. A maintainer account gets phished. A malicious version ships. It harvests npm and GitHub tokens, AWS keys, SSH keys — and in the self-propagating cases, uses those stolen credentials to publish itself into the next set of packages. The whole campaign burns hot and fast, and the ecosystem's defenders — competing scanners, the registries themselves, the maintainers — typically catch and yank the bad version within hours to a few days. By the time a CVE is filed, if one ever is, the window that mattered has already closed. The attack lived and died in the gap before any vulnerability database knew about it. We've watched this exact worm-speed pattern land in live sales conversations, because it's the risk AppSec leads feel in their gut and can't answer with a CVE scanner.
A cooldown is our answer to that gap, and it's built the way we think this whole fight has to be fought: decouple the security outcome from headcount by automating the detection end-to-end, running it efficiently enough that it costs nothing to run everywhere, and keeping it controlled and measurable so a security team owns the policy rather than trusting a black box. Concretely, that means a recency-only install filter — a dependency version whose registry publish timestamp is younger than your configured window is treated as risky until the ecosystem has had time to detect and pull a malicious release — that runs with zero LLM cost on a deterministic pipeline and exposes every knob through a REST API and CLI. It never consults CVE or vulnerability data. That's not an oversight — it's the entire point. We are catching the window before a CVE would even exist, which is exactly when real supply-chain worms do their damage. The idea itself didn't even come from us: it came from a prospect's AppSec lead, who described a policy his team wanted but couldn't easily enforce — don't let developers pull a brand-new package version for about a week after it's published — and the more we sat with it, the more we liked it, because it attacks a class of risk every CVE-based scanner on the market is structurally blind to.
It's worth being precise about the boundary, because it's easy to conflate two things that look adjacent but answer opposite questions. Our SCA product answers "is this dependency known-bad?" — it's CVE-based, and when it finds a vulnerable dependency it can reason about whether your code even reaches the flaw. That's per-CVE reachability, and it's the right tool once a vulnerability is known and cataloged. Cooldowns answer the inverse: "is this dependency too new to know yet?" A version can be perfectly clean of any known vulnerability and still trip a cooldown purely because it was published yesterday. There is no known-bad list involved, because the threat model is precisely the versions that no known-bad list has caught up to.
We verified this separation isn't just conceptual. There is no shared code between the cooldown rules and SCA's CVE machinery — grep all of SCA for "cooldown" and you get nothing. Two clean detection paths, two different questions. Cooldowns don't replace CVE scanning; they cover the blind spot that sits temporally in front of it.
The core detector is deliberately dull, in the way good deterministic checks are dull. When a pull request adds or bumps a direct or development dependency, the system looks up that package version's first-published date. If the time since publish is less than the configured window for that ecosystem, it raises a finding. Once the version ages past the window, the check passes automatically on the next scan — no action needed, no manual clearing.
The design work that matters here is all in the guardrails, because a recency check that fires indiscriminately is worse than no check at all:
*, latest, or version ranges, because a cooldown needs a concrete published version to age. And a negative age — a future publish timestamp from registry clock skew — is treated as unknown rather than misread as a "published today" High. That last guard wasn't in the first cut: it was added during the PR #7645 review-hardening pass (commit 2d5b2c8244, finding M1), after review caught that a future/clock-skewed publish time was producing a false High.That fail-open posture is the recurring theme of the whole feature. Every failure mode degrades toward silently under-protecting — never toward blocking or crashing your pipeline. A cooldown check that took down a build over a registry hiccup would get disabled within a week, and then it protects nothing.
This is the part we're most proud of, and it's the part that runs against the grain of how "AI-native" is usually marketed. The entire cooldown detection path involves zero LLM calls. Not "cheap" LLM calls — zero. There is no model reasoning about whether a package is too new; that's a timestamp comparison, and timestamp comparisons are the last thing on earth you want to spend inference tokens on. Not everything needs an LLM, and a security program that reaches for one on every dependency in every repo is lighting money on fire for answers arithmetic already has.
But "just compare timestamps" hides a real scaling problem: where do the timestamps come from, and what happens when a monorepo has ten thousand dependencies and half the registries are slow? The answer is a data pipeline built to make that question irrelevant:
react, requests, express — hits a warm cache, not a live registry, and the cost of cataloging the ecosystem amortizes across every tenant.Put those together and you get a detection path that is deterministic, costs no tokens, and holds its latency flat whether you point it at a five-file service or a ten-thousand-dependency monorepo. That's the least human work and the least AI spend for a real security outcome — which is the bar we hold every feature to.
A genuinely interesting wrinkle showed up once we started implementing: some package managers can now enforce a cooldown natively, at install time, without Nullify in the loop at all.
That second list is why the recent-publish rule escalates to High severity for those ecosystems. If you're pulling a fresh Go module or a fresh NuGet package, there is no mechanical backstop the tooling can offer — Nullify's static detection is doing work the ecosystem itself cannot yet do for that language. For the ecosystems that do support it natively, our finding maps onto a control the package manager could also enforce, which sets up the more durable move: getting that control turned on so the enforcement outlives any single scan.
Catching a fresh install after the fact is good. Making sure it can never quietly happen again is better — and it's where this crosses from a one-time finding into a program-management control. Most teams already run an automated dependency-update bot: Renovate or Dependabot. Those bots can enforce a release-age gate themselves, going forward, on every future bump. So we audit whether they actually do:
Dependabot's cooldown is configured independently per ecosystem block in .github/dependabot.yml, so each ecosystem gets its own compliance check rather than a single pass/fail for the repo. And the Renovate parser catches a real-world gotcha a naive "does the key exist?" check would sail right past: internalChecksFilter set to "none" or "flexible" silently disables an otherwise-configured minimumReleaseAge. The config looks compliant; it isn't.
Underneath sits one tested conversion table, because every tool measures release age in its own units. Renovate, Dependabot, Poetry, and Bundler use days; pnpm and Yarn Berry use minutes; Bun uses seconds; Hex uses a custom Nd/Nw/Nmo suffix grammar; uv's exclude-newer accepts either a bare ISO date or a duration string. We reverse-engineered each of those from the tools' actual docs rather than guessing, so the conformance check reads every config in its native dialect. The result is that a cooldown stops being something Nullify catches for you and becomes something your own bot enforces on every future update — the finding pays for itself once and then keeps paying.
None of this is a black box you have to accept as-is. The full policy surface is exposed through both a REST API and a first-class CLI, so a security team can tune every dimension without an engineer:
CooldownEnabled) — the kill switch.CooldownDays) — the default cooldown length.CooldownOverrides) — e.g. npm at 10 days, PyPI at 14, if you judge one ecosystem riskier than another.From the CLI that's as direct as scpm settings set --cooldown-days 7 --ecosystem npm=10 --ecosystem pypi=14. The platform default is cooldowns enabled with a 7-day window — enough time for the ecosystem to detect and pull a malicious release — and you can configure the window anywhere up to 365 days. Policy loading is itself fail-safe: a tenant with no settings row, or a transient database error, degrades to the platform default with a warning log rather than failing the scan. The feature's worst case is always "quietly fall back to the safe default," never "break the pipeline."
We'd rather tell you where the edges are than imply a finished taxonomy. Today, the recent-publish rule and the updater-bot conformance checks are what's shipped. The code carries a numbering scheme with slots for further recency rules we haven't built yet, and we're not going to pretend those exist. There's no maintainer-change or ownership-transfer detector in the product today. And there's no cooldown-specific autofix path yet — remediation is handled by our general-purpose autofix agent reasoning over the finding's structured context (ecosystem, publish date, age, window, scope), not a dedicated deterministic fixer that auto-pins you to a safe version. The obvious next steps — pin to the most recent version already outside the window, or open a PR adding a minimumReleaseAge gate to your Renovate/Dependabot config — are implied by the data model, and they're where we're headed. Wiring cooldown findings into configurable block/warn levels alongside our other supply-chain categories is on that same path.
The shape of the thing is what we want you to take away. A supply-chain worm doesn't wait for a CVE, so neither do we. The check that catches it is deterministic, costs no tokens, holds its latency flat at any repo size, degrades safely when the registries misbehave, and hands your security team a lever on every knob. It came from a customer's offhand suggestion, and it turned out to be one of the cleanest examples we have of a real security outcome bought with the least possible human effort and the least possible AI spend. Sometimes the right answer to a hard problem isn't a smarter model. It's a timestamp and the discipline to wait a week.