
Orkes Conductor OSS 3.21.21 before 3.30.2 accepts a workflow definition on its workflow API and evaluates the expressions inside it before checking who sent it. NVD's entry for CVE-2026-58138 names the path precisely: inline workflow definitions carrying JavaScript or Python, handled by "unsandboxed GraalVM evaluators configured with HostAccess.ALL or allowAllAccess(true)" through the INLINE, LAMBDA, DO_WHILE and SWITCH task types, reaching arbitrary OS commands "via Java reflection or direct subprocess calls." VulnCheck, which disclosed it, scored it 9.8 on CVSS 3.1 and 9.3 on CVSS 4.0.
The fix shipped in 3.30.2 on June 3. NVD published the CVE 27 days later, on June 30. Proof-of-concept code appeared in early August; Empirical Security recorded exploitation in its telemetry on August 21, 79 days after the fixed release, and Fortinet, as SecurityWeek reported on September 18, blocked roughly 1,300 attempts between September 8 and 9.

What 3.30.2 changed, and what it left alone
Both fix commits are public. 87a7d96, landed May 5, replaced a one-line host-access grant in ScriptEvaluator with a denylist:
HostAccess hostAccess =
HostAccess.newBuilder(HostAccess.ALL)
.denyAccess(Class.class)
.denyAccess(ClassLoader.class)
.denyAccess(java.lang.reflect.Method.class)
.denyAccess(java.lang.reflect.Field.class)
.denyAccess(java.lang.reflect.Constructor.class)
.denyAccess(java.lang.reflect.Array.class)
.denyAccess(Runtime.class)
.denyAccess(ProcessBuilder.class)
.denyAccess(Process.class)
.denyAccess(System.class)
.denyAccess(Thread.class)
.denyAccess(ThreadGroup.class)
.build();
The same commit deleted allowAllAccess(true) from the Python evaluator outright. c691e35, three weeks later, added the capability flags that should have been there from the start — allowHostClassLoading(false), allowNativeAccess(false), allowCreateProcess(false), allowIO(IOAccess.NONE), allowEnvironmentAccess(EnvironmentAccess.NONE) — and turned off js.load, js.print and js.console.
Read the first block again, though. The base is still HostAccess.ALL, with twelve classes denied on top of it. That is a denylist standing in for a sandbox, and it is the correct engineering call here, because the alternative — deny everything, allow back what workflows need — would break existing definitions. The important part is what none of it touches: Conductor still evaluates an expression that arrived over the API. It has to. INLINE, LAMBDA, DO_WHILE and SWITCH exist so that a workflow author can write one.
The defect was the gap between two defaults set by different people. The evaluator was configured as though whoever writes a workflow is a trusted operator. The open-source server, as Empirical puts it, "enforces no authentication by default and leaves its workflow API open." Neither decision is unreasonable alone. Together they make the trusted author anyone who can reach the port.
Inventory evaluators by host access, not by product name
The durable version of this is not "patch Conductor." It is a list: every component in your stack that evaluates an expression, template or script supplied at runtime. Orchestrators, rules engines, CI systems, notification templating, and — the ones most likely to be missing from the list — the agent frameworks stood up this year, which exist specifically to run generated logic.
For each entry, record two facts. What the evaluator is allowed to touch on the host, and what authentication sits in front of the endpoint that accepts the expression. Either answer alone is not enough; the pairing is the finding. On JVM services using GraalVM polyglot, the strings are short enough to grep for directly:
allowAllAccess(true) HostAccess.ALL allowHostClassLoading(true)
allowCreateProcess(true) allowIO(IOAccess.ALL)
Elsewhere the equivalents are Jinja2 running Environment rather than SandboxedEnvironment, SpEL with a StandardEvaluationContext, and Node services on vm instead of an isolate.
This costs real time and does not come back as a closeable ticket. It is roughly an afternoon per service, done by someone who can read the configuration, and it produces a document rather than a remediation. A scanner will not do it for you: Empirical notes that no credentialed Tenable or Qualys plugin existed for this CVE, so a clean scan report says nothing about your exposure. Where the evaluator belongs to a vendor product you cannot reconfigure, the whole answer moves to the boundary — authentication in front of the endpoint, and a network position where an unauthenticated request cannot arrive.
Two things this CVE does not resolve for you. It is absent from CISA's KEV catalog — the September 18 release carries 1,715 entries and this is not one of them — which is a clean example of why KEV membership makes a poor patch trigger. And if an instance was reachable and unpatched through late August, the upgrade closes the vulnerability without answering what ran on the host in the meantime, the same distinction Cisco drew when it told operators of an exploited email gateway to rebuild rather than upgrade.
What to check this week
- Which Conductor deployments you have and what version each runs. Anything at or above 3.21.21 and below 3.30.2 is affected.
- Whether the workflow API answers an unauthenticated request from outside your network at all. Send one and read the response code rather than assuming the deployment inherited a boundary.
- The output of that grep across your own JVM and Node services, including internal tooling nobody scans.
- For every evaluator on the list, the name of the authentication in front of it. If the answer is "it is internal," write down which network and who else is on it.
- If an instance was internet-reachable and unpatched after August 21: child processes of the Conductor JVM, and outbound connections from it, before you treat the upgrade as the end of the matter.
North InfoSec runs AI-assisted penetration testing and security assessments, including reachability testing of internal service endpoints. northinfosec.com