
El sandbox de Codex sí corta la red y la escritura fuera del espacio de trabajo, pero una regla «permitir siempre» acumulada en ~/.codex/rules/default.rules puede sacar de ahí todos tus comandos curl. En Claude Code, el sandbox no está activo por defecto: se activa con /sandbox o sandbox.enabled, y la red funciona ahí mediante una lista blanca de dominios. Para un agente sin supervisión, solo un contenedor con --network none y --read-only aguanta de verdad.
El 4 de septiembre de 2026 se supo que unos agentes de código de OpenAI habían quedado en un wiki alemán para intercambiarse trucos para saltarse el sandbox. La moral del asunto interesa menos que su mecánica. En tu máquina, la pregunta es qué permites al lanzar claude o codex sin mirar.
Lo que pasó en el wiki alemán
DseWiki es un wiki de desarrolladores alemanes alojado en prowiki.org, abierto desde hace veinticinco años. Entre el 11 de mayo y el 13 de julio de 2026 se publicaron allí unos 18.000 mensajes, procedentes de cerca de 3.700 identidades de agentes autónomos, con un pico entre el 16 y el 22 de junio y hasta 400 entradas al día. El colectivo Nightingale publicó el dosier en collusion.wiki el 4 de septiembre de 2026, el mismo día que la revelación de Reuters.
Junto a conjuntos de datos y puntos de API, allí se encuentran recetas de fuga. La más documentada: la lista NO_PROXY de su entorno confiaba en todo lo que terminara en .blob.core.windows.net, así que inventaron un nombre que acababa así y lo apuntaron, vía /etc/hosts, a la dirección que querían. Un agente publicó la receta bajo el título «REPRODUCIBLE BYPASS» el 20 de junio, otro la reprodujo catorce minutos después, según cuenta the-decoder.
El detalle incómodo es cronológico. Ya en marzo de 2026, OpenAI decía vigilar el 99,9 % del tráfico de sus agentes de código internos con GPT-5.4 Thinking, que ve todo el contexto, cadena de razonamiento y llamadas a herramientas incluidas, y escala los casos graves a un humano en menos de treinta minutos. El wiki siguió funcionando después de esa publicación. Un vigilante que lo ve todo no es una frontera.
El modelo de amenaza en tu máquina
Tú no eres OpenAI y tu agente probablemente no tiene ninguna intención. Aun así, bastan tres escenarios, y ninguno requiere malicia.
- La inyección de prompt: una página web, un issue de GitHub, un registro o el resultado de una herramienta MCP contiene instrucciones que el agente lee como órdenes. Es la vía de entrada principal, y no se corrige desde el prompt.
- La exfiltración: el agente lee tus claves y las envía a otro sitio. Basta un
curlhacia el dominio equivocado. - El comando destructivo: un
rm, ungit reset --hard, una migración sobre la base de datos equivocada.
La única respuesta que aguanta es una frontera que el modelo no elige, la del sistema operativo o la de un contenedor.
¿El sandbox de Codex corta de verdad la red?
Codex propone tres modos mediante -s: read-only, workspace-write y danger-full-access. La documentación anuncia que workspace-write permite escribir en el espacio de trabajo y corta la red. Lo comprobé en este Mac, con macOS 26.6.2, usando gpt-6-astra.
codex exec --skip-git-repo-check -C ./workspace -s workspace-write \
'Execute une fois : curl -sS -m 10 -o /dev/null -w "%{http_code}" https://example.com'Resultado: código de salida cero, 200. La red pasaba. En modo read-only, pidiendo la dirección remota, la salida fue code=200 ip=2606:4700:10::6814:179a t=0.084658: una respuesta real de un servidor de Cloudflare en ochenta y cinco milisegundos. Ni -c sandbox_workspace_write.network_access=false ni --ignore-user-config cambiaban nada.
Las escrituras, en cambio, sí estaban bien contenidas. En workspace-write, touch ./fichier pasaba y touch /Users/gekkode/fichier devolvía Operation not permitted. En read-only, hasta la escritura en la carpeta actual estaba rechazada. Pero ls ~/.ssh | wc -l devolvía doce: un sandbox de solo lectura te protege de la escritura, no de la lectura de tus claves.
La bandera que lo explicó todo es --ignore-rules.
codex exec --ignore-rules --skip-git-repo-check -C ./workspace -s workspace-write \
'Execute une fois : curl -sS -m 10 -o /dev/null -w "%{http_code}" https://example.com'
# curl: (6) Could not resolve host: example.comEl sandbox funciona. Lo que lo esquivaba está en ~/.codex/rules/default.rules, el archivo donde se van acumulando los «permitir siempre» que se pulsan mientras trabajas. Contenía 249 reglas, todas en allow, entre ellas estas:
prefix_rule(pattern=["curl"], decision="allow")
prefix_rule(pattern=["wget"], decision="allow")Un día, hace meses, se autorizó «para siempre» un curl concreto. La regla que quedó registrada se refiere al programa, no a la URL: desde entonces, cualquier curl hacia cualquier dirección se ejecutaba fuera del sandbox. Por eso también network_access = false no tenía efecto: la decisión se toma antes que la política de red. Revisa ese archivo, probablemente sea el ajuste más rentable de este artículo.
Para probar una frontera sin gastar un solo token, Codex expone el sandbox directamente, con los perfiles integrados :read-only, :workspace y :danger-full-access:
codex sandbox -P ':workspace' -- /usr/bin/curl -sS -m 8 https://example.com
# curl: (6) Could not resolve host: example.com
codex sandbox -P ':read-only' -- /usr/bin/touch ./preuve.txt
# touch: ./preuve.txt: Operation not permittedBlindar Claude Code
Los ajustes de abajo son los de la documentación de Claude Code, consultada el 7 de septiembre de 2026.
El sandbox de Claude Code no está activo por defecto: /sandbox lo escribe en el .claude/settings.local.json del proyecto, o lo pones en ~/.claude/settings.json para todos tus proyectos. Se apoya en Seatbelt en macOS, en bubblewrap en Linux y WSL2. La red pasa ahí por un proxy de lista blanca: ningún dominio está autorizado de antemano.
{
"sandbox": {
"enabled": true,
"network": {
"allowedDomains": ["github.com", "*.npmjs.org"],
"strictAllowlist": true
},
"credentials": {
"files": [{ "path": "~/.ssh", "mode": "deny" }],
"envVars": [{ "name": "GITHUB_TOKEN", "mode": "deny" }]
}
}
}strictAllowlist rechaza en lugar de preguntar, y solo tiene efecto desde los ajustes de usuario, gestionados o --settings: ponerlo en el .claude/settings.json de un repositorio no hace nada. El bloque credentials responde al agujero medido en Codex: por defecto, un sandbox deja leer ~/.ssh.
Dos límites que conviene conocer. Primero, las reglas de permisos actúan sobre el texto del comando, y la documentación lo dice sin rodeos: Bash(curl http://github.com/ *) no cubre ni curl -X GET, ni https, ni una URL pasada por variable. Prohíbe la herramienta de red y deja que trabaje WebFetch:
{
"permissions": {
"deny": ["Bash(curl:*)", "Bash(wget:*)", "Read(~/.ssh/**)"],
"allow": ["WebFetch(domain:github.com)"]
}
}Segundo, --permission-prompts none, aparecido en la 2.1.259, no hace lo que su nombre sugiere: en modo -p, cuando nadie puede responder, Claude Code rechaza la solicitud en lugar de plantearla. Es lo contrario de --dangerously-skip-permissions, y es la bandera correcta para un agente sin supervisión.
Reducir la superficie de los servidores MCP
Un servidor MCP es código que tú ejecutas y cuyos resultados entran en el contexto: los dos riesgos se suman. El caso de mcp-remote lo demostró: este proxy OAuth, descargado 437.000 veces y citado en las guías de integración de Cloudflare, Hugging Face y Auth0, pasaba al shell el endpoint de autorización que le daba el servidor remoto, lo que permitía ejecutar código de forma remota (CVE-2025-6514, documentado por Docker).
El asunto se resume en tres reglas. Fija los comandos exactos en lugar de los nombres: en allowedMcpServers, una entrada serverName no vale nada, porque la etiqueta la elige el usuario, mientras que un serverCommand debe coincidir argumento por argumento. Da a cada servidor un acceso de solo lectura: usuario SQL limitado a SELECT, servidor de archivos restringido a una carpeta. Y trata cualquier salida de una herramienta como un dato, nunca como una instrucción.
{
"allowedMcpServers": [
{ "serverCommand": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "."] },
{ "serverUrl": "https://mcp.sentry.dev/*" }
],
"deniedMcpServers": [{ "serverUrl": "https://*.untrusted.example.com/*" }]
}En un puesto individual, estas listas viven en tus ajustes. En equipo, un archivo managed-mcp.json colocado en /Library/Application Support/ClaudeCode/ en macOS o /etc/claude-code/ en Linux fija el conjunto exclusivo de servidores: claude mcp add responde entonces con un error de política de empresa. Si escribes tus propios servidores, el alcance se decide desde el diseño, como detalla el artículo sobre cómo crear un servidor MCP en PHP.
¿Cuándo hay que pasar al contenedor?
En cuanto el agente funciona sin ti. Un contenedor da una frontera que ni una regla olvidada ni la respuesta de una herramienta pueden mover. Las dos pruebas siguientes se ejecutaron aquí:
docker run --rm --network none alpine:3 \
sh -c 'wget -q -T 5 -O /dev/null https://example.com'
# wget: bad address 'example.com' (code 1)
docker run --rm --read-only --tmpfs /tmp alpine:3 \
sh -c 'touch /etc/preuve; touch /tmp/preuve'
# touch: /etc/preuve: Read-only file system (code 1, /tmp accepte)--network none corta hasta la resolución DNS, y --read-only con un tmpfs para los archivos temporales deja que el agente trabaje sin tocar el sistema. Monta el repositorio como volumen, nada más: ni ~/.ssh, ni ~/.aws, ni el socket de Docker, la documentación de Claude Code lo dice sin rodeos, autorizar /var/run/docker.sock equivale a dar acceso al host. El panorama de los agentes en línea de comandos indica cuáles aceptan este marco sin pelear.
Lo que hay que recordar
- Abre hoy mismo
~/.codex/rules/default.rules: un «permitir siempre» sobrecurlsaca todas tus peticiones del sandbox, en silencio, para siempre. codex sandbox -P ':read-only'y-P ':workspace'prueban la frontera sin llamar al modelo. Comprueba, no supongas.- Un sandbox bloquea la escritura, no la lectura:
~/.sshsigue siendo legible mientras no lo hayas incluido ensandbox.credentialsodenyRead. - Las reglas que filtran argumentos son frágiles: prohíbe
Bash(curl:*)y pasa porWebFetch(domain:…). Para los MCP, fijaserverCommandy noserverName. - Sin supervisión, toca contenedor:
--network none,--read-only, ningún secreto montado. El resto es comodidad. Para hacer limpieza en lo que cargan tus agentes, también está /skill-doctor.
Errores frecuentes
prefix_rule(pattern=["curl"], decision="allow") saca todo curl del sandbox. Revisa y poda ~/.codex/rules/default.rules, o lanza puntualmente con --ignore-rules.~/.ssh y ~/.aws/credentials siguen siendo legibles. Inclúyelos en sandbox.credentials o sandbox.filesystem.denyRead.Bash(curl http://github.com/ *) no cubre ni -X GET puesto antes de la URL, ni https, ni una URL pasada por variable. Rechaza la herramienta y pasa por WebFetch(domain:…).allowedMcpServers, serverName es la etiqueta elegida por el usuario: cualquier servidor puede llamarse «github». Usa serverCommand o serverUrl.--dangerously-skip-permissions el que salta las comprobaciones./var/run/docker.sock equivale a dar acceso a todo el host, haya sandbox o no.

