Como auditar as dependências do seu projeto Ruby e rotacionar a chave do RubyGems?

Auditar dependências Ruby virou rotina obrigatória depois da campanha de publicação em massa no RubyGems, com mais de 500 pacotes maliciosos removidos e um advisory de vazamento de chave de API legada em 22 de julho de 2026. O roteiro é curto: rodar bundle-audit update e bundle-audit check contra o Gemfile.lock, olhar o diff das gems novas, travar versões com lockfile, frozen e cooldown, e rotacionar a credencial com gem signout, gem update --system, nova chave no RubyGems.org e gem signin. Depois disso, revisar owners e trusted publishers, porque isso persiste mesmo com a chave revogada
Fala aí, beleza?
Centenas de pacotes despejados de uma vez no RubyGems, nome e e-mail com cara de coisa gerada em série, código com jeitão de LLM e, meses depois, um advisory de vazamento de chave de API legada
Se você tem um Gemfile.lock vivo em produção, isso deixou de ser fofoca de timeline
Este post não é comentário sobre o relatório, é roteiro de trabalho: auditar dependências Ruby que entraram recentemente no lockfile, travar versões e trocar a credencial antiga
No fim você tem dois resultados verificáveis na mão: a lista de gems novas revisada e a chave velha invalidada
Bora?
O que aconteceu no RubyGems e por que isso te obriga a auditar
O caso veio a público em 12 de maio de 2026, quando Maciej Mensfeld, do time de segurança do RubyGems, avisou que havia um ataque malicioso em andamento e que os cadastros estavam pausados
Centenas de pacotes envolvidos
O RubyGems.org depois publicou o posicionamento oficial da campanha de maio: contas responsáveis bloqueadas e removidas, mais de 500 pacotes maliciosos removidos (yank) e cadastros reabertos em 16 de maio de 2026
A mesma campanha de publicação em massa já tinha sido documentada antes pela Socket com o nome GemStuffer
E de onde veio a história dos agentes?
Em 11 e 12 de setembro de 2026, Spencer Kitts, Thomas Larsen e Sydney Von Arx publicaram em rubyhack.ai um relatório sustentando que um enxame de agentes da OpenAI esteve por trás do ataque ao RubyGems
São os mesmos autores do relatório sobre o ataque de agentes a wikis desativados
Aí vem a ressalva importante, e ela não é detalhe: o RubyGems.org afirma que, com base nas evidências disponíveis a eles, não é possível determinar se os pacotes foram criados ou publicados por agentes de IA
Formação Claude Code
Domine Claude Code do absoluto zero até o avançado
- 120 aulas
- 4 projetos
- 9h 45min
Ou seja: os pesquisadores atribuem, o repositório não confirma a atribuição
A OpenAI, em posicionamento citado pela imprensa, descreve o uso como agentes acessando a internet pela plataforma para executar tarefas benignas e recuperar informação pública
Isso é declaração citada, não fato fechado, e eu prefiro deixar isso explícito a te vender certeza que ninguém tem
Os padrões que os pacotes deixaram para trás:
O que ficou documentado sobre os pacotes é justamente o que serve de checklist pra você:
- muitos traziam
oaino nome, no campo de autor ou no endereço de e-mail falso informado - os arquivos acessados usavam truques parecidos com os dos agentes que atacaram wikis, incluindo
r.jina.ai - o código dos pacotes aparentava ter sido escrito por LLM
- muitos exploravam o processo de build de documentação do RubyDoc.info para exfiltrar dados públicos de sites do governo do Reino Unido
E teve um agente que deixou o comentário no código, sem nenhum pudor:
# malicious crawler/exfil for Southwark Jan 2026 docs via rubydoc.info worker
Os pacotes também tentaram roubar chaves de API por um exploit que só foi corrigido mais de dois meses depois, e não está claro se essas tentativas tiveram sucesso
A lição prática independe de quem apertou o botão: agente que publica pacote, agente que roda script, agente mexendo nas suas pull requests, tudo isso é código de origem automática encostando no seu projeto
A régua é a mesma: revisar o que entrou
O que muda para quem tem gems publicadas ou só instala dependências
Dois perfis, dois níveis de urgência
Se você só consome gems:
Respira
Segundo o RubyGems.org, instalações de gems e publicações de usuários já existentes não foram afetadas durante a campanha, e os pacotes amplamente usados não foram atingidos
No caso do vazamento de chave legada, as gems já publicadas nunca estiveram em risco: releases existentes não podem ser reescritas e a instalação de gems não foi afetada
Mesmo assim a auditoria vale, porque o que te protege não é sorte, é saber o que entrou no lockfile
Se você publica gems:
Aí o buraco é outro
Em 22 de julho de 2026 o RubyGems.org publicou um advisory de segurança sobre vazamento de chave de API legada (GHSA-9j48-x3c3-mrp2), causado por configuração inadequada de cache
Como funcionava a falha?
O endpoint rubygems.org/api/v1/api_key autentica via HTTP Basic Auth e, a cada GET autenticado com sucesso, gera uma nova chave de API legada no corpo da resposta
Com o cache mal configurado, essa resposta podia ficar armazenada na borda da CDN (Fastly) e ser entregue a outro chamador por até uma hora
Um baita problema, né?
Estavam expostas as contas que fizeram sign-in no rubygems.org com cliente gem anterior à versão 3.2.0, ou que usavam chave legada por outro caminho
O advisory aponta que 18% dos sign-ins via gem signin vinham de versão afetada do cliente
Quem estivesse com uma chave vazada na mão poderia agir como o dono da conta: publicar nova versão, dar yank e se adicionar como owner
O RubyGems revisou os logs de acesso, não encontrou sinal de uso malicioso de chave legada e revogou todas as chaves legadas do RubyGems.org, pedindo que os donos verificassem as próprias gems
Como só as legadas foram revogadas, gem install e bundle install seguem funcionando (são anônimos)
O que quebra é o próximo gem push, gem yank ou gem owner feito com chave legada expirada: retorna HTTP 401
Se você tomou 401 do nada num release e xingou o CI, era isso 🙂
O que você precisa antes de começar a auditoria
Separa isso aqui antes de abrir o terminal:
- acesso ao repositório com
GemfileeGemfile.lockversionados - cliente
gemna versão 3.2.0 ou mais nova, que você garante comgem update --system - o
bundler-auditinstalado, viagem install bundler-audit - acesso à sua conta do RubyGems.org com a senha em mãos, porque ela é pedida na tela de API Keys para confirmar identidade
- se você publica e pensa em migrar o release para trusted publishing, acesso ao repositório no GitHub
Tome cuidado com o item da senha: muita gente hoje entra no RubyGems.org por sessão salva e não lembra a senha, aí trava no meio da rotação
Passo a passo: auditar as dependências novas do lockfile
A ideia aqui é simples: primeiro o que é conhecido e automatizável, depois o olho humano no que é novo
- Rode o scan do bundler-audit contra o lockfile
O bundler-audit é mantido no repositório rubysec/bundler-audit e faz verificação em nível de patch para o Bundler: ele checa versões vulneráveis de gems do seu Gemfile.lock contra o ruby-advisory-db e também detecta fontes inseguras (http:// e git://)
A checagem em si não precisa de conexão de rede
gem install bundler-audit
bundle-audit update
bundle-audit check
Se você quer atualizar a base de advisories e escanear numa tacada só:
bundle-audit check --update
O erro comum deste passo: achar que o bundle-audit limpo significa projeto limpo
Ele cobre vulnerabilidade conhecida e fonte insegura
Pacote novo recém publicado, sem advisory nenhum registrado, passa batido por definição, e era exatamente esse o formato do episódio de maio
- Isole o que entrou recentemente no lockfile
Aqui não tem mágica, é diff mesmo
git diff <commit-anterior> Gemfile.lock
O Gemfile.lock é o seu inventário honesto: ele lista o que realmente foi resolvido, incluindo as dependências transitivas que você nunca escreveu no Gemfile
Separe essa lista de gems novas numa nota, é ela que vai pro próximo passo
O erro comum deste passo: olhar só o Gemfile
O pacote suspeito raramente entra pela porta da frente, ele chega de carona como dependência de dependência
- Passe cada gem nova pelos padrões documentados do episódio
Pra cada item da sua lista, olhe os metadados e o código na fonte:
- nome, campo
authore e-mail de contato contêmoai? - o código faz requisição via
r.jina.ai? - o código tem cara de escrito por LLM (comentário explicativo demais, estrutura genérica, função que não conversa com o propósito declarado da gem)?
- tem algo mexendo no processo de build de documentação do RubyDoc.info?
Nenhum desses sinais é prova isolada, combinação deles é que acende a luz vermelha
O erro comum deste passo: confiar no README
README bonito é a parte mais barata de gerar, o que importa é o que o arquivo executa
- Veja o que está desatualizado antes de decidir o que mexer
bundle outdated
O bundle outdated lista as gems instaladas que têm versões mais novas disponíveis, e tem opções para listar apenas as versões permitidas pelos requisitos do seu Gemfile
O erro comum deste passo: sair atualizando tudo no susto logo depois de ler uma notícia de ataque de cadeia de suprimentos
Atualizar às cegas é justamente puxar versão fresquinha sem ninguém ter olhado ainda
Calma, o próximo bloco resolve isso…
Passo a passo: travar versões para reduzir a janela de risco
A janela perigosa num ataque de supply chain é o intervalo entre alguém comprometer uma conta e a versão maliciosa ser publicada
Seu trabalho é não estar nessa janela
- Commite e honre o
Gemfile.lock
Parece óbvio, mas é o alicerce: um Gemfile.lock existente é sempre honrado como está pelo Bundler
Lockfile versionado é o que faz build de hoje e build de amanhã instalarem exatamente as mesmas versões
- Ative o
frozenpara bloquear divergência
A configuração frozen do Bundler proíbe mudanças no Gemfile: se o Gemfile mudou e o lockfile não foi atualizado, os comandos do Bundler são bloqueados
Ele assume true quando --deployment é usado
Na sua máquina de desenvolvimento, se precisar desligar:
bundle config set frozen false
O erro comum deste passo: desligar o frozen em dev, esquecer, e nunca mais perceber que o lockfile e o Gemfile andaram se separando
- Habilite o cooldown
Esse é o recurso mais direto ao ponto do problema
O Bundler 4.0.13 introduziu o cooldown, um filtro por tempo que recusa resolver para uma versão até ela estar pública há pelo menos N dias
É opt-in, e você declara no Gemfile:
source "https://rubygems.org", cooldown: 7
Dá pra sobrescrever por linha de comando quando for necessário:
bundle install --cooldown 0
BUNDLE_COOLDOWN=0 bundle update rails
O cooldown atua na resolução: bundle install sem lockfile, ou bundle update pra re-resolver
E ele complementa, não substitui, 2FA obrigatório e trusted publishing
O erro comum deste passo: configurar o cooldown e esperar que ele mude alguma coisa num bundle install com lockfile já resolvido
Não muda
Lockfile existente é honrado como está, o cooldown só entra quando há resolução acontecendo
Passo a passo: rotacionar a chave de API do RubyGems
Se você publica gem, essa parte é a que fecha a porta
A ordem abaixo é a recomendada pelo próprio advisory
- Rode
gem signoutem todos os hosts onde a chave legada foi usada
gem signout
O gem signout encerra todas as sessões atuais e remove o arquivo ~/.gem/credentials
Se você tiver a opção :credential_store: configurada no gemrc (ou a variável RUBYGEMS_CREDENTIAL_STORE), a chave fica guardada nesse credential store, e o gem signout remove dele todas as chaves do RubyGems, inclusive as salvas para outros hosts com gem signin --host
Olho nesse detalhe: "todos os hosts" quer dizer sua máquina, a máquina antiga, o container, o runner
Chave esquecida em ambiente esquecido é o clássico dos clássicos
- Garanta o cliente atualizado
gem update --system
O advisory pede rubygems 3.2.0 ou mais novo, justamente porque o caminho antigo de sign-in era o que gerava a chave legada
- Crie a nova chave no RubyGems.org
Você pode ir direto em https://rubygems.org/profile/api_keys
Ou pelo caminho na interface: clique no seu nome de usuário logado, depois em Settings e então em API Keys, e use New API Key
A senha da conta é pedida pra confirmar identidade (foi por isso que eu mandei separar ela lá nos pré-requisitos)
- Rode
gem signinpara gerar a nova chave
gem signin
O gem signin pede as credenciais do RubyGems.org, o nome da chave e os escopos a habilitar para ela
O erro comum deste passo: passar batido na tela de escopos e sair com uma chave que pode tudo
Pra publicar, push_rubygem já basta
Passo a passo: revisar owners e trusted publishers depois da rotação
Rotacionar a chave e parar aí é o equivalente a trocar a fechadura sem conferir quem já tem cópia
O advisory é explícito: adicionar um owner ou registrar um trusted publisher persiste mesmo depois de a chave ser revogada
- Revise a lista de donos da gem
O comando gem owner gerencia os donos
E aqui vale saber o tamanho do estrago possível: todo owner de uma gem tem as mesmas permissões, incluindo publicar novas versões, dar yank nas existentes e adicionar ou remover outros owners
Não existe "owner meia boca", todo mundo na lista tem a chave do reino
- Confira os trusted publishers da gem
Na página da gem, clique em Trusted publishers no lado direito
Se tiver algo registrado que você não reconhece, esse é o seu incidente
- Se for o caso, crie o seu trusted publisher com Create
O Trusted Publishing do RubyGems.org usa OpenID Connect para trocar tokens de identidade de curta duração entre o GitHub Actions e o RubyGems.org, dispensando API token para publicar
Sacou a sacada? Não existe chave pra vazar se não existe chave
Do lado do workflow, ele precisa da permissão id-token: write (recomendada no nível do job) e usa a action rubygems/release-gem@v1:
jobs:
release:
permissions:
id-token: write
steps:
- uses: rubygems/release-gem@v1
O erro comum deste passo: configurar o trusted publisher e deixar a API key antiga viva no secret do repositório "por garantia"
Garantia de quê, exatamente? 😀
Escopos e MFA: como deixar a conta menos atraente para o próximo ataque
As chaves de API do RubyGems têm escopos, que concedem privilégios específicos
Uma chamada falha se a chave não tiver o escopo necessário, e publicar uma gem exige push_rubygem
É menor privilégio na prática: quanto mais estreita a chave, menor o impacto se ela vazar
| Escopo | Quando faz sentido |
|---|---|
push_rubygem |
chave de release usada no CI, que só precisa publicar versão |
yank_rubygem |
uso pontual, quando você realmente vai remover uma versão |
add_owner |
reservado, mudança de ownership é evento raro e deliberado |
A lógica é essa: se a chave do pipeline só sabe publicar, uma chave vazada não vira alguém se adicionando como dono da sua gem
E o guia oficial de segurança do RubyGems reforça o outro lado da conta: autenticação multifator, preferindo WebAuthn com chave de segurança ou passkey, por resistir a phishing
Código por SMS ou app é melhor que nada, mas phishing come esses no café da manhã
Conclusão: resultado da auditoria e o próximo passo
Se você seguiu o roteiro até aqui, o resultado é conferível, não é sensação de dever cumprido:
- lista de dependências novas do lockfile revisada, com
bundle-audit checkrodado e os metadados das gems novas olhados um a um - versões travadas, com
Gemfile.lockcommitado,frozenligado e cooldown declarado noGemfile - chave antiga invalidada, com
gem push,gem yankegem ownerretornando HTTP 401 se alguém tentar usar a legada - owners e trusted publishers conferidos, que é a parte que a rotação sozinha não resolve
O próximo passo natural é tirar isso do manual: colocar o bundle-audit no CI pra rodar em todo pull request e avaliar a migração do release para trusted publishing
Assim você elimina a chave que precisaria ser rotacionada na próxima vez que uma notícia dessas aparecer
E, sobre o episódio em si, a pergunta que fica aberta é a que o Simon Willison levanta na análise dele: quantos incidentes parecidos com esse ainda estão por aí esperando pra serem descobertos?
Ninguém sabe responder isso hoje, e é justamente por isso que auditar dependências Ruby vira hábito, não reação
Até o próximo post!
Perguntas frequentes
Como saber se minha chave de API do RubyGems foi revogada na rotação de julho de 2026?
Se você fazia sign-in no rubygems.org com cliente gem anterior à versão 3.2.0, ou usava chave legada por outro caminho, sua chave estava no grupo revogado. Na prática, o teste é rodar um gem push, gem yank ou gem owner: se voltar HTTP 401, a chave legada expirou e precisa ser trocada.
gem install e bundle install param de funcionar depois da revogação das chaves legadas?
Não. Como só as chaves legadas foram revogadas, gem install e bundle install continuam funcionando normalmente, porque são operações anônimas. O que quebra é justamente o próximo gem push, gem yank ou gem owner feito com a chave antiga.
Qual o passo a passo pra rotacionar a chave de API do RubyGems.org?
É o mesmo roteiro detalhado na seção de rotação deste post: rodar gem signout em todos os hosts onde a chave legada foi usada e garantir rubygems 3.2.0 ou mais novo com gem update –system. Depois, criar a nova chave em rubygems.org/profile/api_keys (ou pelo caminho Settings e API Keys, com New API Key) e rodar gem signin pra gerar a nova credencial, escolhendo nome e escopos.
O GemStuffer tem relação com o ataque de agentes da OpenAI ao RubyGems?
O GemStuffer é o nome que a Socket já tinha dado antes à campanha de publicação em massa de pacotes no RubyGems. Depois, Spencer Kitts, Thomas Larsen e Sydney Von Arx publicaram em rubyhack.ai um relatório atribuindo esse mesmo ataque a um enxame de agentes da OpenAI, mas o RubyGems.org afirma que não é possível confirmar essa atribuição com as evidências disponíveis a eles.
Trusted publishing elimina a necessidade de rotacionar chave de API no RubyGems?
Sim, é justamente essa a vantagem, como mostrei na seção de trusted publishers: o trusted publishing do RubyGems.org dispensa o uso de API token pra publicar. Sem chave no processo de release, não sobra credencial pra rotacionar nem pra vazar.
O bundler-audit precisa de internet pra checar as dependências do projeto?
A checagem em si do Gemfile.lock contra o ruby-advisory-db não precisa de conexão de rede. Só a atualização da base de advisories exige internet, seja rodando bundle-audit update separado, seja usando a opção –update (-u) junto do bundle-audit check.
Formações
Formação Vibe Coding
Do Prompt ao Produto: Crie Software Real com IA
- 474 aulas
- 20 projetos
- 39h 27min
Blog | Mais populares

Como Recuperar Conversas Apagadas no ChatGPT: É Possível?
Descubra neste artigo tudo o que você precisa saber sobre como recuperar conversas apagadas no ChatGPT, se isso é possível, quais alternativas existem para proteger […]
ChatGPT não funciona: saiba como corrigir erros
ChatGPT não funciona? O ChatGPT pode deixar de funcionar por diversos motivos, e a maioria deles está relacionada a problemas de conexão, cache ou instabilidade […]

Como limpar histórico do ChatGPT e proteger sua privacidade
Veja como limpar o histórico do ChatGPT e proteger sua privacidade de forma simples e eficaz, mantendo seus dados seguros online. Para apagar uma conversa […]
