cargo-audit: Vulnerabilidades na CI Rust | Rust Brasil

Use cargo-audit e RustSec para encontrar vulnerabilidades em dependências Rust. Guia com Cargo.lock, CI, JSON, triagem, correção e políticas de segurança.

Use cargo-audit na CI para comparar as versões exatas do seu Cargo.lock com a base de vulnerabilidades do RustSec. O fluxo mínimo é instalar a ferramenta com cargo install cargo-audit --locked, executar cargo audit na raiz do repositório e falhar o pipeline quando houver uma vulnerabilidade conhecida sem tratamento. O relatório aponta o advisory, o crate afetado, a versão instalada e, quando existe, a faixa de versões corrigidas.

A ferramenta oferece um controle importante para a cadeia de suprimentos, mas não é um botão mágico de segurança. Um alerta precisa ser triado: descubra se a dependência é direta ou transitiva, confirme se o código vulnerável é alcançável no seu produto, atualize o grafo com o menor risco possível e registre qualquer exceção temporária. Da mesma forma, uma auditoria “verde” não prova que o software está seguro — ela apenas indica que o lockfile não corresponde a advisories conhecidos pela base consultada.

Este guia mostra como usar cargo-audit e RustSec em projetos Rust, integrar a checagem à CI, interpretar resultados sem pânico, corrigir dependências transitivas, lidar com alertas informativos e construir uma política que não seja abandonada depois do primeiro falso senso de urgência.

Resposta rápida: fluxo recomendado

EtapaComando ou açãoObjetivo
Instalarcargo install cargo-audit --lockedObter uma versão reproduzível da ferramenta
Auditarcargo auditComparar o Cargo.lock com o RustSec
Investigarcargo tree -i crate-afetadoEncontrar quem introduziu a dependência
Atualizarcargo update -p crate-afetadoTentar uma atualização focada do lockfile
Validarcargo test --lockedConfirmar que a correção não quebrou o projeto
Automatizarexecutar no pipelineEvitar reintrodução silenciosa
Registrar exceçãoID, motivo, prazo e responsávelImpedir ignores permanentes sem contexto

Em um repositório real, faça a auditoria junto de testes, lints e revisão de dependências. O guia de CI/CD para Rust apresenta a esteira mais ampla, enquanto o artigo sobre supply chain, cargo-deny e SBOM ajuda a cobrir licenças, fontes e inventário além das vulnerabilidades conhecidas.

O que são cargo-audit e RustSec

O cargo-audit é uma ferramenta de linha de comando mantida no ecossistema RustSec. Ela lê o grafo resolvido no Cargo.lock e consulta a RustSec Advisory Database, uma base pública de avisos de segurança relacionados a crates Rust.

Um advisory normalmente contém:

  • um identificador como RUSTSEC-AAAA-NNNN;
  • o pacote e as versões afetadas;
  • uma descrição do problema;
  • categorias ou palavras-chave relevantes;
  • referências para issue, patch, CVE ou análise técnica;
  • versões corrigidas, quando uma correção foi publicada;
  • informações sobre pacotes não mantidos ou considerados problemáticos.

A separação de responsabilidades é importante:

  • RustSec Advisory Database guarda e revisa os avisos;
  • cargo-audit compara esses avisos com as versões do seu lockfile;
  • Cargo resolve e atualiza as dependências;
  • sua equipe decide como corrigir, mitigar ou aceitar temporariamente o risco.

Isso evita uma interpretação comum e errada: cargo audit não faz análise estática completa do seu código e não descobre qualquer vulnerabilidade possível. Ele encontra correspondências entre pacotes resolvidos e conhecimento público estruturado.

Por que o Cargo.lock é essencial

O Cargo.toml declara intervalos permitidos. O Cargo.lock registra as versões exatas escolhidas pelo resolvedor naquele momento. Para uma auditoria reproduzível, você precisa saber se o build usa 1.2.3, 1.2.8 ou 1.3.0 — apenas a restrição ampla no manifesto não responde.

Aplicações, serviços, CLIs e outros binários devem normalmente versionar o Cargo.lock. Isso permite que desenvolvimento, CI e produção partam do mesmo grafo. Também torna comandos como estes mais significativos:

cargo build --locked
cargo test --locked
cargo audit

O --locked nos comandos Cargo impede uma atualização implícita do lockfile. Assim, o pipeline não “conserta” ou altera o grafo silenciosamente durante a validação.

Bibliotecas possuem uma nuance: consumidores resolvem suas próprias versões, portanto o lockfile da biblioteca não controla o grafo final deles. Mesmo assim, mantenedores podem gerar e auditar um lockfile na CI para verificar a combinação atualmente resolvida pelos testes. O resultado representa o grafo testado pelo mantenedor, não todas as combinações que cada consumidor poderá obter.

Se o projeto não possui lockfile no ambiente de auditoria, gere um de forma explícita:

cargo generate-lockfile
cargo audit

Depois defina no repositório se esse arquivo será versionado ou criado apenas no job. Não deixe a decisão variar de acordo com o computador de quem executou o pipeline.

Instalando e fazendo a primeira auditoria

Instale a ferramenta com o lockfile publicado por ela:

cargo install cargo-audit --locked
cargo audit --version

Na raiz do projeto:

cargo audit

Em linhas gerais, o comando:

  1. encontra o Cargo.lock;
  2. obtém ou atualiza a base de advisories;
  3. analisa os pacotes e versões resolvidos;
  4. mostra vulnerabilidades e outros avisos aplicáveis;
  5. encerra com um status que pode ser usado pela CI.

Para automação que precisa armazenar ou transformar o resultado, a ferramenta também oferece saída em JSON:

cargo audit --json > cargo-audit.json

Antes de acoplar campos específicos a um parser interno, salve um exemplo da versão fixada na sua CI e confira cargo audit --help. Formatos e opções podem evoluir; um parser rígido pode quebrar justamente quando a equipe mais precisa do relatório.

Nunca publique o relatório automaticamente sem revisar o conteúdo. Ele costuma conter apenas metadados de dependências, mas nomes de pacotes privados, caminhos ou detalhes do ambiente podem revelar informações que o repositório não deveria expor.

Como interpretar um alerta

Quando a auditoria encontra um problema, não comece atualizando tudo indiscriminadamente. Responda quatro perguntas.

1. Qual advisory foi encontrado?

Abra o ID RUSTSEC e leia o texto original. Identifique:

  • natureza da falha;
  • versões vulneráveis;
  • pré-condições para exploração;
  • existência de patch;
  • severidade e impacto descritos;
  • referências técnicas.

Um título alarmante sem contexto pode levar a uma migração perigosa. O inverso também é verdadeiro: um advisory sem pontuação alta pode ser crítico para um produto que expõe exatamente a superfície afetada.

2. Quem trouxe o crate?

Se o pacote não aparece diretamente no Cargo.toml, descubra o caminho inverso:

cargo tree -i nome-do-crate

Em workspaces ou grafos com múltiplas versões, use também:

cargo tree -d
cargo tree -p nome-do-crate

O resultado pode mostrar que uma dependência direta antiga prende o crate vulnerável, ou que uma feature opcional habilita um backend desnecessário. O guia de gerenciamento de dependências explica como ler e reduzir esse grafo.

3. O código vulnerável é alcançável?

Alcançabilidade muda a prioridade, mas não apaga o advisory. Pergunte:

  • o crate entra no binário de produção ou apenas em testes?
  • a feature afetada está habilitada?
  • entradas não confiáveis alcançam a função vulnerável?
  • o alvo afetado é compilado no seu sistema operacional?
  • existe isolamento ou limite que reduz o impacto?

“Não usamos essa função” precisa de evidência, não de intuição. Dependências transitivas podem mudar, features podem ser unificadas e caminhos aparentemente inativos podem entrar em builds futuros.

4. Há uma versão corrigida compatível?

Se houver patch dentro do intervalo já permitido, tente uma atualização focada:

cargo update -p nome-do-crate
cargo audit
cargo test --locked

Revise o diff do Cargo.lock. Uma atualização focada ainda pode alterar pacotes relacionados, especialmente quando o resolvedor precisa satisfazer restrições compartilhadas.

Corrigindo uma dependência direta

Para dependência direta, prefira atualizar a restrição de forma explícita no Cargo.toml, regenerar o lockfile e validar o comportamento.

Exemplo conceitual:

[dependencies]
biblioteca = "1.4"

Depois:

cargo update -p biblioteca
cargo test --all-targets --locked
cargo audit

Se a correção exige uma versão principal nova, leia o changelog e trate como migração. Não use cargo update como substituto de revisão de API. Testes de integração são especialmente importantes para crates de parsing, criptografia, serialização, TLS, banco de dados e runtimes assíncronos.

Em bibliotecas publicadas, confira também sua MSRV com cargo-msrv. Uma versão segura da dependência pode exigir um compilador mais novo; isso pode ser aceitável, mas precisa virar uma decisão documentada em vez de uma quebra acidental para consumidores.

Corrigindo uma dependência transitiva

Uma dependência transitiva exige localizar o pacote “pai”. As opções mais seguras, em ordem aproximada, são:

  1. atualizar a dependência direta que introduz o pacote;
  2. desabilitar uma feature que traz um componente não utilizado;
  3. trocar o backend ou a dependência direta por uma alternativa mantida;
  4. colaborar com o projeto upstream para liberar uma versão corrigida;
  5. aplicar um patch temporário com origem e revisão bem controladas.

Exemplo de investigação:

cargo tree -i crate-vulneravel
cargo tree -e features -i crate-vulneravel

A visualização de features é útil quando o pacote entra somente por uma integração opcional. Remover a feature pode reduzir risco e tempo de compilação ao mesmo tempo.

Evite adicionar uma dependência direta artificial apenas para “forçar” uma versão transitiva sem entender as restrições. O resolvedor pode manter duas versões no grafo ou a solução pode quebrar na próxima atualização.

Quando um patch Git for inevitável, fixe um commit revisado e abra uma tarefa para voltar a uma release normal. Dependências por branch móvel transferem controle do seu build para mudanças futuras do repositório remoto.

Vulnerabilidade, pacote não mantido e outros avisos

Nem todo aviso representa uma vulnerabilidade explorável naquele instante. O RustSec também registra situações como crate não mantido, sem manutenção ativa ou com recomendação de migração.

A triagem deve separar:

  • vulnerabilidade conhecida: há um comportamento de segurança descrito e versões afetadas;
  • unmaintained: o projeto não recebe manutenção adequada;
  • unsound: existe risco de violar garantias de segurança de memória em determinadas condições;
  • yanked: uma versão foi retirada do índice, o que pede investigação, mas não prova vulnerabilidade por si só;
  • notice informativo: existe contexto relevante sem a mesma natureza de uma falha confirmada.

Pacote não mantido continua sendo risco de supply chain. Ele pode não exigir um hotfix hoje, mas merece plano de substituição antes que uma falha futura fique sem resposta. A política da equipe pode dar prazos diferentes para cada categoria sem transformar todos os avisos em bloqueio imediato.

Integrando cargo-audit à CI

O job mais simples instala uma versão conhecida da ferramenta e executa a auditoria antes do merge:

name: security-audit

on:
  pull_request:
  push:
    branches: [main]
  schedule:
    - cron: "17 6 * * *"

jobs:
  cargo-audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: Instalar cargo-audit
        run: cargo install cargo-audit --locked
      - name: Auditar dependências
        run: cargo audit

O exemplo usa GitHub Actions, mas a lógica é igual em Gitea Actions, GitLab CI, Jenkins ou outra plataforma. Para uma CI eficiente:

  • fixe ou controle a versão instalada do cargo-audit;
  • use cache da ferramenta quando isso não esconder atualizações da base;
  • mantenha o Cargo.lock disponível;
  • execute em pull requests e também em agenda;
  • proteja a branch para não ignorar o job;
  • configure atualização automática de dependências em outro fluxo;
  • registre o resultado como artefato apenas quando houver necessidade operacional.

A execução agendada é indispensável. Seu código pode ficar parado por semanas enquanto um novo advisory é publicado para uma versão já presente no lockfile. Auditar somente quando alguém abre um pull request cria um período cego.

Para acelerar o restante da esteira, combine a auditoria com cargo-nextest e cache de compilação com sccache. Segurança não precisa ocupar o mesmo job dos testes, mas todos os checks obrigatórios devem convergir antes do merge.

Como lidar com exceções sem esconder risco

Às vezes não existe versão corrigida ou a migração não cabe no mesmo dia. A pior resposta é desativar a auditoria inteira. Uma exceção temporária precisa conter:

  • ID exato do advisory;
  • pacote e caminho no grafo;
  • justificativa técnica;
  • análise de alcançabilidade;
  • mitigação existente;
  • responsável pela decisão;
  • data de expiração;
  • link para issue de acompanhamento.

O cargo-audit oferece mecanismos para ignorar advisories específicos. Consulte o help da versão usada antes de configurar a sintaxe:

cargo audit --help

A exceção deve ser a menor possível: ignore um ID conhecido, nunca todos os avisos. Faça o pipeline imprimir os ignores ativos e falhar quando o prazo acordado terminar por meio de uma checagem auxiliar ou processo de revisão.

Um exemplo de registro no repositório:

Advisory: RUSTSEC-AAAA-NNNN
Motivo: crate presente apenas em ferramenta interna sem entrada não confiável
Mitigação: job isolado, sem credenciais e sem acesso à rede de produção
Prazo: remover ou atualizar até 2026-09-15
Responsável: equipe de plataforma

Não confunda “risco aceito” com “problema resolvido”. O advisory continuará verdadeiro até que o pacote seja atualizado, removido ou substituído.

cargo-audit versus cargo-deny

As ferramentas se complementam:

Necessidadecargo-auditcargo-deny
Advisories do RustSecSimSim, por política configurável
Auditoria rápida do lockfileExcelentePossível
Política de licençasNãoSim
Bloquear fontes Git desconhecidasNãoSim
Detectar versões duplicadasNão é o focoSim
Allowlist/denylist de cratesNão é o focoSim
Primeira adoção simplesMuito diretaExige configuração maior

Comece com cargo-audit se a necessidade imediata é não perder vulnerabilidades conhecidas. Adote cargo-deny quando a organização precisa formalizar licenças, origens, duplicidade e políticas de advisories em um arquivo versionado. Não rode duas ferramentas sem definir quem é responsável por cada regra, pois alertas duplicados e comportamentos diferentes geram fadiga.

O que cargo-audit não detecta

Uma pipeline verde ainda pode conter riscos como:

  • código vulnerável que ainda não recebeu advisory;
  • dependência maliciosa recém-publicada;
  • conta de mantenedor comprometida;
  • script de build executando comportamento indesejado;
  • secret vazado no repositório ou na imagem;
  • configuração insegura de TLS, autenticação ou autorização;
  • uso incorreto de uma API segura;
  • artefato de produção diferente do lockfile auditado;
  • pacote obtido de uma fonte não revisada;
  • vulnerabilidade no sistema operacional ou na imagem base.

Por isso, a defesa precisa incluir revisão de código, testes, atualização contínua, menor privilégio na CI, assinatura ou proveniência de artefatos quando aplicável, inventário de componentes e observabilidade em produção. Rust reduz classes importantes de bugs de memória, mas não elimina falhas lógicas nem risco de cadeia de suprimentos. Veja também a visão geral de segurança em Rust.

Checklist para colocar em produção

Antes de declarar a auditoria implantada, confirme:

  • o Cargo.lock auditado é o mesmo usado no build;
  • cargo audit roda em pull requests;
  • existe execução diária ou semanal agendada;
  • a branch principal exige o check;
  • a versão da ferramenta é controlada;
  • o pipeline consegue atualizar a base de advisories;
  • há processo para encontrar o dono da dependência;
  • atualizações passam por testes e revisão do lockfile;
  • ignores possuem justificativa, responsável e prazo;
  • alertas informativos têm política própria;
  • dependências Git usam commits fixos quando inevitáveis;
  • cargo-deny, SBOM e scanners de imagem cobrem outras camadas;
  • o time sabe que “0 vulnerabilidades conhecidas” não significa “0 risco”.

Perguntas frequentes

Para que serve o cargo-audit?

O cargo-audit compara as versões exatas no Cargo.lock com advisories da base RustSec. Ele identifica correspondências conhecidas e apresenta contexto para atualização ou mitigação.

Como instalar e executar?

cargo install cargo-audit --locked
cargo audit

Execute na raiz do projeto e automatize o mesmo comando na CI.

Ele funciona sem Cargo.lock?

O fluxo normal depende do lockfile. Se necessário, gere um com cargo generate-lockfile, audite e defina uma política consistente para aplicações e bibliotecas.

Ele corrige vulnerabilidades automaticamente?

A ferramenta aponta o problema; a equipe atualiza ou substitui a dependência, regenera o lockfile e valida o software. Correção automática sem testes pode trocar uma vulnerabilidade conhecida por uma regressão operacional.

Ele substitui cargo-deny?

Não. cargo-audit é uma porta de entrada direta para advisories. cargo-deny aplica uma política mais ampla sobre advisories, licenças, fontes e duplicidade. Muitos projetos usam ambos com responsabilidades bem definidas.

Conclusão

O melhor uso do cargo-audit é simples e disciplinado: auditar o lockfile real, rodar continuamente, investigar o caminho da dependência e tratar exceções como dívida com prazo. A ferramenta entrega um sinal objetivo sobre vulnerabilidades já conhecidas; seu valor depende da rapidez e da qualidade da resposta da equipe.

Comece hoje com cargo audit, corrija o primeiro alerta de maneira focada e coloque o comando em um job agendado. Depois amplie a cobertura com cargo-deny e SBOM, testes automatizados e uma política de atualização de dependências. Assim, segurança deixa de ser uma varredura ocasional antes da release e passa a fazer parte do ciclo normal de desenvolvimento Rust.