Cutting mean time-to-patch from 11 days to 2.4: what changed.
A field report on the workflow shifts that pulled patch latency below the industry baseline. It turned out the hard part was not finding the vulnerabilities. It was getting the fix in front of the right developer, at the right moment, with enough context to act on it.
Every team we talked to in the first months of CyberDebunk said the same thing in slightly different words. It is not that we do not know about the vulnerability. It is that fixing it never gets to the top of the list.
Patch latency is one of the few security metrics that maps cleanly onto outcomes. Eleven days of exposure is enough time for a published CVE to be weaponised and traded. Two and a half days is not. The gap between those two numbers is where most breaches happen, and it is rarely a tooling gap. It is a workflow gap.
This is a short field report on the four changes that, in our pilot cohort, moved median time-to-patch from roughly eleven days down to under three. None of them required buying new tools. All of them required changing the framing.
1. Stop treating findings as tickets
The default workflow at most companies looks like this: a scanner emits a finding, the finding becomes a Jira ticket, the ticket waits in a backlog, an engineer eventually picks it up during a sprint. By the time the engineer reads it, the original context, what changed, who introduced it, what the actual blast radius is, has decayed.
The single biggest shift was treating the finding as a diff rather than a ticket. A vulnerability in aiohttp 3.9.1 is not a unit of work to schedule. It is a one-line change to requirements.txt that someone has to look at and approve. The moment we started shipping the fix as a draft pull request alongside the finding, latency collapsed.
"When the PR is already open, the question changes from 'when do we schedule this?' to 'is there a reason not to merge this?'. Those are very different questions."
2. Surface the fix where the developer already is
The second shift was the delivery channel. Email digests get triaged into "later". Dashboards get checked weekly. Slack channels for security alerts get muted within three weeks of being created.
What works is putting the draft fix directly where the developer is already paying attention:
- The pull-request review queue, alongside their teammate's feature work.
- The repository's own notifications, the same place a CI failure lands.
- A single message in the on-call channel, with the diff and the severity, and nothing else.
Specifically not: a separate "security inbox" that nobody opens, or a daily digest that buries the critical ones with the low-severity ones.
3. Prioritise by exploitability, not by CVSS
CVSS scores treat every vulnerability as if it exists in isolation. A 9.8 in a library you import but never call is not a 9.8 in your environment. A 6.2 in a library that handles untrusted input on a public endpoint absolutely is.
The framing that worked: only escalate when the vulnerability is reachable from external traffic, or touches a credential, or affects production. Everything else stays on the list, but does not interrupt anyone's day. The signal-to-noise improvement made the team treat the remaining alerts seriously.
From 47 alerts a week to 6.
The pilot team was getting roughly 47 vulnerability alerts a week before the change. After filtering to "reachable from prod traffic", they got 6. Those 6 were merged within hours. The other 41 still got scanned, logged, and queued. They just stopped owning the on-call rotation.
4. Measure the right thing
Mean time-to-detect is a metric scanners love because they look great at it. It is also nearly useless. The right metric, the one that maps to risk, is mean time from disclosure to fix landed in production. Detection-to-fix is the only segment of the timeline that the engineering team actually controls.
The actual timeline that delivered the result
| Stage | Before | After |
|---|---|---|
| Detect | ~1 day (scheduled scans) | ~2 min (continuous) |
| Triage | 2-3 days (manual review) | ~0 (auto-prioritise + auto-PR) |
| Author fix | 1-2 days (engineer reads CVE, picks version) | ~0 (PR pre-drafted) |
| Review & merge | 3-5 days (queue + sprint cadence) | ~1-2 days (normal PR review) |
| Deploy | 1-2 days | ~hours (existing CI/CD) |
| Total median | ~11 days | ~2.4 days |
Notice that the deploy and review stages did not change much. The pipeline was already good. The compression came from collapsing detect, triage, and author into approximately zero, because the scanner now produces the diff directly instead of just the warning.
5. Account for the hidden cost of a "simple" version bump
Here is the lie that vulnerability dashboards tell you. "Just bump the version." In about a third of real cases, that single line is the beginning of a multi-day rabbit hole that ends with a developer reading a CHANGELOG, rewriting the call sites, debugging a runtime, and explaining to product why the sprint slipped.
Most version bumps are not actually one-line changes. They are some combination of the following:
- The function was deprecated. The new release still imports, but the call site emits a warning, or the behavior changed subtly enough that a test goes red. The developer now has to learn the replacement API.
- The function was removed. The build passes. Deployment passes. The first request hits production.
AttributeError: module 'aiohttp' has no attribute 'old_function'. Production is down. - The signature changed. Same name, different arguments, different return value. The unit tests pass because they mock the call. The integration test catches it twelve hours later, if you are lucky.
- The transitive dependency tree shifted. The patched version of
library-anow requires[email protected], which conflicts withlibrary-c. The resolver picks something nobody asked for.
None of this is rare. It is the default. And it is the actual reason that "we will patch it next sprint" turns into "we will patch it next quarter".
An eight-line CVE fix that took three days.
A team was running requests 2.27.1, vulnerable to CVE-2024-35195. The advisory said "upgrade to 2.32.0". The bump itself was one line. What it actually required: rewriting how the team handled session verification across four services, because the default behavior of verify changed. Three days. One production incident that almost shipped. Zero new features that sprint.
How CyberDebunk handles this
The auto-PR is not just a version bump. It is a complete change set, including the code that needs to move with it. When the scanner detects that the upgrade target deprecates or removes a function the codebase actually calls, the pull request contains:
- The version bump itself. The first commit.
- The migrated call sites. A second commit that rewrites every usage of the deprecated or removed API to use the replacement, with the new signature and the new return-value handling.
- The reasoning, inline. A comment block on each changed call site explaining what the old function did, what the new one does differently, and why the rewrite is equivalent.
- The test diff. Where existing tests would break, the PR also updates the tests, or flags them as needing review if the behavior change is too semantic to auto-translate.
The developer's job is to read the diff and approve it, not to learn a new API under deadline pressure. The work that took three days in the example above looks like a ten-minute review when it shows up pre-staged.
"We stopped fighting upgrades. They stopped feeling like research projects and started feeling like normal PRs."
This is the part most security tools quietly avoid. They flag the vulnerable version, hand the developer a CVE number, and leave them to figure out the migration. The flagging is the easy 10% of the work. The migration is the 90% that decides whether the fix actually ships.
What did not work
For completeness, the things we tried that did not move the needle:
- Mandating shorter SLAs. Telling engineers "patch within 48 hours" without changing the delivery model just produced more excuses. The constraint was attention, not willingness.
- Weekly security retros. Useful for culture, useless for latency. The information arrived seven days late in a meeting room, not on the relevant pull request.
- Severity-only dashboards. Pretty, easy to put on a TV in the office, but the team stopped looking at them within a month.
The takeaway
Patch latency is not a security problem. It is a product problem. The teams that ship fastest treat security findings the same way they treat any other change request: a small, well-scoped, reviewable diff with a clear owner and an explicit reason. Every minute spent dressing it up as something else, a ticket, a dashboard, a meeting, is a minute the exposure stays open.
Two and a half days is not a ceiling. The teams in our cohort that are best at this routinely close criticals within six hours. The pattern is always the same. Make the fix the easiest thing on the engineer's desk that morning. Get out of the way.
Want to see this in your repository?
Connect a repository and the first auto-PR usually lands within four minutes. You can see exactly what the diff looks like, exactly where it surfaces, and whether it would actually shorten your team's patch cycle.