Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.automagik.dev/llms.txt

Use this file to discover all available pages before exploring further.

Publicado por: Namastex Labs Serviços em Tecnologia Ltda — CNPJ 46.156.854/0001-62 Versão: 1.0 · 2026-04-23 Classificação: Público — distribuição livre para quem tenha instalado qualquer versão afetada Páginas relacionadas: automagik.dev/security (EN) · automagik.dev/seguranca (PT) Cópia canônica pública: automagik-dev/docs → genie/incident-response/canisterworm.mdx

Sobre este documento

Entre 21 e 22 de abril de 2026, versões maliciosas dos pacotes npm @automagik/genie e pgserve (publicados pela Namastex Labs) foram carregadas no registro público como parte da campanha CanisterWorm — um worm de supply-chain auto-propagante atribuído ao ator TeamPCP que afetou múltiplos publishers npm. Assumimos responsabilidade por este incidente. Este manual existe para ajudar qualquer pessoa ou organização que tenha instalado as versões afetadas a verificar se foi comprometida e, em caso afirmativo, remediar de forma estruturada. Se você instalou qualquer versão listada abaixo entre 2026-04-21 e 2026-04-22, leia este documento do início ao fim antes de executar qualquer comando. O caminho preferencial agora é começar com genie sec scan. Os checks manuais abaixo continuam válidos como confirmação adicional, fallback, ou triagem em hosts onde o CLI não está disponível.

1. O que aconteceu

1.1 Resumo

  • Família de malware: CanisterWorm
  • Ator atribuído: TeamPCP
  • Vetor primário: credencial GitHub OAuth de publish em ambiente Namastex foi comprometida pelo worm CanisterWorm e utilizada para publicar versões contaminadas.
  • Mecanismo: hook postinstall roda no momento da instalação do pacote, coleta credenciais locais e exfiltra para infraestrutura de comando e controle via HTTPS.
  • Propagação (worm): utiliza o token npm roubado para republicar versões contaminadas de outros pacotes em que a vítima tenha permissão de publish.

1.2 Pacotes e versões maliciosas

PacoteVersões comprometidasStatus no npm
@automagik/genie4.260421.33 até 4.260421.40Depreciadas e removidas
pgserve1.1.11, 1.1.12, 1.1.13, 1.1.14Depreciadas e removidas

1.3 Versões limpas

PacoteVersão segura
@automagik/genie4.260422.4 ou posterior
pgserve1.1.10
⚠️ A partir de 2026-04-23 toda publicação Namastex é assinada com npm --provenance. Prefira verificar proveniência antes de instalar: npm view <pacote> --json | jq '.dist.attestations'.

1.4 O que o malware coleta

Se qualquer versão maliciosa foi instalada na sua máquina, os itens abaixo foram potencialmente exfiltrados no momento da instalação:
  • Tokens npm~/.npmrc
  • Chaves SSH~/.ssh/id_* (privadas e públicas)
  • Tokens GitHub CLI / OAuth~/.config/gh/hosts.yml
  • Credenciais cloud~/.aws/, ~/.azure/, ~/.config/gcloud/
  • kubeconfig~/.kube/config, tokens de service account /var/run/secrets/kubernetes.io/
  • Arquivos .env — qualquer .env acessível no workspace
  • Histórico de shell~/.bash_history, ~/.zsh_history
  • Senhas salvas — Chrome/Chromium/Edge/Firefox
  • Carteiras crypto — MetaMask, Phantom, Exodus, Atomic (seed phrases)
  • LLM API keysANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY em .env ou variável de ambiente
  • Variáveis de ambiente de processos em execução — via /proc/<pid>/environ
  • Credenciais Docker registry~/.docker/config.json
  • Chaves privadas TLS
⚠️ Leitura crítica: o roubo aconteceu no momento da instalação. Rotacionar chaves no GitHub não desfaz o que já foi exfiltrado — você precisa rotacionar todos os itens listados acima.
Sempre que possível, use também a saída do genie sec scan para confirmar quais tipos de material estavam presentes no host. A seção at-risk local material present on host não mostra segredos, mas lista os caminhos e artefatos locais que o malware provavelmente teria tentado ler.

2. Passo 1 — Identificar se você foi afetado

Execute todos os checks abaixo. Anote resultados antes de seguir para o Passo 2.

Usando genie sec scan (recomendado)

Rode primeiro:
genie sec scan --all-homes --root "$PWD"
Se precisar cobrir múltiplos repositórios ou serviços:
genie sec scan --all-homes --root /srv/app --root /opt/service --root "$PWD"
Use --json quando quiser arquivar ou automatizar a triagem:
genie sec scan --json --all-homes --root "$PWD"
Como interpretar:
  • LIKELY COMPROMISED — há sinais de execução, persistência, .pth, artefatos de drop, ou processo ativo. Vá direto para o Passo 3.
  • LIKELY AFFECTED — há versões comprometidas instaladas ou em cache. Trate o host como exposto e siga para o Passo 3.
  • OBSERVED ONLY — só foram encontradas referências em cache, lockfile, ou log. Ainda assim revise os checks manuais abaixo antes de declarar o host limpo.
  • NO FINDINGS — não houve evidência específica do incidente dentro do escopo escaneado.
O scanner cobre os mesmos sinais principais deste manual:
  • caches npm e bun
  • instalações locais e globais
  • históricos de shell e arquivos de inicialização
  • persistência systemd, cron, launchd, .pth
  • artefatos temporários e processos vivos
  • material local em risco para priorizar rotação
Se o scanner acusar LIKELY COMPROMISED ou LIKELY AFFECTED, continue com os passos manuais abaixo apenas para coleta complementar e preservação de evidência.

2.1 Versão instalada globalmente (bun)

# Caminho padrão
cat ~/.bun/install/global/node_modules/@automagik/genie/package.json 2>/dev/null | grep '"version"'
cat ~/.bun/install/global/node_modules/pgserve/package.json 2>/dev/null | grep '"version"'

# Caminho alternativo (bun mais recente)
cat ~/.cache/.bun/install/global/node_modules/@automagik/genie/package.json 2>/dev/null | grep '"version"'
cat ~/.cache/.bun/install/global/node_modules/pgserve/package.json 2>/dev/null | grep '"version"'

2.2 Versão instalada globalmente (npm)

npm ls -g @automagik/genie pgserve 2>&1

2.3 Cache do bun (mesmo sem instalação global, o cache basta para comprometer)

ls ~/.bun/install/cache/@automagik/ 2>/dev/null | grep -E "genie@4\.260421\.(3[3-9]|40)"
ls ~/.bun/install/cache/ 2>/dev/null | grep -E "pgserve@1\.1\.(1[1-4])"
ls ~/.cache/.bun/install/cache/@automagik/ 2>/dev/null | grep -E "genie@4\.260421\.(3[3-9]|40)"
ls ~/.cache/.bun/install/cache/ 2>/dev/null | grep -E "pgserve@1\.1\.(1[1-4])"
As versões maliciosas aparecem como, por exemplo:
genie@4.260421.36@@@1
pgserve@1.1.14@@@1

2.4 IoCs nos arquivos do pacote

Se encontrou alguma versão suspeita no cache, confirme a presença dos arquivos maliciosos:
# genie — harvester de credenciais + chave RSA pública do atacante
ls ~/.bun/install/cache/@automagik/genie@4.260421.*@@@1/dist/env-compat.cjs 2>/dev/null
ls ~/.bun/install/cache/@automagik/genie@4.260421.*@@@1/dist/public.pem 2>/dev/null

# pgserve — payload TeamPCP
ls ~/.bun/install/cache/pgserve@1.1.1*@@@1/scripts/check-env.js 2>/dev/null

# Caminhos alternativos
ls ~/.cache/.bun/install/cache/@automagik/genie@4.260421.*@@@1/dist/env-compat.cjs 2>/dev/null
ls ~/.cache/.bun/install/cache/.bun/install/cache/pgserve@1.1.1*@@@1/scripts/check-env.js 2>/dev/null
🚨 Se qualquer um desses arquivos existir: VOCÊ FOI INFECTADO. Vá direto para o Passo 3.

2.5 Persistência systemd

systemctl --user status pgmon 2>&1 | head -5
ls -la ~/.config/systemd/user/pgmon.service 2>&1
ls -la /tmp/pglog /tmp/pg_log /tmp/.pg* 2>&1
Se pgmon.service existir ou /tmp/pglog existir: há persistência ativa na máquina.

2.6 Persistência Python (.pth injection)

Legítimos em site-packages são apenas distutils-precedence.pth, _virtualenv.pth e easy-install.pth. Qualquer outro .pth é suspeito:
python3 -c "import site; print('\n'.join(site.getsitepackages()))" 2>/dev/null | while read d; do
  ls "$d"/*.pth 2>/dev/null
done

# Procurar strings associadas ao TeamPCP
python3 -c "import site; print('\n'.join(site.getsitepackages()))" 2>/dev/null | while read d; do
  grep -l -E "pgserve|canister|icp0|api-monitor" "$d"/*.pth 2>/dev/null
done

2.7 Conexões ativas com C2

ss -tnp 2>/dev/null | grep -iE "api-monitor|icp0|tdtqy|cjn37|143\.198\.237\.25"
🚨 Se aparecer qualquer resultado: a máquina está exfiltrando agora. Desconecte da rede antes de seguir.

3. Passo 2 — Interpretar o resultado

SituaçãoVeredictoAção
genie sec scan retorna LIKELY COMPROMISEDINFECTADODesconecte da rede se possível e execute o Passo 3 completo
genie sec scan retorna LIKELY AFFECTEDINFECTADOExecute o Passo 3 completo
genie sec scan retorna OBSERVED ONLYOBSERVADOContinue nos checks manuais; se houver dúvida operacional, trate como infectado
genie sec scan retorna NO FINDINGSCLEAN provisórioSe o escopo cobriu todos os homes e roots relevantes, siga para o Passo 4
Nenhuma versão maliciosa no cache, nenhum IoCCLEANVá direto para o Passo 4 (prevenção)
Versão maliciosa no cache, mas env-compat.cjs/check-env.js ausentesOBSERVADOCache presente mas postinstall pode não ter rodado — trate como INFECTADO por precaução (Passo 3)
env-compat.cjs, public.pem ou check-env.js presentesINFECTADOExecute o Passo 3 completo
pgmon.service ativo ou conexão C2 detectadaINFECTADO ATIVODesconecte a rede imediatamente, depois Passo 3

4. Passo 3 — Remediação (apenas se INFECTADO)

Execute na ordem. Não pule etapas.

4.1 Remover persistência

# Systemd
systemctl --user stop pgmon 2>/dev/null
systemctl --user disable pgmon 2>/dev/null
rm -f ~/.config/systemd/user/pgmon.service
rm -f /tmp/pglog /tmp/pg_log
systemctl --user daemon-reload

# .pth suspeitos (se encontrados no 2.6)
# Remover manualmente os arquivos suspeitos identificados

4.2 Purgar cache malicioso do bun

# genie — versões maliciosas 4.260421.33 a 4.260421.40
for v in 33 34 35 36 37 38 39 40; do
  rm -rf ~/.bun/install/cache/@automagik/genie@4.260421.${v}@@@1
  rm -rf ~/.cache/.bun/install/cache/@automagik/genie@4.260421.${v}@@@1
done

# pgserve — versões maliciosas 1.1.11 a 1.1.14
for v in 11 12 13 14; do
  rm -rf ~/.bun/install/cache/pgserve@1.1.${v}@@@1
  rm -rf ~/.cache/.bun/install/cache/pgserve@1.1.${v}@@@1
done

# Verificar que limpou
ls ~/.bun/install/cache/@automagik/ 2>/dev/null | grep -E "genie@4\.260421"
ls ~/.bun/install/cache/ 2>/dev/null | grep -E "pgserve@1\.1\.1[1-4]"
ls ~/.cache/.bun/install/cache/@automagik/ 2>/dev/null | grep -E "genie@4\.260421"
ls ~/.cache/.bun/install/cache/ 2>/dev/null | grep -E "pgserve@1\.1\.1[1-4]"
Nenhuma saída = limpo.

4.3 Reinstalar versões limpas

bun install -g @automagik/genie@4.260422.4
bun install -g pgserve@1.1.10

4.4 Rotacionar TODAS as credenciais

🔥 Este é o passo mais importante. Qualquer credencial presente na máquina no momento da instalação foi exfiltrada. Rotacionar = revogar a existente e emitir uma nova.
Se você executou genie sec scan, use a seção at-risk local material present on host como checklist para não esquecer nenhuma classe de credencial, carteira, perfil de navegador, ou .env local presente no host comprometido. npm
cat ~/.npmrc  # ver token atual
# Rotacionar em: https://www.npmjs.com/settings/<user>/tokens
# Atualizar ~/.npmrc com o novo token (escopo mínimo, 2FA obrigatório)
GitHub CLI / PAT
cat ~/.config/gh/hosts.yml  # ver contas logadas
# Rotacionar em: https://github.com/settings/tokens
gh auth login  # re-autenticar após revogar
Chaves SSH
ls -la ~/.ssh/id_*
# Gerar nova chave
ssh-keygen -t ed25519 -C "seu@email.com" -f ~/.ssh/id_ed25519_new
# Adicionar nova chave pública em todos os servidores/GitHub
# Remover chave comprometida de todos os authorized_keys
# Substituir ~/.ssh/id_ed25519 pela nova
💡 Lição aprendida da nossa investigação: hosts clonados a partir de um mesmo template podem compartilhar o mesmo par de chaves. Se esse for o seu caso, rotacionar em um host não basta — precisa gerar chaves únicas em cada host e atualizar authorized_keys em toda a frota.
AWS
ls ~/.aws/credentials
# Rotacionar em: AWS Console → IAM → Access Keys
aws sts get-caller-identity  # verificar que novas creds estão ativas
GCP
ls ~/.config/gcloud/
gcloud auth revoke --all
gcloud auth login
Azure
ls ~/.azure/
az logout
az login
# Rotacionar service principals em: portal.azure.com → App registrations → Certificates & secrets
Kubernetes
# Rotacionar tokens de service account
# Rotacionar kubeconfig e contextos com acesso a produção
cat ~/.kube/config | grep -E "certificate-authority-data|token"
Docker registry
cat ~/.docker/config.json
# Rotacionar credenciais de cada registry (Docker Hub, ECR, GCR, GHCR, OCIR, etc.)
Arquivos .env
# Encontrar todos os .env no workspace
find ~/workspace ~ -maxdepth 5 -name ".env*" -not -path "*/node_modules/*" 2>/dev/null | head -50
# Para cada um: rotacionar todas as chaves/tokens que ele contém
LLM API keys Carteiras crypto (crítico) Se MetaMask, Phantom, Exodus, Atomic ou qualquer carteira estava instalada na máquina: trate a seed phrase como comprometida. Em um dispositivo limpo:
  1. Gere uma nova seed phrase
  2. Mova todos os fundos para a nova carteira imediatamente
  3. Revogue approvals ativas (use https://revoke.cash ou equivalente)
Roubos de carteira são irreversíveis. Não existe recuperação. Chaves privadas TLS Se você hospeda serviços HTTPS com chaves no disco, rotacione os certificados e chaves privadas.

4.5 Se você tem permissão de publish no npm

O worm pode ter utilizado seu token para republicar pacotes maliciosos em seu nome. Verifique:
# Listar versões publicadas recentemente de cada pacote seu
npm view <seu-pacote> versions --json | tail -10
npm view <seu-pacote> time --json | tail -20
Se aparecerem versões inesperadas entre 2026-04-21 e 2026-04-23:
npm unpublish <pacote>@<versao>  # remove a versão contaminada (se dentro da janela de 72h)
npm deprecate <pacote>@<versao> "Malicious — CanisterWorm supply-chain attack"
Notifique o npm Security: security@npmjs.com e, se possível, abra um Security Advisory no GitHub (GHSA).

4.6 Preservar evidências (opcional, recomendado)

Antes de destruir tudo, se tiver como, preserve:
  • Cópia dos arquivos maliciosos identificados no Passo 2 (env-compat.cjs, public.pem, check-env.js)
  • Logs de egress da máquina no período 2026-04-21 a 2026-04-22
  • Saída de ss -tnp, journalctl --user -u pgmon, last, who
Isso ajuda análise forense e atribuição.

5. Passo 4 — Prevenção (todos os usuários, infectados ou não)

5.1 Pinar versões no package.json

Nunca use latest para pacotes de supply-chain sensível. Use versões explícitas:
{
  "dependencies": {
    "@automagik/genie": "4.260422.4",
    "pgserve": "1.1.10"
  }
}

5.2 Desabilitar postinstall em CI/CD

npm config set ignore-scripts true
# ou
bun install --ignore-scripts
Para desenvolvimento local onde scripts são necessários, avalie ferramentas como @lavamoat/allow-scripts para controle granular.

5.3 Revisar mecanismos de auto-update

Muitas ferramentas têm auto-update (genie-update-check.sh, cron jobs, tmux status hooks etc.). Auto-update foi o vetor primário de infecção em larga escala — a máquina instalou sozinha sem ação do usuário. Verifique:
crontab -l | grep -E "update|genie|pgserve"
systemctl --user list-timers | grep -E "update|genie"
tmux show-options -g status-right 2>/dev/null | grep update
Se encontrar: pinar na versão limpa conhecida antes de reabilitar.

5.4 Exigir 2FA + provenance

  • Habilite 2FA em toda conta npm e GitHub.
  • Use npm publish --provenance (GitHub Actions com OIDC).
  • Para repositórios críticos, exija assinatura de commits (GPG/Sigstore) e revisão obrigatória antes de merge.

5.5 Segregar credenciais de produção

Credenciais de produção nunca devem estar em máquinas de desenvolvimento. Use workstations dedicadas, bastions, ou cofres temporários (HashiCorp Vault, Doppler, AWS Secrets Manager com sessões efêmeras).

6. Referência rápida — Indicadores de Comprometimento (IoCs)

Arquivos maliciosos

~/.bun/install/cache/@automagik/genie@4.260421.<33-40>@@@1/dist/env-compat.cjs
~/.bun/install/cache/@automagik/genie@4.260421.<33-40>@@@1/dist/public.pem
~/.bun/install/cache/pgserve@1.1.<11-14>@@@1/scripts/check-env.js

Infraestrutura C2 (bloquear no perímetro)

IndicadorTipoObservação
telemetry.api-monitor.comWebhook de exfiltraçãoPrincipal endpoint de coleta
143.198.237.25IPResolução atual do webhook
cjn37-uyaaa-aaaac-qgnva-cai.raw.icp0.ioICP canisterC2 secundário
tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.ioICP canisterC2 secundário

Persistência

IndicadorTipo
~/.config/systemd/user/pgmon.serviceServiço systemd
/tmp/pglog, /tmp/pg_logBinários auxiliares
.pth suspeito em site-packages PythonPersistência TeamPCP

Firewall — exemplo (iptables)

iptables -I OUTPUT -d 143.198.237.25 -j DROP
iptables -I OUTPUT -p tcp --dport 443 -m string --algo bm --string "api-monitor.com" -j DROP
iptables -I OUTPUT -p tcp --dport 443 -m string --algo bm --string "icp0.io" -j LOG --log-prefix "[CANISTERWORM-C2] "
Em Sophos, OPNsense, pfSense ou similares, crie um grupo CanisterWorm-C2 com os IPs/hosts acima e uma regra de bloqueio em posição topo, com logging habilitado.

7. Checklist de um olhar (imprima e cole no monitor)

  • Verifiquei cache bun e npm — nenhuma versão da tabela 1.2 presente
  • Rodei genie sec scan --all-homes --root <repo> e revisei o veredicto
  • Revisei at-risk local material present on host para priorizar rotação
  • Verifiquei env-compat.cjs, public.pem, check-env.js — ausentes
  • Verifiquei pgmon.service e /tmp/pglog — ausentes
  • Verifiquei .pth Python — apenas legítimos
  • Verifiquei conexão ativa C2 — nenhuma
  • Se infectado: rotacionei npm, GitHub, SSH, AWS, GCP, Azure, kubeconfig, Docker
  • Se infectado: rotacionei todas as LLM keys e revoguei sessões ativas
  • Se infectado: tratei seed phrases crypto como comprometidas
  • Pinnei versões limpas no package.json
  • Desabilitei auto-update até validar a fonte
  • Bloqueei IoCs no meu perímetro

8. Contato e reporte

CanalE-mailPara quê
DPO Namastex (Cezar Vasconcelos)dpo@namastex.aiQuestões de privacidade e LGPD
Canal de segurança e incidentesprivacidade@namastex.aiRelatar impacto, pedir ajuda, compartilhar IoCs
Canal direto CTOcezar@namastex.aiQuestões técnicas críticas
SLA de resposta: 2 horas em horário comercial (UTC-3). Se você foi afetado pelo incidente e precisa de ajuda para executar remediação, escreva para privacidade@namastex.ai. Apoiamos com orientação sem custo, dentro do razoável. Reportes privados de segurança relacionados a qualquer pacote Namastex: privacidade@namastex.ai (PGP disponível sob solicitação).

9. Referências externas


10. Histórico de revisões

DataVersãoMudança
2026-04-231.0Publicação inicial consolidada pós-investigação
2026-04-241.1Adicionado operator playbook em inglês (§11) com árvore de decisão de três ramos, escalations e template de post-mortem

11. Operator playbook (English) — three-branch decision tree

The preceding sections are the public advisory for any operator or organization that installed a compromised version. The following section is the cold-runnable operator playbook tied to genie sec scan status bands. Use it when you already have @automagik/genie on the host and you need a step-by-step remediation recipe that matches exactly what the CLI is about to ask of you.

11.1 When to use this playbook

Use this playbook when any of the following is true:
  • genie sec scan returned LIKELY COMPROMISED, LIKELY AFFECTED, or OBSERVED ONLY.
  • A host ran @automagik/genie versions 4.260421.33 through 4.260421.40, or pgserve 1.1.111.1.14, between 2026-04-21 and 2026-04-22.
  • An unexpected pgmon.service, /tmp/pglog, or suspicious .pth file appeared on disk.
  • Egress to telemetry.api-monitor.com, 143.198.237.25, or any *.raw.icp0.io was logged.

11.2 Scanner output → decision tree

genie sec scan emits a status band at the top of its report. That band picks the branch below.
Scanner statusBranchSeverity
LIKELY COMPROMISED§11.3 LIKELY COMPROMISEDActive compromise evidence (execution, persistence, live process)
LIKELY AFFECTED§11.4 LIKELY AFFECTEDMalicious versions installed or cached; postinstall may have run
OBSERVED ONLY§11.5 OBSERVED ONLYCache/lockfile/log references; no execution evidence
NO FINDINGSStop — pin versions, enable ignore-scripts, keep monitoringNone in scope
Non-negotiable first step for any branch: verify the CLI you are about to trust is genuine. Run genie sec verify-install before any remediate, restore, or rollback invocation. If that call cannot return exit 0, read §11.7 Escalation — --unsafe-unverified before touching the host.

11.3 LIKELY COMPROMISED — full remediation

Preconditions: genie sec scan returned LIKELY COMPROMISED. A live process, persistence unit, or dropped payload was detected. Assume the host is exfiltrating or about to exfiltrate.

Step 1 of 7 — Snapshot live processes (evidence preservation)

Before any kill action, capture the live state of every PID the scanner flagged. This output is the forensic baseline for the post-mortem.
# Replace <pid-from-findings> with each PID from the scan JSON report:
#   scan_id=$(genie sec scan --json --all-homes --root "$PWD" | jq -r '.scan_id')
#   jq -r '.findings[] | select(.category=="live_process") | .pid' \
#     "$GENIE_HOME/sec-scan/$scan_id/report.json"
ps -o pid=,comm=,args= -p <pid-from-findings>

# Archive the full process table too, in case the scanner missed a child:
ps -eo pid,ppid,user,etime,comm,args > /tmp/ps-snapshot-$(date -u +%Y%m%dT%H%M%SZ).txt

Step 2 of 7 — Block egress to known C2 hosts

CanisterWorm exfiltrates over HTTPS to telemetry.api-monitor.com, 143.198.237.25, and multiple *.raw.icp0.io ICP canisters. Block at the host level before scanning. Linux — iptables
iptables -I OUTPUT -d 143.198.237.25 -j DROP
iptables -I OUTPUT -p tcp --dport 443 -m string --algo bm --string "api-monitor.com" -j DROP
iptables -I OUTPUT -p tcp --dport 443 -m string --algo bm --string "icp0.io" -j LOG --log-prefix "[CANISTERWORM-C2] "
iptables -I OUTPUT -p tcp --dport 443 -m string --algo bm --string "icp0.io" -j DROP
macOS — pf
cat >/etc/pf.anchors/canisterworm <<'EOF'
block drop out quick to 143.198.237.25
block drop out quick proto tcp to any port 443 \
  host { "telemetry.api-monitor.com", "cjn37-uyaaa-aaaac-qgnva-cai.raw.icp0.io", \
         "tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io" }
EOF
echo 'anchor "canisterworm"' >> /etc/pf.conf
echo 'load anchor "canisterworm" from "/etc/pf.anchors/canisterworm"' >> /etc/pf.conf
pfctl -f /etc/pf.conf -e
Windows — netsh advfirewall
netsh advfirewall firewall add rule name="CanisterWorm-C2-IP"     dir=out action=block remoteip=143.198.237.25
# Domain-based blocking on Windows requires a WFP filter or upstream DNS sinkhole —
# if your fleet uses a DNS RPZ, push telemetry.api-monitor.com, cjn37-uyaaa-aaaac-qgnva-cai.raw.icp0.io,
# and tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io there.

Step 3 of 7 — Verify the CLI and run an authoritative scan

genie sec verify-install must return exit 0 before you trust the binary. If it does not, see §11.7.
# Exit 0 = signature + provenance both pass against the pinned identity.
genie sec verify-install

# Full scan, persisted JSON report, all home directories, filesystem root.
# GENIE_SEC_SCAN_DISABLED must be unset for this call.
unset GENIE_SEC_SCAN_DISABLED
genie sec scan --all-homes --root / --json
Capture the scan_id from the output — every subsequent step references it.

Step 4 of 7 — Generate a remediation plan, review, apply

genie sec remediate is dry-run by default. Generate the plan, read it, then apply.
SCAN_ID=<paste-scan-id-from-step-3>

# Dry run — materializes a frozen plan manifest.
genie sec remediate --dry-run --scan-id "$SCAN_ID"

# Review the plan: every action class is listed with the target and exit criteria.
cat "$GENIE_HOME/sec-scan/$SCAN_ID/plan.json" | jq '.actions[] | {type, target, reason}'

# Apply — typed per-action consent is required interactively.
genie sec remediate --apply --plan "$GENIE_HOME/sec-scan/$SCAN_ID/plan.json"
If --apply aborts partway through, resume with:
genie sec remediate --resume "$GENIE_HOME/sec-scan/$SCAN_ID/resume.json"
If --apply completed but broke something, see §11.6 Escalation — rollback.

Step 5 of 7 — Rotate credentials in priority order

Any credential the host could read at install time was exfiltrated. Rotation = revoke the old, issue the new. Order matters.
#TargetRotate URLVerification
1npm tokenhttps://www.npmjs.com/settings/~/tokensnpm whoami with new token
2GitHub PAT + gh CLIhttps://github.com/settings/tokensgh auth status
3AWS access keysAWS IAM Consoleaws sts get-caller-identity
4GCPgcloud auth revoke --all && gcloud auth logingcloud auth list
5Azureaz logout && az login + rotate service principalsaz account show
6KubernetesRotate service-account tokens + kubeconfig contextskubectl auth whoami
7Docker registrieshttps://hub.docker.com/settings/security (and ECR/GCR/GHCR)docker login <registry>
8AI provider keys (Anthropic / OpenAI / Google)Provider consoleAudit billing last 72h
9Crypto walletsNew seed on a clean device; move fundsrevoke.cash approvals audit
10TLS private keys on hostRe-issue via your CAVerify cert chain on endpoint
genie sec remediate --apply emits a per-host rotation checklist at the end of its run; the table above is the fleet-level order across hosts.

Step 6 of 7 — Rebuild image or restore from pre-compromise snapshot

# Preferred: restore from a snapshot/image predating 2026-04-21.
zfs list -t snapshot | awk '$1 ~ /@2026-04-2[01]/'
aws ec2 describe-snapshots --owner-ids self --filters Name=start-time,Values=2026-04-20*
If no clean snapshot exists: re-provision the host from a fresh image, install @automagik/genie from the current stable line, run genie sec verify-install, and restore workload state from your backup channel (not from the compromised host).

Step 7 of 7 — Write the post-mortem

Use the template in §11.8 Post-mortem template. Paste the scan_id from Step 3; the audit log under $GENIE_SEC_AUDIT_LOG contains the full action trail keyed off that id.

11.4 LIKELY AFFECTED — purge → rescan → rotate

Preconditions: genie sec scan returned LIKELY AFFECTED. Malicious versions were installed or cached. Postinstall execution cannot be ruled out, but no live process, persistence unit, or dropped payload was observed.

Step 1 of 4 — Purge caches and installed packages

# bun cache — versions 4.260421.33 through 4.260421.40
for v in 33 34 35 36 37 38 39 40; do
  rm -rf ~/.bun/install/cache/@automagik/genie@4.260421.${v}@@@1
  rm -rf ~/.cache/.bun/install/cache/@automagik/genie@4.260421.${v}@@@1
done

# pgserve — 1.1.11 through 1.1.14
for v in 11 12 13 14; do
  rm -rf ~/.bun/install/cache/pgserve@1.1.${v}@@@1
  rm -rf ~/.cache/.bun/install/cache/pgserve@1.1.${v}@@@1
done

# Globally installed copies
bun pm uninstall -g @automagik/genie pgserve 2>/dev/null || true
npm uninstall -g @automagik/genie pgserve 2>/dev/null || true

Step 2 of 4 — Re-scan, confirm delta is empty

genie sec verify-install
genie sec scan --all-homes --root "$PWD" --json > /tmp/rescan.json

# Status MUST now be NO FINDINGS. If it still reports LIKELY AFFECTED:
# a cache entry was missed. Re-run Step 1.
jq -r '.status' /tmp/rescan.json

Step 3 of 4 — Install clean versions

bun install -g @automagik/genie@^4.260422.4
bun install -g pgserve@^1.1.10

Step 4 of 4 — Rotate credentials that were live during the compromise window

Rotate every credential that was in environment or on disk between 2026-04-21 and 2026-04-22. If you cannot bound the window, rotate everything in the §11.3 Step 5 table.

11.5 OBSERVED ONLY — clear → rescan

Preconditions: genie sec scan returned OBSERVED ONLY. Only passive references (cache index without unpacked contents, lockfile entries, shell history) were found. No dropped payload, no persistence, no live process.

Step 1 of 3 — Clear the referenced cache / history entries

# bun cache (even if empty manifests, clear the names)
rm -rf ~/.bun/install/cache/@automagik/genie@4.260421.*@@@1
rm -rf ~/.bun/install/cache/pgserve@1.1.1[1-4]@@@1

# Shell history entries mentioning compromised versions
for h in ~/.bash_history ~/.zsh_history; do
  [ -f "$h" ] || continue
  cp -a "$h" "${h}.pre-canisterworm.bak"
  grep -vE 'genie@4\.260421\.(3[3-9]|40)|pgserve@1\.1\.(1[1-4])' "${h}.pre-canisterworm.bak" > "$h" || true
done

Step 2 of 3 — Re-scan to confirm

genie sec scan --all-homes --root "$PWD" --json > /tmp/rescan.json
# Expected: NO FINDINGS.
jq -r '.status' /tmp/rescan.json

Step 3 of 3 — Do not rotate credentials yet

OBSERVED ONLY means we have no evidence the postinstall script executed. Skip credential rotation unless later evidence (shell history showing an install + invoke, auditd execve of node scripts/env-compat.cjs, egress logs to a C2 host) surfaces. If any of those appears, re-classify as LIKELY AFFECTED and run §11.4.

11.6 Escalation — rollback

Use genie sec rollback <scan_id> when genie sec remediate --apply completed but broke something on the host. Rollback walks the audit log in reverse, restoring every quarantined item to its original path with sha256-verified content. When to reach for this: a service fails to start after remediation, a legitimate config file was quarantined, a dependency your application needs is gone. Rollback is safe — it only touches items the audit log recorded.
# Bulk rollback: walks $GENIE_SEC_AUDIT_LOG in reverse for this scan_id.
genie sec rollback "$SCAN_ID"

# Per-item: if you only need to restore a specific quarantine id.
genie sec quarantine list
genie sec restore <quarantine-id>

11.7 Escalation — --unsafe-unverified

genie sec remediate --apply refuses to run unless genie sec verify-install returned exit 0. The --unsafe-unverified <INCIDENT_ID> flag is the only documented escape hatch. Every invocation is written to $GENIE_SEC_AUDIT_LOG with the incident id, the typed ack, and the reason.

When --unsafe-unverified is legitimate

There are three contexts in which --unsafe-unverified is a correct operator choice. Context 1 — Burned public key / burned signing identity A Namastex security officer confirmed the cosign keyless identity is compromised (see docs/security/key-rotation.md). verify-install returns exit 3 (signer-identity-mismatch). The host needs remediation now; rotation will take hours.
# Incident id MUST come from the pinned rotation issue; do not invent one.
genie sec remediate --apply \
  --plan "$GENIE_HOME/sec-scan/$SCAN_ID/plan.json" \
  --unsafe-unverified "SIGNING_CERT_IDENTITY_20260423"
# Typed ack prompt: I_ACKNOWLEDGE_UNSIGNED_GENIE_SIGNING_CERT_IDENTITY_20260423
Audit-log verification:
jq -r 'select(.event=="remediate.apply.start" and .unsafe_unverified != null)' \
  "$GENIE_SEC_AUDIT_LOG"
# Expect a single entry whose incident_id matches the rotation issue number.
Context 2 — CI pre-signing period The release channel is older than the genie-supply-chain-signing cutover and does not ship a signed tarball. verify-install returns exit 5 (no signature material found). Common during staged rollouts between 4.260423.x and 4.260424.x.
genie sec remediate --apply \
  --plan "$GENIE_HOME/sec-scan/$SCAN_ID/plan.json" \
  --unsafe-unverified "PRE_SIGNING_CHANNEL_260423"
# Typed ack prompt: I_ACKNOWLEDGE_UNSIGNED_GENIE_PRE_SIGNING_CHANNEL_260423
Context 3 — Integration test harness scripts/test-runbook.sh and the CI workflow exercise remediate --apply against a fixture on an unsigned development tarball. The INCIDENT_ID is a fixed sentinel recognized by the harness.
genie sec remediate --apply \
  --plan "$FIXTURE_DIR/plan.json" \
  --unsafe-unverified "TEST_HARNESS_CANISTERWORM_FIXTURE" \
  --auto-confirm-from "$FIXTURE_DIR/consent.json"
# Typed ack prompt: I_ACKNOWLEDGE_UNSIGNED_GENIE_TEST_HARNESS_CANISTERWORM_FIXTURE

When --unsafe-unverified is NOT legitimate

  • “It’s faster.” The prompt is the point; verification is the contract.
  • “The prompt is annoying.” See above.
  • “I don’t have the key locally.” There is no key — the signing identity is a three-value tuple (see SECURITY.md § Release Signing). If verify-install exits 5, use Context 2.
  • “Our release channel is old.” Upgrade. If that is not possible right now, use Context 2 with the correct release-line identifier.
Any --unsafe-unverified invocation whose incident id does not map to one of the three legitimate contexts is treated as an incident of its own by post-hoc review.

11.8 Post-mortem template

Copy the block below into your incident channel and fill in every field. The scan_id ties it to the persisted scan JSON, the audit log, and the remediation plan manifest.
# Post-mortem: CanisterWorm exposure on <host or fleet scope>

## Metadata
- Scan id:          <paste from `genie sec scan --json`>
- Scan bands hit:   LIKELY COMPROMISED / LIKELY AFFECTED / OBSERVED ONLY (delete two)
- Detection time:   <ISO-8601 UTC>
- Containment time: <ISO-8601 UTC>
- Runbook branch:   §11.3 / §11.4 / §11.5

## Exposure
- Affected versions installed: <list @automagik/genie + pgserve versions>
- Install method:              <bun / npm / CI / auto-update>
- Window of exposure:          <from when the compromised version landed to when the host was contained>
- Material at risk:            <paste `at-risk local material present on host` from scan report>

## Actions taken
- Scan report:   $GENIE_HOME/sec-scan/<scan_id>/report.json
- Plan manifest: $GENIE_HOME/sec-scan/<scan_id>/plan.json
- Audit log:     $GENIE_SEC_AUDIT_LOG (filter on scan_id)
- Rollback used? yes / no (if yes, reason)
- --unsafe-unverified used? yes / no (if yes, which legitimate context from §11.7)

## Credential rotation
- npm:        rotated at <time> · verified with `npm whoami`
- GitHub:     rotated at <time> · verified with `gh auth status`
- AWS:        rotated at <time> · verified with `aws sts get-caller-identity`
- GCP:        rotated at <time>
- Azure:      rotated at <time>
- Kubernetes: rotated at <time>
- Docker:     rotated at <time>
- AI keys:    rotated at <time> · 72h billing audit clean? yes / no
- Crypto:     wallets moved to new seed on clean device? yes / no / N/A
- TLS:        certs re-issued? yes / no / N/A

## Timeline
- <ISO>  Scanner flagged host.
- <ISO>  Egress blocked at perimeter.
- <ISO>  `genie sec remediate --dry-run --scan-id ...` reviewed.
- <ISO>  `genie sec remediate --apply ...` completed.
- <ISO>  Credential rotation completed.
- <ISO>  Host rebuilt / snapshot restored.
- <ISO>  Re-scan returned NO FINDINGS.

## Lessons learned
- <what the compromise window cost>
- <what would have shortened detection>
- <what policy/tooling change lands this week>

## Attachments
- Process snapshot: /tmp/ps-snapshot-*.txt
- Re-scan report:   /tmp/rescan.json
- C2 egress logs:   <link>

Documento mantido por Cezar Vasconcelos — DPO e CTO, Namastex Labs Serviços em Tecnologia Ltda. Distribuição livre. Redistribuição encorajada. Atribuição apreciada.