
sandbox-runtime v0.0.74
Una herramienta de sandboxing ligera para aplicar restricciones de sistema de archivos y red en procesos arbitrarios a nivel del sistema operativo, sin necesidad de un contenedor.
Anthropic Sandbox Runtime (srt)
Una herramienta de sandboxing ligera para aplicar restricciones de sistema de archivos y red a procesos arbitrarios a nivel del sistema operativo, sin necesidad de un contenedor.
srt utiliza primitivas nativas de sandboxing del sistema operativo (sandbox-exec en macOS, bubblewrap en Linux) y filtrado de red basado en proxy. Puede usarse para aplicar sandboxing al comportamiento de agentes, servidores MCP locales, comandos bash y procesos arbitrarios.
Vista previa de investigación Beta
El Sandbox Runtime es una vista previa de investigación desarrollada para Claude Code con el fin de habilitar agentes de IA más seguros. Se pone a disposición como una vista previa temprana de código abierto para ayudar al ecosistema en general a construir sistemas agénticos más seguros. Como se trata de una vista previa temprana de investigación, las APIs y los formatos de configuración pueden evolucionar. ¡Agradecemos los comentarios y las contribuciones para hacer que los agentes de IA sean más seguros por defecto!
Instalación```bash
npm install -g @anthropic-ai/sandbox-runtime
## Uso básico```bash
# Network restrictions
$ srt "curl anthropic.com"
Running: curl anthropic.com
<html>...</html> # Request succeeds
$ srt "curl example.com"
Running: curl example.com
Connection blocked by network allowlist # Request blocked
# Filesystem restrictions
$ srt "cat README.md"
Running: cat README.md
# Anthropic Sandb... # Current directory access allowed
$ srt "cat ~/.ssh/id_rsa"
Running: cat ~/.ssh/id_rsa
cat: /Users/ollie/.ssh/id_rsa: Operation not permitted # Specific file blocked
Descripción general
Este paquete proporciona una implementación de sandbox independiente que puede usarse tanto como herramienta CLI como biblioteca. Está diseñado con una filosofía de seguro por defecto adaptada a los casos de uso comunes de los desarrolladores: los procesos se inician con acceso mínimo, y tú abres explícitamente solo los agujeros que necesitas.
Capacidades clave:
- Restricciones de red: Controla qué hosts/dominios pueden ser accedidos vía HTTP/HTTPS y otros protocolos
- Restricciones del sistema de archivos: Controla qué archivos/directorios pueden ser leídos/escritos
- Restricciones de sockets Unix: Controla el acceso a sockets IPC locales
- Monitoreo de violaciones: En macOS, accede al almacén de registros de violaciones del sandbox del sistema para alertas en tiempo real
Caso de uso de ejemplo: Sandboxing de servidores MCP
Un caso de uso clave es el sandboxing de servidores Model Context Protocol (MCP) para restringir sus capacidades. Por ejemplo, para hacer sandbox del servidor MCP de sistema de archivos:
Sin sandboxing (.mcp.json):```json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem"]
}
}
}
**Con sandboxing** (`.mcp.json`):```json
{
"mcpServers": {
"filesystem": {
"command": "srt",
"args": ["npx", "-y", "@modelcontextprotocol/server-filesystem"]
}
}
}
Luego configure las restricciones en ~/.srt-settings.json:```json
{
"filesystem": {
"denyRead": [],
"allowWrite": ["."],
"denyWrite": ["~/sensitive-folder"]
},
"network": {
"allowedDomains": [],
"deniedDomains": []
}
}
Ahora el servidor MCP tendrá bloqueada la escritura en la ruta denegada:```
> Write a file to ~/sensitive-folder
✗ Error: EPERM: operation not permitted, open '/Users/ollie/sensitive-folder/test.txt'
Cómo funciona
El sandbox utiliza primitivas a nivel de sistema operativo para aplicar restricciones que se aplican a todo el árbol de procesos:
- macOS: Utiliza
sandbox-execcon perfiles Seatbelt generados dinámicamente - Linux: Utiliza bubblewrap para la contenedorización con aislamiento de espacio de nombres de red
- Windows: Ejecuta el proceso en sandbox bajo una cuenta de usuario local dedicada
srt-sandbox, con una barrera de egreso de Windows Filtering Platform vinculada al SID de esa cuenta y ACEs explícitas por sesión en el árbol de trabajo
0d1c612947c798aef48e6ab4beb7e8544da9d41a-4096x2305
Modelo de doble aislamiento
Tanto el aislamiento del sistema de archivos como el de red son necesarios para un sandboxing efectivo. Sin aislamiento de archivos, un proceso comprometido podría exfiltrar claves SSH u otros archivos sensibles. Sin aislamiento de red, un proceso podría escapar del sandbox y obtener acceso de red sin restricciones.
Aislamiento del sistema de archivos aplica restricciones de lectura y escritura:
- Lectura (patrón denegar-luego-permitir): Por defecto, el acceso de lectura está permitido en todas partes. Puedes denegar regiones amplias (por ejemplo,
/Users) y luego volver a permitir rutas específicas dentro de ellas (por ejemplo,.).allowReadtiene precedencia sobredenyRead— lo opuesto a la escritura, dondedenyWritetiene precedencia sobreallowWrite. Una entradadenyReadque es más específica que la regiónallowReaden la que se encuentra (por ejemplo,denyRead: ["**/.env"]o["./secrets"]conallowRead: ["."]) permanece denegada. - Escritura (patrón solo-permitir): Por defecto, el acceso de escritura está denegado en todas partes. Debes permitir explícitamente rutas (por ejemplo,
.,/tmp). Una lista de permisos vacía significa que no hay acceso de escritura.
Aislamiento de red (patrón solo-permitir): Por defecto, todo acceso de red está denegado. Debes permitir dominios explícitamente. Una lista allowedDomains vacía significa que no hay acceso de red. El tráfico de red se enruta a través de servidores proxy que se ejecutan en el host:
-
Linux: Las solicitudes se enrutan a través del sistema de archivos mediante un socket de dominio Unix. El espacio de nombres de red del proceso en sandbox se elimina por completo, por lo que todo el tráfico de red debe pasar por los proxies que se ejecutan en el host (escuchando en sockets Unix que se montan mediante bind en el sandbox)
-
macOS: El perfil Seatbelt permite la comunicación solo a un puerto específico de localhost. Los proxies escuchan en este puerto, creando un canal controlado para todo el acceso de red
-
Windows: Un conjunto de filtros WFP a nivel de máquina bloquea todas las conexiones salientes que se originan desde la cuenta
srt-sandboxexcepto el loopback al rango de puertos del proxy. Los proxies escuchan dentro de ese rango, creando un canal controlado para todo el acceso de red
Tanto el tráfico HTTP/HTTPS (a través del proxy HTTP) como otro tráfico TCP (a través del proxy SOCKS5) son mediados por estos proxies, que aplican tus listas de dominios permitidos y denegados.
Para obtener más detalles sobre el sandboxing en Claude Code, consulta:
- Documentación de sandboxing de Claude Code
- Beyond Permission Prompts: Making Claude Code More Secure and Autonomous
Arquitectura```
src/ ├── index.ts # Library exports ├── cli.ts # CLI entrypoint (srt command) ├── utils/ # Shared utilities │ ├── debug.ts # Debug logging │ ├── settings.ts # Settings reader (permissions + sandbox config) │ ├── platform.ts # Platform detection │ └── exec.ts # Command execution utilities └── sandbox/ # Sandbox implementation ├── sandbox-manager.ts # Main sandbox manager ├── sandbox-schemas.ts # Zod schemas for validation ├── sandbox-violation-store.ts # Violation tracking ├── sandbox-utils.ts # Shared sandbox utilities ├── http-proxy.ts # HTTP/HTTPS proxy for network filtering ├── socks-proxy.ts # SOCKS5 proxy for network filtering ├── linux-sandbox-utils.ts # Linux bubblewrap sandboxing ├── macos-sandbox-utils.ts # macOS sandbox-exec sandboxing └── windows-sandbox-utils.ts # Windows srt-win sandboxing
## Uso
### Como herramienta CLI
El comando `srt` (Anthropic Sandbox Runtime) envuelve cualquier comando con límites de seguridad:```bash
# Run a command in the sandbox
srt echo "hello world"
# With debug logging
srt --debug curl https://example.com
# Specify custom settings file
srt --settings /path/to/srt-settings.json npm install
Como biblioteca```typescript
import { SandboxManager, type SandboxRuntimeConfig, } from '@anthropic-ai/sandbox-runtime' import { spawn } from 'child_process'
// Define your sandbox configuration const config: SandboxRuntimeConfig = { network: { allowedDomains: ['example.com', 'api.github.com'], deniedDomains: [], }, filesystem: { denyRead: ['~/.ssh'], allowWrite: ['.', '/tmp'], denyWrite: ['.env'], }, }
// Initialize the sandbox (starts proxy servers, etc.) await SandboxManager.initialize(config)
// Wrap a command with sandbox restrictions const sandboxedCommand = await SandboxManager.wrapWithSandbox( 'curl https://example.com', )
// Execute the sandboxed command const child = spawn(sandboxedCommand, { shell: true, stdio: 'inherit' })
// Handle exit and cleanup after child process completes
child.on('exit', async code => {
console.log(Command exited with code ${code})
// Cleanup when done (optional, happens automatically on process exit)
await SandboxManager.reset()
})
**Atribución de violaciones (`commandId` / `commandText`).** Las violaciones observadas mientras se ejecuta un comando envuelto (líneas de registro de seatbelt, eventos de seccomp, denegaciones del proxy) se almacenan bajo una clave de atribución, y `annotateStderrWithSandboxFailures(key, stderr)` / `getViolationsForCommand(key)` las buscan por esa misma clave. Por defecto, la clave es la propia cadena envuelta. Pasa un `commandId` opaco por invocación (p. ej. un id de uso de herramienta) para usar esa clave en su lugar — recomendado: las claves se comparan por sus primeros 100 caracteres, por lo que comandos largos que comparten un prefijo se atribuirían de forma cruzada, y una reejecución del mismo texto heredaría los eventos de la ejecución anterior. Si la cadena que *ejecutas* no es el comando que la invocación *representa* (p. ej. envuelves un `source <snapshot> && eval '<cmd>'` ensamblado), pasa también `commandText: '<cmd>'`: es contra lo que coinciden los patrones de comando de `ignoreViolations` y lo que cada violación reporta como su `command`.```typescript
const wrapped = await SandboxManager.wrapWithSandbox(
assembledCommand, // what actually runs
undefined,
undefined,
undefined,
{ commandId: invocationId, commandText: rawCommand },
)
// ... run it ...
const annotated = SandboxManager.annotateStderrWithSandboxFailures(invocationId, stderr)
Exportaciones disponibles```typescript
// Main sandbox manager export { SandboxManager } from '@anthropic-ai/sandbox-runtime'
// Violation tracking export { SandboxViolationStore } from '@anthropic-ai/sandbox-runtime'
// TypeScript types export type { SandboxRuntimeConfig, NetworkConfig, FilesystemConfig, IgnoreViolationsConfig, SandboxAskCallback, FsReadRestrictionConfig, FsWriteRestrictionConfig, NetworkRestrictionConfig, } from '@anthropic-ai/sandbox-runtime'
## Configuración
### Ubicación del archivo de configuración
Por defecto, el entorno de ejecución del sandbox busca la configuración en `~/.srt-settings.json`. Puedes especificar una ruta personalizada usando el flag `--settings`:```bash
srt --settings /path/to/srt-settings.json <command>
Ejemplo de configuración completa```json
{ "network": { "allowedDomains": [ "github.com", ".github.com", "lfs.github.com", "api.github.com", "npmjs.org", ".npmjs.org" ], "deniedDomains": ["malicious.com"], "allowUnixSockets": ["/var/run/docker.sock"], "allowLocalBinding": false }, "filesystem": { "denyRead": ["~/.ssh"], "allowRead": [], "allowWrite": [".", "src/", "test/", "/tmp"], "denyWrite": [".env", "config/production.json"] }, "ignoreViolations": { "*": ["/usr/bin", "/System"], "git push": ["/usr/bin/nc"], "npm": ["/private/tmp"] }, "enableWeakerNestedSandbox": false, "enableWeakerNetworkIsolation": false, "allowAppleEvents": false }
### Opciones de configuración
#### Configuración de red
Utiliza un **patrón de solo permitidos** — todo acceso a la red está denegado por defecto.
- `network.allowedDomains` - Array de dominios permitidos (admite comodines como `*.example.com`). Array vacío = sin acceso a la red. Un sufijo `:port` opcional (`api.example.com:443`, `*.example.com:8443`) restringe una entrada a ese puerto de destino; las entradas sin puerto coinciden con cualquier puerto.
- Los literales IPv6 deben ir entre corchetes, al estilo RFC 3986: `[::1]`, `[2001:db8::1]:443`. Una entrada sin corchetes con múltiples dos puntos se rechaza por ambigua (`2001:db8::1:443` es en sí misma una dirección válida).
- `network.deniedDomains` - Array de dominios denegados (se comprueba primero, tiene precedencia sobre allowedDomains). Mismo sufijo `:port`, y se acepta un `*` solo (o `*:22`) para denegar todo.
- `network.deniedDomainReasons` - Mapa opcional desde una entrada de `deniedDomains` (coincidencia por cadena exacta) a un motivo orientado al modelo que aparece en la línea `<sandbox_violations>` cuando esa entrada deniega una conexión — indica qué está bloqueado y la alternativa autorizada (p. ej. `{"github.com:22": "SSH pushes to GitHub are blocked; use an https:// remote"}`). Las entradas sin motivo informan de uno genérico. Para destinos SSH (puerto 22), el motivo también se entrega en banda: un cliente SSH tunelizado a través de un ProxyCommand SOCKS sin autenticación (p. ej. BSD `nc -X 5`) recibe una desconexión SSH previa al intercambio de claves cuya descripción es el motivo, que OpenSSH imprime textualmente — mantén dichos motivos por debajo de ~400 caracteres ASCII, en imperativo al principio, ya que OpenSSH trunca y escapa los caracteres no ASCII.
- `network.allowLocalBinding` - Permitir el enlace a puertos locales (booleano, por defecto: false)
**Comprobación de direcciones resueltas.** Las listas de permitidos/denegados coinciden por _nombre_, pero quien controla el DNS de un nombre permitido (o cualquier etiqueta bajo un comodín permitido) controla aquello a lo que resuelve. Así que antes de marcar un **nombre de host** permitido directamente, el proxy lo resuelve una vez, descarta cualquier dirección de un conjunto denegado y se conecta a una dirección superviviente (la dirección que pasó la comprobación es la que se marca — no hay una segunda consulta). Si no sobrevive ninguna, la conexión se rechaza como cualquier otra denegación de política: HTTP/CONNECT reciben `403` (`X-Proxy-Error: blocked-by-sandbox-runtime`, con el motivo en el cuerpo), SOCKS recibe "connection not allowed by ruleset", y se registra en el almacén de violaciones una línea `deny network-outbound host:port (resolved to a loopback address)` — nombrando la clase de dirección (loopback, link-local, de este host, metadatos de nube, en lista de denegados, listada, …), no la dirección en sí, que solo aparece en el registro de depuración.
El conjunto denegado es: loopback (`127.0.0.0/8`, `::1`), no especificada (`0.0.0.0/8`, `::`), link-local (`169.254.0.0/16`, `fe80::/10`), multicast (`224.0.0.0/4`, `ff00::/8`), broadcast, los endpoints de metadatos de instancia de nube / plataforma que viven fuera de link-local (`100.100.100.200`, `168.63.129.16`, `192.0.0.192`, `fd00:ec2::/32`, `fd20:ce::254`, `fd00:c1::a9fe:a9fe`, `fd00:42::42`), cada dirección asignada actualmente a una de las interfaces de red de este host (un servicio enlazado a `0.0.0.0` responde en la LAN o en la dirección global exactamente igual que en loopback), cada literal IP listado en `deniedDomains` (respetando su `:port` si lo tiene), y cualquier cosa en `deniedResolvedAddresses`. Las entradas IPv4 también coinciden con las formas IPv6 que portan una dirección IPv4 — las direcciones IPv4-mapped, IPv4-compatible e IPv4-translated, el prefijo conocido de NAT64 (`64:ff9b::/96`) y 6to4 (`2002::/16`) se juzgan por la dirección IPv4 que incorporan. El prefijo NAT64 de uso local `64:ff9b:1::/48` y los prefijos específicos de red no se decodifican — su disposición (la RFC 6052 permite la IPv4 en varias posiciones) no puede reconocerse solo a partir de la dirección; en una red así, lista las traducciones del prefijo de los rangos que deniegas (p. ej. `<prefix>::a00:0/104` para `10.0.0.0/8`). Las direcciones que alcanzan este host sin estar asignadas a él — una dirección pública con NAT 1:1 de una instancia de nube, un reenvío de puerto de un router, un alias de puerta de enlace de host de contenedor o VM — no están cubiertas automáticamente; lístalas en `deniedResolvedAddresses`.
Lo que la comprobación deja intacto: las entradas de la lista de permitidos que **son** literales IP (permitir `127.0.0.1:3000` es una elección explícita) — y, por la misma razón, un nombre de host puede resolver a una dirección por lo demás denegada cuando ese literal IP (en ese puerto) está a su vez en `allowedDomains`, ya que alcanzarlo por nombre no concede nada que la entrada literal no conceda (un literal IP en `deniedDomains` sigue ganando, exactamente como lo hace para una solicitud literal). Así, una configuración de desarrollo en la que `myapp.test` se asigna a un servidor local mediante `/etc/hosts` incluye en la lista de permitidos `["myapp.test", "127.0.0.1:3000"]`; no hay una lista de excepciones separada. `localhost` y los nombres bajo `.localhost` resuelven a loopback (o a un literal permitido) y nada más. La comprobación no se evalúa para conexiones enrutadas a través de `parentProxy` (incluido uno tomado de `HTTP_PROXY` / `HTTPS_PROXY` en el propio entorno de srt) o `mitmProxy` — ese salto resuelve el nombre y posee su propia política de direcciones — y solo gobierna lo que marca el proxy: en macOS, `allowLocalBinding` permite por separado que el proceso en sandbox se conecte a puertos de loopback sin pasar por el proxy en absoluto.
- `network.deniedResolvedAddresses` - Direcciones IP / rangos CIDR adicionales (IPv4 o IPv6, sin corchetes, cualquier puerto) a los que los nombres de host permitidos no deben resolver. El espacio de uso privado no se deniega por defecto porque permitir un nombre de host de intranet es legítimo; lístalo aquí cuando los nombres permitidos deban mantenerse fuera de él, p. ej. `["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/10", "fc00::/7"]`. Lista los rangos IPv4 e IPv6 por separado — un rango IPv6 lo bastante amplio para cubrir el bloque IPv4-mapped (`::ffff:0:0/96`), como `::/0`, coincide con respuestas IPv4 en algunos runtimes pero no en otros, así que no confíes en él para denegar IPv4.
**Terminación TLS** (`network.tlsTerminate`, experimental): cuando se establece, los CONNECT de HTTPS se terminan en proceso para que SRT pueda ver (y filtrar, mediante `network.filterRequest`) las solicitudes descifradas. El proceso en sandbox se apunta a un paquete de confianza que contiene la CA MITM (`caCertPath`/`caKeyPath`, o una CA efímera si se omiten) más las raíces habituales del host, de modo que tanto los certificados emitidos por el proxy como los certificados reales del upstream se verifican.
- `network.tlsTerminate.excludeDomains` - Patrones de dominio (misma sintaxis que `allowedDomains`) que **no** se terminan. Los CONNECT coincidentes se tunelizan de forma opaca: siguen sujetos a la lista de dominios permitidos, pero el cliente dentro del sandbox completa su propio handshake TLS con el upstream real, y `filterRequest` / la inyección de credenciales no se aplican a su tráfico HTTPS. Úsalo para los dos casos que la terminación TLS rompe fundamentalmente:
- **Upstreams mTLS** - solo el cliente dentro del sandbox posee el certificado de cliente, por lo que el proxy no puede reoriginar la conexión en su nombre.
- **Clientes con certificate-pinning** - clientes que verifican ellos mismos la identidad del upstream (CA personalizadas, pinning de SAN) y rechazan el certificado MITM.
- `network.tlsTerminate.extraCaCertPaths` - Rutas a archivos de certificado CA PEM añadidos a ese paquete de confianza, después de la CA MITM y las raíces habituales del host. Los hosts excluidos (no terminados) son verificados por el cliente dentro del sandbox, y las variables de entorno de confianza que establece SRT (`SSL_CERT_FILE`, `GIT_SSL_CAINFO`, ...) _reemplazan_ la configuración de confianza propia de cada herramienta, por lo que una raíz local del sitio (p. ej. una CA mTLS interna) debe estar en el paquete o esos hosts nunca podrán verificarse. Solo se copian en el paquete los bloques `CERTIFICATE` de cada archivo (cualquier otra cosa, p. ej. una clave privada en un PEM combinado, nunca se expone al sandbox); los archivos que faltan, no son legibles o no contienen ningún bloque `CERTIFICATE` PEM se omiten, por lo que es seguro listar rutas que existen solo en algunos hosts.```json
{
"network": {
"allowedDomains": ["*.example.com", "internal-mtls.example.net"],
"deniedDomains": [],
"tlsTerminate": {
"excludeDomains": ["internal-mtls.example.net"],
"extraCaCertPaths": ["/etc/internal-mtls-roots.pem"]
}
}
}
Configuración de sockets Unix (comportamiento específico de la plataforma):
| Configuración | macOS | Linux |
|---|---|---|
allowUnixSockets: string[] | Lista de rutas de socket permitidas | Ignorado (seccomp no puede filtrar por ruta) |
allowAllUnixSockets: boolean | Permitir todos los sockets | Deshabilitar el bloqueo de seccomp |
Los sockets Unix están bloqueados por defecto en ambas plataformas.
- macOS: Use
allowUnixSocketspara permitir rutas específicas (p. ej.,["/var/run/docker.sock"]), oallowAllUnixSockets: truepara permitir todos. - Linux: El bloqueo usa filtros seccomp (solo x64/arm64). Si seccomp no está disponible, los sockets no están restringidos y se muestra una advertencia. Use
allowAllUnixSockets: truepara deshabilitar el bloqueo explícitamente.
Configuración del sistema de archivos
Usa dos patrones diferentes:
Restricciones de lectura (patrón denegar-luego-permitir) - todas las lecturas permitidas por defecto:
filesystem.denyRead- Array de rutas a las que denegar acceso de lectura. Array vacío = acceso de lectura completo.filesystem.allowRead- Array de rutas a las que volver a permitir acceso de lectura dentro de regiones denegadas (tiene precedencia sobre denyRead). Nota: esto es lo opuesto a escritura, dondedenyWritetiene precedencia sobreallowWrite.
Restricciones de escritura (patrón solo-permitir) - todas las escrituras denegadas por defecto:
filesystem.allowWrite- Array de rutas a las que permitir acceso de escritura. Array vacío = sin acceso de escritura.filesystem.denyWrite- Array de rutas a las que denegar acceso de escritura dentro de rutas permitidas (tiene precedencia sobre allowWrite)
Sintaxis de rutas (macOS):
Las rutas admiten patrones glob al estilo git en macOS, similares a la sintaxis de .gitignore:
*- Coincide con cualquier carácter excepto/(p. ej.,*.tscoincide confoo.tspero no confoo/bar.ts)**- Coincide con cualquier carácter incluyendo/(p. ej.,src/**/*.tscoincide con todos los archivos.tsensrc/)?- Coincide con cualquier carácter individual excepto/(p. ej.,file?.txtcoincide confile1.txt)[abc]- Coincide con cualquier carácter del conjunto (p. ej.,file[0-9].txtcoincide confile3.txt)
Ejemplos:
"allowWrite": ["src/"]- Permitir escritura en todo el directoriosrc/"allowWrite": ["src/**/*.ts"]- Permitir escritura en todos los archivos.tsensrc/y subdirectorios"denyRead": ["~/.ssh"]- Denegar lectura al directorio SSH"denyRead": ["/Users"], "allowRead": ["."]- Denegar lectura a todo/Users, pero volver a permitir el directorio actual"denyWrite": [".env"]- Denegar escritura al archivo.env(incluso si el directorio actual está permitido)
Sintaxis de rutas (Linux):
Linux actualmente no admite coincidencia glob. Use solo rutas literales:
"allowWrite": ["src/"]- Permitir escritura en el directoriosrc/"denyRead": ["/home/user/.ssh"]- Denegar lectura al directorio SSH"denyRead": ["/home"], "allowRead": ["."]- Denegar lectura a todo/home, pero volver a permitir el directorio actual
Todas las plataformas:
- Las rutas pueden ser absolutas (p. ej.,
/home/user/.ssh) o relativas al directorio de trabajo actual (p. ej.,./src) ~se expande al directorio home del usuario
Otra configuración
ignoreViolations- Objeto que asigna patrones de comandos a arrays de rutas donde las violaciones deben ignorarseenableWeakerNestedSandbox- Habilita el modo de sandbox más débil para entornos Docker (booleano, por defecto: false)javaAgentJarPath- macOS/Linux: ruta absoluta asrt-proxy-agent.jar, el agente JVM inyectado medianteJAVA_TOOL_OPTIONS(ver "JVM tools" en Aislamiento de red). Solo lo necesitan los consumidores que empaquetan sandbox-runtime y distribuyen el jar por separado; una instalación normal de npm lo encuentra envendor/java-proxy-agent/.enableWeakerNetworkIsolation- Permite el acceso acom.apple.trustd.agenten el sandbox de macOS (booleano, por defecto: false). Esto es necesario para que los programas Go (gh,gcloud,terraform,kubectl, etc.) verifiquen certificados TLS cuando se usahttpProxyPortcon un proxy MITM y una CA personalizada. Advertencia de seguridad: habilitar esto abre un posible vector de exfiltración de datos a través del servicio trustd.allowAppleEvents- Permite enviar Apple Events y solicitudes de apertura de Launch Services desde el sandbox de macOS (booleano, por defecto: false). Sin esto, comandos comoopen,osascript, y cualquier cosa que abra URLs o ejecute scripts de otras aplicaciones mediante AppleScript falla con el error de AppleScript-600("Application isn't running") o errores de LaunchServices (-10822,-54). Advertencia de seguridad: habilitar esto significa que el sandbox ya no proporciona aislamiento de ejecución de código. Un comando en sandbox puede lanzar otras aplicaciones medianteopensin solicitud al usuario, y cualquier cosa que lance se ejecuta fuera de las restricciones de sistema de archivos y red del sandbox; la ejecución de scripts de aplicaciones ya en ejecución mediante Apple Events está además condicionada por el consentimiento de automatización TCC por aplicación del usuario. Los integradores solo deben obtener esta opción de configuración de confianza a nivel de usuario — nunca de archivos locales del proyecto en un repositorio clonado, lo que permitiría a un proyecto creado por un atacante elevar sus propios permisos de sandbox.
Recetas de configuración comunes
Permitir acceso a GitHub (todos los endpoints necesarios):```json { "network": { "allowedDomains": [ "github.com", "*.github.com", "lfs.github.com", "api.github.com" ], "deniedDomains": [] }, "filesystem": { "denyRead": [], "allowWrite": ["."], "denyWrite": [] } }
**Restringir a directorios específicos:**```json
{
"network": {
"allowedDomains": [],
"deniedDomains": []
},
"filesystem": {
"denyRead": ["~/.ssh"],
"allowWrite": [".", "src/", "test/"],
"denyWrite": [".env", "secrets/"]
}
}
Acceso al sistema de archivos solo del espacio de trabajo (denegar lecturas fuera del espacio de trabajo):```json { "network": { "allowedDomains": [], "deniedDomains": [] }, "filesystem": { "denyRead": ["/Users"], "allowRead": ["."], "allowWrite": ["."], "denyWrite": [] } }
Esto deniega la lectura de cualquier cosa bajo `/Users` (o `/home` en Linux), y luego vuelve a permitir el directorio de trabajo actual. Las rutas del sistema (`/usr`, `/lib`, etc.) permanecen legibles.
### Problemas comunes y consejos
**Ejecutar Jest:** Use el flag `--no-watchman` para evitar violaciones del sandbox:```bash
srt "jest --no-watchman"
Watchman accede a archivos fuera de los límites del sandbox, lo que provocará errores de permisos. Deshabilitarlo permite que Jest se ejecute con el observador de archivos integrado en su lugar.
Compatibilidad de plataformas
- macOS: Utiliza
sandbox-execcon perfiles personalizados (sin dependencias adicionales) - Linux: Utiliza
bubblewrap(bwrap) para la contenedorización - Windows: Alpha — utiliza un asistente
srt-win.exeincluido (sin dependencias adicionales). Consulta Windows (alpha) a continuación para la configuración, el modelo de seguridad y las limitaciones conocidas
Dependencias específicas de la plataforma
Linux requiere:
bubblewrap- Entorno de ejecución de contenedores- Ubuntu/Debian:
apt-get install bubblewrap - Fedora:
dnf install bubblewrap - Arch:
pacman -S bubblewrap
- Ubuntu/Debian:
socat- Retransmisión de sockets para el puenteo de proxy- Ubuntu/Debian:
apt-get install socat - Fedora:
dnf install socat - Arch:
pacman -S socat
- Ubuntu/Debian:
ripgrep- Herramienta de búsqueda rápida para la detección de rutas denegadas- Ubuntu/Debian:
apt-get install ripgrep - Fedora:
dnf install ripgrep - Arch:
pacman -S ripgrep
- Ubuntu/Debian:
Nota para Ubuntu 24.04+: Estas versiones habilitan kernel.apparmor_restrict_unprivileged_userns de forma predeterminada, lo que permite unshare(CLONE_NEWUSER) pero elimina las capacidades del espacio de nombres resultante. Tanto bubblewrap como la capa de aislamiento seccomp necesitan espacios de nombres de usuario con capacidades. Deshabilita la restricción con:```bash
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
o añade un perfil de AppArmor que otorgue `userns` a los binarios correspondientes.
**Dependencias opcionales de Linux (para el fallback de seccomp):**
El paquete incluye filtros BPF de seccomp pregenerados para las arquitecturas x86-64 y arm. Estas dependencias solo son necesarias si estás en una arquitectura diferente donde los filtros pregenerados no están disponibles:
- `gcc` o `clang` - Compilador de C
- `libseccomp-dev` - Archivos de desarrollo de la librería Seccomp
- Ubuntu/Debian: `apt-get install gcc libseccomp-dev`
- Fedora: `dnf install gcc libseccomp-devel`
- Arch: `pacman -S gcc libseccomp`
**macOS requiere:**
- `ripgrep` - Herramienta de búsqueda rápida para la detección de rutas denegadas
- Instalar mediante Homebrew: `brew install ripgrep`
- O descargar desde: https://github.com/BurntSushi/ripgrep/releases
**Windows requiere:**
- Sin dependencias adicionales. El asistente `srt-win.exe` (x64 y arm64) está incluido en el paquete npm. Se requiere un paso único de `windows-install` con privilegios elevados — ver más abajo.
## Windows (alpha)
El soporte para Windows está en **alpha**. El proceso en sandbox se ejecuta bajo una cuenta de usuario local dedicada `srt-sandbox`, aislada del usuario que la invoca mediante primitivas de seguridad nativas de Windows — una barrera de egreso de Windows Filtering Platform (WFP) basada en el SID de la cuenta del sandbox, y ACEs explícitas por sesión que otorgan o deniegan a ese SID el acceso a las rutas del sistema de archivos configuradas.
### Configuración
Ejecutar una vez por máquina (se auto-eleva; un aviso de UAC):```powershell
npx @anthropic-ai/sandbox-runtime windows-install
Esto aprovisiona la cuenta de usuario local srt-sandbox (con una contraseña aleatoria almacenada cifrada con DPAPI en HKLM\SOFTWARE\sandbox-runtime — a nivel de máquina, de modo que las instalaciones en flota ejecutándose como SYSTEM funcionan y la rotación de un usuario actualiza la copia que los demás leen), el grupo local sandbox-runtime-users, e instala un conjunto de filtros WFP a nivel de máquina vinculado al SID de srt-sandbox. Es idempotente — volver a ejecutarlo rota la contraseña de la cuenta sandbox y reconcilia el conjunto de filtros.
No se requiere cerrar sesión. Los filtros WFP se vinculan al SID de la cuenta sandbox dedicada, por lo que tu propia red, servicios y cualquier otro principal en la máquina no se ven afectados.
Tras la instalación, SandboxManager.initialize() y la CLI srt funcionan como en otras plataformas. initialize() verifica que la cuenta sandbox y la valla WFP estén activas, y falla con un error accionable si no lo están.
La instalación/desinstalación programática se exporta como installWindowsSandbox() / uninstallWindowsSandbox().
Modelo de seguridad
El comando en sandbox se ejecuta como la cuenta srt-sandbox, no como el usuario que lo invoca. El helper incluido srt-win.exe realiza un lanzamiento de dos saltos: el broker llama a CreateProcessWithLogonW para iniciar un runner como srt-sandbox, y el runner lanza el objetivo bajo un token restringido dentro de un objeto job. El hijo hereda el perfil aislado de la cuenta sandbox (%USERPROFILE%, %TEMP%, HKCU) y un entorno nuevo superpuesto únicamente con el PATH del broker y las variables de proxy generadas.
Ejecutar bajo un SID de usuario distinto cierra estructuralmente la clase de escape por spawn sustituto (Task Scheduler, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS sobre un proceso propiedad del broker, BITS, COM fuera de proceso con RunAs="Interactive User"): cualquier proceso que el hijo logre lanzar fuera de banda sigue llevando el SID srt-sandbox, por lo que permanece sujeto a la valla de egreso WFP y no tiene derechos sobre los archivos del usuario que lo invoca.
El aislamiento de red es un conjunto de dos filtros WFP en FWPM_LAYER_ALE_AUTH_CONNECT_V4/V6: un PERMIT para destinos de loopback dentro del rango de puertos de proxy configurado (por defecto 60080–60089), y un BLOCK para cualquier conexión cuyo token lleve el SID srt-sandbox. El proceso en sandbox alcanza internet solo a través de los proxies HTTP/SOCKS5 de JS que escuchan en ese rango; un proceso que elimine su entorno de proxy y se conecte directamente queda bloqueado en el kernel.
El aislamiento del sistema de archivos se aplica mediante ACLs discrecionales de NTFS. La cuenta srt-sandbox no tiene derechos inherentes sobre los archivos del usuario que invoca, por lo que en initialize() el sandbox escribe ACEs explícitas aditivas y heredables solo para el SID srt-sandbox — nunca reescribe ni reemplaza el descriptor de seguridad existente de una ruta:
filesystem.allowWrite→ una ACE ALLOWMODIFYheredable (READ|WRITE|EXECUTE|DELETE, conFILE_DELETE_CHILDretenido). El proceso en sandbox puede crear, modificar y eliminar archivos dentro del árbol de trabajo; retenerFILE_DELETE_CHILDde la concesión es defensa en profundidad para los sellos de denegación a continuación, no una protección sobre la raíz del árbol.filesystem.allowRead→ una ACE ALLOWREAD|EXECUTEheredablefilesystem.denyRead/filesystem.denyWrite→ una ACE DENY heredable sobre el objetivo, más una DENYFILE_DELETE_CHILDheredable sobre su padre — junto con elFILE_DELETE_CHILDretenido en la concesión del árbol de trabajo, esto impide que el proceso en sandbox renombre o elimine una ruta denegada a través de su directorio padre
reset() elimina cada ACE que esta sesión añadió (con conteo de referencias entre los hosts concurrentes de este usuario mediante la base de datos de sesión por usuario; una pasada de recuperación ante fallos en el siguiente initialize() limpia tras una salida no limpia). Se admiten objetivos de directorio (las ACEs se heredan a todo el subárbol). Los patrones glob se expanden a rutas concretas en el momento de initialize() — una ruta coincidente que aparezca después no queda cubierta.
Terminación TLS en Windows
network.tlsTerminate requiere que la CA MITM esté presente en el almacén de certificados CurrentUser\Root del usuario sandbox (schannel — el backend TLS usado por System32\curl.exe, PowerShell Invoke-WebRequest, .NET y git con backend predeterminado — solo confía en el almacén del SO, no en variables de entorno). Este es un paso en el momento de la instalación, separado de windows-install:```typescript
import { windowsTrustCa } from '@anthropic-ai/sandbox-runtime'
windowsTrustCa('/path/to/mitm-ca.crt') // or: srt-win user trust-ca
`initialize()` compara la huella digital del CA de la sesión con la instalada y falla con un mensaje procesable en caso de discrepancia, de modo que un CA obsoleto instalado en el momento de la instalación no puede romper TLS silenciosamente dentro del sandbox.
Los clientes respaldados por OpenSSL (`curl` de msys2, `git -c http.sslBackend=openssl`, Node, Python, cargo) están cubiertos por la capa de confianza basada en variables de entorno: el mismo paquete de confianza usado en macOS/Linux se pasa al sandbox mediante `NODE_EXTRA_CA_CERTS`, `SSL_CERT_FILE`, `CURL_CA_BUNDLE`, `GIT_SSL_CAINFO`, `CARGO_HTTP_CAINFO`, etc., y la ruta del paquete se añade a la concesión `allowRead` de la sesión para que la cuenta del sandbox pueda abrirlo.
### Configuración específica de Windows
Los bloques multiplataforma `filesystem` y `network` se aplican como se describe arriba. Los ajustes exclusivos de Windows se encuentran bajo `windows`:
- `windows.proxyPortRange` — rango de puertos inclusivo `[low, high]` en el que se vinculan los proxies JS. **Debe coincidir** con el rango pasado a `windows-install --proxy-port-range` (por defecto `[60080, 60089]`) — el PERMIT de loopback de WFP solo cubre ese rango.
- `windows.sublayerGuid` — GUID de la subcapa de WFP bajo la cual se instalaron los filtros. Omitir para usar el valor predeterminado en tiempo de compilación; establecer solo cuando herramientas empresariales instalaron los filtros bajo una subcapa personalizada.
- `windows.srtWin.path` — ruta al binario `srt-win`. Omitir para resolver el `vendor/srt-win/<arch>/srt-win.exe` empaquetado. Establecer al integrar la CLI de `srt-win` en un binario multicall; los spawns entonces pasan `--srt-win` como `argv[1]` para que el despachador del integrador pueda enrutar a `srt_win::run_from_args`.
### Limitaciones conocidas
- **Revocación de certificados bajo schannel.** La obtención de CRL/OCSP de CryptoAPI sale a través de WinHTTP bajo el token del llamador, ignorando el entorno del proxy, por lo que es bloqueada por la valla de egreso de WFP. Las herramientas que usan schannel con la comprobación de revocación activada por defecto fallan con `CRYPT_E_REVOCATION_OFFLINE` (`0x80092013`) a menos que se desactive la revocación por herramienta: `curl --ssl-no-revoke`, `git -c http.schannelCheckRevoke=false`, `CARGO_HTTP_CHECK_REVOKE=false`. `Invoke-WebRequest`, `HttpClient` de .NET y `gh` no comprueban la revocación por defecto y no se ven afectados. Está previsto servir un punto de distribución de CRL desde el proxy de loopback para eliminar este workaround.
- **Las instalaciones de herramientas por usuario no son accesibles.** El proceso en sandbox se ejecuta como `srt-sandbox`, no como tú, por lo que las herramientas instaladas bajo tu perfil (Node gestionado por nvm/fnm, paquetes `winget`/Scoop por usuario, `pip install --user`, `%LOCALAPPDATA%\Programs\…`) se resuelven en el `PATH` heredado pero no pueden ser abiertas por la cuenta del sandbox. Prefiere instalaciones para toda la máquina (`Program Files`, `choco`/`winget --scope machine`), o añade las rutas de perfil específicas a `filesystem.allowRead`.
- **Las anulaciones de `filesystem.allowRead` / `filesystem.allowWrite` por ejecución no están soportadas.** Las `allowRead`/`allowWrite` a nivel de sesión (en la configuración pasada a `initialize()`) funcionan como se describe arriba; pasarlas por comando en el `customConfig` de `wrapWithSandbox` lanza una excepción — las concesiones se aplican a toda la sesión mediante `srt-win acl grant` en `initialize()`, y `srt-win exec` solo expone denegaciones por ejecución.
- **`proxyAuthToken` es visible en la línea de comandos del runner.** El entorno del proxy (incluyendo `HTTP_PROXY=http://srt:<token>@127.0.0.1:…`) se pasa al runner de dos saltos como argumentos `--env` en el argv de `srt-win exec`, por lo que el token es legible por cualquier principal local que pueda abrir el proceso del runner para `PROCESS_QUERY_LIMITED_INFORMATION`. El token existe para que el proceso en sandbox pueda autenticarse ante el proxy de loopback, por lo que no es un secreto para el propio sandbox; en una máquina de desarrollo de un solo usuario esto es generalmente aceptable, pero en un host compartido trata la lista de permitidos del proxy como accesible por otros principales de la misma sesión.
- **La resolución DNS mediante el resolutor del sistema no está vallada.** `getaddrinfo()` es atendido por el servicio `Dnscache` ejecutándose como `NETWORK SERVICE`, por lo que la resolución de nombres tiene éxito aunque el `connect()` posterior desde el proceso en sandbox esté bloqueado. Las herramientas que hacen su propio UDP/53 (`nslookup`, `dig`) están valladas. Esto refleja el comportamiento de macOS.
### Desinstalación```powershell
npx @anthropic-ai/sandbox-runtime windows-uninstall
Elimina el conjunto de filtros WFP, la cuenta srt-sandbox y su perfil, el grupo sandbox-runtime-users, y elimina la clave HKLM\SOFTWARE\sandbox-runtime (credencial, marcador, registro de CA) — un solo aviso de UAC. %ProgramData%\sandbox-runtime (el material de la clave de CA) se deja en su lugar; elimínalo (y %LOCALAPPDATA%\sandbox-runtime por usuario) manualmente para un barrido completo.
Desarrollo```bash
Install dependencies
npm install
Build the project
npm run build
Run tests
npm test
Type checking
npm run typecheck
Lint code
npm run lint
Format code
npm run format
### Compilación de binarios Seccomp
El filtro BPF y el cargador `apply-seccomp` se compilan desde el código fuente en C en `vendor/seccomp-src/` mediante `npm run build:seccomp` (solo Linux; requiere `gcc` y `libseccomp-dev`). CI lo ejecuta antes de las pruebas en cada arquitectura Linux, y el flujo de trabajo de publicación compila ambas arquitecturas y las empaqueta en el paquete publicado.
## Detalles de implementación
### Arquitectura de aislamiento de red
El sandbox ejecuta servidores proxy HTTP y SOCKS5 en la máquina host que filtran todas las solicitudes de red según las reglas de permisos:
1. **Tráfico HTTP/HTTPS**: Un servidor proxy HTTP intercepta las solicitudes y las valida contra los dominios permitidos/denegados
2. **Otro tráfico de red**: Un proxy SOCKS5 gestiona todas las demás conexiones TCP (SSH, conexiones a bases de datos, etc.)
3. **Aplicación de permisos**: Los proxies aplican las reglas de `permissions` de tu configuración
**Comunicación del proxy específica de la plataforma:**
- **Linux**: Las solicitudes se enrutan a través del sistema de archivos mediante sockets de dominio Unix (usando `socat` como puente). El espacio de nombres de red se elimina del contenedor bubblewrap, lo que garantiza que todo el tráfico de red deba pasar por los proxies.
- **macOS**: El perfil de Seatbelt permite la comunicación solo a puertos específicos de localhost donde escuchan los proxies. Todo el demás acceso a la red está bloqueado.
- **Windows**: Un filtro WFP `ALE_AUTH_CONNECT` bloquea toda conexión saliente desde la cuenta `srt-sandbox` excepto el loopback al rango de puertos del proxy configurado. Los proxies se enlazan dentro de ese rango. Las variables de entorno (`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, …) apuntan las herramientas a los proxies, pero el filtro WFP es el límite — un proceso que las ignore o las desestablezca sigue estando cercado.
**Herramientas JVM (macOS/Linux):** la JVM ignora `HTTPS_PROXY`/`NO_PROXY` y no tiene variable de entorno para las credenciales del proxy — la selección del proxy proviene de las propiedades del sistema `https.proxyHost` y la credencial solo puede proporcionarse a través de `java.net.Authenticator`. Por lo tanto, las herramientas basadas en JVM (la caché remota gRPC de Bazel, Gradle, Maven, …) de otro modo marcarían el objetivo directamente y fallarían, o alcanzarían el proxy sin su token y recibirían un 407. Para cerrar esa brecha, srt inyecta un pequeño `-javaagent` mediante `JAVA_TOOL_OPTIONS` (la variable de entorno solo lleva la ruta del jar, la credencial permanece en `HTTPS_PROXY`). Al iniciar la JVM, el agente establece `http[s].proxyHost`/`Port` y `http.nonProxyHosts` a partir de las variables de entorno del proxy, vuelve a habilitar la autenticación Basic para los túneles CONNECT e instala un Authenticator para el endpoint del proxy. Las propiedades de proxy `-D` explícitas en la línea de comandos de la JVM siguen teniendo prioridad, y cualquier `JAVA_TOOL_OPTIONS` heredada se conserva (a menos que sea una variable de entorno de credenciales denegada). Como resultado, cada JVM imprime una línea `Picked up JAVA_TOOL_OPTIONS: …` en stderr; un runtime jlink'd compilado sin el módulo `java.instrument` no puede cargar agentes y se negará a iniciarse bajo el sandbox — desestablece `JAVA_TOOL_OPTIONS` en el comando para dicha herramienta. El jar se distribuye en el paquete npm como `vendor/java-proxy-agent/srt-proxy-agent.jar` (fuente: `vendor/java-proxy-agent-src/`; compilado por el flujo de trabajo de publicación, o localmente con `npm run build:java-agent` — requiere un JDK ≥ 17). Si no se encuentra, `JAVA_TOOL_OPTIONS` se deja intacta y las JVM se comportan como antes; los empaquetadores pueden apuntar a su propia copia con `javaAgentJarPath`.
### Aislamiento del sistema de archivos
Las restricciones del sistema de archivos se aplican a nivel del sistema operativo:
- **macOS**: Usa `sandbox-exec` con perfiles de Seatbelt generados dinámicamente que especifican las rutas de lectura/escritura permitidas
- **Linux**: Usa `bubblewrap` con montajes bind, marcando los directorios como de solo lectura o de lectura-escritura según la configuración
- **Windows**: Escribe ACEs explícitas aditivas `(OI)(CI)` para el SID `srt-sandbox` en las rutas configuradas (ALLOW en `allowRead`/`allowWrite`, DENY en `denyRead`/`denyWrite`), y luego las elimina en `reset()`
**Permisos predeterminados del sistema de archivos:**
- **Lectura** (denegar-luego-permitir): Permitida en todas partes por defecto. Puedes denegar regiones amplias y luego volver a permitir rutas específicas dentro de ellas. `allowRead` tiene prioridad sobre `denyRead`.
- Ejemplo: `denyRead: ["~/.ssh"]` para bloquear el acceso a las claves SSH
- Ejemplo: `denyRead: ["/Users"], allowRead: ["."]` para bloquear todo `/Users` excepto el espacio de trabajo
- `denyRead: []` vacío = acceso de lectura completo (nada denegado)
- **Escritura** (solo-permitir): Denegada en todas partes por defecto. Debes permitir rutas explícitamente.
- Ejemplo: `allowWrite: [".", "/tmp"]` para permitir escrituras en el directorio actual y /tmp
- `allowWrite: []` vacío = sin acceso de escritura (nada permitido)
- `denyWrite` crea excepciones dentro de las rutas permitidas (la denegación tiene prioridad)
**La precedencia es intencionalmente opuesta para lecturas vs escrituras:** `allowRead` anula `denyRead`, mientras que `denyWrite` anula `allowWrite`. Esto te permite delimitar regiones legibles dentro de áreas denegadas, y delimitar regiones protegidas dentro de áreas escribibles.
### Rutas de denegación obligatorias (archivos autoprotegidos)
Ciertos archivos y directorios sensibles están **siempre bloqueados para escritura**, incluso si se encuentran dentro de una ruta de escritura permitida. Esto proporciona defensa en profundidad contra escapes del sandbox y manipulación de la configuración.
**Archivos siempre bloqueados:**
- Archivos de configuración de shell: `.bashrc`, `.bash_profile`, `.zshrc`, `.zprofile`, `.profile`
- Archivos de configuración de Git: `.gitconfig`, `.gitmodules`
- Otros archivos sensibles: `.ripgreprc`, `.mcp.json`
**Directorios siempre bloqueados:**
- Directorios de IDE: `.vscode/`, `.idea/`
- Directorios de configuración de Claude: `.claude/commands/`, `.claude/agents/`
- Hooks y configuración de Git: `.git/hooks/`, `.git/config`
Estas rutas se bloquean automáticamente - no necesitas añadirlas a `denyWrite`. Por ejemplo, incluso con `allowWrite: ["."]`, escribir en `.bashrc` o `.git/hooks/pre-commit` fallará:```bash
$ srt 'echo "malicious" >> .bashrc'
/bin/bash: .bashrc: Operation not permitted
$ srt 'echo "bad" > .git/hooks/pre-commit'
/bin/bash: .git/hooks/pre-commit: Operation not permitted
Nota (Linux): En Linux, las rutas de denegación obligatoria solo bloquean archivos que ya existen. Los archivos inexistentes en estos patrones no pueden ser bloqueados por el enfoque de bind-mount de bubblewrap. macOS utiliza patrones glob que bloquean tanto archivos existentes como nuevos.
Profundidad de búsqueda en Linux: En Linux, el sandbox utiliza ripgrep para escanear archivos peligrosos en subdirectorios dentro de las rutas de escritura permitidas. Por defecto, busca hasta 3 niveles de profundidad por rendimiento. Puedes configurar esto con mandatoryDenySearchDepth:```json
{
"mandatoryDenySearchDepth": 5,
"filesystem": {
"allowWrite": ["."]
}
}
- Predeterminado: `3` (busca hasta 3 niveles de profundidad)
- Rango: `1` a `10`
- Valores más altos proporcionan más protección pero un rendimiento más lento
- Los archivos en el CWD (profundidad 0) siempre están protegidos independientemente de esta configuración
### Restricciones de sockets Unix (Linux)
En Linux, el sandbox utiliza **seccomp BPF (Berkeley Packet Filter)** para bloquear la creación de sockets de dominio Unix a nivel de llamada al sistema. Esto proporciona una capa adicional de seguridad para evitar que los procesos creen nuevos sockets de dominio Unix para IPC local (a menos que se permita explícitamente).
**Cómo funciona:**
1. **Filtro BPF integrado**: El paquete incluye un binario estático `apply-seccomp` para x64 y arm64 con el filtro seccomp BPF compilado. El filtro es específico de la arquitectura pero independiente de libc, por lo que el binario funciona tanto con glibc como con musl.
2. **Detección en tiempo de ejecución**: El sandbox detecta automáticamente la arquitectura de tu sistema y utiliza el binario `apply-seccomp` correspondiente.
3. **Filtrado de llamadas al sistema**: El filtro BPF intercepta la llamada al sistema `socket()` y bloquea la creación de sockets `AF_UNIX` devolviendo `EPERM`. Esto evita que el código en sandbox cree nuevos sockets de dominio Unix.
4. **Aplicación en dos etapas usando el binario apply-seccomp**:
- El bwrap externo crea el sandbox con restricciones de sistema de archivos, red y espacio de nombres PID
- Los procesos de puenteo de red (socat) se inician dentro del sandbox (necesitan sockets Unix)
- apply-seccomp crea un espacio de nombres anidado de usuario+PID+mount y vuelve a montar `/proc`
- Dentro del espacio de nombres anidado, apply-seccomp actúa como PID 1 (init/reaper no volcable)
- apply-seccomp hace fork, aplica el filtro seccomp mediante `prctl()` y ejecuta el comando del usuario
- El comando del usuario se ejecuta con todas las restricciones del sandbox más el bloqueo de creación de sockets Unix
**Aislamiento del espacio de nombres PID**: El espacio de nombres PID anidado garantiza que el comando del usuario no pueda ver ni direccionar ningún proceso que se ejecute sin el filtro seccomp (el init de bwrap, el envoltorio de shell o los ayudantes de socat). Esto mantiene intacta la frontera de seccomp independientemente de `kernel.yama.ptrace_scope`, ya que los ayudantes sin filtrar no son accesibles mediante `ptrace` o `/proc/N/mem`. El PID 1 interno establece `PR_SET_DUMPABLE=0` para que tampoco sea rastreable con ptrace. Si falla la creación del espacio de nombres anidado, apply-seccomp aborta en lugar de ejecutarse sin aislamiento.
**Limitaciones de seguridad**: El filtro bloquea `socket(AF_UNIX, ...)` y las llamadas al sistema `io_uring_setup`/`io_uring_enter`/`io_uring_register` (las tres últimas porque `IORING_OP_SOCKET` en Linux 5.19+ de lo contrario eludiría la regla de `socket()`). No impide operaciones sobre descriptores de archivo de sockets Unix heredados de procesos padre o pasados mediante `SCM_RIGHTS`. Para la mayoría de los escenarios de sandbox, bloquear la creación de sockets es suficiente para evitar IPC no autorizada.
**Cero dependencias en tiempo de ejecución**: Se incluyen binarios estáticos precompilados de apply-seccomp y filtros BPF pregenerados para las arquitecturas x64 y arm64. No se requieren herramientas de compilación ni dependencias externas en tiempo de ejecución.
**Soporte de arquitecturas**: x64 y arm64 son totalmente compatibles con binarios precompilados. Otras arquitecturas no son compatibles actualmente. Para usar el sandbox sin el bloqueo de sockets Unix en arquitecturas no compatibles, establece `allowAllUnixSockets: true` en tu configuración.
### Detección y monitoreo de violaciones
Cuando un proceso en sandbox intenta acceder a un recurso restringido:
1. **Bloquea la operación** a nivel del sistema operativo (devuelve el error `EPERM`)
2. **Registra la violación** (mecanismos específicos de la plataforma)
3. **Notifica al usuario** (en Claude Code, esto desencadena un aviso de permiso)
**macOS**: El entorno de ejecución del sandbox se conecta al almacén de registros de violaciones del sandbox del sistema de macOS. Esto proporciona notificaciones en tiempo real con información detallada sobre lo que se intentó y por qué se bloqueó. Este es el mismo mecanismo que utiliza Claude Code para la detección de violaciones.```bash
# View sandbox violations in real-time
log stream --predicate 'process == "sandbox-exec"' --style syslog
Linux: Bubblewrap no proporciona informes de infracciones integrados. Utilice strace para rastrear llamadas al sistema e identificar operaciones bloqueadas:```bash
Trace all denied operations
strace -f srt 2>&1 | grep EPERM
Trace specific file operations
strace -f -e trace=open,openat,stat,access srt 2>&1 | grep EPERM
Trace network operations
strace -f -e trace=network srt 2>&1 | grep EPERM
### Avanzado: Trae tu propio proxy
Para un filtrado de red más sofisticado, puedes configurar el sandbox para que use tu propio proxy en lugar de los integrados. Esto permite:
- **Inspección de tráfico**: Usa herramientas como [mitmproxy](https://mitmproxy.org/) para inspeccionar y modificar el tráfico
- **Lógica de filtrado personalizada**: Implementa reglas complejas más allá de las simples listas de dominios permitidos
- **Registro de auditoría**: Registra todas las solicitudes de red para cumplimiento o depuración
**Ejemplo con mitmproxy:**```bash
# Start mitmproxy with custom filtering script
mitmproxy -s custom_filter.py --listen-port 8888
Nota: La configuración de proxy personalizada aún no es compatible con el nuevo formato de configuración. Esta función se añadirá en una versión futura.
Consideración de seguridad importante: Incluso con listas de dominios permitidos, pueden existir vectores de exfiltración. Por ejemplo, permitir github.com permite que un proceso haga push a cualquier repositorio. Con un proxy MITM personalizado y una configuración de certificados adecuada, puedes inspeccionar y filtrar llamadas API específicas para evitarlo.
Limitaciones de seguridad
- Limitaciones del sandboxing de red: El sistema de filtrado de red opera restringiendo los dominios a los que los procesos tienen permitido conectarse. No inspecciona de otro modo el tráfico que pasa por el proxy y los usuarios son responsables de asegurarse de que solo permiten dominios de confianza en su política. Los nombres de host permitidos se comprueban además contra un conjunto denegado de direcciones resueltas antes de una conexión directa (véase Comprobación de direcciones resueltas arriba), por lo que un nombre permitido no puede apuntarse a loopback, link-local, las propias direcciones de este host o una IP que hayas listado en
deniedDomains; otros rangos privados solo quedan cubiertos si los listas endeniedResolvedAddresses(una entrada comodín en un dominio cuyo DNS no controlas puede, de lo contrario, apuntarse a servicios en tu LAN), y las conexiones que salen a través deparentProxy/mitmProxydependen de ese salto para la comprobación equivalente.
- Escalada de privilegios mediante sockets Unix: La configuración
allowUnixSocketspuede conceder inadvertidamente acceso a servicios del sistema potentes que podrían provocar elusiones del sandbox. Por ejemplo, si se usa para permitir el acceso a/var/run/docker.sock, esto concedería efectivamente acceso al sistema anfitrión mediante la explotación del socket de docker. Se anima a los usuarios a considerar cuidadosamente cualquier socket unix que permitan a través del sandbox. - Escalada de permisos del sistema de archivos: Permisos de escritura en el sistema de archivos excesivamente amplios pueden habilitar ataques de escalada de privilegios. Permitir escrituras en directorios que contienen ejecutables en
$PATH, directorios de configuración del sistema o archivos de configuración del shell del usuario (.bashrc,.zshrc) puede provocar la ejecución de código en distintos contextos de seguridad cuando otros usuarios o procesos del sistema acceden a estos archivos. - Fortaleza del sandbox de Linux: La implementación de Linux proporciona un fuerte aislamiento del sistema de archivos y de red, pero incluye un modo
enableWeakerNestedSandboxque le permite funcionar dentro de entornos Docker sin namespaces privilegiados. Esta opción debilita considerablemente la seguridad y solo debe usarse en casos en los que se aplique aislamiento adicional por otros medios. - Aislamiento de red más débil (macOS): La opción
enableWeakerNetworkIsolationvuelve a habilitar el acceso acom.apple.trustd.agent, que es necesario para que los programas Go verifiquen certificados TLS a través del framework Security de macOS. Esto abre un posible vector de exfiltración de datos a través del servicio trustd y solo debe habilitarse cuando se requiera la verificación TLS de Go (p. ej., al usarhttpProxyPortcon un proxy MITM y una CA personalizada). - Apple Events (macOS): La opción
allowAppleEventsvuelve a habilitar el envío de Apple Events y solicitudes de apertura de Launch Services ((allow appleevent-send),(allow lsopen)y mach-lookups paracom.apple.coreservices.appleevents,com.apple.CoreServices.coreservicesdycom.apple.coreservices.quarantine-resolver), que requierenopen,osascripty los ayudantes de apertura de URL. Con estos permitidos, un comando en sandbox puede lanzar aplicaciones arbitrarias sin ningún aviso al usuario, y las aplicaciones lanzadas se ejecutan completamente fuera del sandbox, por lo que esta opción elimina el aislamiento de ejecución de código, no solo lo debilita. La automatización por scripting de aplicaciones ya en ejecución mediante Apple Events está además condicionada por el consentimiento de automatización de TCC de macOS, pero el lanzamiento medianteopenno lo está. Habilita esto solo cuando los comandos dentro del sandbox necesiten genuinamente abrir URLs o aplicaciones.
Limitaciones conocidas y trabajo futuro
Bypass del proxy en Linux: Actualmente usa variables de entorno (HTTP_PROXY, HTTPS_PROXY, ALL_PROXY) para dirigir el tráfico a través de proxies. Esto funciona para la mayoría de las aplicaciones, pero puede ser ignorado por programas que no respetan estas variables, lo que provoca que no puedan conectarse a internet.
Mejoras futuras:
-
Soporte de proxychains: Añadir soporte para
proxychainsconLD_PRELOADen Linux para interceptar llamadas de red a un nivel inferior, dificultando más el bypass -
Monitorización de violaciones en Linux: Implementar detección automática de violaciones basada en
stracepara Linux, integrada con el almacén de violaciones. Actualmente, los usuarios de Linux deben ejecutarstracemanualmente para ver las violaciones, a diferencia de macOS, que cuenta con monitorización automática de violaciones a través del almacén de registros del sistema