Five SSRF Records Against One Orchestrator: What conductor-oss’s MCP and A2A Code Actually Does
Between 12:17Z and 13:17Z on 13 October 2026, NVD published five CVEs against conductor-oss/conductor — the Apache-2.0 workflow orchestrator with 32,292 stars that has grown an agent runtime, an MCP client and an A2A client over the past year. All five are CWE-918, server-side request forgery; all five are assigned through VulDB; all five state that “the exploit has been disclosed to the public.” Each names a specific file and a specific argument:
- CVE-2026-109402 (2.1) —
MCPService.listTools, argumentmcpServer, in 3.22.0. - CVE-2026-109190 (2.1) —
ListApiToolsTask.start, argumentspecUrl, in 3.31.2. - CVE-2026-109403 (5.5) —
A2AService.getAgentCard, argumentagentUrl, up to 3.32.5. - CVE-2026-109404 (5.5) — “some unknown functionality of the file
AgentController.java”, Agentspan agent runtime, up to 3.33.0-rc10. - CVE-2026-109405 (5.5) —
KafkaPublishTask.start, argumentkafka_request.bootStrapServers, up to 3.33.0-rc10.
Five records, one project, one weakness class, one afternoon. That is a pattern worth taking seriously — in an orchestrator, every field that holds a URL is an outbound-request primitive, and a workflow definition is a user-supplied program that fills those fields. So we read every named file at main HEAD, and the two that carry explicit version boundaries at the tags they name. The results are not uniform, and the differences are the story.
MCPService: no outbound policy, anywhere on the path
The MCP record checks out, and it is the one we would act on. In ai/src/main/java/org/conductoross/conductor/ai/mcp/MCPService.java at main HEAD, listTools(serverUrl, headers) delegates to listToolsHttp, which delegates to listToolsDirectHttp, which builds an OkHttp request with .url(serverUrl) and sends it. The same shape repeats for callTool. The only validation anywhere in the file runs on the response: two calls to ExternalDataLimits.validateStructure, which caps what comes back, not where the request goes.
Nor is the policy hiding in the client. The ai/http package contains five files — AIHttpClientConfiguration, AIHttpClientProperties, AIHttpClients, ExternalDataLimits and RetryInterceptor — and the shared client builder installs exactly one interceptor, the retry one. There is no address filter, no scheme allowlist, no metadata-endpoint block. The project’s own MCPServiceSecurityTest contains a single test method, rejectsOversizedMcpResponsesBeforeNormalizingToolOutput: response size is a tested security property; destination is not. An MCP server URL that reaches the orchestrator reaches whatever the orchestrator can reach.
A2AService: the guard the record says is missing is already there
Thirty lines of directory away, the same project does the opposite. A2AService.getAgentCard opens by calling validateAgentUrl(agentUrl). That method rejects a blank URL, rejects any scheme that is not http or https, and then rejects resolved addresses with two distinct errors whose text we quote verbatim: “agentUrl resolves to a cloud metadata address — SSRF blocked” and “agentUrl resolves to a private/reserved address — SSRF blocked.” The same validation is called on the streaming path, with an in-code comment that reads “SSRF guard — must run on the streaming path too,” and the client disables automatic redirects with the reason stated in the source: “Disable auto-redirects for A2A calls: validateAgentUrl() checks only the initial URL.” Someone on this project has thought carefully about SSRF.
CVE-2026-109403 names affected versions “up to 3.32.5.” We fetched A2AService.java at tag v3.32.5 (commit dated 25 September 2026), at v3.33.0-rc10, at v3.33.0-rc12 and at main. The guard is present in all four, and the file’s most recent commit predates the 3.32 line’s later tags. We could not retrieve VulDB’s underlying entry to see what the public proof-of-concept demonstrates — the CVE page returned HTTP 403 to us and the entry page redirected — so we make no claim that the report is baseless. A DNS-rebinding or TOCTOU argument against a resolve-then-connect check is a real category, and the authors clearly anticipated the redirect variant. What we can say, from the artefact, is narrower and still useful: the record as written describes a missing control that is present in the version it names, and an operator triaging on the description alone will reach the wrong conclusion about their exposure.
The API-spec task promises a policy its loop does not implement
ListApiToolsTask — the Agentspan task that turns an OpenAPI, Swagger or Postman document at specUrl into callable tools — sits between the two. It fetches through a helper named sendWithValidatedRedirects, whose Javadoc reads: “Follows a bounded redirect chain only after each destination passes the outbound policy.”
Read the loop and the outbound policy is not there. What it enforces is a redirect cap of five, a required Location header, a refusal to forward authorization, cookie or proxy-authorization headers across an origin change, and a CR/LF check on caller-supplied header names and values. Those are good controls — header injection and credential leakage across redirects are exactly the mistakes this code avoids, and the test file proves the credential rule with refusesToForwardCredentialsAcrossAnApiSpecRedirect. But no destination address is ever checked against anything. The comment describes the control that A2AService has; the code implements a different, narrower set. A reviewer skimming for “is this fetch guarded?” would answer yes on the strength of the method name.
The remaining two records we would hold loosely. AgentController.java at main is 442 lines containing no outbound HTTP whatsoever — the only URL in the file is the Apache licence header — so CVE-2026-109404’s “some unknown functionality” does not correspond to any fetch in the file it names; any SSRF reachable through that controller would come from the services it invokes. And KafkaPublishTask accepting bootStrapServers is a broker-connection primitive rather than a classic HTTP SSRF; it checks only that the value is non-blank. Whether pointing a workflow’s Kafka producer at an arbitrary host is a vulnerability or the feature depends entirely on who may define workflows.
Scoring, and the thing worth generalising
The batch is internally inconsistent in a way that will distort anyone’s triage. The two records naming concrete, reachable fetch arguments — MCP mcpServer and API-spec specUrl — carry PR:L and score 2.1, below every reasonable alerting threshold. The three vaguer ones carry PR:N and score 5.5. In an orchestrator, defining a workflow task is a privileged act; PR:N for a field that only a workflow author can set is the harder number to defend. A 7.0 cut-off surfaces none of these, and the one we would actually fix is the lowest-scored of the five.
The generalisable finding is architectural, and it is not unique to this project. conductor-oss has written a competent SSRF guard — scheme allowlist, metadata block, private-range block, redirect handling, applied on the streaming path too. It lives as a private method inside one service. The sibling MCP client has nothing; the Agentspan fetcher has a partial, differently-shaped set whose documentation overstates it. This is the same per-callsite pattern we described in nginx-ui’s four CVEs, where the MCP interface reached a sink the HTTP API had learned to guard, and in the reference MCP fetch server’s unpatched SSRF. When a protocol integration is added next to an existing one, it inherits the directory, not the defences.
What to do
- Treat workflow-definition authority as outbound-request authority. Anyone who can create or edit a conductor workflow can choose the destination of an MCP tool listing, an API-spec fetch and a Kafka connection. Scope that permission like you would scope
curlon the worker host. - Put the egress policy in the network, not only in the code. Run conductor workers behind an egress proxy or NetworkPolicy that denies link-local (169.254.169.254), loopback, and RFC1918 ranges by default. That covers all five records at once, including the ones whose code-level status is ambiguous.
- If you use the MCP task, allowlist server URLs out of band. There is no destination check on the
MCPServicepath atmain; configuration is the only place to impose one today. - Do not downgrade your A2A posture on the strength of CVE-2026-109403. The guard is in
v3.32.5and later. Verify it in your build — grepvalidateAgentUrlinA2AService.java— rather than assuming either the record or this analysis. - For maintainers: centralise. One outbound HTTP client with an address policy, installed as an interceptor in
AIHttpClientsand used by every AI task, would close the MCP gap and align the Agentspan fetcher with the guard A2A already has — and would make thesendWithValidatedRedirectscomment true.
Verification note. All five CVE identifiers, publication timestamps, CVSS 4.0 vectors and base scores, CWE assignments, affected versions, file and function names, and reference URLs were read first-hand from the NVD 2.0 API. Repository metadata (32,292 stars, 1,035 forks, Apache-2.0, last push 12 October 2026), the full tag list, tag commit dates and per-file commit histories were read first-hand from the GitHub API. MCPService.java, A2AService.java, ListApiToolsTask.java, AgentController.java, KafkaPublishTask.java, the ai/http package listing and the two security test files were read directly from the repository at main, and A2AService.java additionally at tags v3.32.5, v3.33.0-rc10 and v3.33.0-rc12; all in-code comments and error strings are quoted verbatim. VulDB’s CVE page returned HTTP 403 and its entry page redirected, so we did not see the referenced public proof-of-concept and draw no conclusion about its contents. Conductor was not installed, no workflow was defined or executed, no MCP server was contacted, and no request was sent to any third-party deployment. The scoring critique, the architectural reading and the remediation ordering are our editorial analysis. We have no evidence of exploitation and imply none.
Sources:
- NVD — CVE-2026-109402, MCPService.listTools SSRF via mcpServer (CWE-918, 2.1)
- NVD — CVE-2026-109403, A2AService.getAgentCard SSRF via agentUrl, up to 3.32.5 (CWE-918, 5.5)
- NVD — CVE-2026-109190, ListApiToolsTask.start SSRF via specUrl (CWE-918, 2.1)
- conductor-oss/conductor — MCPService.java at main (no outbound destination policy)
- conductor-oss/conductor — A2AService.java at tag v3.32.5, containing validateAgentUrl