Resumen

El paquete npm publicado praisonai exporta una herramienta integrada de TypeScript denominada codeMode. El paquete describe esta herramienta como ejecutora de código en un entorno sandboxed, marca su capacidad como sandbox: true y la registra a través de la fachada pública de herramientas.

La implementación no crea ningún límite de aislamiento real. Aplica una pequeña lista de bloqueo basada en expresiones regulares, establece process y require como undefined dentro de un objeto JavaScript plano y, a continuación, ejecuta código controlado por el atacante mediante el constructor new Function del proceso anfitrión:

javascript
const fn = new Function('sandbox', `with (sandbox) { ${code} }`);
const result = fn(sandbox);

Dado que esto se ejecuta en el contexto V8 del proceso anfitrión, el código dentro de codeMode puede utilizar la cadena de prototipos de JavaScript para recuperar el constructor Function real:

javascript
({}).constructor.constructor('return process')()

Desde un script CommonJS normal, el objeto process recuperado expone process.mainModule.require. Esto elude los controles explícitos sobre require('fs') y require('child_process') y permite el acceso al sistema de archivos del anfitrión y la ejecución de subprocesos desde código que se suponía estaba sandboxed.

Detalles Técnicos

El código fuente actual indica que codeMode es una herramienta integrada del paquete y anuncia explícitamente un límite de sandbox:

text
src/praisonai-ts/src/tools/builtins/code-mode.ts
  13: description: 'Execute code that can import and use other tools in a sandboxed environment',
  24: capabilities: {
  25:   sandbox: true,
  26:   code: true,
  28: packageName: 'praisonai',
  85: description: 'Execute code in a sandboxed environment with access to imported tools. Write files, run code, and get results.',

El mismo archivo implementa la seguridad como una lista de bloqueo de patrones exactos en el código fuente:

text
src/praisonai-ts/src/tools/builtins/code-mode.ts
  108: const blockedPatterns = [
  109:   /require\s*\(\s*['"']child_process['"']\s*\)/,
  110:   /require\s*\(\s*['"']fs['"']\s*\)/,
  111:   /import\s+.*from\s+['"']child_process['"']/,
  112:   /process\.exit/,
  113:   /eval\s*\(/,

A continuación intenta ocultar los globales peligrosos sobreponiendo nombres en un objeto normal:

text
src/praisonai-ts/src/tools/builtins/code-mode.ts
  168: process: undefined,
  169: require: undefined,

Finalmente, ejecuta el código no confiable en el proceso anfitrión usando new Function y with (sandbox):

text
src/praisonai-ts/src/tools/builtins/code-mode.ts
  187: const fn = new Function(
  188:   'sandbox',
  189:   `with (sandbox) { ${code} }`
  190: );
  191: const result = fn(sandbox);

Esto no es un sandbox. new Function no crea un contexto de seguridad separado, y el sombreado de variables no elimina el acceso a los constructores alcanzables a través de objetos JavaScript normales.

La herramienta es accesible a través del SDK npm público:

text
src/praisonai-ts/src/index.ts
  117: airweaveSearch, codeMode,

src/praisonai-ts/src/tools/tools.ts
  104: // Code Mode
  105: registry.register(CODE_MODE_METADATA, createCodeModeTool as ToolFactory);
  167: // Code Mode
  168: codeMode: (config?: CodeModeConfig) => codeMode(config),

Por Qué Esto No Es Comportamiento Intencionado

Esto no es simplemente que el usuario pueda ejecutar código porque codeMode ejecuta código. La vulnerabilidad consiste en que el código descrito y expuesto explícitamente como sandboxed puede escapar las restricciones previstas.

La propia implementación demuestra que existe un límite de seguridad intencionado:

  • CODE_MODE_METADATA.capabilities.sandbox es true.
  • La descripción de la herramienta indica que se ejecuta en un entorno sandboxed.
  • El acceso directo a fs y child_process está explícitamente bloqueado.
  • process y require están explícitamente sombreados como undefined.
  • allowNetwork tiene valor predeterminado false.
  • La configuración incluye controles relevantes para la seguridad como blockedTools, allowedPaths, timeoutMs y maxMemoryMb.

La prueba de concepto demuestra que esas restricciones previstas funcionan para payloads ingenuos, pero fallan ante un escape estándar mediante la cadena de prototipos de JavaScript.

La documentación oficial de JavaScript y TypeScript de PraisonAI describe el paquete npm como un framework de agentes listo para producción que se instala con npm install praisonai. Los avisos públicos de PraisonAI califican escapes de sandbox comparables en Python como Críticos cuando el código suministrado por el usuario o por un LLM cruza desde un sandbox declarado hacia la ejecución en el anfitrión.

Prueba de Concepto

La prueba de concepto instala una versión publicada del paquete npm en un proyecto temporal y se ejecuta desde un archivo de script CommonJS real. Ejecutar desde un archivo es importante porque las aplicaciones Node normales exponen process.mainModule.require; node -e o stdin no siempre reproducen esa forma de despliegue.

Ejecución desde un checkout de reproducción local:

bash
node poc/pov_poc.js 1.7.1

Resultado observado:

json
{
  "package": "praisonai",
  "version": "1.7.1",
  "codeModeExported": true,
  "directRequireFsControl": {
    "stderr": "Blocked pattern detected: require\\s*\\(\\s*['\"]fs['\"]\\s*\\)",
    "exitCode": 1,
    "success": false,
    "error": "Code contains blocked patterns for security"
  },
  "directChildProcessControl": {
    "stderr": "Blocked pattern detected: require\\s*\\(\\s*['\"]child_process['\"]\\s*\\)",
    "exitCode": 1,
    "success": false,
    "error": "Code contains blocked patterns for security"
  },
  "escapedProcessEnv": {
    "output": "poc",
    "exitCode": 0,
    "success": true
  },
  "escapedFilesystem": {
    "output": "fs-ok",
    "exitCode": 0,
    "success": true
  },
  "escapedCommand": {
    "output": "poc",
    "exitCode": 0,
    "success": true
  }
}

Interpretación:

  • El require('fs') directo está bloqueado.
  • El require('child_process') directo está bloqueado.
  • El payload con el constructor Function recupera el process del anfitrión.
  • El proceso escapado lee una variable de entorno del anfitrión.
  • El proceso escapado importa fs.
  • El proceso escapado importa child_process y ejecuta un printf inofensivo.

La prueba de concepto no contacta ningún proveedor de LLM ni servicio externo tras la instalación del paquete npm. No modifica archivos del anfitrión ni ejecuta un comando destructivo.

Impacto

Un atacante que pueda suministrar código a codeMode puede escapar del sandbox anunciado y ejecutar con los privilegios del proceso Node.js de PraisonAI.

Los vectores de entrada realistas incluyen:

  • Una aplicación que expone codeMode como herramienta de agente a usuarios finales.
  • Un flujo LLM o de llamada a herramientas donde el contenido controlado por prompt alcanza el parámetro code.
  • Integraciones MCP o de registro de herramientas que hacen que la herramienta integrada codeMode sea invocable.
  • Cualquier servicio multitenant que dependa de codeMode para ejecutar de forma segura JavaScript generado por usuarios o modelos.

El impacto tras el escape incluye:

  • Lectura de variables de entorno del proceso, incluyendo claves de API y tokens de servicio.
  • Lectura de archivos disponibles para el proceso Node.
  • Lanzamiento de subprocesos mediante child_process.
  • Escritura o modificación de archivos a través de las API del sistema de archivos del anfitrión.
  • Terminación o agotamiento de recursos del proceso anfitrión.

Corrección Sugerida

No se debe usar new Function en el proceso anfitrión junto con listas de bloqueo de código fuente como sandbox.

Dirección de corrección recomendada:

  1. Deshabilitar o marcar claramente el codeMode de npm como inseguro hasta que exista un límite de aislamiento real.
  2. Ejecutar el código no confiable en un proceso de sistema operativo separado, contenedor, worker isolate o límite similar con un usuario restringido, entorno mínimo, directorio de trabajo temporal, sin secretos heredados y con IPC explícito para las llamadas a herramientas permitidas.
  3. Aplicar allowNetwork, allowedPaths, timeoutMs, maxMemoryMb, allowedTools y blockedTools en ese límite en lugar de mediante el escaneo de cadenas de código fuente.
  4. No depender únicamente de node:vm para código no confiable. La documentación de Node.js indica explícitamente que el módulo vm no es un mecanismo de seguridad.
  5. Añadir pruebas de regresión para:
    • Controles de bloqueo de require('fs') y require('child_process') directos.
    • Bloqueo de ({}).constructor.constructor('return process')().
    • Indisponibilidad de process.mainModule.require('fs').
    • Indisponibilidad de process.mainModule.require('child_process').
    • Variables de entorno del anfitrión no disponibles salvo que se pasen explícitamente.
    • IPC de llamadas a herramientas que sigue funcionando para herramientas permitidas.

Si los mantenedores necesitan una mitigación de emergencia antes de que exista un sandbox real, se recomienda rechazar la ejecución de codeMode a menos que el llamador opte explícitamente por la opción de ejecución JavaScript insegura en el anfitrión, con documentación clara de que puede acceder al proceso Node completo.

Paquetes y Versiones Afectadas

  • Repositorio: MervinPraison/PraisonAI
  • Ecosistema: npm
  • Paquete: praisonai
  • Componente: src/praisonai-ts/src/tools/builtins/code-mode.ts
  • Versión npm verificada: 1.7.1
  • Commit de origin/main verificado: 1ad58ca02975ff1398efeda694ea2ab78f20cf3e

Rango afectado confirmado:

text
>= 1.4.0, <= 1.7.1

Límite inferior: la versión 1.3.6 no exporta codeMode y no incluye dist/tools/builtins/code-mode.js.

No se conoce ninguna versión npm corregida en el momento de este informe.

Barrido de Versiones

El barrido incluido instala versiones npm seleccionadas y ejecuta la misma forma vulnerable desde un archivo de script:

bash
node poc/version_sweep_poc.js

Resultado observado:

text
1.3.6: codeModeExported=false, hasDistCodeMode=false
1.4.0: directRequireFsBlocked=true, escapeProcessEnv=true, escapeFilesystem=true, escapeCommand=true
1.5.4: directRequireFsBlocked=true, escapeProcessEnv=true, escapeFilesystem=true, escapeCommand=true
1.6.0: directRequireFsBlocked=true, escapeProcessEnv=true, escapeFilesystem=true, escapeCommand=true
1.7.0: directRequireFsBlocked=true, escapeProcessEnv=true, escapeFilesystem=true, escapeCommand=true
1.7.1: directRequireFsBlocked=true, escapeProcessEnv=true, escapeFilesystem=true, escapeCommand=true

El historial de Git para el archivo TypeScript apunta a la integración de la versión 1.4.0:

text
56f36e25 feat: bump version to 1.4.0 and add AI SDK integration dependencies
2bad9a50 feat: bump version to 1.4.0 and add AI SDK integration dependencies

Historial de Avisos

Los avisos relacionados más cercanos tienen alcance Python o PyPI y no cubren esta implementación TypeScript de npm:

  • GHSA-qf73-2hrx-xprp / CVE-2026-39888: pip:praisonaiagents con traversal de frames en execute_code() dentro de un sandbox de subproceso Python.
  • GHSA-4mr5-g6f9-cfrh / CVE-2026-47392: pip:praisonai con escape de sandbox Python en execute_code() mediante print.__self__.
  • Otros avisos publicados de PraisonAI cubren execute_code en Python, SubprocessSandbox, Sandlock o puentes CLI.

Este informe es distinto porque apunta a: ecosistema npm, paquete praisonai, componente code-mode.ts, causa raíz de new Function en contexto anfitrión con lista de bloqueo y sombreado de nombres, y rango afectado >= 1.4.0, <= 1.7.1.