The security embargo is dead: patching in the age of AI-generated exploits
AI agents build a working exploit from just the rumour of a bug. What that does to your .NET patch pipeline, and what you can fix on Monday.
Jean-Pierre Broeders
Freelance .NET Developer
Anil Madhavapeddy, who maintains the OCaml library cohttp, got a private bug report over Slack. He did what any decent maintainer does: he opened a public PR with the fix. About ten minutes later the first automated probes hit his servers, aimed at exactly the percent-encoded traversal sequences the PR addressed. Nobody had published a proof-of-concept. There wasn't one. The diff was enough.
His conclusion is right there in the title of his post: the rumour of a bug is now enough to find an exploit. I read it, thought about a couple of my own clients, and went quiet.
Because this doesn't touch the theory of security. It touches the way all of us patch. And at most of the teams I walk into, that process is still built on an assumption that just expired.
The assumption that broke
For years, responsible disclosure ran on an implicit deal. A researcher finds a bug, reports it privately, and the maintainer gets a window. Ninety days, say. Inside that window you build the fix, test it, and ship before the details go public. The embargo bought you time. The attacker still had to do the work of turning a vague advisory into a working exploit, and that work was slow enough that the window was worth something.
That work is now nearly free.
Anil points to the Fang et al. research. They gave a 91-line GPT-4 agent access to fifteen real one-day vulnerabilities along with the CVE description. The agent produced a working exploit in 87 percent of cases. Take the CVE description away and that drops to 7 percent. Read that gap again. The agent isn't finding the bug on its own, but the moment it gets a hint of where to look, the exploit is a matter of minutes.
And the hint doesn't have to be a CVE. A tidy PR title like "fix path traversal in request handler" is a hint. A changelog line is a hint. A commit that suddenly adds an extra .. check is a hint. Anil mentions DeepSeek V4 Pro, which, after a question about path normalization, found several related bugs on its own and wrote an exploit to probe a local server in under a minute.
The numbers he cites are plainly uncomfortable. The marimo vulnerability CVE-2026-39987 went from advisory to first exploitation attempt in nine hours, with no PoC ever existing. And the mean time to exploit, he says, now sits at minus seven days. On average the attack lands before the patch does.
Why this is a .NET problem, not someone else's problem
You could file this under open-source maintainer trouble. It isn't. You run NuGet packages maintained by exactly those people. The moment one of those packages ships a security fix, the race is already on. The question is no longer whether you make the embargo window. There is no window.
The question is: how fast can your team get from "a fix exists" to "that fix is running in production"? If the answer is measured in days or weeks, that is your new exposure window. Not the researcher's ninety days. Your own lead time.
I've seen teams where a dependency update needs three approvals, a manual QA cycle, and a release window that opens once per sprint. In the old model that was slow but defensible. In this one it's an open door you're personally holding open for two weeks.
So let's talk about what you can do on Monday. No abstract principles, just things that work.
Know what you're running
You can't patch what you can't see. The first step is dull and almost always skipped: know which vulnerable packages you have in your solution right now, transitive ones included.
dotnet list package --vulnerable --include-transitive
That --include-transitive is the whole point. Most teams check their direct dependencies and miss that the real bug sits three layers down, in a package they've never heard of that got pulled in by way of something else. Run this locally, then run it in CI so it doesn't depend on anyone's discipline.
An SBOM helps once you run several services. With Microsoft.Sbom.Tool you generate a CycloneDX or SPDX list of everything in a build, so that at the next panicked report you know within minutes which of your twelve services actually uses that one package. Without an SBOM that answer is an afternoon of grep. With one it's a query.
Automate the update loop, and let CI hold it back
An update that waits on a human waits too long. Turn on Dependabot or Renovate and let security updates open a PR automatically. Renovate lets you separate ordinary version bumps from vulnerability fixes, and that split is exactly what you want: most updates can happily sit in a batch, but a security fix belongs at the front of the queue.
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"vulnerabilityAlerts": {
"labels": ["security"],
"automerge": true,
"schedule": ["at any time"]
},
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch"],
"groupName": "non-security updates",
"schedule": ["before 6am on monday"]
}
]
}
The vulnerabilityAlerts block with automerge is the core of it. A security PR that passes your tests may merge itself, at any hour. Your ordinary updates get parked in a weekly batch so the noise doesn't wear you down. And then the guardrail: make your pipeline fail when a known high or critical is present that you haven't patched.
name: dependency-scan
on:
pull_request:
schedule:
- cron: "0 6 * * *"
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: "9.0.x"
- name: Restore
run: dotnet restore
- name: Fail on vulnerable packages
run: |
dotnet list package --vulnerable --include-transitive 2>&1 | tee scan.txt
if grep -E "Critical|High" scan.txt; then
echo "::error::Vulnerable package with High/Critical severity found"
exit 1
fi
This is not a complete security scan. It's a smoke detector. But a smoke detector that goes off every morning at six and blocks every PR is worth an enormous amount next to a team that reaches for dotnet list package only once the place is already on fire.
Deploy speed is now a security property
Here's where the real hit lands for most teams. You can have the fastest dependency scan on earth, but if a merge to main takes nine days to reach production, you've fixed nothing. Your exposure window is your deploy lead time, full stop.
That turns a boring subject urgent. Continuous deployment, a green main you're willing to ship at any moment, a rollback that works in seconds instead of an hour-long change ticket. Those were always good ideas. Now they're security controls.
Feature flags belong in that list. If you have a suspect endpoint in production and the fix is still an hour out, you want to be able to shut that endpoint off without a release. A simple flag check costs you almost nothing and gives you an emergency brake.
app.MapPost("/api/import", async (ImportRequest req, IFeatureManager features) =>
{
if (!await features.IsEnabledAsync("LegacyImport"))
return Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
// ... normal handling
return Results.Ok();
});
Keep that flag in a central store and you can disable a vulnerable path while you build the real fix at a calm pace. No panic deploy at half past eleven at night.
Defend at the edge, not just in the fix
The exploits Anil describes often aim at classics: path traversal, percent-encoding tricks, double decoding. Those are exactly the things you can catch at the edge of your application, before a request ever reaches your business logic. Never trust a path that came from outside.
static bool IsSafePath(string root, string userInput)
{
var combined = Path.Combine(root, userInput);
var fullPath = Path.GetFullPath(combined);
var fullRoot = Path.GetFullPath(root);
return fullPath.StartsWith(fullRoot + Path.DirectorySeparatorChar,
StringComparison.Ordinal);
}
The trick is Path.GetFullPath, which normalizes .. segments before you compare. Someone sending ../../etc/passwd or a double-encoded variant falls outside your root and gets rejected. Pair this with rate limiting on your sensitive endpoints, because an AI agent probing for exploits generates a recognisable pattern of failed attempts.
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("sensitive", opt =>
{
opt.PermitLimit = 10;
opt.Window = TimeSpan.FromSeconds(30);
opt.QueueLimit = 0;
});
});
This won't stop a determined attacker, but it raises the cost and gives your logs something to alert on. And alerting is the point: if you see those ten-minute probes arriving on your own servers, you want a notification, not a discovery after the fact in last week's logs.
Assume something gets through
Secret rotation belongs in this story because the honest assumption is that you'll get hit eventually. When you do, you want the damage to have an expiry date. Short-lived tokens, credentials that rotate on their own, a key you can revoke within the hour without a deploy. Azure Key Vault with managed identities handles most of this, and the difference between "we need to replace every secret by hand" and "rotation already ran overnight" is the difference between an incident and a footnote.
Think about the other end of your pipeline too. If an attacker gets into your build environment through a vulnerable dependency, your CI secrets are the target. Use OIDC instead of long-lived cloud credentials in your GitHub Actions, so there's no static key to steal. A token that expires after the job is a token an attacker can't reuse.
The flip side is that you have the same tools
It's easy to get gloomy about this, so here's the other side. The same agentic tools that write exploits can read your diffs before they go public. I increasingly use a model as a second pair of eyes on a security fix: "what does this PR title leak, and how would someone abuse it?" That's precisely the attacker's question, so ask it first.
Those same agents can scan your legacy code for the patterns now in the crosshairs, generate test cases around your input validation, and review your patch PRs for regressions. The asymmetry is real, because the defender has to hold every door shut and the attacker only needs to find one. But the tooling isn't the attacker's alone. The teams that survive this are the ones that automate the loop from detection to patch to deploy as hard as the attacker automates exploit generation.
Frequently asked questions about AI-generated exploits and your patch pipeline
Does this mean responsible disclosure is pointless now? No, but the window it used to buy you is largely gone. Private reporting still helps by letting the maintainer fix first, you just can't assume you have days or weeks between the public fix and the first exploitation attempt anymore. Treat every security PR as a semi-public announcement of where the bug lives.
How fast do I need a security update in production? As fast as your pipeline reliably allows, and the honest target is hours, not days. The concrete figures in Anil's post show exploitation within nine hours and probes within ten minutes of a public fix, so your merge-to-production lead time is now your actual exposure window.
Is dotnet list package --vulnerable enough as a security scan?
No, it's a smoke detector, not an alarm system. It catches known vulnerabilities in your NuGet graph, transitive ones included, but says nothing about your own code, your configuration, or unknown bugs. Use it as a first guardrail in CI and back it with static analysis and a real dependency scanner.
Should I really turn on automerge for security PRs? Only if your test suite and deploy pipeline are trustworthy enough that you'd rely on them at six in the morning with no human present. If not, that's your actual work: not the automerge, but the tests and rollback that make automerge safe. Start with automerge for patch-level security fixes and widen it as your confidence grows.
Further reading
- Serving markdown to AI agents with Accept headers: the .NET version
- Debian Is Mandating Reproducible Builds. What Does That Mean for .NET?
- Self-Hosting Your Git Forge: What Moving to Forgejo Means for Your .NET Pipelines
Conclusion: Your exposure window is no longer the researcher's embargo, it's your own lead time from merge to production. Measure it, shorten it, and automate the path from detection to deploy as aggressively as the attacker automates exploit generation.
Sources: Rumour is the exploit (Anil Madhavapeddy) · Hacker News discussion.
