Category Archives: Security

Authentication Bypass in Microsoft Agent Governance Toolkit at 573f989

You install a security system. The app on your phone shows everything armed. The dashboard logs every event. The notifications fire on schedule. You glance at the screen and the system reports green across the board. Then you look at your front door sitting wide open. The dashboard is reporting on a check that never ran.

That describes Microsoft’s agent governance toolkit at commit 573f989.

One Header, One Flag

The Go port of agentmesh exposes an HTTP middleware. The middleware reads an HTTP header into a struct field, and that struct field becomes the agent identity every downstream governance check trusts.

curl -H “X-Agent-ID: davi-spoofed-this” <your-mcp-endpoint>

The audit log records the action under “davi-spoofed-this.” The rate-limit bucket counts against “davi-spoofed-this.” The policy decision is attributed to “davi-spoofed-this.” One header, one flag, and the entire chain of governance attaches itself to whatever string the caller chose to send.

The code lives in packages/agentmesh/middleware.go at line 241. The header flows directly into GovernedOperation.AgentID, with no authentication call anywhere on that path.

Five Ports, One Property

The toolkit ships five languages. Four of them accept the agent ID directly from caller input at the request entry point. All five export a verify_peer-shaped trust primitive, and all five have zero non-test callers of that primitive in their respective workspaces.

Port Entry-point ID source Auth Trust Live callers
Rust pub agent_id: String on DTO None TrustManager::verify_peer 0
Python agent_id: str method parameter None MCPSessionAuthenticator, MCPMessageSigner 0
TypeScript config.agentId at SDK construction None (delegated to embedder) TrustManager.verifyPeer 0
.NET required string AgentId; integration falls back to literal “did:mcp:anonymous” None on core entry points TrustVerifier.VerifyPeer 0
Go X-Agent-ID HTTP header None TrustManager.VerifyPeer (length-only) 0

The Rust port reads a public field on the request DTO. The Python port reads a method parameter. The Go port reads an HTTP header. The .NET port has three layered entry points, all caller-typed. The TypeScript port ships as an SDK with the agent identity set once at construction. The surfaces differ yet every architectural property is identical: none of the ports binds a verified identity to the agent ID before the audit, policy, and rate-limit primitives consume it.

Default Lie Enabled

The .NET port has the most important default. The integration runtime tries three claim names from ClaimsPrincipal in order. If none match, it falls back to a caller-controlled Items dictionary, and if that is empty, it falls back to a hardcoded literal: “did:mcp:anonymous.”

Anonymous.

In any deployment that has not explicitly configured ASP.NET authentication middleware to populate one of the three claim names, every request gets attributed to that same anonymous identity. The audit log records one subject for everything. The rate limiter buckets all traffic against one key. The policy engine evaluates every action under the same subject.

The deployment does not give an error. It does not warn. It does not log a message saying “no authentication configured.” It just sits open.

The operator watches the audit log fill, watches the rate limiter enforce, watches policy decisions get recorded, and the entire log resolves to one subject because the identity being attributed to every action is a literal string in McpGovernanceOptions.cs at line 68.

A correctly configured deployment and a misconfigured one look identical until someone spoofs a claim and the audit log records the wrong attribution.

Six Primitives, Zero Callers

A workspace search for non-test callers of the security primitives in the Rust crate returns the same result for each: TrustManager::verify_peer, RingEnforcer::check_access, AuditLogger::verify, TrustManager::is_trusted, McpMessageSigner, McpSessionAuthenticator. Six security primitives. Each exported from the crate’s public surface. Each with tests that exercise the function in isolation. Each with zero call sites in production code paths.

The pattern shows up in the other four ports. Five trust primitives across five languages, three of which replicate the verify-itself bug verbatim, with Go validating only key length and Python shipping separate session and message authenticators that no production caller invokes. The call-graph property is the same in every port: zero non-test callers.

No Wires, No Way

The MCP gateway constructor in Rust takes (config, sanitizer, rate_limiter, audit_sink, metrics, clock). The .NET constructor takes (config, sanitizer, rateLimiter, maxCallsPerMinute). The Go HTTP middleware constructor takes (rateLimiter, policyEngine, auditLog).

None of them takes an Authenticator interface. None takes a Signer. None takes a TrustVerifier. The integration surface does not invoke the primitives, and there is no parameter on the constructors to plug one in. The primitives exist as exported symbols. The constructors that compose the request flow have no slot to receive them.

Verify-Peer Verifies Itself

TrustManager::verify_peer in trust.rs at lines 188 to 193 takes a peer_id parameter. The parameter name has an underscore prefix, which the Rust compiler reads as “intentionally unused.”

The function generates a 32-byte random challenge in the local process, calls peer_identity.sign(&challenge) in the local process, and verifies the resulting signature against peer_identity’s public key in the local process. The local process signs a value of its own choosing using a key it already holds, then verifies its own signature. Any AgentIdentity passed in returns true.

The doc comment on the function reads: “Generates a random 32-byte challenge, asks the peer to sign it, then verifies the signature against the peer’s public key.”

The .NET TrustVerifier reproduces the same logic at Trust/TrustVerifier.cs lines 42 to 82, with a DID-equality precheck. The TypeScript port reproduces it at src/trust.ts lines 48 to 66. The Go port skips challenge-response entirely and validates len(PublicKey) == 32.

Audit Log

The Rust AuditLogger holds entries in a Mutex<Vec<AuditEntry>> at audit.rs line 16, with no persistence layer behind it. A process restart loses every record from before the restart.

When with_max_entries(N) is set and the buffer overflows, entries.drain(..overflow) at audit.rs lines 62 to 69 removes entries from the front of the chain. After the drain, the new index-zero entry has a non-empty previous_hash field, while AuditLogger::verify at audit.rs lines 78 to 103 requires index-zero to have an empty previous_hash and returns false on that check.

The audit log silently transitions from “tamper-evident” to “always reports tampered” under a normal operating condition: buffer full. There is no checkpoint, no rotated-out signed root, no recovery path. AuditLogger::verify has zero non-test callers anyway, so the chain integrity check that breaks on eviction is never invoked from production code regardless. The audit subsystem ships an integrity primitive that nothing calls, that breaks under normal load, against an in-memory store that does not survive process restart.

Same Crate, Twice

agent-governance-rust/agentmesh-mcp/ is byte-identical to agent-governance-rust/agentmesh/src/mcp/. Twelve files, every one matching under diff -q. The agentmesh-mcp directory ships to crates.io as agent-governance-mcp per its Cargo.toml, and no build script enforces sync between the two locations. Every finding in the mcp tree exists in two crates that ship to crates.io independently. A patch that fixes the agentmesh tree does not fix the agentmesh-mcp crate unless the patch is mirrored.

Different Surface, Same Class

Enclave’s writeup of CVE-2026-32173 documents an authentication failure in Microsoft’s Azure SRE Agent. The /agentHub WebSocket endpoint required a token to connect, and the underlying app registration was multi-tenant, which meant any Entra account from any company anywhere could obtain a valid one. The hub checked that the token was valid and that the audience was correct. It never checked that the caller belonged to the target’s tenant. Once connected, the hub broadcast every event to every client with no per-message identity filter.

A check that ran every step except the only one that mattered.

The agentmesh codebase shows the inverse: checks that would have mattered, written, exported, tested, and never invoked from any production path. Same architectural class, two presentations.

The category here is AI agent governance, and the structural property is that the security surface and the integration surface drift apart unobserved. Reviews of the security surface find primitives that look correct in isolation. Reviews of the integration surface find caller-asserted identity strings flowing into audit, policy, and rate-limit consumers. Neither review catches the gap because the gap lives between them.

What This Means For You

If you have this toolkit in production, check the audit log.

The agent ID it records comes from caller input. Nothing on the request path verifies that the caller is the agent it claims to be. An attacker spoofing the header gets attributed to whichever identity they chose to send.

Hand this to an auditor in the .NET case with default configuration and you are handing them a single-subject log of every action regardless of caller. The store behind that log is in-memory, does not survive a process restart, and breaks chain integrity on overflow. The trust primitives the marketing material describes do exist. They were written. They have tests. They are imported. They are not called.

This is the failure pattern to watch for in every AI agent governance product, not just this one.

Vendors are racing to ship “zero-trust identity” and “cryptographic verification” for agents because the OWASP Top 10 for Agentic Applications calls out Identity and Privilege Abuse (ASI03) and procurement asks vendors to demonstrate the mitigation. The primitives are the easy part. Wiring them into the request flow such that they actually run before audit, policy, and rate-limit consumers see the agent ID is the part that does not happen by accident.

What To Do

Audit any deployment of this toolkit for three things.

First, find every path where an agent ID enters the system from caller input (HTTP header, request body field, method parameter, SDK construction config) and confirm whether anything between that input and the governance consumers verifies the caller. In any deployment that follows the toolkit’s documented integration pattern, the answer will be no.

Second, in the .NET integration, do not rely on the default ClaimsPrincipal probe. Configure ASP.NET authentication middleware explicitly, populate one of the three claim names the toolkit reads, and treat any appearance of “did:mcp:anonymous” in your logs as a configuration alarm rather than expected output.

Third, do not treat the in-memory audit log as durable evidence. If you have a compliance obligation that touches agent activity, route audit entries to external persistent storage on write, not via post-hoc export. The chain integrity check is broken under normal operation, and nothing calls it anyway.

If you are evaluating any AI agent governance product, ask the vendor for the call graph of their authentication primitives in production code paths, not in the test suite.

The test suite proves the primitive works in isolation. The call graph proves the primitive runs when a request arrives. Reviews that look only at the security surface miss this. Reviews that look only at the integration surface miss it too. The gap lives between them, and someone on your side has to look at both at once.

The Class Remains

The agentmesh toolkit ships authentication primitives in every language port with zero production callers in any of them. The verify_peer primitive in three ports signs values it chooses with keys it already holds and verifies its own signature. The audit subsystem is in-memory only, breaks chain integrity under normal load, and has zero callers of the integrity check. The same crate ships twice. Patches will close specific paths while the class remains… surprisingly open.

Injuries Are Up Where Waymo Goes

Not surprised. It’s what I’ve said since at least 2016.

In San Francisco, where Waymo began operating in June 2024, traffic injuries actually increased by 2.6 percent from 2,896 in 2023 to 2,907 in 2025, according to data tracked by the city. …the wrong direction for a company that has argued it will make cities safer.

And Phoenix, where Waymo has operated since 2020, remains one of the most dangerous places to be a pedestrian in the country: In 2019, 80 pedestrians died in crashes there; by 2023, that number increased to 109, a 36.3-percent increase.

See also:

Waymo’s future is easy to predict from their past: unaccountable robots blocking humans, then killing them.

If you think analysis of 2025 incidents is just speculation, Waymo has officially announced in 2026 the things that have been causing harm are now “normal practice”.

  • 2021: “we’ve built the Waymo Driver to share the road with cyclists”
  • 2025: injured cyclist sues Waymo for blocking bike lanes
  • 2026: London Waymo launch with announcement it will be “normal practice” to veer into and block cycle lanes

Nothing like lowering the bar until you find success. It’s how I pump my tires down before every ride.

After all, bicyclists are the top business threat to the company. Anyone who knows how to ride a bike, and hasn’t yet been murdered by Waymo “normal practice”, isn’t likely to get in one to experience the murder of their fellow cyclists.

Alisa Esage Throws Mythos Under Zero Day Bus

I’ve been trying to get real work done, but the Mythos disaster keeps landing in my inbox, so here’s a quick nod.

Remember Cybernews framing of the CVE-2026-5873 demo? The researcher “intervened when the model became stuck, redirecting it and providing debugging feedback.” That’s Clever Hans. The horse isn’t counting. The handler is cueing the horse. Clever Mythos.

Gather around everyone as the future of security research is about to change. Behold Mythos, a counting horse!

Now Alisa Esage has posted claims of a Google Chrome exploit she found that proves the case. “Zero dupes so far” with Mythos. If Anthropic’s disclosures to Google covered the frontier, we should expect overlap with what she is reporting. She reports none.

That throws Glasswing under the big vulnerability bus. The April 7 Mythos rollout positioned the model as discovering apex-target bugs at scale. CVE-2026-5873 in V8, multi-chain renderer-plus-OS sandbox escape, the “too dangerous to release” label. If the model were operating where Esage operates, the two sets would intersect. They don’t.

Her business depends on the market for human-found Zero-days. Anthropic’s Mythos narrative attempts to price her out by running a cartel. She made a falsifiable claim anyway, timestamped, in public. Anthropic holds the disclosure set that would settle the dispute in one post. Let’s see it.

I write about this because I’ve spent a long career finding, reporting, triaging and managing vulns, and because the industry needs to take a hard look at the sudden threat to common sense from an AI corporation, lacking security expertise, trying to corner the security market. My business of putting out fires isn’t threatened by a sloppy fire-starter like Mythos. If anything they are generating a boom for security professionals who have to clean up their mess. I appreciate what Esage is saying and why, because she’s proving the mess.

Anthropic is clearly attempting to fence the entire security industry. Controlling what counts as a finding. What gets disclosed. Which CVE numbers get briefed to press. How the resulting narrative gets shaped.

Google receives the submissions. Reporters write the receipt. Nobody in the corrupted loop of the Church of Anthropic has to demonstrate that disclosed bugs sit anywhere near the actual exploitation frontier. The resolution mechanism sits inside the claimant. Centuries of enlightenment spent building external verification, abandoned for a press release.

Every time a CISO brings their entire team to me and asks “what is this AI speedup of vulns about?” I point them to the above analysis. Read and weep. It’s disinformation and narrative control by a get-rich-quick corporation, top-heavy with PhDs, that doesn’t understand risk management.

The next Anthropic step is probably to trick private equity muscle to act like enforcers and shove “enterprise-ready AI” into places it can’t work and doesn’t belong. This is radical capitalist predatory market behavior, completely decoupled from measures of engineering quality. It’s how you end up with a Tesla on your road, the worst piece of shit in automotive history killing hundreds of people with defective AI. Oops. Hard failure with actual deaths as a result, while some talent-less 2016 AI windbag vacuums up dollars for a decade on broken promises.

Look at the “call 0x41414141“.

Source: Mastodon

She landed the hardest primitive class on a target where the mitigation stack is designed to prevent exactly that. That’s damning evidence. Zero dupes from someone demonstrating that level of control is a substantive technical smackdown on Anthropic’s capability. Esage posted a view of the unassisted frontier for a reason. Mythos is NOT on it.

Sorry Clever Mythos, you’ve been served.