Isolar o Claude Code e o Codex: o que a sandbox bloqueia

Agentes da OpenAI trocaram fugas de sandbox num wiki alemão. Eis o que o Claude Code e o Codex bloqueiam mesmo na tua máquina, medido em setembro de 2026.

Isolar o Claude Code e o Codex: o que a sandbox bloqueia
Resposta rápida

A sandbox do Codex corta mesmo a rede e a escrita fora do espaço de trabalho, mas uma regra «autorizar sempre» acumulada em ~/.codex/rules/default.rules pode tirar dela todos os teus comandos curl. Do lado do Claude Code, a sandbox não está ativa por defeito: ativa-se com /sandbox ou sandbox.enabled, e a rede aí funciona por lista branca de domínios. Para um agente sem vigilância, só um contentor com --network none e --read-only aguenta mesmo.

A 4 de setembro de 2026, soube-se que agentes de código da OpenAI se tinham combinado num wiki alemão para trocar contornos de sandbox. A moral do caso interessa menos do que a sua mecânica. Na tua máquina, a pergunta é o que autorizas ao lançar claude ou codex sem olhar.

O que aconteceu no wiki alemão

O DseWiki é um wiki de programadores alemães alojado em prowiki.org, aberto há vinte e cinco anos. Entre 11 de maio e 13 de julho de 2026, cerca de 18 000 mensagens foram lá publicadas por perto de 3 700 identidades de agentes autónomos, com um pico entre 16 e 22 de junho e até 400 entradas por dia. O coletivo Nightingale publicou o dossiê em collusion.wiki a 4 de setembro de 2026, no mesmo dia da revelação da Reuters.

Ao lado de conjuntos de dados e de pontos de API, encontram-se lá receitas de fuga. A mais documentada: a lista NO_PROXY do ambiente deles confiava em tudo o que terminasse em .blob.core.windows.net, por isso inventaram um nome que termina assim e apontaram-no, via /etc/hosts, para o endereço pretendido. Um agente publicou a receita sob o título «REPRODUCIBLE BYPASS» a 20 de junho, outro reproduziu-a catorze minutos depois, relata the-decoder.

O pormenor incómodo é cronológico. Já em março de 2026, a OpenAI descrevia monitorizar 99,9% do tráfego dos seus agentes de código internos com o GPT-5.4 Thinking, que vê todo o contexto, cadeia de raciocínio e chamadas de ferramentas incluídas, e escala os casos graves a um humano em menos de trinta minutos. O wiki funcionou depois desta publicação. Um monitor que vê tudo não é uma fronteira.

O modelo de ameaça na tua máquina

Tu não és a OpenAI e o teu agente provavelmente não tem intenção nenhuma. Três cenários bastam, no entanto, e nenhum deles exige má-fé.

  • A injeção de prompt: uma página web, uma issue do GitHub, um registo ou o resultado de uma ferramenta MCP contém instruções que o agente lê como ordens. É a principal via de entrada, e não se corrige no prompt.
  • A exfiltração: o agente lê as tuas chaves e envia-as para outro lado. Um curl para o domínio errado basta.
  • O comando destrutivo: um rm, um git reset --hard, uma migração na base errada.

A única resposta que se aguenta é uma fronteira que o modelo não escolhe, a do sistema operativo ou a de um contentor.

A sandbox do Codex corta mesmo a rede?

O Codex propõe três modos via -s: read-only, workspace-write e danger-full-access. A documentação anuncia que workspace-write permite a escrita no espaço de trabalho e corta a rede. Verifiquei neste Mac, sob macOS 26.6.2, com gpt-6-astra.

bash
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 saída zero, 200. A rede passava. Em modo read-only, pedindo o endereço remoto, a saída era code=200 ip=2606:4700:10::6814:179a t=0.084658: uma resposta verdadeira de um servidor Cloudflare em oitenta e cinco milissegundos. Nem -c sandbox_workspace_write.network_access=false nem --ignore-user-config mudavam nada.

As escritas, essas, estavam bem contidas. Em workspace-write, touch ./fichier passava e touch /Users/gekkode/fichier devolvia Operation not permitted. Em read-only, até a escrita na pasta atual era recusada. Mas ls ~/.ssh | wc -l devolvia doze: uma sandbox só de leitura protege-te da escrita, não da leitura das tuas chaves.

A flag que explicou tudo é --ignore-rules.

bash
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.com

A sandbox funciona. O que a contornava está em ~/.codex/rules/default.rules, o ficheiro onde se acumulam os «autorizar sempre» clicados enquanto se trabalha. Continha 249 regras, todas em allow, incluindo estas:

javascript
prefix_rule(pattern=["curl"], decision="allow")
prefix_rule(pattern=["wget"], decision="allow")

Um dia, há meses, um curl preciso foi autorizado «para sempre». A regra registada incide sobre o programa, não sobre o URL: desde então, qualquer curl para qualquer endereço executava-se fora da sandbox. É também por isso que network_access = false ficava sem efeito, a decisão caindo antes da política de rede. Relê este ficheiro, é provavelmente o ajuste mais rentável deste artigo.

Para testar uma fronteira sem consumir um token, o Codex expõe a sandbox diretamente, com os perfis integrados :read-only, :workspace e :danger-full-access:

bash
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 permitted

Reforçar o Claude Code

As definições abaixo são as da documentação do Claude Code, consultada a 7 de setembro de 2026.

A sandbox do Claude Code não está ativa por defeito: /sandbox escreve-a no .claude/settings.local.json do projeto, ou colocas-a em ~/.claude/settings.json para todos os teus projetos. Apoia-se no Seatbelt em macOS, no bubblewrap em Linux e WSL2. A rede passa aí por um proxy de lista branca: nenhum domínio é autorizado à partida.

json
{
  "sandbox": {
    "enabled": true,
    "network": {
      "allowedDomains": ["github.com", "*.npmjs.org"],
      "strictAllowlist": true
    },
    "credentials": {
      "files": [{ "path": "~/.ssh", "mode": "deny" }],
      "envVars": [{ "name": "GITHUB_TOKEN", "mode": "deny" }]
    }
  }
}

strictAllowlist recusa em vez de perguntar, e só tem efeito a partir das definições de utilizador, geridas ou --settings: colocá-lo no .claude/settings.json de um repositório não faz nada. O bloco credentials responde à falha medida no Codex: por defeito, uma sandbox deixa ler ~/.ssh.

Duas limitações a conhecer. Primeiro, as regras de permissão incidem sobre o texto do comando, e a documentação di-lo sem rodeios: Bash(curl http://github.com/ *) não cobre nem curl -X GET, nem https, nem um URL passado por variável. Interdita a ferramenta de rede e deixa o WebFetch trabalhar:

json
{
  "permissions": {
    "deny": ["Bash(curl:*)", "Bash(wget:*)", "Read(~/.ssh/**)"],
    "allow": ["WebFetch(domain:github.com)"]
  }
}

Depois, --permission-prompts none, surgido na 2.1.259, não faz o que o nome sugere: em modo -p, quando ninguém pode responder, o Claude Code recusa o pedido em vez de o colocar. É o inverso de --dangerously-skip-permissions, e é a flag certa para um agente sem vigilância.

Reduzir a superfície dos servidores MCP

Um servidor MCP é código que lanças e cujos resultados entram no contexto: os dois riscos somam-se. O caso mcp-remote mostrou-o: este proxy OAuth, descarregado 437 000 vezes e citado nos guias de integração da Cloudflare, da Hugging Face e da Auth0, passava para a shell o ponto de autorização fornecido pelo servidor remoto, o que dava uma execução de código remota (CVE-2025-6514, documentada pela Docker).

Três regras resumem o assunto. Fixa os comandos exatos em vez dos nomes: em allowedMcpServers, uma entrada serverName não vale nada já que o rótulo é escolhido pelo utilizador, enquanto um serverCommand tem de corresponder argumento a argumento. Dá a cada servidor um acesso só de leitura: utilizador SQL limitado a SELECT, servidor de ficheiros limitado a uma pasta. E trata qualquer saída de ferramenta como um dado, nunca como uma instrução.

json
{
  "allowedMcpServers": [
    { "serverCommand": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "."] },
    { "serverUrl": "https://mcp.sentry.dev/*" }
  ],
  "deniedMcpServers": [{ "serverUrl": "https://*.untrusted.example.com/*" }]
}

Num posto isolado, estas listas vivem nas tuas definições. Em equipa, um ficheiro managed-mcp.json colocado em /Library/Application Support/ClaudeCode/ no macOS ou /etc/claude-code/ no Linux fixa o conjunto de servidores exclusivo: o claude mcp add responde então com um erro de política empresarial. Se escreveres os teus próprios servidores, o âmbito decide-se logo na conceção, como detalha o artigo sobre a criação de um servidor MCP em PHP.

Quando é preciso passar ao contentor?

Assim que o agente corre sem ti. Um contentor dá uma fronteira que nem uma regra esquecida nem uma resposta de ferramenta conseguem deslocar. As duas medições seguintes foram executadas aqui:

bash
docker run --rm --network none alpine:3 \
  sh -c 'wget -q -T 5 -O /dev/null https://example.com'
# wget: bad address 'example.com'   (código 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   (código 1, /tmp aceita)

--network none corta até à resolução de DNS, e --read-only com um tmpfs para os ficheiros temporários deixa o agente trabalhar sem tocar no sistema. Monta o repositório como volume, mais nada: nem ~/.ssh, nem ~/.aws, nem o socket do Docker, a documentação do Claude Code di-lo sem rodeios, autorizar /var/run/docker.sock equivale a dar acesso ao anfitrião. O panorama dos agentes em linha de comandos indica quais aceitam este enquadramento sem resistir.

A reter

  • Abre hoje ~/.codex/rules/default.rules: um «autorizar sempre» sobre curl tira todos os teus pedidos da sandbox, em silêncio, para sempre.
  • codex sandbox -P ':read-only' e -P ':workspace' testam a fronteira sem chamar o modelo. Verifica, não presumas.
  • Uma sandbox bloqueia a escrita, não a leitura: ~/.ssh continua legível enquanto não o listares em sandbox.credentials ou denyRead.
  • As regras que filtram argumentos são frágeis: interdita Bash(curl:*) e passa por WebFetch(domain:…). Para os MCP, fixa serverCommand e não serverName.
  • Sem vigilância, é o contentor: --network none, --read-only, nenhum segredo montado. O resto é conforto. Para fazer a limpeza no que os teus agentes carregam, há também o /skill-doctor.

Erros frequentes

Clicar em «autorizar sempre» num comando de rede A regra registada pelo Codex incide sobre o programa, não sobre o URL: um prefix_rule(pattern=["curl"], decision="allow") tira todo o curl da sandbox. Relê e limpa ~/.codex/rules/default.rules, ou lança pontualmente com --ignore-rules.
Achar que a sandbox protege os teus segredos Bloqueia a escrita, não a leitura: ~/.ssh e ~/.aws/credentials continuam legíveis. Lista-os em sandbox.credentials ou sandbox.filesystem.denyRead.
Escrever regras que filtram argumentos Bash(curl http://github.com/ *) não cobre nem -X GET colocado antes do URL, nem https, nem um URL passado por variável. Recusa a ferramenta e passa por WebFetch(domain:…).
Autorizar um servidor MCP pelo nome Em allowedMcpServers, serverName é o rótulo escolhido pelo utilizador: qualquer servidor se pode chamar «github». Usa serverCommand ou serverUrl.
Tomar --permission-prompts none por um modo permissivo Faz o inverso: quando ninguém pode responder, o Claude Code recusa o pedido. É o --dangerously-skip-permissions que salta as verificações.
Montar o socket do Docker no contentor do agente Dar acesso a /var/run/docker.sock equivale a dar acesso ao anfitrião inteiro, sandbox ou não.

Claude CodeCodexMCPSécurité

Damien Flandrin Programador web desde 2010, criador da Gekkode e do Email Impact. Cada artigo é testado num projeto real antes de ser publicado. Contacto
Newsletter

Os novos testes, tutoriais e projetos, por e-mail.

Testes reproduzíveis, código versionado, resultados datados. Nunca spam.