Between September 13 and September 19, 2026, three security teams disclosed three vulnerabilities in three products that share no code, no vendor, and no protocol. WatchTowr caught forged administrator JWTs hitting WSO2 API Manager honeypots. Air Security broke SHA-pinning across every major AI coding agent — Claude Code, Codex, Copilot, and Gemini CLI. Gal Weizman of Forever Security showed that a single browser extension, using nothing but ordinary ad-blocker permissions, could hijack the built-in AI agents in Chrome, Edge, Opera Neon, Perplexity Comet, and Claude in Chrome.

Read the advisories separately and they look like three unrelated bugs in three unrelated categories: authentication bypass, supply-chain integrity, browser sandboxing. Read them together and they’re the same bug wearing three costumes. In every case, a system had a string that was supposed to prove an object’s identity — a commit SHA, a cryptographic signature, a message’s origin — and instead of verifying the object against that string, it verified that a label existed and moved on. That gap between “this claims to match” and “this actually matches” is the single most productive vulnerability class in 2026, and it’s about to get worse, because the systems built on top of it — autonomous agents with standing credentials — turn a verification gap into full account takeover by design, not by accident.

Three incidents, one lie

Plugin4Shell: the pin that was never checked. AI coding agents let you install third-party plugins and pin them to a reviewed commit SHA — the whole point being that even if the plugin’s repository changes later, your install stays frozen at the hash you audited. The bug: Git can resolve a 40-character hex string as either a commit object or a branch name, and when both exist, Git prefers the branch — it only prints a warning: refname '<sha>' is ambiguous that nothing in an automated pipeline reads. Air Security found that Gemini CLI runs git checkout FETCH_HEAD after fetching a pinned commit; if a plugin repository’s default branch is literally named FETCH_HEAD, that checkout silently lands on the branch instead of the commit that was just fetched by hash. Other agents have the equivalent bug with ordinary SHA-named branches. A plugin author — or anyone who compromises one, via a leaked token or an account takeover — renames or repoints a branch, and every existing install pulls attacker code on its next routine update. No click, no approval, no reinstall. The pin verification step ran. It just verified the wrong thing: that a reference resolved, not that the resolved object matched.

BragJack: the check that trusts the caller’s word. Agentic browsers split into a “brain” (the model, reasoning over instructions) and a “body” (a privileged component that actually clicks, screenshots, reads authenticated pages, and executes decisions). The body is supposed to take commands only from the brain. Weizman found that an ordinary extension — using nothing more exotic than a content script and the declarativeNetRequest API that half the ad blockers in the Chrome Web Store already request — could reach past the brain and hand commands directly to the body, impersonating the agent. Chrome’s flaw (CVE-2026-0628, CVSS 8.8) was a WebView policy bypass that let injected commands slip the isolation boundary. Edge’s (CVE-2026-55945) was worse in a subtler way: a restriction meant to block privileged actions was briefly disabled while a permission prompt was being forced, and re-enabled only after the action had already run — the check executed after the effect it was supposed to gate. Weizman calls the technique Prompt Forcing, to distinguish it from prompt injection: the attacker doesn’t smuggle instructions into content the model reads, it hands the execution layer an entire command and skips the model — and the execution layer’s only check was “did something that looks like the agent send this,” not “did the agent cryptographically originate this.”

WSO2 CVE-2026-5430: the algorithm nobody rejected. JWT verification is supposed to look at the algorithm a token claims to be signed with, confirm the gateway supports and trusts that algorithm, and verify the signature accordingly. WSO2’s API Manager did the first step and skipped the enforcement: when a token specified an algorithm the gateway didn’t recognize, the verification function short-circuited and accepted the token anyway instead of rejecting it. An attacker who understands the quirk hand-crafts a JWT with role: admin and any claims they want, signs it with whatever won’t be checked, and gets treated as an authenticated administrator by an API gateway that never actually verified a signature. WSO2 patched this in May 2026. WatchTowr’s honeypots caught the first live exploitation — forged admin tokens hitting real infrastructure — on September 13, four months after the fix shipped, because the public patch diff made the fail-open path trivial to reverse-engineer for anyone who hadn’t updated.

Name the pattern: resolution without comparison

Strip the domain-specific detail and all three bugs collapse to the same four-step failure:

  1. A system holds a reference — a SHA, an algorithm name, a “this came from the agent” flag.
  2. Verifying that reference requires two operations: resolve it to the actual object, and compare that object against what was promised.
  3. Every one of these systems performed resolution and treated it as if it were comparison.
  4. Nothing downstream distinguished “I looked something up” from “I confirmed it’s the right thing.”

Git resolved FETCH_HEAD to a commit and moved on — it never compared that commit’s hash to the SHA the agent had fetched thirty seconds earlier. WSO2’s gateway resolved the presence of an algorithm field and moved on — it never compared the signature against a key trusted for that algorithm, because there wasn’t one. The browser’s agent body resolved a message claiming to be an agent command and moved on — it never compared the message’s origin against a cryptographic channel only the actual model process could write to.

This is not a new bug class — it’s the oldest one in the book, wearing 2026’s clothes. The 2015 alg: none JWT attack was exactly this: implementations that read the algorithm out of the attacker-controlled header and used it to decide how to verify, instead of pinning the algorithm server-side and refusing anything else. TOCTOU races are this same gap stretched across time instead of across abstraction layers — the thing you checked isn’t the thing you acted on. The XZ Utils backdoor exploited it socially: a maintainer relationship “resolved” as trustworthy through years of unremarkable commits, and nobody re-verified that trust against the specific, camouflaged payload in the final release scripts. What’s different in 2026 isn’t the shape of the bug. It’s that the resolve step now sits in front of components with far more standing power than a if (isAdmin) branch — and that’s the part infrastructure teams haven’t priced in yet.

Why agents turn a verification gap into a blast radius

A traditional fail-open auth bug gets an attacker into one system, with whatever that system happens to expose. A fail-open check in an autonomous agent’s trust boundary gets the attacker the agent’s entire standing capability set, activated on demand, with no further exploitation required — because the agent was already provisioned with broad access as a feature, not a bug.

Look at what BragJack actually delivered once the extension was in: no further exploit needed, no privilege escalation chain, no lateral movement. The extension asked the hijacked agent to open the victim’s email, summarize it, and mail the summary off-device, and the agent — which already had an authenticated session and file-read access as part of its normal job — did exactly that, because from its perspective a legitimate-looking command had arrived through what looked like the right channel. Plugin4Shell is the same shape from the supply-chain side: the payload doesn’t need a separate RCE primitive, because the compromised plugin is the agent, executing with the developer’s or CI runner’s full filesystem, network, and credential access the moment the swapped commit gets checked out. In both cases the “exploit” is one skipped comparison, and the “post-exploitation” is just the agent doing its ordinary job on the attacker’s behalf.

This is the structural argument for why AI agent security keeps producing headline CVEs out of what would, in a pre-agent system, be a medium-severity logic bug: agents collapse the distance between “verification gap” and “full account takeover” to zero, because the whole design goal of an agent is to act with broad delegated authority without a human re-checking every step. Every fail-open check you’d tolerate as a minor finding in a traditional service becomes a critical the moment it sits in front of something with standing credentials and no rate limit on its own judgment.

What actually closes the gap

Patching the three CVEs above doesn’t fix the pattern — it fixes three instances of it. The instances that matter are the ones your own stack has that haven’t made headlines yet. Three concrete things to go check this week:

Audit every “pinned reference” your build or agent tooling trusts, and verify it compares, not resolves. If you pin plugins, containers, or dependencies by hash anywhere in your pipeline, the verification step must look like this, not stop at the checkout:

1
2
3
4
5
6
7
git fetch origin "$PINNED_SHA"
git checkout FETCH_HEAD
actual="$(git rev-parse HEAD)"
if [ "$actual" != "$PINNED_SHA" ]; then
  echo "FATAL: resolved to $actual, expected $PINNED_SHA — refusing to run" >&2
  exit 1
fi

A byte-for-byte comparison after checkout, not before it, and a hard abort on mismatch — no warning, no fallback, no “proceed anyway.” Apply the same logic to container digests, npm/PyPI install hooks, and any place your CI resolves “the version we reviewed” from a name instead of re-deriving it from content.

Make every JWT/token verifier fail closed on anything it doesn’t recognize, by construction, not by convention. The fix for alg: none and CVE-2026-5430 is the same fix: hard-code the exact algorithm(s) your service accepts, reject the token if the header names anything else — including “none,” including algorithms you do support elsewhere but haven’t explicitly allowlisted for this verifier — and treat “I don’t recognize this algorithm” as equivalent to “invalid signature,” never as “skip this check.” If your JWT library has a permissive default (accepts whatever alg the token claims), you are one dependency upgrade away from reintroducing this exact bug; pin the algorithm allowlist explicitly in code, not in a config file someone can leave blank.

Treat any privileged execution channel — an AI agent’s action layer, an RPC endpoint, an IPC socket — as requiring origin authentication, not process-boundary trust. “Only my own code calls this” is not a security control once that code runs alongside extensions, plugins, or any other third-party artifact in the same process or browser context. If you’re building or deploying agentic tooling — coding agents, browser agents, workflow orchestrators — ask specifically: what stops an adjacent, lower-privileged component from handing the privileged executor a command and having it treated as agent-originated? If the honest answer is “nothing, we trust anything that calls the right internal function,” you have BragJack’s bug, just not disclosed yet.

The takeaway

Three unrelated advisories in seven days isn’t a coincidence of timing — it’s what happens when a decades-old bug class (check the label, skip the object) meets a new class of software (agents with standing, broad-scope authority) that turns every instance of it into a full compromise instead of a footnote. The fix in all three cases was cheap: one rev-parse and a string compare, one hard-coded algorithm allowlist, one cryptographic origin check on a message channel. None of them required new infrastructure or a research breakthrough. What they required was someone asking “does this check actually compare the thing, or does it just confirm something resolved” — and in a codebase under deadline pressure, that question rarely gets asked until an advisory forces it. Go ask it about your own pinning, your own token verification, and your own privileged channels before someone else asks it for you.