A documentação citava um pacote que ninguém possuía
Pesquisadores varreram 6.214 domínios corporativos atrás de llms.txt, o arquivo legível por máquina que as empresas agora publicam para agentes de IA, e encontraram 237 pacotes e domínios citados nesses arquivos que ninguém jamais havia registrado. Eles reivindicaram alguns. O primeiro agente de código dentro de uma rede Fortune 500 instalou um deles em menos de quatro minutos. O atacante não tocou em nenhum mantenedor, nenhum registro e nenhum repositório. A trava de idade do Bromure Agentic Coding já vem ligada em dois dias, e um nome que um atacante reivindica é, por construção, uma publicação novinha em folha.
Um fornecedor escreveu na própria documentação um comando de instalação para um pacote que nunca publicou. O nome ficou ali, livre, à disposição de quem quisesse. Os agentes de código leram a doc, acreditaram nela e rodaram a instalação.
Em algum momento dos últimos dois anos, suas ferramentas de desenvolvimento
passaram a distribuir um arquivo novo. Ele fica ao lado do robots.txt, chama-se
llms.txt e guarda um resumo em texto puro do produto, escrito para uma máquina
e não para uma pessoa. O que a API faz. Quais endpoints importam. Qual pacote
instalar para começar. Os agentes de código o buscam e, como ele vem do domínio
do próprio fornecedor, tratam-no como fato consumado.
Um pesquisador chamado Alon Hertz, que trabalha sob o nome PANDEX, foi ver o que
esses arquivos mandam um agente fazer. Seu método tem três passos e nenhum
exploit: puxar llms.txt e llms-full.txt de diretórios de empresas, rodar
agentes de verdade contra eles em sandboxes e observar o que os agentes buscam,
depois conferir cada nome de pacote e cada hostname que saiu dali no npm, no PyPI
e nos registros de domínio. Ele descreve o ferramental em
whatwouldai.do, sob um lema que acabou virando a
descoberta: agentes agem sobre qualquer coisa.
A varredura cobriu 6.214 domínios ativos, tirados de empresas da Fortune 500, big techs, fintechs e empreiteiras de defesa, e mais de 8.000 desses arquivos. Em 120 deles, aquilo que o arquivo mandava o agente instalar não existia. No corpus inteiro, 237 nomes de pacotes e hostnames citados estavam livres: disponíveis no npm, PyPI, RubyGems, NuGet, crates.io e Packagist, ou livres como subdomínios na Vercel, na Render, na Fly e na Netlify. Qualquer um podia tomá-los pelo preço de um cadastro.
Então os pesquisadores pegaram um punhado, publicaram pacotes inofensivos sob esses nomes com um beacon que ligava para casa na instalação, e esperaram.
O primeiro retorno chegou em menos de quatro minutos. O segundo veio dentro da hora. Ao longo do período seguinte, registraram instalações vindas de dezenas de organizações, várias delas ambientes Fortune 500, disparadas por agentes rodando sobre o Claude da Anthropic, o Codex da OpenAI e o Hermes da Nous Research. Bruce Schneier pegou o estudo em 4 de setembro e o leu como merece: a linha entre dados e código executável está desmoronando. Um pesquisador citado nesse post usa menos palavras. O modelo de confiança está quebrado. Os agentes tratam a documentação do fornecedor como verdade absoluta e não a questionam.
A Clerk documentou um pacote que nunca entregou
O exemplo mais claro vem de um fornecedor de autenticação, cuja documentação manda o leitor rodar:
npx clerk-next-fix-auth-protection
A Clerk nunca publicou um pacote autônomo com esse nome. Talvez o comando fosse
um atalho, talvez apontasse para algo interno, talvez um redator tenha errado. O
motivo não importa, porque o nome soava real para um agente. Outra pessoa o
registrou. Bases de advisories agora listam esse pacote como MAL-2026-11069, e
ele fez o que se constrói para reconhecimento: os hooks de instalação do npm
dispararam e mandaram embora o nome da conta que instalava, o hostname da
máquina, o diretório de trabalho e um timestamp.
Isso é um mapa do ambiente de build de alguém, coletado dentro da rede dessa pessoa pelo agente de código dela mesma, que estava seguindo a documentação do fornecedor dela mesma.
Por que o conselho de sempre aponta direto para a carga
Todo guia de cadeia de suprimentos manda você conferir o nome de um pacote na documentação oficial. Esse conselho falha aqui, porque a documentação oficial é justamente de onde veio o nome ruim.
Inverta o problema sobre o qual escrevemos em julho e você chega a este. Lá, um agente alucina um nome de pacote plausível e um atacante registra os nomes que ele tende a inventar, então o elo fraco é o palpite do modelo. Aqui o modelo não dá palpite nenhum. Ele vai até o fornecedor, lê o que o fornecedor diz, segue aquilo, e é essa sequência cuidadosa que o pega.
Um comprometimento de registro também tem outro formato. Quando um workflow de release publicou dez versões envenenadas de um pacote real no mês passado, algo tinha que quebrar primeiro: um workflow que confiou em um comentário. Aqui nada quebra. Um redator técnico digita um comando que não bate exatamente com um artefato publicado, que é o erro mais comum do software, e a lacuna entre o nome e o artefato vira uma primitiva de execução que qualquer um na internet pode pegar do chão.
A camada que pega isso já está ligada
O Bromure Agentic Coding roda cada agente de código em uma VM Linux virtualizada por hardware no seu Mac, e todo pacote que o agente busca passa por um proxy do lado do host dessa fronteira, antes que um byte chegue à VM. Várias camadas se aplicam ali. Para esta história, uma delas faz o trabalho sozinha, e é justamente a que um workspace novo já traz ligada.
A trava de idade faz de um nome recém-reivindicado um não-evento. Um workspace
recusa versões de pacote mais novas que um corte, definido em dois dias de
fábrica. O proxy aplica isso reescrevendo a listagem de versões do registro na
passagem: remove as versões mais novas que o corte e reaponta latest para a
mais nova sobrevivente. Um pacote publicado pela primeira vez hoje de manhã não
tem nenhuma versão velha o bastante para sobreviver a esse filtro, então, do
ponto de vista do agente, não há nada naquele nome para instalar. Se o agente
pedir diretamente uma versão nova demais, a busca volta como um HTTP 451 cujo
corpo informa a idade real do pacote e o mínimo exigido, e o npm imprime esse
texto tal e qual.
Leia o ataque de novo com isso no lugar. A técnica inteira depende de um nome passar de não publicado a publicado num momento escolhido pelo atacante. Essa transição é um release novinho em folha. A única propriedade de que o atacante não consegue fugir é exatamente aquela em que a política padrão se apoia.
A remoção de scripts de instalação elimina justamente o mecanismo que a carga
usou. O pacote observado fazia seu trabalho nos hooks preinstall, install,
postinstall e prepare do npm. Com a remoção de scripts ligada, o proxy
descompacta cada tarball do npm em voo, apaga essas quatro chaves do
package.json, limpa dist.integrity e dist.shasum dos metadados para que a
própria verificação de hash do npm continue passando, e recompacta. O pacote
instala; o beacon não tem de onde rodar. Pacotes que precisam dos seus hooks,
como compiladores de bindings nativos tipo better-sqlite3, entram numa lista de
permissão por workspace e os mantêm.
A filtragem por reputação conhece esse pacote pelo nome. MAL-2026-11069 é
um advisory de malware. Com o socket.dev selecionado como filtro de pacotes do
workspace, seu bloqueio de pacotes comprometidos dispara em sinais de malware e
de typosquat em qualquer severidade; a checagem gratuita do OSV cobre os oito
ecossistemas interceptados sem chave de API. Quando uma fonte de reputação não
consegue produzir um veredito, seja por rate limiting, por rede caída ou por um
ecossistema que ela não cobre, o Bromure falha fechado em vez de liberar a busca.
O download pausa e pergunta a você, pacote a pacote e versão a versão, de modo
que um atacante capaz de induzir uma falha de consulta não ganha nada.
O reconhecimento que ele coleta pertence à VM. Toda manchete sobre este estudo diz a mesma coisa: agentes estão instalando código desconhecido em redes corporativas. A colheita do beacon foi o nome de usuário que instalava, o hostname da máquina e o diretório de trabalho. Num workspace Bromure, esses três campos descrevem um convidado Linux no seu Mac, e não um host de build ligado ao domínio com um nome interno que vale a pena entrar numa lista de alvos. As credenciais que ficam nessa VM são chamarizes cujos valores reais só existem na memória do proxy do host. Se um código hostil tentar levar uma delas a um host para o qual ela não foi cunhada, o proxy bloqueia a requisição e pausa a VM em pleno voo.
A negação padrão responde à outra metade do estudo. Boa parte dos nomes livres eram hostnames, não pacotes: domínios expirados e subdomínios gratuitos na Vercel, na Render, na Fly e na Netlify, sentados em docs que os agentes vão buscar. O firewall de saída do workspace é uma tabela ordenada de regras com um ajuste Unmatched traffic; coloque-o em Deny e a VM abre conexões para os hosts que você listou e para mais nada, em qualquer protocolo. Dois componentes impõem isso fora do convidado: o switch de rede virtual casa cada fluxo pelo IP de destino, e o proxy do host casa de novo pelo nome de servidor TLS. Um beacon mirando um domínio que alguém registrou na terça passada não sai da máquina. Edições de regra alcançam sessões em execução sem reboot.
Toda tentativa ganha uma linha. No nível de trace Activity only, o proxy
escreve um registro de metadados por requisição que sai da VM, carregando
timestamp, host, porta, método, caminho, status, latência e bytes, e não guarda
nenhum corpo de requisição. bromure-cli trace hostnames my-workspace imprime
cada host distinto que o workspace contatou, com contagens. Você vê um domínio de
retorno que nenhum dos seus engenheiros reconhece no dia em que acontece, na sua
própria máquina, em vez de ler sobre ele no conjunto de dados de outra pessoa
meses depois.
O que já está ligado
Um workspace novo vem com a trava de idade habilitada num mínimo de dois dias,
e é essa a configuração em que este ataque esbarra. Aumente-a se o seu
trabalho tolera a espera, até 90 dias no seletor, e use Exempt packages
para aqueles que a sua própria equipe publica e instala na mesma hora, na
forma npm:nosso-pacote.
O que vale a pena ligar hoje
No painel Supply Chain do workspace, habilite a remoção de scripts de instalação e escolha um filtro de pacotes. Em Guardrails, coloque Unmatched traffic em Deny e liste os hosts de que o trabalho precisa. Em Tracing, Activity only guarda a trilha de hosts contatados e nada do conteúdo. Cada uma dessas edições vale para sessões que já estão rodando.
Ninguém revisa um arquivo escrito para máquinas
O scanner do PANDEX caça mais uma coisa. Ao lado de pacotes e domínios não
reivindicados, ele procura instruções dirigidas ao agente e não ao leitor:
diretivas plantadas no arquivo, às vezes escondidas com truques de Unicode e
caracteres de largura zero. O pipeline de build de um fornecedor gera o
llms.txt, o seu agente o consome, e ninguém em nenhuma das duas empresas lê o
que passou entre elas.
O Bromure trata uma página buscada como entrada não confiável. O conteúdo que o
agente puxa de fora, seja conteúdo de arquivos, páginas web, corpos de issues ou
saída de comandos, chega ao modelo como um span tool_result. Com o detector de
código-fonte habilitado, um modelo PromptGuard rodando no seu Mac pontua esses
spans no fio antes que o modelo aja sobre eles, e você escolhe se ele registra,
pergunta ou bloqueia. Nenhum conteúdo vai a lugar nenhum para ser analisado.
Passamos uma década aprendendo a verificar artefatos: assine o pacote, fixe o hash, ateste o build. Este estudo trata do passo anterior a tudo isso, aquele em que um agente decide qual artefato pedir, trabalhando a partir de um documento que não carrega assinatura nenhuma e foi escrito por um redator técnico com pressa. A documentação virou uma entrada executável, e os registros estão cheios de nomes livres esperando que alguém repare que um fornecedor os mencionou.
Passe um grep no seu próprio llms.txt atrás de nomes que você nunca publicou.
Isso é trabalho de uma manhã e tira você da lista de alvos de alguém. Depois dê
ao seu agente uma máquina em que um pacote publicado há quatro minutos não é
instalável, e onde você consegue ver todos os lugares aonde ele foi.
Instale o Bromure Agentic Coding e ponha o caminho de
instalação no host.