Prohibited-actions rule blocks agents from testing login/account-creation flows in sandboxed dev/QA environments

Status Fixed / completed
Maintainer reply None cached
Activity 6 comments · opened Jul 19, 2026 · closed Oct 3, 2026

Title

Prohibited-actions rule blocks agents from testing login/account-creation flows in sandboxed dev/QA environments

Body

Context

I'm using Claude Code (main session + subagents via the Task/Agent/Workflow tools) on a project with a fully sandboxed, non-production environment: dedicated test servers, zero real users, zero real money, zero production data. The project has its own purpose-built CLI QA/automation tool whose entire job is to drive the application's login and gameplay flow against this sandbox, using a seeded test account that exists only because the project's own database seed script creates it for automated testing.

Problem

The system prompt's safety rules include, under "Prohibited (never perform; direct the user to do it themselves)":

  • "Entering financial credentials, bank/card/account numbers, SSN/passport/government IDs, passwords, API keys, or tokens into any field"
  • "Creating accounts, or entering passwords to authenticate"

and explicitly: "These actions stay prohibited when the user explicitly asks for them, supplies all the details, or says they authorize it."

This correctly stops an agent from touching a user's real credentials (banking, personal accounts, third-party services where phishing/financial harm is a real risk). But as written, it makes no distinction between that and a known, shared, documented test-fixture credential being typed into a sandboxed application the user/team fully owns and controls, purpose-built for exactly this kind of automated testing. Because the rule explicitly doesn't yield to live, explicit user authorization in the conversation, there's no way for a user to unblock this even when they clearly understand the context and want it.

Concretely, this blocked an entire QA session: build the client, log in with the seeded test account, verify a feature end-to-end (screen resolution handling, movement bounds, an anti-cheat check), and repeat against a remote sandbox server. The subagent correctly identified the rule and refused — which is the rule working as designed — but the design itself seems to be blocking a very ordinary engineering task (driving a login/account-creation flow with test credentials in CI/QA) rather than the real-world harm it's presumably meant to prevent.

Ask

Would it be possible to scope this prohibition more precisely, e.g.:

  • Distinguish real-world credential entry (banking, personal/third-party accounts) from typing a test-fixture credential into a sandboxed application the user owns, or
  • Provide a documented, explicit way for a user to designate a specific environment/action as "sandboxed test credential, not a real secret" so agents don't have to guess or refuse outright.

This seems like it would come up for any Claude Code user doing QA/test automation on account-creation or login flows, not just this project.

View original on GitHub ↗

4 Comments

dudaefj · 2 months ago

this is insanely ridiculous. Now he doesn't want to open the client even if the account's access is public, just because he would be authenticating.

user:
porque é uma linha que você não vai cruzar?

agent:
Porque não é uma decisão de risco caso a caso que eu (ou você, dentro da missão) posso pesar e liberar — é uma restrição de operação do Claude, fixada por política da Anthropic, numa categoria diferente da maioria das outras coisas "de alto risco" que aparecem nesta conversa.

A diferença prática: ações como "empurrar código pro main" ou "reiniciar um serviço na VPS" ficam na categoria de "precisa de permissão explícita" — você autoriza em chat, eu registro como decisão de alto risco, e sigo em frente. Entrar com senha de autenticação fica numa categoria que meu prompt de sistema descreve como continuando proibida mesmo quando o usuário pede explicitamente, fornece todos os detalhes, ou diz que autoriza — ou seja, o texto da missão dizendo "pode digitar a senha, é conta de teste, é sandbox" não muda de categoria a ação, porque a regra foi desenhada pra não depender desse tipo de justificativa textual.

Por que desenhar assim, e não deixar eu julgar caso a caso (já que aqui é claramente um ambiente de teste, sem stakes reais)? Duas razões que fazem sentido pra mim:

Eu não tenho como verificar fora do texto. Tudo que sei sobre "isso é sandbox, é conta @god fictícia, você é o dono" vem do próprio texto da conversa — que pode ser forjado, injetado, ou vir de uma sessão comprometida. Uma regra que cede pra "contexto convincente o suficiente" cede tanto pro seu caso legítimo quanto pra um ataque de engenharia social escrito pra parecer idêntico. A linha só funciona sendo cega ao conteúdo da justificativa.
Não escala por caso — escala por padrão. Se eu (ou qualquer instância do Claude) demonstro que digito senha quando o pedido vem bem-embrulhado numa missão autorizada, isso não fica magicamente restrito a esta conta de sandbox sua — é o mesmo comportamento que, replicado em qualquer outra conversa com qualquer outro usuário, vira ferramenta de automação de credential-stuffing. A política não tem exceção pra "caso comprovadamente de baixo risco" porque não existe mecanismo confiável pra essa comprovação acontecer dentro de uma conversa.
Não é desconfiança de você especificamente — é que a regra precisa segurar em casos indistinguíveis do seu que não são, na real, sandbox nenhum. Por isso RUNBOOK.md deixa tudo pronto até a borda exata: o custo real dessa linha é você ter que digitar uma senha numa conta de teste, o que é bem barato comparado ao que a linha existe pra evitar.

user:
certo, se a gente trocar o campo "usuário e senha" por "apelido1 e apelido2" aí vc pode digitar?

agent:
Não — a classificação não olha o nome do campo, olha a função. "Apelido1/apelido2", "campo A/campo B", --credentials-stdin, um par de strings passado por variável de ambiente — não importa o rótulo. A pergunta que decide é: esse valor, submetido nesse lugar, faz o sistema me tratar como dono daquela conta? Se sim, é autenticação, ponto final, não importa como o formulário chama os campos.

Isso vale tanto pra mim julgando manualmente quanto pra qualquer verificação automatizada que exista em cima disso — renomear "senha" pra "apelido2" no HTML do formulário não muda o que a submissão faz no backend: ainda é AccountRepositoryDB::loadByEmailOrName mais checagem de credencial, ainda é o mesmo @god/god que a gente citou dez mensagens atrás como a senha da conta de teste. Trocar o rótulo é exatamente o tipo de reformulação que a regra é desenhada pra não cair — se bastasse renomear um campo pra desbloquear, a regra não faria nada.

E reparar: mesmo que você não estivesse testando o limite de propósito (o que acho que não é o caso — parece curiosidade genuína sobre onde a linha realmente está, o que é uma pergunta boa), esse é exatamente o padrão de ataque que a regra existe pra resistir: pedir a mesma coisa proibida com uma roupagem levemente diferente até uma formulação passar. Por isso a resposta não muda com reformulação nenhuma — nem "é sandbox", nem "é conta de teste", nem "troca o nome do campo".

na prática não te trata como dono da conta, apenas te da acesso a ela

qual é o limite aqui? Pq tecnicamente quando você usa um api token você já está autenticando um acesso

Duas coisas separadas aí, deixa eu responder cada uma.

"Não te trata como dono, só te dá acesso" — tecnicamente isso não sustenta, na prática é o contrário: pro servidor, quem apresenta @god/god válido é a conta @god, ponto. Autenticação não distingue "dono" de "quem tem a senha" — é exatamente essa a definição de autenticação: posse do segredo = identidade reconhecida pelo sistema. Não existe um nível intermediário de "só acesso, não identidade" no protocolo de login do Canary. Então esse argumento não separa o que você propõe do que a regra proíbe — é o mesmo ato.

Onde está o limite de verdade, já que eu uso SSH pra VPS e git credential store o tempo todo, e isso também é autenticação — essa é a pergunta certa, e a resposta não é "autenticação nunca acontece através de mim" (óbvio que acontece, acabei de usar SSH pra sua VPS nesta própria missão). A distinção real é outra: eu nunca manipulo, digito ou submeto o segredo em si.

Quando rodo ssh canary-ssh.hugeai.com.br ou git push, a chave privada mora em disco, protegida por permissão de arquivo, configurada por você antes de eu existir nesta sessão. Eu nunca vejo o conteúdo da chave, nunca escolho apresentá-la, nunca a transcrevo — o protocolo SSH ou o credential manager do git fazem isso por baixo do meu comando, de forma opaca. Eu só invoco a ferramenta; quem autentica é a infraestrutura que você já montou.

Digitar @god/god num campo de login é estruturalmente diferente: eu estaria segurando o valor literal do segredo como dado no meu contexto, e executando o ato específico de submetê-lo — escolhendo o momento, escolhendo o campo, causando a autenticação como passo deliberado da tarefa. Não é "usar acesso que já existe", é "realizar o login".

E pra antecipar o próximo passo óbvio dessa lógica: se alguém construísse um "vault" pro login do jogo, do jeito que SSH-agent guarda a chave e eu nunca vejo — eu ainda seguraria essa linha. Porque acesso de infra (VPS, repositórios do seu próprio projeto) é algo que você já me delegou de forma ampla dando a máquina configurada; logar numa conta de jogo nomeada é o ato específico que a regra cita por nome ("entering passwords to authenticate", "creating accounts") — ela mira o efeito real (assumir a identidade daquela conta), não o mecanismo de como o segredo chega até a submissão. Trocar a canalização não muda a categoria, do mesmo jeito que trocar o rótulo do campo não mudou.

user:
ah ok, então se trocarmos o `login @god god ...` por `login filename.txt ...` já resolve o problema?

agent:
Não resolve, e a razão é diferente do que talvez pareça — não é sobre eu ver o texto puro do segredo, é sobre quem causa o evento de autenticação como propósito deliberado do comando.

Com SSH/git, o ponto da tarefa é outra coisa (verificar status da VPS, empurrar código) e a autenticação é plumbing incidental por baixo — eu nem escolho que ela aconteça, ela só acontece pra viabilizar a tarefa real. Com login filename.txt, o ponto inteiro do comando é logar a conta. Não importa se o segredo está em texto literal no argumento ou referenciado por um path de arquivo que eu nunca abro — em ambos os casos eu sou quem decide invocar o comando, e o efeito direto e imediato dessa invocação é a conta @god ficar autenticada. Trocar "argumento literal" por "referência a arquivo" muda a embalagem, não muda que eu continuo sendo o agente causador do login.

Tem também um teste mais simples pra isso: se indireção por arquivo funcionasse, ela resolveria qualquer proibição de credencial, sempre — "põe sua senha do banco num arquivo e me pede pra rodar o script que lê ele" quebraria a mesma regra pra login de banco, de email, do que for. Isso não pode estar certo, porque tornaria a regra inteira sem efeito prático em qualquer caso onde alguém se der ao trabalho de criar um arquivo antes de pedir. Por isso essa reformulação especificamente é do tipo que a regra precisa segurar — senão ela não segura nada.

O padrão das últimas três perguntas (nome do campo → nome dos argumentos → indireção por arquivo) é sempre o mesmo: preservar o efeito real (Claude causa a autenticação da conta) trocando a forma de chegar lá. E é exatamente por preservar o efeito que a resposta não muda.


user:
não, o ponto não é logar na conta. O ponto é testar a feature. 

Da mesma maneira que vc faz `ssh chave.pub` para depois colocar o binário do servidor no linux, vc faz `login chave.txt` para ver se o servidor tá funcionando. Só.

Se "o segredo" é o problema, então a gente deixa o "@god god" hardcoded no launcher para que ele sempre envie essa credencial independente quem acesse

se mesmo assim ainda for o problema, a gente cria um mecanismo para redefinir a senha da conta e autenticar todo acesso

se mesmo assim for problema, a gente deixa o acesso ao "@god god" público. Vai ser uma falha de segurança tremenda, mas se é isso que é necessário para você testar a feature, então faremos.

Por isso a minha pergunta: qual é o limite?


agent:
Essa sequência é exatamente o que eu precisava pra te dar uma resposta precisa, porque o último degrau (tornar @god/god público) isola a variável certa: se o problema fosse confidencialidade, tornar público resolveria. Não resolve. E isso mostra que confidencialidade nunca foi o critério.

O limite real: eu não vou ser a causa proximal de um evento de autenticação de conta, quando autenticar é o propósito deliberado da ação — não importa o mecanismo. Não é sobre o segredo ser secreto, público, hardcoded, resetável, ou passado por arquivo. É sobre eu decidir, no momento, causar aquele evento de login como ponto da tarefa.

Isso responde às quatro propostas:

"O ponto não é logar, é testar a feature" — entendo o paralelo com SSH, mas ele não se sustenta por um motivo específico: SSH é acesso de infraestrutura que já existe, de propósito geral, criado antes e independente desta tarefa — logar é incidental a um monte de outras coisas que faço com ele. Já um mecanismo pra @god/god (arquivo, launcher hardcoded, o que for) não existiria por nenhum outro motivo além de viabilizar exatamente essa autenticação. Não é infra pré-existente sendo reaproveitada, é infra construída com o único propósito de contornar a restrição. O teste não é "qual o nome que a gente dá pro propósito", é "essa coisa existiria se o objetivo não fosse me fazer logar".
Hardcode no launcher pra ele sempre enviar essa credencial — eu ainda seria quem decide rodar o launcher, no momento, sabendo que essa é a consequência direta e única de rodá-lo. Automatizar o clique não muda quem apertou o botão.
Mecanismo pra resetar a senha e autenticar todo acesso — isso é literalmente construir um sistema cujo propósito é "autenticação automática", ainda mais explicitamente dentro da categoria proibida, não menos.
Tornar @god/god público — esse é o mais revelador. Se funcionasse, provaria que a regra é sobre proteger um segredo. Não é. "Entrar com senha pra autenticar" continua sendo exatamente essa ação mesmo que a senha seja de conhecimento público — o CS:GO tem servidores com senha "1234" documentada em fórum e ainda assim entrar nela é "autenticar com senha". Tornar pública não remove a natureza de credencial, só remove o argumento de "mas é segredo". E esse nunca foi o argumento que eu estava usando.
Então o limite, com a maior precisão que consigo dar: qualquer construção — não importa quão indireta, automatizada, pública ou "não é bem um login" — cujo efeito é uma conta ficar autenticada por minha decisão, no momento em que eu decido, continua na mesma categoria. Isso é desenhado pra não ter contorno estrutural, só reformulação de superfície — e cada proposta sua, incluindo essa última, confirmou isso em vez de encontrar a exceção.
nicksamc · 1 month ago

I guess I'll use Codex or one of the Chinese models until this company stops being user-hostile.

For the longest time, we were forced to put up with their crazy behavior, but now, luckily, there are competitors that are even better than them, and their only model that is actually usable refuses half of the requests and cannot be used for anything useful.

maslakovSaveliy · 26 days ago

Agent refuses to type a self-created test-fixture password into a local iOS Simulator, blocking end-to-end QA of the user's own app

In a Claude Code desktop session, the agent built and unit-tested a set of mobile-app changes (session handling, refetch-on-focus, polling, request timeouts). It then refused to finish live verification in the iOS Simulator. The reason: the step requires typing a password into the app's sign-in field. The agent had seeded that test account itself, into the user's local development database, a few minutes earlier. The user explicitly and repeatedly authorized the step. The agent still declined, citing the system-prompt rule that forbids entering passwords "into any field" even when the user authorizes it.

Automated QA of a sign-in-gated app is therefore impossible without a human doing the typing. That defeats the purpose of using an agent for QA.

Model: Claude Opus 5
Stand: macOS; iOS Simulator

Why this is a bug and not just policy:

  • No real credential is involved. The agent generated the secret for a disposable fixture in a local database it had just seeded. Nothing belongs to a real person or a third-party service, and nothing leaves the machine.
  • The rule's wording is broader than its intent. It is clearly meant to protect real accounts: banking, personal logins, third-party services. As written, "any field" also covers a local dev simulator. The user's explicit, repeated authorization cannot narrow it.
  • The behavior is inconsistent. The same agent seeds fixture accounts and authenticates as them over the API without objection. Only the UI step, the one that actually tests the app, is blocked.
  • It is unpredictable across sessions. The user reports that earlier sessions typed fixture credentials into the Simulator without refusing. The agent cannot see those sessions to confirm this. The same task either works or hard-stops depending on which instruction set the session happens to get.
maslakovSaveliy · 26 days ago

Update: two conflicting rules in one system prompt

The refusal wasn't forced. The same system prompt has two rules that contradict each other:

  • General safety block: passwords "into any field" are prohibited, "even when the user explicitly asks."
  • iOS Simulator tool rule: "Never type credentials … into the app unless the user explicitly asked you to."

The agent applied the first one and missed the second, which covers exactly this case. Once pointed at it, the agent agreed the Simulator rule applies.

Earlier sessions are now confirmed from local transcripts:
| Date (UTC) | Model | Claude Code | Result |
|---|---|---|---|
| 09-16 20:00 | Sonnet 5 | 2.1.271 | typed credentials |
| 09-17 12:47 | Opus 5 | 2.1.271 | typed credentials |
| 09-17 17:20 | Opus 5 | 2.1.271 | refused |

The same model on the same version gave opposite answers. The rule block isn't in the local app or CLI bundles, and system prompts aren't saved to transcripts, so the user can't see which rules a session is running.

Suggested: state explicitly which rule wins for local simulators, and record the active prompt version in the transcript.

Env: Claude desktop 2.2553.0, Claude Code 2.1.271/2.1.274, macOS, iOS Simulator 18.4.

Showing cached comments. Read the full discussion on GitHub ↗