August 12, 2026
Three CVEs in Activepieces: The Sandbox Was Real. The Code Just Didn't Run Inside It.
A command injection that fires before any sandbox exists, a V8 isolate that protects the wrong half of your module, and an XSS in the OAuth…

By Aviral Srivastava
2 min read
A command injection that fires before any sandbox exists, a V8 isolate that protects the wrong half of your module, and an XSS in the OAuth callback.
When a product's whole premise is "you can write arbitrary code and we will run it safely," the sandbox is not a feature. It is the product. Everything else is a UI on top of that one promise.
So the question I ask about these platforms is never "is the sandbox any good." Sandboxes are usually fine. V8 isolates work. Process isolation works. The interesting question is narrower and almost nobody asks it:
What runs outside it?
Not what escapes it. What never enters it in the first place. Because a sandbox only protects the code that executes inside its boundary, and the amount of attacker-influenced work that happens outside that boundary is usually larger than anyone intends.
I asked that question about Activepieces. The answer was two CVEs.
The Target
Activepieces is an open source workflow automation platform, roughly 23,400 stars, the sort of thing people self-host to wire together SaaS integrations. You build flows on a canvas, and one of the step types is a Code step where you write JavaScript that runs as part of the flow.
That Code step is the whole reason a sandbox exists. Activepieces ships four execution modes:
UNSANDBOXED no isolation
SANDBOX_PROCESS process isolation only
SANDBOX_CODE_ONLY V8 Isolate, no Node.js APIs
SANDBOX_CODE_AND_PROCESS bothUNSANDBOXED no isolation
SANDBOX_PROCESS process isolation only
SANDBOX_CODE_ONLY V8 Isolate, no Node.js APIs
SANDBOX_CODE_AND_PROCESS bothUnder SANDBOX_CODE_ONLY, the V8 isolate is the only thing standing between a user's Code step and the host. That mode is the one worth attacking, because it is the one where the isolate is load-bearing.
The Question I Actually Asked
I did not start by trying to escape the isolate. I started by tracing the path a Code step takes from the moment it is submitted to the moment it executes, and marking where the sandbox begins.
That turns out to be the productive question, because a Code step is not just code. It is a record with a name, an ID, settings, and a body. All of those fields cross the same trust boundary. Only one of them was ever going to end up inside the isolate.
Finding One: The Name Never Enters the Sandbox
CVE-2026-73081, CVSS 8.7 High.
Before a Code step can run, it has to be compiled. The worker takes the step, writes it to disk, and builds it.
The on-disk path for that step was constructed from the step's name. And the name was validated as z.string(). That is a Zod schema meaning any string at all.
That path was then passed to a shell-invoked build command. The command runner split the command string and ran the result through /bin/sh, so a name containing ;, |, $(), or backticks broke out of the intended bun build or esbuildinvocation and ran whatever the attacker wanted.
Here is the part that matters, and it is the reason this finding is worth more than a normal command injection:
The injection occurred during the compilation phase in the worker process, before any code sandbox was created, so every execution mode was affected.
Read that against the mode table. It does not matter which sandbox you configured. The compilation step happens first, in the worker process, as the worker user. In the default container image that user is root.
The advisory says so explicitly in the workarounds section: process-level sandboxing does not mitigate this, because the injection fires during compilation before the process sandbox is applied.
So the payload is not in the code you sandbox. It is in the name of the code you sandbox, processed by a shell, before the sandbox exists.
From there: read and write the worker filesystem, pull environment secrets, and reach anything the worker can reach, which is the database, Redis, and the internal API. On a multi-tenant instance that is every tenant's data.