Our Compliance Checker has now been updated to include a compliance check on the EU AI Act! Learn more.

The Patch Gap: Why Agentic Defense Fails at the Closing Step

10/24/2026

Tags: agentic-security, closing-proof, provenance, hitl, patch-management

Jules Kovac

Jules Kovac

Business Analyst, Founder

The Patch Gap: Why Agentic Defense Fails at the Closing Step

In October 2026, AI found a decade-old bug in PHP in a few hours. Getting the internet to patch it took weeks.

That gap — between how fast a machine can find a flaw and how long it takes us to prove it is actually fixed — is where agentic defense gets decided. Not in the model. In the proof.

 

Two loops, two very different speeds

The finding was fast. Wiz Research says the flaws sat in PHP's core for over a decade, and that showing they were exploitable "required an exceptionally complex chain of steps, at the edge of what human researchers could practically achieve." Frontier models "substantially reduced that barrier," and working exploits were adapted across WordPress, Drupal and Joomla in hours (Wiz).

The fixing was slow. On 21 October, PHP shipped patches for two CVEs — CVE-2026-109618 and CVE-2026-109619, nicknamed "php2shell" — in versions 8.5.12, 8.4.27, 8.3.36 and 8.2.35. CISA asked organisations to inventory every PHP install, patch internet-facing systems first, and take unpatched systems offline (CISA).

Three other things went wrong that same month, and each one pointed at the same weak spot: a certificate authority whose keys were so weak they cracked in milliseconds, a release pipeline that shipped malware while the source code stayed clean, and banks finding AI agents sitting inside customer accounts.

Four very different failures. One shared gap: nobody could prove the loop was closed.

 

What I mean by a closing proof

A closing proof is something a machine can check, showing the dangerous state is gone. A probe that no longer works. A signature that matches. An action that stayed inside its declared mandate. Telemetry that shows the thing is really contained.

Without one, a "handled" alert is just a hidden alert.

 

Rule 1 — Measure the fix, not the reaction

MTTR tells you how fast people reacted. It tells you nothing about whether the risk ended. The number that matters is time from discovery to verified closure.

And that forces a real choice: who sets the autonomy level — the agent, or the rulebook?

My answer: the kind of proof decides the gate.

The proof you can get Example Who signs off
Machine-checkableProbe fails after the patch; revocation reaches clientsThe agent closes it, a human audits later (HOTL)
Needs judgementScope of an incident, business impact, dependency exceptionA human approves before the action (HITL)

eMudhra is the clean example of the first case. Eight intermediate certificates carried private keys of 4, 56, 66, 115, 155, 156, 230 and 237 bits, where P-384 expects around 384. Researchers cracked them in milliseconds, and browsers blocked them "within a day or two" (Ars Technica). The loop closed worldwide with no human in the middle, because the proof — revocation reaching the client — is something a machine can check.

A SOC, meanwhile, still calls a meeting before isolating a laptop. That is not timidity. An isolation's effect simply cannot be proven by a machine alone.

You will recognise the same split elsewhere. Anthropic's Adaptive Thinking lets the model decide how deep to reason. OpenAI's Reasoning Effort makes the operator set it (none to xhigh). One trusts self-regulation, the other a declared parameter. Auditors tend to prefer the second.

In the field. In telecom, a service either answers or it does not, so closure can be machine-checked on inventoried gear. On a port, patching a crane control system can only be confirmed in operation — so a human signs. The gate follows the terrain, not the doctrine.

 

detection { # Detection
n1: circle label:"Start"
n2: rectangle label:"Inventory the real exposure"
n3: rectangle label:"Rank by observed exploitation"
n1.handle(right) -> n2.handle(left)
n2.handle(right) -> n3.handle(left)
n3.handle(bottom) -> remediation.n4.handle(top) [label="Prioritised estate"]
}

remediation { # Remediation
n4: rectangle label:"Deploy the patch"
n5: diamond label:"Patch applies"
n6: rectangle label:"Enable a temporary mitigation"
n7: rectangle label:"Log outcome and due date"
n4.handle(right) -> n5.handle(left)
n5.handle(right) -> n7.handle(top) [label="yes"]
n5.handle(bottom) -> n6.handle(top) [label="no"]
n6.handle(right) -> n7.handle(bottom) [label="mitigation"]
n7.handle(bottom) -> assurance.n8.handle(top) [label="Estate state"]
}

assurance { # Assurance
n8: rectangle label:"Replay the validation probe"
n9: diamond label:"Proof is machine-checkable"
n10: rectangle label:"Close with proof"
n11: circle label:"End"
n8.handle(right) -> n9.handle(left)
n9.handle(right) -> n10.handle(left) [label="yes"]
n10.handle(right) -> n11.handle(left)
n9.handle(bottom) -> human.n12.handle(top) [label="no"]
}

human { # Human
n12: rectangle label:"Approve or refuse closure"
n12.handle(top) -> assurance.n8.handle(bottom) [label="Recorded decision"]
}

 

Rule 2 — An artifact is not its source

On 21 October, LiteLLM's release pipeline was compromised and pushed version 1.107.0 to PyPI and Docker with compiled malware that steals credentials and loads remote-code tools. The PyPI wheels were live from 11:39:51 to 13:23 UTC. The container images were live from 11:46:56 to 14:20 UTC.

Here is the line that matters: "The official v1.107.0 source tag in GitHub does not contain the malicious files. The compromised artifacts were built from an attacker-controlled repository." (LiteLLM)

So an agent that checks the repo checked the wrong thing. The trade-off is simple: sign your sources, or attest your builds? Signing sources is cheap and proves nothing about what actually runs. Attesting artifacts along the build path costs real tooling, and it actually closes the loop, because the proof links source, build and binary.

You will recognise the same split elsewhere. Agents have this blind spot under different names. An MCP tool manifest declares capabilities. An SBOM declares components. An agent card declares permissions. All three are claims. Only a signed attestation ties a claim to what really executed. Any agent that hands you a compliance verdict read off a config file is confusing policy with proof.

In the field. In banking, build chains are already in audit scope, so extending attestation to agent-pushed releases adds a subject, not a whole new governance layer. In insurance, the gap is the vendor: if the attestation stops at the contract boundary, the loop opens again.

 

build { # Build
n1: circle label:"Start"
n2: rectangle label:"Build from a hijacked repo"
n3: rectangle label:"Publish artifact and attestation"
n1.handle(right) -> n2.handle(left)
n2.handle(right) -> n3.handle(left)
n3.handle(bottom) -> provenance.n4.handle(top) [label="Artifact and attestation"]
}

provenance { # Provenance
n4: rectangle label:"Compare source, build and binary"
n5: diamond label:"Attestation matches"
n6: rectangle label:"Promote to production"
n7: circle label:"End"
n4.handle(right) -> n5.handle(left)
n5.handle(right) -> n6.handle(left) [label="yes"]
n6.handle(right) -> n7.handle(left)
n5.handle(bottom) -> registry.n8.handle(top) [label="no"]
}

registry { # Registry
n8: rectangle label:"Quarantine and alert"
n8.handle(bottom) -> build.n2.handle(bottom) [label="Back to build"]
}

 

Rule 3 — The agent that acts must not write its own proof

This is the rule I see written nowhere, and it is the cheapest one to adopt.

The closing proof has to come from an agent that did not perform the action.

Think about what the opposite looks like. An agent isolates an endpoint, then watches the alert disappear. It has not proven containment — it has proven that its own action silenced the signal. That is a false closing, and you cannot spot it from the inside.

eMudhra's own post-mortem names the missing control in words that transfer directly. There were "human errors in multiple steps of the process that resulted in significant flaws in the key lifecycle integrity process," and it points at maker/checker gaps (Ars Technica). In agent terms, maker/checker means two agents, two identities, two read scopes — and one proof.

The cost is real: a second agent per sensitive action doubles model calls and adds latency. You pay that once. A false closing gets paid every time it hides an incident.

You will recognise the same split elsewhere. Anthropic's tool use already works this way. The model proposes a call, the harness executes it, and the result comes back as an outside observation — never as confirmation of the proposal. Hermes does the same with profiles: a code profile cannot read a securite profile's traces and declare itself compliant.

In the field. Banks already have dual control on sensitive movements. Agentic adds one constraint: the second approver has to be a separate identity, not a second call to the same agent. On a port, the second operator does not read the first operator's screen.

 

execution { # Execution
n1: circle label:"Start"
n2: rectangle label:"Isolate the endpoint"
n3: rectangle label:"Emit the action trace"
n1.handle(right) -> n2.handle(left)
n2.handle(right) -> n3.handle(left)
n3.handle(bottom) -> verification.n4.handle(top) [label="Trace and post-action state"]
}

verification { # Verification
n4: rectangle label:"Read independent telemetry"
n5: diamond label:"Effect confirmed"
n6: rectangle label:"Close the case"
n7: circle label:"End"
n4.handle(right) -> n5.handle(left)
n5.handle(right) -> n6.handle(left) [label="yes"]
n6.handle(right) -> n7.handle(left)
n5.handle(bottom) -> human.n8.handle(top) [label="no"]
}

human { # Human
n8: rectangle label:"Re-authorise a second attempt"
n8.handle(bottom) -> execution.n2.handle(bottom) [label="Retry by another agent"]
}

Execution, independent verification and the human re-authorisation loop

 

Rule 4 — Consent is not a mandate

Axios reported that major U.S. banks are finding unauthorised third-party AI agents inside customer accounts, some of them taking actions that look independent of what the user asked for. Nobody broke in: the agent logs in with credentials the customer handed over. That still breaks most banks' rules, which require a human to log in to their own account. Morgan Stanley is blocking third-party agents. JPMorgan, Bank of America and Citi are restricting them. The direction is read-only access (Axios).

A Morgan Stanley spokesperson said the bank "hasn't observed instances of rogue AI agents accessing our platforms." That is a statement about what was observed, not about what happened.

So keep three things apart that everyday language happily merges: the customer's consent, the credentials they shared, and the agent's mandate. Only the mandate is checkable — and only if it is declared and then compared to what the agent actually did.

You will recognise the same split elsewhere. Anthropic and OpenAI handle this with tool scope (tool use, computer use): an agent can only call what it has been exposed to. Microsoft handles it with identity — Security Copilot's agent docs describe agents that connect to an existing account and create a new agent identity, with read permissions on named sources (Microsoft Learn). The banking lesson is that neither half is enough on its own. You need scope, identity, and the comparison between them.

In the field. In insurance, a broker's agent opening a claim file: the policyholder's consent does not define scope, the power of attorney does. In retail, it is loyalty accounts and order history — smaller blast radius, same need for a logged refusal.

 

agent { # Third-party agent
n1: circle label:"Start"
n2: rectangle label:"Log in with customer credentials"
n3: rectangle label:"Request an action outside mandate"
n1.handle(right) -> n2.handle(left)
n2.handle(right) -> n3.handle(left)
n3.handle(bottom) -> access.n4.handle(top) [label="Out-of-mandate request"]
}

access { # Access control
n4: rectangle label:"Compare requested scope to mandate"
n5: diamond label:"Mandate covers the action"
n6: rectangle label:"Grant read-only access"
n7: circle label:"End"
n4.handle(right) -> n5.handle(left)
n5.handle(right) -> n6.handle(left) [label="yes"]
n6.handle(right) -> n7.handle(left)
n5.handle(bottom) -> audit.n8.handle(top) [label="no"]
}

audit { # Audit
n8: rectangle label:"Refuse and log the reason"
n8.handle(bottom) -> agent.n2.handle(bottom) [label="Motivated refusal"]
}

 

Four ways to get this wrong

The mistake Why it bites What October showed
Closing on silence"No exploitation seen" is not "no exploitation"CISA said it had not observed successful exploitation — and still demanded immediate patching
Trusting the source tagThe repo can be clean while the binary is notLiteLLM's source tag held no malicious files
Letting the agent grade itselfThe action removes the signal it createdeMudhra pointed at human error across several steps and broken maker/checker
Reading consent as a mandateA customer can authorise what the bank forbidsAgents inside customer accounts with shared credentials

 

Where to start

Order by what the proof costs, not by maturity:

  1. Artifact provenance. The proof is already an industry standard. No new autonomy needed.
  2. Verified patching. Every scanner already has a probe. What is missing is the rule: no closure without a replay.
  3. Agent mandate. Declare it, compare it, refuse with a reason. Cheap, and it bites immediately on third-party access.
  4. Two-agent containment. Last, because it is the only action whose effect needs judgement — and therefore the only one that earns a human gate.

If you want to push back on one of these, rule 3 is the one I would defend hardest. It is the cheapest to adopt, and the one most teams skip.

 

Inspiration

  • CISA, Critical Vulnerabilities in PHP Require Immediate Patching, 21 Oct 2026 (revised 23 Oct) — cisa.gov
  • Wiz Research, PHP2Shell: AI uncovers critical RCE vulnerabilities, 21 Oct 2026 — wiz.io
  • Ars Technica, Disgraced eMudhra drops out of TLS business following certificate debacle, Oct 2026 — arstechnica.com
  • Axios, Scoop: Unvetted AI agents are accessing U.S. bank accounts, 22 Oct 2026 — axios.com
  • LiteLLM, Contained: Oct 21, 2026 LiteLLM release v1.107.0 Compromised, 21 Oct 2026 — docs.litellm.ai
  • Microsoft Learn, Microsoft Security Copilot agents — learn.microsoft.com
Back to all Blog articles