
safer-dependencies v0.6.1
Camada automatizada de segurança de dependências para assistentes de codificação de IA que audita pacotes em busca de CVEs, typosquats, abandono, problemas de idade de versão e integridade de hash nos ecossistemas npm, PyPI, RubyGems, Maven, Go e Rust.
Dependências Mais Seguras para o Claude Code
Quando assistentes de codificação com IA, como o Claude, adicionam pacotes ao seu projeto, eles frequentemente escolhem qualquer versão que pareça adequada — sem verificar se ela possui vulnerabilidades de segurança conhecidas, se o pacote ainda é mantido ativamente ou se o nome está a um erro de digitação de um imitador malicioso.
safer-dependencies é uma camada de segurança para o Claude Code: ela fica entre o Claude e seus arquivos de manifesto e executa suas verificações de segurança automaticamente: instalações vulneráveis são negadas antes de serem executadas, e uma versão arriscada escrita em um manifesto é corrigida no disco logo após a escrita. Ela detecta e corrige dependências arriscadas — CVEs, typosquats, pacotes abandonados e problemas de idade de versão, além de um período de espera para lançamentos totalmente novos — em npm, PyPI, RubyGems, Maven, Go, Rust e PHP (Composer). Consulte CAPABILITIES.md para saber exatamente o que é e o que não é coberto.
Novo aqui? GETTING-STARTED.md leva você do zero a uma instalação funcional em cerca de cinco minutos.
Segurança e privacidade: consulte SECURITY.md (divulgação de vulnerabilidades), PRIVACY.md (saída de dados, sem telemetria) e CAPABILITIES.md (o que a ferramenta defende e o que não defende).
Licença (código-fonte disponível — NÃO é "código aberto" OSI): Livre para usar e modificar para seus próprios fins, incluindo uso interno com fins lucrativos/empresarial e construção de produtos que você vende. Uma licença paga separada é necessária apenas para monetizar o software em si — vendê-lo, distribuí-lo dentro de um produto ou serviço que é vendido, ou oferecer sua funcionalidade a terceiros mediante taxa (incluindo hospedado/SaaS/API). Redistribuição e derivados devem manter a licença e creditar este projeto. Consulte LICENSE (Seção 4 para a restrição comercial); solicitações de licença comercial via github.com/robert-auger.
Conteúdo
- Introdução — do zero à instalação em cerca de cinco minutos
- O que ele faz
- O que o aciona
- O que há neste repositório
- Ecossistemas suportados
- Instalação
- Níveis de aviso
- Como funciona
- Registro de auditoria
- Requisitos
- FAQ
Introdução
GETTING-STARTED.md leva você do zero a uma instalação funcional em cerca de cinco minutos — pré-requisitos, a instalação interativa e a verificação. Para a referência completa de instalação (instalações globais/projeto/manuais, especificidades do Windows, a lista de permissões, atualização e desinstalação), consulte INSTALLATION.md.
Uso diário: uma vez que os hooks estão instalados, não há nada para executar — safer-dependencies funciona automaticamente em segundo plano. À medida que o Claude adiciona ou instala pacotes, ele sinaliza dependências arriscadas e atualiza versões vulneráveis para uma versão segura no local — e bloqueia uma instalação conhecidamente vulnerável antes mesmo de ela ser executada — para que pacotes inseguros sejam detectados e corrigidos sem que você precise pedir. Você ainda pode invocá-lo diretamente a qualquer momento: "[email protected] é seguro?", "verifique a configuração do safer-dependencies" ou "mostre estatísticas do safer-dependencies".
O que ele faz
Quando o Claude está prestes a adicionar um pacote ao seu projeto, o safer-dependencies intercepta e executa 5 verificações:
- Proveniência — registro oficial, detecção de typosquat (npm/PyPI/RubyGems/Maven/crates.io), idade do pacote
- Idade da versão — escolhe a versão estável mais recente publicada há 7+ dias (janela de espera)
- Varredura de vulnerabilidades — API OSV, com ferramentas nativas do ecossistema (npm audit, pip-audit, bundle audit) quando disponíveis
- Integridade de pin de hash — para linhas de
requirements.txtdo PyPI com pins--hash=sha256:..., o hash declarado é validado contra os hashes publicados do PyPI; uma incompatibilidade emite um AVISO - Pacotes abandonados e desatualizados — pacotes conhecidamente abandonados (ex.:
paperclip,request,pycrypto,github.com/dgrijalva/jwt-go) são bloqueados imediatamente com uma substituição sugerida; pacotes sem lançamento estável em 2+ anos recebem um aviso consultivoSTALE:. Pacotes bloqueados são removidos do manifesto e o Claude perguntará como proceder; pacotes apenas desatualizados são mantidos no local.
Se problemas forem encontrados, o Claude emite avisos e pode recuar para uma versão mais segura. Todas as verificações são registradas em ~/.claude/safer-dependencies-audit-YYYY-MM.log (um arquivo por mês calendário).
O que o aciona
A skill é acionada automaticamente quando o Claude:
Operações de manifesto / instalação
- Adiciona ou atualiza um pacote em
package.json,requirements.txt,Gemfile,pom.xml,build.gradle,Cargo.toml,go.modou qualquer outro manifesto suportado - Escreve um
import,requireouusepara um pacote ainda não declarado no manifesto - Gera ou atualiza um arquivo de bloqueio (verifica apenas entradas novas/alteradas)
- Executa uma instalação do gerenciador de pacotes via Bash (
npm install,bundle install,poetry install,uv sync,go mod tidy, etc.) — Pré-Instalação audita os argumentos do comando, Pós-Instalação audita o arquivo de bloqueio resultante - Escreve um
Dockerfileou fluxo de trabalho de CI (.github/workflows/*.yml, etc.) que incorpora etapas de instalação fixadas do gerenciador de pacotes
Perguntas de seleção e recomendação
- Comparações de bibliotecas/frameworks: "devo usar axios ou node-fetch?", "moment vs dayjs?", "qual é melhor X ou Y?"
- Solicitações de recomendação: "qual é um bom cliente HTTP para Python?", "recomende uma biblioteca de logging para Go", "qual pacote lida com CSV no Node?"
- Seleção de versão: "qual versão do Django devo usar?", "Flask estável mais recente?"
Expressões de intenção de uso (pré-adição)
- "Quero usar FastAPI para isso", "estou pensando em adicionar Celery", "estamos considerando Prisma como o ORM", "vamos usar Tailwind"
Perguntas sobre saúde e confiança de pacotes
- "O moment.js ainda é mantido?", "este gem ainda está ativo?", "X está abandonado?", "X está em fim de vida?", "posso confiar neste pacote?", "quando o faker foi atualizado pela última vez?"
Comandos de scaffolding
npx create-react-app,npm create vite@latest,django-admin startproject,rails new,cargo new+cargo add, "inicializar um novo projeto FastAPI"
Adições implícitas de pacotes (solicitações de recursos que implicam uma nova dependência)
- "Adicione cache Redis ao aplicativo", "conecte ao Postgres", "adicione autenticação JWT", "escreva código para enviar e-mails" — dispara quando nenhum pacote para essa capacidade já está no manifesto
Migração e portabilidade
- "Migre de requests para httpx", "mude de CRA para Vite", "porte de moment para date-fns" — audita o pacote de entrada
Ele não dispara para:
- Imports da biblioteca padrão (
os,fs,java.util.*, etc.) - Dependências já declaradas que não estão sendo alteradas
- Discussão acadêmica sobre como um pacote funciona internamente ("explique o reconciler do React", "como funciona a resolução de módulos do webpack?") — perguntas de comparação e seleção ainda disparam
- Instalação de aplicativos de nível de sistema operacional, runtimes ou extensões de IDE (o próprio Python, Docker, Homebrew, extensões do VS Code)
O que há neste repositório
Este é um pacote de skill + hooks, não um único arquivo de skill. Uma instalação completa implanta estas peças:
| Arquivo | Função |
|---|---|
skills/safer-dependencies.md | A skill (SKILL.md após a instalação). Descreve procedimentos de auditoria e inclui modo de gerenciamento para instalação/estatísticas. |
skills/safer-dependencies-shim.sh | Hook PostToolUse:Write/Edit — audita gravações de manifesto e arquivo de bloqueio e corrige automaticamente versões vulneráveis no local (Modo de Interceptação). |
skills/safer-dependencies-pretooluse-bash.sh | Hook PreToolUse:Bash — auditoria OSV pré-voo de comandos de instalação do gerenciador de pacotes; nega pins concretos vulneráveis antes de a instalação ser executada (Modo Pré-Instalação). |
skills/safer-dependencies-posttooluse-bash.sh | Hook PostToolUse:Bash — auditoria pós-voo após comandos Bash; captura CVEs transitivos em arquivos de bloqueio recém-escritos, manifestos editados via sed/jq/scripts e o ambiente resolvido de pip install simples (Modo Pós-Instalação). |
skills/safer-dependencies-pretooluse-agent.sh + skills/safer-dependencies-posttooluse-agent.sh | Par de hooks PreToolUse:Agent + PostToolUse:Agent — fecha a lacuna de cobertura de subagentes. Os Modos 2–4 só disparam para chamadas de ferramentas da sessão raiz, portanto qualquer manifesto que um subagente escreva os ignora. Pós-Agente audita o que o subagente escreveu após cada chamada de ferramenta Agent retornar (Modo Pós-Agente). |
skills/scripts/ | Biblioteca Python compartilhada (safedep/) e scripts de resolução independentes usados por todos os hooks. |
skills/scripts/safer_dependencies_manager.py | Módulo de gerenciamento para instalação interativa, estatísticas de uso e validação de configuração. |
Apenas o arquivo de skill não é suficiente — sem hooks, a invocação automática depende de o Claude decidir alcançar a skill. Instale todas as cinco peças para cobertura completa; muitas skills e comandos de barra despacham subagentes internamente, então o par Pós-Agente importa mesmo se você nunca gerar um explicitamente. (Consulte FAQ.md para saber por que uma skill sozinha não pode garantir cobertura.)
Ecossistemas suportados
| Ecossistema | Manifesto | Arquivo de bloqueio |
|---|---|---|
| npm | package.json | package-lock.json, yarn.lock, pnpm-lock.yaml |
| PyPI | requirements.txt, pyproject.toml, Pipfile, setup.py, setup.cfg | Pipfile.lock, poetry.lock, uv.lock |
| RubyGems | Gemfile, *.gemspec | Gemfile.lock |
| Maven | pom.xml, build.gradle, libs.versions.toml | -- |
| Go | go.mod | go.sum |
| Rust | Cargo.toml | Cargo.lock |
| PHP (Composer) | composer.json | composer.lock |
Instalação
Novo no projeto? Comece com GETTING-STARTED.md. A versão resumida:```bash git clone https://github.com/robert-auger/safer-dependencies /tmp/safer-dependencies python3 /tmp/safer-dependencies/skills/scripts/safer_dependencies_manager.py interactive_install
O instalador solicita o escopo (global vs projeto) e quais hooks habilitar, depois grava o `settings.json` para você — tanto as entradas de hooks **quanto** a lista de permissões que permite que os comandos de verificação da skill sejam executados sem um prompt de aprovação a cada auditoria.
Todo o resto relacionado à instalação está em **[INSTALLATION.md](https://github.com/robert-auger/safer-dependencies/blob/main/INSTALLATION.md)**, a referência única para a mecânica de instalação: instalações manuais arquivo por arquivo (global e em nível de projeto), especificidades do Windows, hooks do Post-Agent, a [lista de permissões](https://github.com/robert-auger/safer-dependencies/blob/main/INSTALLATION.md#permissions-allowlist), verificação da configuração, atualização, fixação em uma tag de release e desinstalação.
Após a instalação, o gerenciamento do dia a dia funciona por linguagem natural com o Claude — `install safer-dependencies` (re-executar / alterar hooks), `show safer-dependencies stats`, `check safer-dependencies setup` — ou pelo menu `/safer-dependencies`. A atualização também acontece na sessão: `/safer-dependencies update` aplica o release mais recente (`update --check` para uma simulação, `update --rollback` para desfazer); veja [INSTALLATION.md](https://github.com/robert-auger/safer-dependencies/blob/main/INSTALLATION.md#in-session-self-updater-safer-dependencies-update) para o modelo de confiança.
> **Nota sobre plataformas:** macOS, Linux e Windows são suportados. O Windows precisa do Git for Windows (que fornece o bash) e do Python 3 no `PATH` — sem necessidade de WSL. Os testes práticos até o momento focaram em **macOS e Windows**; o suporte a Linux é exercitado pela matriz de CI automatizada.
### Configuração
Duas coisas são configuráveis após a instalação:
- **Lista de permissões** — pré-aprova os comandos de verificação somente leitura da skill (as regras de forma exata do `npm audit` / `bundle audit` e os scripts de resolução da própria skill) para que as auditorias sejam executadas sem um prompt de aprovação a cada vez; `curl` nunca é pré-aprovado, e `npm view` / `pip-audit` são opcionais por meio do perfil Convenience. O instalador interativo grava as entradas principais para você; instalações manuais adicionam o bloco completo à mão. Bloco completo e justificativa: [INSTALLATION.md → Lista de permissões](https://github.com/robert-auger/safer-dependencies/blob/main/INSTALLATION.md#permissions-allowlist).
- **Política de segurança** — a janela/modo de espera da idade do release e um nível `off`/`warn`/`block` por verificação para cada tipo de verificação, editados com `/safer-dependencies config` e armazenados em `~/.config/safer-dependencies/config.toml`. Esquema e semântica dos níveis: [`skills/references/configuration.md`](https://github.com/robert-auger/safer-dependencies/blob/main/skills/references/configuration.md).
### Alterando o período de espera
O período de espera (chamado de **cooloff** na configuração) é a idade mínima que um release deve atingir antes que a skill o selecione — padrão de **7 dias**. Para alterá-lo, peça ao Claude ou execute o comando de configuração diretamente:```
/safer-dependencies config set cooloff.days 14 # require releases to be 14+ days old
/safer-dependencies config set cooloff.mode block # gate strength: off | warn | block (default: warn)
/safer-dependencies config unset cooloff.days # revert to the 7-day default
/safer-dependencies config # show effective values and where each comes from
As mesmas ações funcionam fora de uma sessão do Claude:```bash python3 skills/scripts/safer_dependencies_manager.py config set cooloff.days 14
A configuração persiste em `~/.config/safer-dependencies/config.toml` (na seção `[cooloff]`); as variáveis de ambiente `SAFE_DEP_COOLOFF_DAYS` e `SAFE_DEP_COOLOFF_MODE` substituem o arquivo por sessão. Três comportamentos a conhecer: `mode = "off"` remove completamente o filtro de idade da seleção de versões; uma reescrita orientada por CVE ignora a barreira, de modo que uma correção de segurança nunca é retida por ser demasiado recente; e a barreira cobre npm, PyPI, RubyGems e crates.io — Maven e Go são intencionalmente não abrangidos. Semântica completa: [`skills/references/configuration.md`](https://github.com/robert-auger/safer-dependencies/blob/main/skills/references/configuration.md).
## Níveis de aviso
| Nível | Significado | Exemplo |
|-------|---------|---------|
| CRITICAL | Parar e perguntar ao utilizador | Typosquat detetado, assinatura adulterada |
| HIGH | Avisar e prosseguir | CVE conhecido, pacote com menos de 30 dias |
| MEDIUM | Avisar e prosseguir | Versão com menos de 7 dias, assinatura em falta |
| LOW | Avisar e prosseguir | Gem Ruby sem assinatura (esperado) |
## Como funciona
A skill opera em cinco modos (resumidos abaixo; a fundamentação de design mais profunda está em `skills/safer-dependencies.md`):
### Modo Normal (Manual)
Quando o Claude está prestes a escrever um `import`, adicionar um pacote a um manifesto ou atualizar um ficheiro de bloqueio, a skill é executada inline na sua sessão:
1. Consulta o registo de pacotes por versões estáveis
2. Seleciona automaticamente a versão mais recente publicada há 7+ dias (determinística — sem julgamento de LLM)
3. Verifica vulnerabilidades conhecidas através de ferramentas do ecossistema e da API OSV
4. Verifica assinaturas de pacotes quando disponíveis
5. Emite avisos se forem encontrados problemas, fixa a versão exata
6. Regista o resultado na trilha de auditoria
A seleção de versões é tratada por scripts Python autónomos incluídos na skill, não pelo LLM a interpretar regras. O comando produz `SELECTED: <version>` e o Claude usa exatamente essa versão.
### Modo de Interceção (Automático)
Configure `.claude/settings.json` com um hook `PostToolUse` para ativar a verificação automática e transparente de pacotes:
1. O Claude escreve um ficheiro de manifesto (ex.: `package.json`) com a versão originalmente solicitada — o ficheiro é gravado no disco
2. O hook `PostToolUse` dispara imediatamente após a conclusão da escrita e invoca `safer-dependencies-shim.sh`
3. O shim lê o ficheiro, analisa os pacotes declarados e executa todas as verificações de segurança (typosquat, abandonado, CVE, desatualização, hash-pin)
4. Se forem necessárias correções, o shim **reescreve o manifesto no local** com versões seguras (ou remove entradas que não tenham versão segura)
5. O shim emite sinais (`UPDATED:`, `BLOCKED:`, `WARNING:`, `STALE:`, `MAJOR-UPDATE-CONFIRM:`, `REFACTOR-REQUIRED:`, `REGRESSION:`, `TYPOSQUAT-CONFIRM:`, `VERIFY:`, `CLEAN:`) via `hookSpecificOutput.additionalContext` no stdout. `REGRESSION:` precede um `MAJOR-UPDATE-CONFIRM:` quando o registo de auditoria mostra que o mesmo (ficheiro, pacote) foi anteriormente corrigido para o mesmo alvo seguro — ou seja, um subagente ou um plano desatualizado reintroduziu uma versão com vulnerabilidade conhecida, e o orquestrador deve restaurar a versão previamente aprovada em vez de redecidir o salto de versão principal.
6. O Claude recebe esses sinais como um lembrete de sistema e executa trabalho de acompanhamento (encontrar imports afetados, executar testes, refatorar para alterações disruptivas)
**Nota de design — Forma C (corretiva pós-escrita):** o hook NÃO bloqueia escritas. Cada versão vulnerável é gravada no disco primeiro e depois corrigida automaticamente dentro do mesmo ciclo de uso da ferramenta. Esta é uma escolha deliberada em relação a um design de bloqueio `PreToolUse` — veja [FAQ.md](https://github.com/robert-auger/safer-dependencies/blob/main/FAQ.md#why-posttooluse-post-write-corrective-instead-of-pretooluse-pre-write-blocking-for-the-manifest-path) para os compromissos.
**Exemplo de sinal:**```
UPDATED: aiohttp 3.8.5 → 3.9.0 (HIGH: 33 CVEs fixed)
O agente pai usa esses sinais para identificar código afetado e refatorar conforme necessário.
Modo Pré-Instalação (Hook Bash)
Configure .claude/settings.json com um hook PreToolUse:Bash para habilitar
auditoria pré-voo de comandos de instalação de gerenciadores de pacotes. Isso complementa
(não substitui) o Modo Interceptação — juntos, eles formam uma defesa em camadas.
- Claude tenta uma chamada de ferramenta Bash (ex.:
npm install [email protected]) - O hook
PreToolUsedispara antes da chamada ser executada e invocasafer-dependencies-pretooluse-bash.sh - Um filtro inicial puramente em bash encurta comandos que não são de gerenciadores de pacotes em ~115 ms
(sem invocação de Python), então
git status/ls/npm testpagam custo desprezível no caminho crítico - Para instalações reconhecidas de gerenciadores de pacotes (
npm/pnpm/yarninstall/i/add), o helper tokeniza viashlex, extrai cada argumentopkg@versione faz POST para o OSV - Qualquer pin concreto vulnerável → o hook retorna
permissionDecision: "deny"com um GHSA-id por achado + CVSS + resumo, além de uma dica para invocar a skill safer-dependencies - A instalação nunca é executada — sem fetch de rede, sem scripts postinstall
Por que isso existe além do Modo Interceptação: o shim pós-escrita
é cego para Bash. npm install [email protected] é executado até o fim (e
scripts postinstall são executados) antes de qualquer auditoria disparar; npm install -g typosquat-pkg não escreve nenhum manifesto de projeto. O Modo
Pré-Instalação fecha essas lacunas estruturalmente.
O Modo Pré-Instalação só vê o que o usuário digitou (argumentos pkg@version na
linha de comando). Ele não consegue ver a árvore transitiva que o resolvedor
realmente instalará. O Modo Pós-Instalação (abaixo) audita o lockfile assim que
a instalação é concluída — os dois modos são complementares, não redundantes.
Escopo: os CLIs de gerenciadores de pacotes cobertos aqui abrangem cinco ecossistemas
(npm/pnpm/yarn/bun/npx/deno, pip/pip3/pipx/pipenv/uv/uvx/poetry, gem/bundle,
go, cargo), além de Maven via Modo Interceptação (dependências Maven são tipicamente
declaradas em pom.xml/build.gradle, não adicionadas via um verbo CLI).
Lacuna conhecida: o CLI do Maven suporta downloads diretos via
mvn dependency:get -Dartifact=group:art:versionemvn dependency:copy. Este hook ainda não reconhece essas invocações. Se você as usa regularmente, o shim pós-escrita existente ainda captura o que quer que chegue ao seu manifesto, mas a proteção pré-fetch só se aplica aos ecossistemas listados acima. Rastreado como acompanhamento.
Sintaxe reconhecida por ecossistema:
| PM | Verbos | Sintaxe de pin concreto |
|---|---|---|
npm, pnpm, yarn, bun | install, i, add (além de yarn/pnpm dlx, bun x, yarn create) | [email protected], @scope/[email protected] |
npx | (sem verbo — o pacote é o primeiro argumento posicional) | [email protected] |
deno | add, install | npm:[email protected] (specs prefixados com npm) |
pip, pip3, pipx, pipenv, uv, uvx, poetry | install (pip/pip3/pipx/pipenv) / add (uv/poetry) / sem verbo (uvx) | pkg==1.2.3 (extras pkg[extra]==X também são tratados) |
gem, bundle | install (gem) / add | -v 1.2.3, --version 1.2.3, --version=1.2.3 (flag separada) |
go | get, install | [email protected] (deve incluir o prefixo v conforme módulos Go) |
cargo | add, install | [email protected] |
Pins de intervalo (npm ^4.17, pip >=, poetry ^/~, Go @latest) e
versões não especificadas passam para o Modo Interceptação após a instalação — o
shim pós-escrita audita o que o resolvedor escolher. A reescrita automática para uma
versão segura está na fila como acompanhamento.
Modo de falha: fail-open. Qualquer erro (Python ausente, instabilidade de rede, entrada malformada) sai com código 0 e sem saída, permitindo que o bash prossiga. O Modo Interceptação ainda é executado após a instalação, então uma falha pré-voo degrada graciosamente para a proteção existente.
Exemplo de negação:``` safer-dependencies pre-flight audit blocked this install. Vulnerable pinned version(s) detected:
- [email protected] → GHSA-35jh-r3h4-6jhm (CVSS:7.4): Command Injection in lodash Re-run with a patched version, or invoke the safer-dependencies skill for a recommended pin.
### Modo Pós-Instalação (Hook Bash)
Configure `.claude/settings.json` com um hook `PostToolUse:Bash` para ativar
auditoria pós-execução após comandos Bash. Ele executa **três verificações independentes**
no `cwd` do comando, cada uma cobrindo uma lacuna que os outros hooks não conseguem resolver:
- **Verificação A — arquivos de bloqueio (lockfiles).** Após um verbo de instalação bem-sucedido (`npm install`,
`bundle install`, `poetry install`, `uv sync`, `go mod tidy`, etc.), audita
arquivos de bloqueio recém-modificados (`package-lock.json`, `Gemfile.lock`,
`poetry.lock`, `uv.lock`, `go.sum`, `yarn.lock`, `pnpm-lock.yaml`,
`Pipfile.lock`). Isso cobre a **lacuna de CVEs transitivos** que o Pré-Instalação não
consegue ver: o usuário digitou `pkg@version`, mas o resolvedor pode ter incluído
dezenas de dependências transitivas que ninguém nomeou.
- **Verificação B — manifestos.** Após qualquer comando Bash que *não* esteja em uma
lista de bloqueio somente-leitura (`ls`, `cat`, `git status`, …), audita manifestos
recém-modificados. Este é o **único** mecanismo de fallback para edições de manifestos feitas via `sed -i`, `jq`, ou
um script — essas ignoram a ferramenta `Write`/`Edit` na qual o Modo Interceptação se baseia.
- **Verificação C — ambiente resolvido.** Um simples `pip install` /
`pip install -r requirements.txt` não grava arquivo de bloqueio, então a Verificação A nunca vê
a árvore resolvida. Após uma instalação no formato pip, a Verificação C reexecuta o mesmo
pip com um `list --format=json` somente-leitura e verifica via OSV todo o ambiente
resolvido (direto + transitivo).
Como uma verificação é executada:
1. Claude executa uma chamada de ferramenta Bash
2. O hook `PostToolUse` dispara *após* o comando ser concluído e invoca
`safer-dependencies-posttooluse-bash.sh`
3. Um filtro inicial puro em Bash encurta comandos que não correspondem a nenhum gatilho de verificação em
~115 ms (mesma convenção de caminho rápido do Pré-Instalação), então `ls` / `git` / `cat`
têm custo desprezível
4. Cada verificação percorre o `cwd` com `find -maxdepth 5` (cobre layouts de monorepo;
exclui `node_modules`, `.git`, `.venv`, `venv`) em busca de arquivos modificados nos
últimos 60 s — substituível via `SAFE_DEP_POSTINSTALL_MTIME_WINDOW`
5. Para cada arquivo recém-modificado (Verificação A/B), o hook forja um payload sintético
de `PostToolUse:Write` e o envia ao shim existente — os auditores de arquivos de bloqueio e
manifestos do shim são executados sem alterações, sem lógica duplicada
6. Os sinais por arquivo são concatenados e emitidos como um único JSON de `hookSpecificOutput`
para o agente pai
**O que ele detecta que o Pré-Instalação não detecta:** vulnerabilidades transitivas.
Um `bundle install` de aparência limpa pode incluir `[email protected]` (CVE-2025-27610)
como dependência transitiva de `sinatra` — o usuário nunca digitou `rack`, então
o Pré-Instalação não consegue ver, mas o Pós-Instalação lê o
`Gemfile.lock` resolvido e reporta a CVE.
**Escopo:** a Verificação A não reescreve versões resolvidas — o contrato de
autocorreção se aplica apenas a manifestos que o Claude escreveu diretamente. Para CVEs transitivos,
a correção normalmente é "atualizar a dependência direta que possui a transitiva", o que
exige julgamento humano. A Verificação B *faz* autocorreção, pois audita manifestos
pelo mesmo caminho de shim do Modo Interceptação. A Verificação A é ignorada quando o
nível de verificação `transitive` está definido como `off` (`config set checks.transitive off`).
**Modo de falha:** fail-open, igual aos outros hooks. Qualquer erro (shim
ausente, payload malformado, Python indisponível) sai com código 0 silenciosamente.
**Exemplo de AVISO:**```
WARNING: [email protected] in lock file has GHSA-29mw-wpgm-hmr9, GHSA-35jh-r3h4-6jhm
Modo Pós-Agente (Par de Hooks de Agente)
Os quatro modos acima só disparam para chamadas de ferramentas da sessão raiz. Quando a sessão raiz despacha um subagente (via a ferramenta Agent — muitas skills e comandos de barra fazem isso internamente), as chamadas Write/Edit/Bash do subagente ignoram todos eles. O Modo Pós-Agente é a rede de segurança reativa para essa lacuna.
- Um hook
PreToolUse:Agent(safer-dependencies-pretooluse-agent.sh) é executado imediatamente antes de cada despacho de Agent e toca um arquivo sentinela em/tmp/.safer-deps-agent-<PPID>-<session_id>.sentinel(recorrendo a um nome apenas com PPID quando não há id de sessão disponível) - O subagente é executado e pode escrever manifests ou lockfiles
- Um hook
PostToolUse:Agent(safer-dependencies-posttooluse-agent.sh) é executado após a chamada do Agent retornar, fazfindem todo manifest e lockfile mais recente que o sentinela e audita cada um pelo mesmo caminho do shim - Os achados surgem como
additionalContextpara a próxima rodada da sessão raiz; o sentinela é removido
Subagentes aninhados são cobertos automaticamente — o PostToolUse:Agent da raiz só dispara depois que todo o trabalho do agente externo (incluindo qualquer coisa que ele tenha despachado) está em disco. A única lacuna é uma instalação global que não escreve manifest nem lockfile (npm install -g …): não há nada para escanear. Como os outros hooks, ele falha aberto — qualquer erro (sentinela ausente, shim ausente, payload ilegível) sai com código 0 silenciosamente. A justificativa completa do design está em skills/safer-dependencies.md.
Log de auditoria
Cada verificação é registrada em ~/.claude/safer-dependencies-audit-YYYY-MM.log (um arquivo por mês calendário, onde YYYY-MM é o ano-mês UTC) como uma única linha JSON. Substitua o caminho completo com a variável de ambiente SAFE_DEP_AUDIT_LOG (quando definida, o sufixo de data não é anexado). Os arquivos também sofrem rotação por tamanho quando excedem SAFE_DEP_LOG_MAX_BYTES (padrão 10 MiB; defina como 0 para desativar). Defina SAFE_DEP_MODEL para substituir o valor do modelo gravado em source.model em cada entrada — útil para comparações A/B entre versões de modelo.
Todos os cinco modos anexam ao mesmo arquivo. Cada entrada carrega um bloco source (esquema 2.2) identificando qual componente o escreveu:
source.component | Escrito por | Gatilho |
|---|---|---|
shim.posttooluse | shim.sh | Gravação de manifest ou lockfile (Modo Interceptação, despacho pós-instalação) |
shim.install_error | shim.sh | Falha de instalação de preflight do shim |
bash.pretooluse | pretooluse-bash.sh | Comando de instalação Bash (Modo Pré-Instalação) |
bash.posttooluse | posttooluse-bash.sh | O próprio hook Bash Pós-Instalação, quando falha aberto antes de alcançar o shim |
agent.pretooluse | pretooluse-agent.sh | Reservado para eventos de falha aberta Pré-Agente (o próprio hook é atualmente silencioso em sucesso) |
agent.posttooluse | posttooluse-agent.sh | Eventos de falha aberta do hook Pós-Agente (ex.: shim ausente, python_missing) |
manual.skill | Claude executando Modo Normal | Auditoria manual invocada inline |
source.model registra o modelo do Claude Code ativo na sessão (ex.: "claude-sonnet-4-6"). Presente no esquema 2.1+; entradas escritas por instalações mais antigas omitem o campo. O comando de estatísticas degrada graciosamente para "unknown" quando ele está ausente.
Filtre por source.component com jq:```bash
jq -r '.source.component' audit.log | sort | uniq -c | sort -rn
jq -c 'select(.source.component == "bash.pretooluse")' audit.log
Surface every silent fail-open across all hooks:
jq -c 'select(.source.mode == "fail_open") | {component: .source.component, reason: .fail_open.reason, ts}' audit.log
Para uma análise mais fácil, peça ao Claude estatísticas de uso em vez de analisar os logs manualmente:```
"Show safer-dependencies stats for the last month"
Este fornece resumos legíveis por humanos de atividade, impacto de segurança e métricas de desempenho extraídos desses logs de auditoria.
Formatos de entrada (esquema 2.2). Três formatos distintos compartilham o mesmo cabeçalho ts / schema / source:
| Formato | Quando é gravado | Campos distintivos |
|---|---|---|
| Entrada de auditoria | Auditoria de manifesto / lockfile / instalação via bash | file, ecosystem, checked, findings, abandoned, stale, typosquat, unknown, signatures, notes, clean |
| Entrada de erro de instalação | Erro de instalação no preflight do shim (componente shim.install_error) | install_error, shim_dir, scripts_dir |
| Entrada de fail-open | Qualquer ponto de entrada de hook sai antecipadamente devido a helper_missing / shim_missing / python_missing. source.mode é "fail_open" | fail_open: { reason, detail? } |
Entradas de auditoria: o Modo de Interceptação executa o pipeline completo (proveniência, idade da versão, OSV, abandonado/obsoleto, typosquat, assinaturas), portanto todos os arrays podem ser preenchidos. O Modo Pré-Instalação executa apenas OSV hoje, então abandoned / stale / typosquat / signatures estão sempre vazios. O despacho pós-instalação (auditoria de lockfile) grava sob shim.posttooluse com findings preenchido por strings WARNING: dos auditores de lockfile. O array notes carrega sinais informativos NOTE: (por exemplo, manifesto-ignorado-porque-não-pinned).
O esquema 2.2 adicionou — de forma aditiva — quatro campos às entradas de auditoria de lockfile: lockfile, manifest_ref, relation_summary (uma classificação direta/transitiva/desconhecida de cada pacote sinalizado em relação ao manifesto irmão) e um bloco policy registrando o nível transitive em vigor. O incremento é retrocompatível: leitores de entradas 2.1 toleram os novos campos, e o campo source.model permanece presente a partir da versão 2.1 em diante.```json
{
"ts": "2026-04-19T12:34:56Z",
"schema": "2.2",
"source": {
"component": "shim.posttooluse",
"script": "shim.sh",
"hook": "PostToolUse:Write",
"tool": "Write",
"mode": "intercept",
"model": "claude-sonnet-4-6"
},
"file": "/path/to/project/package.json",
"ecosystem": "npm",
"checked": ["[email protected]", "[email protected]"],
"findings": ["UPDATED: express 4.18.2 → 4.22.1 (HIGH: 1 CVE fixed)"],
"abandoned": [],
"stale": [],
"typosquat": [],
"unknown": [],
"signatures": [],
"notes": [],
"clean": ["[email protected]"]
}
Pré-Modo de Instalação exemplo (hook Bash, pino vulnerável negado):```json
{
"ts": "2026-04-23T06:56:21Z",
"schema": "2.2",
"source": {
"component": "bash.pretooluse",
"script": "pretooluse-bash.sh",
"hook": "PreToolUse:Bash",
"tool": "Bash",
"mode": "intercept",
"model": "claude-sonnet-4-6"
},
"file": "bash:npm install [email protected] [email protected]",
"ecosystem": "npm",
"checked": ["[email protected]", "[email protected]"],
"findings": [
"BLOCKED: [email protected] GHSA-35jh-r3h4-6jhm (CVSS:3.1/...): Command Injection in lodash"
],
"abandoned": [],
"stale": [],
"typosquat": [],
"unknown": [],
"signatures": [],
"notes": [],
"clean": ["[email protected]"]
}
Fail-open Mode example (hook Bash pós-instalação chamado sem shim adjacente — instalação quebrada):```json { "ts": "2026-05-03T07:14:11Z", "schema": "2.2", "source": { "component": "bash.posttooluse", "script": "safer-dependencies-posttooluse-bash.sh", "hook": "PostToolUse", "tool": "Bash", "mode": "fail_open", "model": "claude-sonnet-4-6" }, "fail_open": { "reason": "shim_missing", "detail": "/home/alice/.claude/skills/safer-dependencies" } }
Uma entrada de fail-open diz: "este hook foi acionado, mas saiu cedo sem auditar porque faltava algum pré-requisito." Use o filtro jq acima (`select(.source.mode == "fail_open")`) para revelar cada evento silencioso de perda de proteção no seu log.
Quando o shim é executado em modo dry-run (`SAFE_DEP_DRY_RUN=1`), as entradas também incluem `"mode": "dry_run"` para que a análise posterior possa filtrar invocações apenas de auditoria.
## Requisitos
- Python 3.9+ (os hooks verificam isso e fazem fail-open em interpretadores mais antigos)
- `curl` (para chamadas de API do registry e verificações de vulnerabilidade OSV)
- Ferramentas de ecossistema (opcionais, a skill recorre à API OSV se estiverem ausentes):
- `npm` para pacotes npm
- `pip-audit` para pacotes Python
- `bundle` para pacotes Ruby
- `dependency-check` para pacotes Java
## FAQ
A justificativa das decisões de design (por que `PostToolUse` em vez de `PreToolUse`, por que assinaturas não são verificadas, por que scripts e shim são duplicados, pegadinhas de carregamento de skill, etc.) está documentada em [`FAQ.md`](https://github.com/robert-auger/safer-dependencies/blob/main/FAQ.md).