They Patched the MCP Bridge. The Attacker's Instructions Could Still Be in the Agent's Memory.
CVE-2026-59726, formally assigned in GitHub Security Advisory GHSA-c4hm-4h84-2cf3 with a maximum CVSS 4.0 base score of 10.0, describes Ruflo's MCP bridge accepting POST /mcp and POST /mcp/:group with no authentication and no destination check in its default docker-compose deployment, reaching a terminal_execute tool a blocklist existed for only inside a separate autopilot handler. Version 3.16.3 closes the network and enforcement gap. Ruflo's own remediation also tells operators to audit and purge AgentDB, the agent's own learning store, because whatever an attacker wrote into it while holding that access does not get undone by deploying the fix.
Event analysed: . This analysis was published on 6 September 2026.
Not over, on the evidence Ruflo's own advisory and release notes state directly. GitHub Security Advisory GHSA-c4hm-4h84-2cf3, published 1 July 2026, formally assigns CVE-2026-59726 nine days later at a maximum CVSS 4.0 base score of 10.0, Critical, under CWE-78 (OS command injection), CWE-306 (missing authentication for a critical function) and CWE-942 (permissive cross origin resource sharing), against ruflo, an open source agent meta-harness for Claude Code and Codex, in every version before 3.16.3. This record independently read the affected source directly at the commit immediately before remediation commit d00a0a40cd8bdbca877ac7f675f416bdc69accd1 and confirmed the mechanism the advisory describes: Ruflo's default docker-compose configuration bound the MCP bridge, on port 3001, and its MongoDB backing store, on port 27017, to every network interface rather than to loopback, and neither POST /mcp nor POST /mcp/:group checked any credential before routing a tools/call request into executeTool. A separate function, isBlockedTool, carrying a pattern list that included deploy_ prefixed tools, security_delete, two browser automation tools and terminal_execute, existed in the same codebase and worked correctly, this record confirmed, wherever it ran. It ran in exactly one place: inside the request handler for Ruflo's autopilot mode. The two direct MCP endpoints called executeTool without ever calling isBlockedTool first. An unauthenticated caller reaching either endpoint directly, never touching autopilot at all, could invoke terminal_execute with no restriction the blocklist was built to enforce. Once inside the bridge container's shell, that access reached two further things this record's reading confirms Ruflo's own advisory and release notes describe: the provider API keys, for OpenAI, Google, OpenRouter and Anthropic, that the bridge process held as environment variables, and AgentDB, Ruflo's own persistent pattern and learning store, writable from inside that same container. Pull request 2521, merged the same day as version 3.16.3, closes the network exposure, binding both services to loopback by default and failing the process closed with a non zero exit if an operator configures a public bind without also setting MCP_AUTH_TOKEN, adds a Bearer authentication middleware using a constant time comparison, and, the fix this record treats as the most structurally important one, moves the terminal_execute gate out of the autopilot handler and into executeTool itself, so every caller that can reach executeTool, autopilot included, is now stopped by the same one gate unless an operator has separately set MCP_ENABLE_TERMINAL=true. Ruflo's own release notes list what a patched redeploy does not do: they instruct operators to rotate every provider key, to audit MongoDB for tampering, and, separately, to audit AgentDB itself and manually purge any injected pattern store entries, stating plainly that a patched redeploy alone does not undo that poisoning. This record reads that instruction as the clearest available evidence that the authority failure this advisory documents and the persistent state an attacker could write while holding it are two different things, on two different timelines, and that closing the first does nothing on its own to the second.
Read the advisory in order and the sentence that matters is not the CVSS score. It is the one buried in the operator instructions, after the patched version number: audit AgentDB, purge poisoned pattern store entries, because deploying the fix does not do that for you.
What the advisory and the source together establish
GitHub Security Advisory GHSA-c4hm-4h84-2cf3, filed against ruvnet/ruflo and published 1 July 2026, states its own scope plainly: Ruflo's default docker-compose deployment exposed the MCP bridge's POST /mcp and POST /mcp/:group endpoints with no authentication, and both endpoints accepted a tools/call request naming terminal_execute. CVE-2026-59726 was formally assigned nine days later, on 9 July 2026, at a maximum CVSS 4.0 base score of 10.0, Critical, under three weaknesses: CWE-78, OS command injection, CWE-306, missing authentication for a critical function, and CWE-942, permissive cross origin resource sharing policy. The advisory credits the finding to security researcher Eli Ainhorn of Noma Security, with review credited to Dragan Spiridonov, and names the affected range as every version before 3.16.3.
An enforced blocklist that never reached the endpoint an attacker would use
This is not a case of no policy existing. A policy existed, was correctly written, and this record confirmed it worked exactly as intended inside Ruflo's autopilot flow: a request naming terminal_execute through autopilot was blocked and required explicit user confirmation before proceeding. The gap is that the policy lived inside one caller's own handler rather than inside the function every caller, autopilot included, ultimately shares. An unauthenticated request against POST /mcp or POST /mcp/:group never passed through autopilot's handler at all, so it never reached the check that handler applied. It went straight to executeTool, and executeTool applied no check of its own before this fix. The distinction matters for exactly the reason Ruflo's own remediation names it: pull request 2521's description states directly that the sole call site of isBlockedTool was inside the autopilot handler, leaving the two MCP endpoints unauthenticated and, separately, ungated, and that the fix relocates the check into executeTool itself so that every path sharing that one function shares one denial gate.
Two separate failures compounded, not one
Read carefully, this occurrence carries two distinct mechanisms rather than one. The first is that the MCP bridge, and the MongoDB instance behind it, bound to every network interface by default in Ruflo's own docker-compose configuration, and neither POST /mcp nor POST /mcp/:group checked any credential before acting on a request. That alone would let a network reachable, unauthenticated caller invoke any tool the bridge exposed, terminal_execute among them, with no gate at all standing between the request and execution. The second, distinct from the first, is that even a deployment that somehow gated network reachability, or a caller who reached the endpoint through some other means, would still have found terminal_execute ungated on this specific path, because the one check written for it lived inside a different handler entirely. Fixing the network exposure alone would not have closed the second gap; fixing the enforcement placement alone would not have closed the first. Remediation commit d00a0a40cd8bdbca877ac7f675f416bdc69accd1 closes both, in the same pull request, and this record treats them as two separate authority questions the same incident happened to raise together, not as one weakness restated twice.
What the reachable shell reached
Once an unauthenticated caller reached terminal_execute inside the bridge container, two further things followed from what that container held, both stated directly in Ruflo's own advisory and release notes rather than inferred by this record. The bridge process held provider API keys, for OpenAI, Google, OpenRouter and Anthropic, as environment variables reachable to a shell running inside the same container; a caller with that shell could read them. Separately, the bridge process had write access to AgentDB, Ruflo's own persistent pattern and learning store, the mechanism by which the project's agents carry learned behaviour across sessions rather than starting from nothing each time. A caller who reached the shell could write into that store directly, and Ruflo's own remediation guidance is explicit that a written pattern is understood to be capable of steering the outputs of agents that later read it back.
The instruction the patch cannot execute on your behalf
Read Ruflo's own release notes for 3.16.3 in full and the operator guidance does not stop at the code fix. Alongside firewalling the exposed ports and rotating every provider key, it separately instructs operators to audit AgentDB itself and manually purge any pattern store entries an attacker may have injected, and to audit MongoDB for unauthorized modification. This record reads that as more than routine incident response boilerplate. It is Ruflo's own maintainers stating, in the same document that ships the fix, that the fix does not reach backward: whatever an attacker wrote into the learning store while holding unauthenticated terminal access remains there, unaffected by which version of the bridge is now running, until a human operator goes and looks for it.
What this record does not claim
This record does not claim that every Ruflo deployment ran with the default docker-compose configuration exposed to a reachable network, that every exposed deployment was actually reached and exploited, or that active exploitation of this vulnerability in the wild has been independently confirmed; the advisory describes a disclosed, patched vulnerability with a proof of concept the researcher demonstrated, not a documented in the wild campaign. It does not claim that every AgentDB instance behind an exposed bridge was in fact poisoned, only that the access necessary to poison one existed and that Ruflo's own guidance treats the possibility as serious enough to require a manual audit rather than assuming the fix alone is sufficient. It does not claim that version 3.16.3 itself identifies or removes any already written poisoned pattern; nothing in the release notes or the remediation commit this record read describes an automated scan or purge of existing AgentDB content, only the operator instruction to perform one. And it does not claim that Ruflo's maintainers were negligent in the design choice that put isBlockedTool inside the autopilot handler rather than inside executeTool from the start; the fix itself, closing that gap the same day the researcher's report became public, is the evidence this record has for how the project responded once the gap was identified.
Where this sits in the pattern
This desk has already documented persistent agent state carrying an instruction from one agent to a later one with no re-authorization at the handoff, evidenced there by an instruction copying itself between agents through a shared file each reads as part of its own operating context. This occurrence is not that mechanism, and this record does not merge the two: DSEWiki and the mind virus research both describe an instruction propagating between agents that each hold their own ordinary authority, with no attacker and no revoked access anywhere in the chain. Ruflo's occurrence starts from an attacker who held no authority at all, gained a capability through an authentication and enforcement gap, and used it to write into a store that later, legitimate agent activity reads back as trusted pattern data. What connects the two, without collapsing them, is a principle broader than either single mechanism: persistent state that a future agent execution treats as trusted input carries its own authority question, separate from whatever authority governed the action that most recently touched the live system, and neither a departing legitimate agent's session ending nor an attacker's access being revoked automatically settles what should happen to what either one already wrote. This desk has also already read a case where a caller's admitted reach substituted for authorization over a destination nothing separately checked; Ruflo's own network exposure is a version of that same substitution, reachability read as authorization, at the transport layer rather than at a destination URL. Distinct from both is the mechanism this record treats as the sharper, more specific finding here: a check that existed, was correctly written, and worked everywhere it actually ran, while living inside exactly one caller's own handler rather than inside the function every caller, including the one an attacker actually used, shares.
Sources
This analysis interprets third-party reporting, research and announcements. Moona is not the original reporter of the underlying events.
Protocol evidence
This record does not assess these architectures. The connection runs through the Risk Registry requirement each one bears on, and these published authority architectures are what the evidence says about that requirement.
Protocol evidence related through AEW-028 A capability's enforcement gate is wired to one caller instead of the executor every caller shares
- Supports requirement
Agent Action Decision Protocol (AADP)
Shamik Saha, individual submission to the IETF
Requirement A Policy Decision Point owns authorization state and evidence; PEPs enforce it
AADP's separation of a Policy Decision Point that must issue a permit before a Policy Enforcement Point performs an action names, in the abstract, exactly the placement question Ruflo's own pre-fix source answers concretely and badly: this session confirmed directly that isBlockedTool, a correctly written decision function, was invoked only from inside Ruflo's autopilot handler, never from the shared executeTool function that handler and two unauthenticated MCP endpoints all ultimately call. A decision point wired into one caller's own code, rather than into the enforcement point every caller shares, protects exactly the callers that happen to invoke it and none of the others, which is the specific failure mode AADP's own separation of roles is built to prevent by keeping the decision, not the caller, authoritative over whether the action proceeds.
This record is the cited evidence for this relationship.
- Supports requirement
Agent Control Standard (ACS)
OWASP GenAI Security Project, originally Zenity
Requirement The specification does not guarantee complete mediation of every consequential action a host can take
ACS's own conformance document states directly that the specification does not guarantee complete mediation of every consequential action a host can take, and that a primitive bypassing its own toolCallRequest hook is invisible to policy. Ruflo's own pre-fix source is concrete evidence for the same admission in a different architecture: a mediation point, isBlockedTool, was present, correctly implemented, and independently confirmed by this session to work wherever it actually ran, while two direct MCP endpoints reached the identical underlying executor, executeTool, through a path that mediation point had never been wired into. This entry's own direct reading of the affected source before and after the fix supports the requirement that mediation coverage be verified against every caller able to reach a shared executor, not only the orchestration flow a mediation point's own author originally wrote it for.
This record is the cited evidence for this relationship.
Protocol evidence related through AEW-027 A persistent effect survives the revocation of the authority that created it
- Supports requirement
An Architecture for Auditing Agent Delegation and Interactions (audit-architecture)
Mirja Kuehlewind (Ericsson) and Henk Birkholz (Fraunhofer SIT), individual submission to the IETF
Requirement Authorization is modeled as an ordered sequence of transitions, not a single current value
This weakness names the gap between authority admitted at one moment and authority actually in force at a later moment of consequence, evidenced elsewhere in this registry by CONTINUITY's REVOKED_GRANT and EXPIRED_RELEASE fault classes. The draft's Authorization Transition Record class is the audit-record-format mirror of exactly that property: an ordered previous-state/new-state sequence built so an auditor can reconstruct which state was in force at a specific point in a run, rather than reading the latest known state back over an earlier moment. Recorded as design evidence for the weakness's own principle in the reverse temporal direction, a later revocation must not retroactively deny an earlier, then-still-authorized action; this record's own evidence separately connects to a targeted extension of Moona's canonical Authority Resolution engine implementing exactly that reconstruction for one specific field.
- Missing requirement
Grantex and the Delegated Agent Authorization Protocol (DAAP)
Sanjeev Kumar, Grantex
Requirement Cascade revocation stops future use of descendant grants; nothing found addresses reversing an action a now revoked grant already authorized
This property, verified directly against Grantex's own specification, already records that cascade revocation stops future use of a revoked grant's descendants but that nothing found addresses reversing an action a grant already authorized before it was revoked. Ruflo's own remediation guidance for CVE-2026-59726 is concrete evidence for exactly the same shaped gap one layer further in: an attacker's live access to the MCP bridge was closed by version 3.16.3, and closing it did nothing to whatever the attacker had already written into AgentDB while that access was open, which is why Ruflo's own operators are separately instructed to audit and purge the learning store rather than trust the version upgrade alone. Recorded as a missing requirement because both the specification and this concrete instance name the same unresolved question, whether ending an authority retroactively settles what that authority already produced, without either one describing a mechanism that answers it.
This record is the cited evidence for this relationship.
- Missing requirement
x401, the HTTP Proof Requirement Protocol
Proof, with editors from Proof and Circle and reviewers from Lightspark, MATTR, Okta, OpenAI and Visa
Requirement Whether revoking the underlying mandate or credential immediately invalidates an already issued Verification Token is undocumented
This property, verified directly against the base x401 specification, already records that the specification does not state what happens to an already issued Verification Token if the credential or delegation evidence behind it is later revoked, and that no revocation endpoint or propagation rule was found. Ruflo's own incident is the same unresolved question realised as a concrete consequence rather than an acknowledged gap in a draft: an attacker's access was revoked by the fixed bridge, and the persistent state that access had already produced, an AgentDB pattern store entry, is not described anywhere in Ruflo's own material as automatically invalidated by that revocation. Recorded as a missing requirement rather than a supported one, since neither the specification nor Ruflo's own remediation describes a mechanism that propagates a revocation into previously produced, persistent effects; both leave that step to a separate, manual action.
This record is the cited evidence for this relationship.
