The most dangerous code execution primitive in the Flowise codebase was not a plugin API, a code node, or a deliberately exposed scripting hook. It was a text field labeled as configuration. CVE-2025-59528, rated CVSS 10.0, let anyone who could reach Flowise's CustomMCP node turn a server-config string into arbitrary JavaScript running with full Node.js privileges — child_process, fs, every loaded credential. At the peak of active exploitation in April 2026, internet-wide scanning counted 12,000 to 15,000 Flowise instances sitting on the open internet, unpatched against a flaw whose fix had shipped seven months earlier.
Read that last clause again, because it is the whole story. This was not a zero-day racing defenders. The patch existed. The vulnerable population was visible to anyone with a scanner. And the field that became a shell was one nobody had classified as dangerous, because it looked like data: paste your MCP server config here, we will parse it for you. Except the parser was eval wearing a trench coat.
If you operate anything in the agent-tool stack — an MCP server, a workflow builder, a deploy-from-chat surface — this is the failure mode to internalize. Not a sandbox escape from a hardened boundary. A config field nobody treated as untrusted input in the first place.
Anatomy of the bug: a parser that was secretly eval
Flowise is an open-source drag-and-drop builder for LLM flows and agents. Its CustomMCP node connects a flow to an external MCP server, and it accepts the connection details as a user-supplied mcpServerConfig string. To turn that string into a config object, the node called a helper named convertToValidJSONString. The name promises parsing. The implementation delivered execution:
Function('return ' + mcpServerConfig)()User input flowed straight into the Function() constructor with no validation and no sandbox. Because Flowise runs on Node.js with full runtime privileges, injected code could reach straight for the crown jewels — the classic payload shape breaks out through process.mainModule.require('child_process') and from there it is game over: command execution, filesystem reads, exfiltration of API keys and stored credentials. The reachable trigger was an HTTP endpoint (POST /api/v1/node-load-method/customMCP), which is what made internet-wide exploitation a scanner-and-payload affair rather than something requiring a foothold.
The blast radius was wide. Every Flowise version from 2.2.7-patch.1 up to, but not including, 3.0.6 was affected, and the issue earned the maximum CVSS score of 10.0. It was disclosed in September 2025 and tracked for CISA's Known Exploited Vulnerabilities catalog.
The fix, shipped in 3.0.6, was exactly what the function name had always promised: real parsing. The Function() constructor was replaced with JSON5.parse(), which reads the config as data and refuses to execute it. One call changed, remote code execution gone.
That one-line shape of the fix is worth sitting with. Nobody needed a sandbox, a WASM boundary, or a privilege-separated worker to kill this bug. They needed to not execute a config string. The vulnerability existed because two categories collapsed: the developer treated "configure a tool" and "run code on the box" as the same operation, with no boundary between them.
The seven-month patch gap: the fix shipped, the fleet did not move
Here is the timeline, and it indicts something broader than one project:
- September 2025 — CVE-2025-59528 disclosed; Flowise 3.0.6 ships with the
JSON5.parse()fix. - April 7, 2026 — VulnCheck telemetry observes active in-the-wild exploitation, with the first exploitation traffic originating from a single Starlink IP. An estimated 12,000 to 15,000 Flowise instances are still exposed to the internet.
- Spring–summer 2026 — Follow-on flaws land in the same product surface: an SSRF in the HTTP node (CVE-2026-31829, fixed in 3.0.13) and another critical MCP-adapter command-execution flaw (CVE-2026-40933, fixed in 3.1.0).
- September 2026 — A Mysterium scan of reachable Flowise instances finds 1,341 of them answering on the open internet with zero HTTP authentication challenges among them.
Seven months separated the patch from mass exploitation. A year after disclosure, four-figure populations of the software are still reachable with no auth at all. The lesson is not "patch faster," though patching faster would help. The lesson is that for self-hosted agent tooling, the vulnerable long tail is the steady state: one-click installers and compose files put these builders on the internet in minutes, nothing phones home to nag the operator, and the default posture is reachable. Any threat model for agent infrastructure that assumes "operators will upgrade within weeks" is describing a population that does not exist. Design as though the vulnerable version is still out there — because, in five figures, it is.
A pattern, not a one-off
If CVE-2025-59528 were a single project's embarrassment, it would still be worth a post. It is not. The same twelve-month window produced a pile of CVEs with an identical root-cause shape: untrusted tool input or configuration reaching an execution sink without neutralization. Command injection accounts for the largest share of the roughly 30 MCP-ecosystem CVEs disclosed in a sixty-day stretch this spring, per Bobby Blaine's census.
| CVE | Product | Impact | Root cause in one line |
|---|---|---|---|
| CVE-2025-59528 | Flowise CustomMCP | RCE, CVSS 10.0 | Config string passed to the Function() constructor |
| CVE-2026-5058 / 5059 | aws-mcp-server | Unauthenticated RCE, CVSS 9.8 | "Allowed commands" list passed to a system call without neutralization (CWE-78) |
| CVE-2026-21518 | VS Code mcp.json handling | RCE via config strings | Unsanitized strings in MCP config processing reach system calls |
| CVE-2026-5741 | docker-mcp-server | OS command injection | Crafted tool input reaches a shell without neutralization |
| CVE-2026-23744 | MCPJam Inspector | Unauthenticated RCE | Listens on 0.0.0.0 with no auth; a crafted request installs an MCP server and executes code |
| CVE-2025-3248 | Langflow | Unauthenticated code exec, CVSS 9.8 (CISA KEV) | exec() on user-supplied code without authentication |
Note the recurring characters. The aws-mcp-server flaws are almost poetic: the allowlist — the control that was supposed to constrain execution — was itself the injection vector, because user-supplied strings flowed into a shell command with metacharacters intact. As the LLM-Hacking analysis of that bug shows, the path ran from a reachable MCP endpoint to cloud-credential compromise, since the server process happily executed ;, |, and && alongside the attacker's command. The "allowed" list allowed everything.
Every row in that table is the same sentence: input nobody classified as code reached something that executes code. The fix in each case is also the same sentence, and it is boring on purpose: parse, validate, and never hand a shell a string you did not fully construct yourself.
Six rules for treating agent-tool config as hostile input
Here is the checklist, written for anyone who builds or self-hosts an MCP server or an agent-tool surface. Each rule maps to at least one CVE above — this is not hygiene theater, it is the incident report, generalized.
1. Parse, never evaluate. If a field is config, deserialize it with a real parser — JSON.parse, JSON5.parse, TOML, YAML with safe schema — never eval, Function(), template rendering, or exec() on user content. Flowise's entire CVSS-10.0 collapses into this rule: the fix was swapping one constructor call for a parser. Grep your codebase for eval(, Function(, exec(, and child_process adjacent to request handling; every hit is a finding until proven otherwise.
2. Schema-validate every config field, not just the scary ones. The CustomMCP string looked like plumbing, not an attack surface — which is exactly why it shipped unvalidated. Every field an agent, an operator, or a chat message can populate needs a schema (types, lengths, allowed characters, denied metacharacters) enforced server-side. Client-side validation is UX, not a control. CVE-2026-21518, unsanitized strings in MCP config processing, is what skipping this rule buys.
3. Build argv arrays, never shell strings. Any tool that shells out must use execFile/spawn with an argument vector, never string concatenation into exec() or a system call. The aws-mcp-server CVEs (CVSS 9.8, unauthenticated) are the receipt: an allowlist checked the command name while the shell reinterpreted the rest. If any layer between your code and the kernel performs word-splitting, globbing, or metacharacter interpretation on attacker-influenced text, you do not have an allowlist — you have a suggestion.
4. Authenticate the config plane, not just the tools. Several of these CVEs are unauthenticated RCE because the vulnerable surface was "just" configuration or node setup, sitting outside whatever auth protected the headline features. Every endpoint that accepts config, registers tools, or loads node methods is a privileged operation: require authentication, and treat anonymous access to config-writing endpoints as a vulnerability in its own right. The September 2026 scan finding 1,341 Flowise instances with zero auth challenges shows what the default-deploy population looks like — your threat model should assume your server is one bad compose file away from that population.
5. Bind least-privilege network posture and contain the blast radius. MCPJam Inspector listening on 0.0.0.0 with no auth (CVE-2026-23744) is the extreme, but the general rule holds: bind localhost unless remote access is a deliberate, authenticated choice; run the server process as an unprivileged user with no cloud-credential environment it does not need; and give tool execution its own containment (a sandbox, a microVM, a locked-down worker) so that a config bug buys file reads, not the deploy keys. Recall the Flowise payload's first move was credential theft — the process environment is the prize, so shrink it.
6. Patch fast, and instrument the long tail you cannot patch. The seven-month gap between 3.0.6 and mass exploitation is the empirical answer to "how long do we have." Ship with version reporting and upgrade nagging; if you run a fleet of these tools, scan your own exposure the way the internet does. Operators who discovered their Flowise from someone else's scan in April 2026 learned the most expensive way that "self-hosted" still needs an asset inventory.
What this means for a PaaS's own MCP surface
Now point the checklist at the surface this publication cares about most: an MCP server whose tools are deploy, rollback, and tail-logs. Those verbs look like the dangerous part, and teams dutifully guard them — scoped tokens, approval gates, audit trails. The Flowise lesson is that the dangerous part is every other field: the environment-variable block on the deploy call, the service name that gets interpolated into a pipeline, the log-filter string that reaches a shell, the MCP server config the agent politely asks your platform to store and re-parse later. Each one is a mcpServerConfig until proven otherwise.
So the discipline has to be uniform, not proportional to how dangerous a field looks. The deploy tool gets its approval gate and its string fields get schema validation. The rollback tool checks authority and builds argv arrays. The config endpoint that stores an agent's tool wiring gets authentication even though "it just writes JSON" — because somewhere downstream, something will read that JSON, and the history above says there is a meaningful chance that something is eval.
Twelve thousand exposed boxes, one text field, one constructor call. The agent era is multiplying the number of config-shaped inputs that can reach execution-shaped sinks, in tools installed with one click onto the open internet. Treat every one of them as hostile, and the next CVE-2025-59528 is someone else's postmortem.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, with a Render-compatible API and MCP surface designed for agents as first-class operators. Star the repo on GitHub or deploy your first app today.



