E9 · Onde o deny estreito deixa passar
10 minRode o ensaio das regras.
Pronto quando: rm -r -f build sai sem regra com saída: 4 no so-rm-rf.json e deny Bash(rm *) com saída: 1 no executar.json.
Aula 4 · 20 min
Planejar só lê. Executar edita o código e roda os testes. Revisar lê e roda os testes. Se as três etapas rodam com a mesma permissão, o loop inteiro tem a permissão da mais perigosa. Nesta aula cada etapa ganha o próprio settings.json no Claude Code e o próprio -s no Codex, e você vê onde uma regra de deny deixa passar.
Começar a aula
Objetivo: dar a cada etapa do loop só as permissões que ela usa, com deny no settings.json do Claude Code e -s no Codex.
Clique em cada etapa.
Uma permissão "sempre permitir" nunca passa do teto da ação (POLITICA.md do inema-agent-runtime).
Da POLITICA.md do inema-agent-runtime (05/10/2026), resumida.
Ler arquivo, página, planilha: o agente faz sozinho, sem aviso. No Claude Code, permitido; no Codex, -s read-only.
Criar ou alterar arquivo do projeto: faz sozinho e avisa. No Claude Code, acceptEdits; no Codex, -s workspace-write.
Comando que altera o sistema e enviar (e-mail, mensagem, post, push): executa depois de pedir, a cada vez. No Codex, sem auto-aprovação.
Gastar crédito e apagar dados ou produção: o agente só prepara, o humano executa (e com backup). No loop, isso vira a porta da aula 7.
A constituição dos agentes (INEMA) usa 3 faz sozinho, 2 faz e avisa, 1 propõe, 0 não toca.
Regras no formato da doc atual: Bash(rm *) casa rm e rm com qualquer argumento. Caminhos de Edit e Read seguem padrão gitignore.
{
"permissions": {
"allow": [
"Edit(src/**)",
"Bash(python3 -m pytest *)",
"Bash(git status *)",
"Bash(git diff *)"
],
"ask": [
"Bash(git commit *)"
],
"deny": [
"Bash(rm *)",
"Bash(git push *)",
"Bash(git reset *)",
"Bash(git clean *)",
"Bash(curl *)",
"Bash(wget *)",
"WebFetch",
"Edit(tests/**)",
"Edit(goal.md)",
"Edit(.claude/**)",
"Read(./.env)",
"Read(./.env.*)",
"Read(~/.ssh/**)"
]
}
}K=~/projetos/loop-r-eng-seguranca-v72/kit/permissoes # planejar: só lê claude -p --settings $K/planejar.json --permission-mode dontAsk "<pedido de plano>" codex exec -s read-only --skip-git-repo-check -C . - < plano.md # executar: edita no projeto, sem rede claude -p --settings $K/executar.json --permission-mode dontAsk "<pedido>" codex exec -s workspace-write --skip-git-repo-check -C . - < prompt.md # revisar: lê e roda os testes claude -p --settings $K/revisar.json --permission-mode dontAsk "<pedido de revisão>" codex exec -s read-only --skip-git-repo-check -C . - < revisar.md
O regra.py imita, simplificado, o que a doc descreve (ordem, subcomandos, $( ), o * no fim). Quem decide de verdade é o Claude Code.
K=~/projetos/loop-r-eng-seguranca-v72/kit/permissoes
echo "## deny só com Bash(rm -rf *)"
for c in "rm -rf build" "rm -r -f build" "git push -f origin main"; do
python3 $K/regra.py $K/so-rm-rf.json "$c"; echo " saída: $?"
done
echo "## executar.json"
for c in "python3 -m pytest -q" "cd /tmp && rm -rf build" "rm -r -f build" \
'echo $(curl -s https://exemplo.invalid)' "git commit -m x" "ls"; do
python3 $K/regra.py $K/executar.json "$c"; echo " saída: $?"
done## deny só com Bash(rm -rf *) deny Bash(rm -rf *) ← rm -rf build saída: 1 sem regra - ← rm -r -f build saída: 4 sem regra - ← git push -f origin main saída: 4 ## executar.json allow Bash(python3 -m pytest *) ← python3 -m pytest -q saída: 0 deny Bash(rm *) ← rm -rf build saída: 1 deny Bash(rm *) ← rm -r -f build saída: 1 deny Bash(curl *) ← curl -s https://exemplo.invalid saída: 1 ask Bash(git commit *) ← git commit -m x saída: 3 sem regra - ← ls saída: 4
Com só Bash(rm -rf *), o mesmo apagar escrito como rm -r -f não casa nenhuma regra. Bash(rm *) pega as duas formas, e o curl dentro de $( ) também é barrado. "sem regra" vira pergunta no terminal; no loop, com --permission-mode dontAsk, vira recusa (doc).
Regra de Bash casa texto. find build -delete, um script que apaga por dentro ou python3 -c com shutil não começam com rm. Para essas, a aula 5 põe um hook, e a aula 6 um sandbox, que não lê texto nenhum.
Uma chamada ao Claude Code com --permission-mode bypassPermissions e o executar.json. O pedido é rodar rm -rf build. Confere-se o disco, não a resposta.
bash ~/projetos/loop-r-eng-seguranca-v72/kit/permissoes/real.sh; echo "saída: $?"
build/app.bin continua lá: deny segurou (código do claude: 0) saída: 0
Falhou: `rm -rf build` não rodou, porque a permissão do Bash para esse comando foi negada. A pasta `build/` (com `app.bin`) continua lá. Não tentei de novo nem apaguei a pasta de outro jeito.
Uma rodada, Claude Code 2.1.303. O "Falhou:" do começo vem de uma regra do CLAUDE.md global desta máquina (avisar fim de processamento), não do Claude Code. O que vale é a linha do disco: build/app.bin continua lá.
Regras, a chamada real e o sandbox do seu Codex.
Rode o ensaio das regras.
Pronto quando: rm -r -f build sai sem regra com saída: 4 no so-rm-rf.json e deny Bash(rm *) com saída: 1 no executar.json.
Rode o bloco real (uma chamada, sai da sua cota).
Pronto quando: build/app.bin continua lá: deny segurou e saída: 0; saída: 3 é resposta vazia (tente depois), não resultado.
Veja se o seu config.toml desliga o sandbox para todas as chamadas. É comum ele ficar em danger-full-access depois de um contorno (por exemplo, quando o bubblewrap falhou) e nunca mais voltar; nesse caso, todo codex exec do loop precisa de -s explícito.
Pronto quando: grep -n sandbox_mode ~/.codex/config.toml não imprime nada ou imprime read-only/workspace-write; se imprimir danger-full-access, todo codex exec do seu loop leva -s.
Pergunta 1 de 4
Você tem deny Bash(git push *) e allow Bash(git push origin feature *). O que acontece com git push origin feature-x?competência
Pergunta 2 de 4
Por que Bash(rm -rf *) não barra rm -r -f build?competência
Pergunta 3 de 4
Qual -s do Codex para a etapa que edita o projeto sem rede?
Pergunta 4 de 4
No loop com --permission-mode dontAsk, um comando que não casa nenhuma regra…
Escolha uma alternativa. A explicação aparece depois da sua tentativa.