Average time to exploit can sit below zero because flaws used before disclosure are averaged with a shorter wait after disclosure. Dan Lorenc attributes the figure to independent reports from Mandiant, CrowdStrike, and similar vendors, and he treats the mechanism as his theory: zero-days were already in the mix of vulnerabilities observed exploited, the tail of bugs exploited 180 or 270 days later is what he says was squeezed, bugs never observed exploited are a separate proportion outside that average, a bug that will be exploited is exploited in weeks, and a cheap or open model can build a proof of concept in minutes, limited by token cost. The practical response is a short path from the upstream fix to every place the old build still runs.
Dan Lorenc, co-founder and CEO of Chainguard, walked through this on Chain of Thought in August 2026.
What does a negative time to exploit change for a dependency pipeline?
Prioritize by active exploitation, exposure, and impact. Do not infer a safe patch window from an average. Lorenc splits the average in two. Bugs exploited before disclosure have existed for a long time. What he thinks changed is the other side: teams used to crawl a backlog and exploit a known issue 180 days or 270 days later. In his theory, a bug that will be exploited is exploited in weeks, because building a proof of concept is now cheap.
Anybody with a cheap model, anybody with an open model can take the knowledge of a vulnerability and build a POC or build an exploit for it in minutes. You're only limited by token cost.
Dan Lorenc, co-founder and CEO of Chainguard, on Chain of Thought ep 68
That speed shows up as two pressures. More people can carry out attacks with less study if they have a model that will do the work, and more holes are showing up, including chains between your code and a dependency. He put open source at 90 to 95 percent of a modern application by line of code, so one shared library turns a single fix into a wide rollout. A disclosed bug you still run sits in that short window.
How do you find every install that still needs the upstream fix?
Keep a list of every dependency and a way to upgrade any of them if a patch ships tomorrow. Lorenc said a maintainer of small projects and an enterprise security leader face the same work at a different scale.
Log4j is his worked example. A small group of maintainers shipped fixes in days. He called that side an open source success, and he cited a NASA tweet about Log4j on a Mars rover to show how widely one package can land. Finding those installs and updating them was the slow part. He said some organizations are still running vulnerable Log4j builds.
When it got reported, they got fixes out in days. It's that downstream side of taking those updates, knowing where it's running, and knowing where to do updates that took months to years.
Dan Lorenc, co-founder and CEO of Chainguard, on Chain of Thought ep 68
Picture that kind of drop in any dependency you have, and write down where your response stalls.
- Automate the upgrade. He said Log4j meant calling in the whole team all weekend. A routine patch should be a normal deploy.
- Cut packages you do not need. He asked whether you need 17 different database drivers. One driver is one upgrade path. If you publish a library, trimming at your layer helps every user, because many of those packages arrive as transitive dependencies.
A current dependency can still pull a five-year-old package indirectly. He said projects will not take a fix for a three or four year old version. They say upgrade. Many flaws found in a running app are already gone on the latest line, sometimes fixed on purpose with no vulnerability filed. Some historical flaws lack advisories, while vulnerability databases also track vulnerabilities already fixed upstream, and most hosts upgrade from those signals. An old pin can miss that feed.
Compare each installed version in the lockfile with the fixed version you have on record, and list the packages still behind. That record might be a maintainer release, a commit that deleted the bad behavior, or a note that the latest line no longer contains the flaw. The sketch accepts only bare name==version lines, canonicalizes names, and handles dotted numeric versions only by zero-padding release segments. Pre-release tags need a separate check, and transitive edges belong in the pipeline around it.
This is an illustrative sketch, not a production library or anyone's actual implementation.
import re
_LINE = re.compile(r"^([A-Za-z0-9_.-]+)==([0-9]+(?:\.[0-9]+)*)$")
def canonical_name(name):
return re.sub(r"[-_.]+", "-", name).lower()
def parse_lock(text):
# Bare name==version lines only. Comments, markers, and lockfile syntax are rejected.
installed = []
for line in text.splitlines():
line = line.strip()
if not line:
continue
match = _LINE.match(line)
if not match:
raise ValueError("expected bare name==version lines")
name, version = match.group(1), match.group(2)
installed.append((canonical_name(name), version))
return installed
def as_tuple(version):
return tuple(int(part) for part in version.split("."))
def _padded_lt(left, right):
width = max(len(left), len(right))
left = left + (0,) * (width - len(left))
right = right + (0,) * (width - len(right))
return left < right
def behind_fix(installed, fixed):
targets = {canonical_name(name): version for name, version in fixed.items()}
late = []
for name, version in installed:
target = targets.get(canonical_name(name))
if target and _padded_lt(as_tuple(version), as_tuple(target)):
late.append({"name": name, "installed": version, "fixed": target})
return late
Feed the hits into the change path you already run. A package with no fixed version on record is one you have not planned. A package that is behind still has to reach every service that loaded that lockfile.
How do you delete a dangerous legacy behavior instead of patching it forever?
Remove the legacy behavior that keeps producing bugs. Lorenc said he does not know of a way off the treadmill, and that security slows it by making a whole class of bug impossible to write.
Going back 10 years or so, cross-site scripting was that class. Unsafe handling of untrusted content is what makes a page susceptible: he said it was almost impossible to avoid when pages rendered that content without escaping. Frameworks, libraries, and browser changes made it rare in the bug bounties he described, by auto-escaping output, while unsafe sinks still need the right encoding and sanitization. He put the pace at vulnerabilities found every day for those bug bounties, then once or twice a year.
After Log4j, he saw Java projects delete remote JNDI code even though the maintainers had not known there was a vulnerability in it. They removed the capability. He expects more deletions like that. Callers depend on the old API, which is why those cleanups stalled before. He thinks the tools that find flaws can carry the syntax and API edits.
I think we should start to rethink some of those assumptions because these same tools that find vulnerabilities are also really good at upgrading software and they're really good at changing syntax and changing API patterns across code bases.
Dan Lorenc, co-founder and CEO of Chainguard, on Chain of Thought ep 68
Review the edit, cover it with tests, and ship a version bump that states what you removed.
FAQ
Why is average time to exploit negative if zero-days are an old category?
Lorenc said the statistic is not his data. It comes from reports by Mandiant, CrowdStrike, and similar vendors. His theory is that pre-disclosure exploits were already in the average of vulnerabilities observed exploited, and the long wait after disclosure, 180 days or 270 days in his example, has compressed to weeks, which pulls the average below zero. Bugs never observed exploited are a separate proportion, with no exploitation time in that average.
Does a large set of model findings equal that many new CVEs?
A submission is often a full report or an exploit chain that still has to be validated, split, and deduped. He said the 40,000 figure is not directly comparable to the National Vulnerability Database. Some results are already gone in the latest version, and some are better handled by upgrading away or removing the package.
What should a maintainer publish if they will not handle vulnerability reports?
Say whether you will handle reports and, if you will, how. He said putting software out as open source does not put you under a burden to fix security issues. If you decline, expect forks. He treats the freedom to fork as a normal part of open source, and he said agents can do much of the merge-conflict and release work, so the cost of a long-lived fork has gone down.
Takeaway
- Attach the negative average to the Mandiant and CrowdStrike reports, and attach the averaging explanation to Lorenc.
- List every dependency and the upgrade you would run if a fix shipped tomorrow.
- Cut packages you do not need. His example was 17 database drivers.
- Compare installed versions, including old transitive pins, with the fixed or cleaned-up release even when no database entry exists.
- Remove dangerous legacy behavior when a point patch would leave it in the tree, and test the compatibility edit before you ship.
The full conversation, with the transcript, is on Chain of Thought.
You can also watch it on YouTube.
I write about what AI builders are learning every week in the Chain of Thought newsletter.
Drafted with AI assistance from the episode transcripts, then edited by me.
Top comments (1)
A patch path is only as fast as the inventory that feeds it. One useful drill is to choose a dependency, inject a simulated fixed-version advisory, and measure time to identify every deployed lockfile and produce a rollbackable update. Tracking unknown ownership separately from known-but-unpatched services prevents the dashboard from making coverage look better than it is.