Pular para conteúdo

AUR-495 — Dependência alterada no PR checada contra o OSV em qualquer ecossistema

Medido antes (pela leitura)

  • Nenhum código lia dependências de um diff. A única menção a OSV era o artefato de dados de análise (AUR-533, internal/artifacts), que publica uma cópia OSV mas não tinha consumidor ("quando um consumidor a usar (AUR-495)", docs/configuration.md).
  • O registro de scanners (internal/scanner) é feito para engines determinísticas sobre a árvore, sem acesso ao modelo; o card pede que o modelo extraia os pacotes e que o scanner seja só conferência.
  • O card cita internal/analysis nos paths; a lógica nova ficou no pacote novo internal/dependencies (citado em docs/architecture.md, exigência do teste estrutural), com o contribuidor em internal/gate e a ligação em cmd/aurumcode, a pedido do coordenador do stream (reaproveitar a arquitetura de fontes e gate).

Decisão

  • internal/dependencies:
  • extract.go: duas perguntas ao modelo (templates Markdown embutidos em prompts/): quais arquivos alterados são manifestos/lockfiles (só paths do diff são aceitos) e quais pacotes mudaram neles (ecossistema, nome, versões ou faixas, explicação, unresolved). Resposta em JSON validada.
  • ground.go: aterra a resposta no diff (AC-008): manifesto dado ao modelo, nome presente no diff do arquivo, cada versão/faixa presente nas linhas alteradas do lado certo; o resto é descartado e declarado.
  • conference.go: Extractor sobre todos os paths alterados; produção OSVScanner roda uma vez osv-scanner scan source --format json --all-packages --recursive <checkout> pelo scanner.Command (ambiente mínimo, scanner.Timeout) e guarda os pacotes dos arquivos alterados. Arquivo reconhecido que o modelo não apontou entra nos manifestos e a omissão é declarada (o modelo nunca remove evidência determinística); versão nova que o scanner não lista é divergência declarada; pacote que o scanner extrai das linhas adicionadas e o modelo omitiu é inconclusivo (dependencies_extraction_gap). Binário ausente ou saída ilegível = inconclusivo (exit 128, "nenhum pacote", é vazio).
  • osv.go: cliente da API OSV (POST /v1/query, paginação limitada, corpo limitado, advisories retirados ignorados). Severidade da fonte (database_specific.severity), versões corrigidas do pacote consultado, link ADVISORY/WEB ou o registro da fonte. Frescor opcional pelo Date+Age da resposta (max_source_age_hours); sem Date com limite declarado = vencida.
  • classify.go: os dois lados consultados (cache por execução); advisory só no head = introduzido, nos dois = pré-existente, só na base = corrigido; identidade por id ou alias. Faixa sem versão consulta o pacote inteiro. Qualquer falha descarta todos os achados e põe o motivo.
  • internal/config/dependencies.go: seção dependencies (ponteiro; nil = não declarada, nada muda), validada no parse e governada pela política central (governedSections). Declarada, o padrão de gate.inconclusive passa a block (scannerEnabled).
  • internal/gate/dependencies.go: DependenciesContributor, nono do pipeline; inconclusivo segue ApplyInconclusiveMode; achados são informativos neste card (o gate é o AUR-527).
  • cmd/aurumcode/dependency_check.go: roda no fechamento da evidência (joinEvidence de --base e --pr), depois da seleção do provedor, sobre o mesmo checkout e recusa dos scanners (scanRoot/scanBlocked). Seção declarada e verificação que não rodou = dependencies_not_run.
  • Dockerfile: estágio que instala osv-scanner v2.0.0 por go install (verificado pelo checksum database do Go) e confere a versão.
  • Docs: seção nova em docs/configuration.md com link em docs/index.md; corrigida a frase velha da seção analysis_data que dizia que o AUR-495 consumiria a cópia OSV (a verificação usa a API ao vivo).

Como cada AC/MUT é provado

tests/acceptance/AUR-495.sh (pacotes internal/dependencies, internal/gate, internal/config; base OSV = httptest servindo internal/dependencies/testdata/osv-fake.json; modelo e scanner falsos):

  • AC-001 TestAUR495AC001IntroducedAdvisory, TestAUR495AC001FindingLine (+ parse/governança da seção).
  • AC-002 TestAUR495AC002Preexisting.
  • AC-003 TestAUR495AC003Fixed.
  • AC-004 TestAUR495AC004UnknownFormatAndDivergence, TestAUR495AC004ScannerEvidenceKept (arquivo reconhecido e não apontado é lido; pacote omitido pelo modelo é inconclusivo).
  • AC-005 TestAUR495AC005Range.
  • AC-006 TestAUR495AC006SourceFailures (HTTP 503, scanner ausente, Date velho, sem modelo, modelo falhando), TestAUR495AC006InconclusiveFollowsPolicy.
  • AC-007 TestAUR495AC007Monorepo.
  • AC-008 TestAUR495AC008InventedDependencyDiscarded.
  • MUT-001: return nil, err da consulta vira return nil, nil (falha lida como lista vazia): AC-006 fica vermelho.
  • MUT-002: status = StatusPreexisting vira StatusIntroduced: AC-002 fica vermelho.

Riscos e fora do escopo

  • Código escrito sem compilar (regra do stream); a prova roda no fluxo único.
  • A severidade vem só da palavra da fonte; advisory sem ela é unknown e o gate (AUR-527) a conta no topo (falha fechada). Cálculo CVSS próprio fora.
  • Os flags do osv-scanner v2.0.0 (--all-packages, --recursive) e o source.path absoluto da saída JSON não foram executados aqui.
  • Arquivos fora dos paths do card: internal/dependencies (pacote novo), internal/gate (contribuidor), docs/architecture.md (pacote e pipeline), docs/index.md (link da seção nova).
  • Os avisos de cmd/aurumcode/dependency_check.go são só em pt-BR, fora do catálogo internal/i18n.
  • Inconclusivos esperados em PRs reais (falha fechada, documentados em docs/configuration.md): diff de manifestos acima de 256 KiB (dependencies_manifests_omitted) e osv-scanner --recursive acima de 120 s num repositório grande (dependencies_scanner_failed).

Correções da revisão estática

  • Saída do osv-scanner: só 0 e 1 trazem relatório; 128 ("no packages found", códigos de retorno documentados do osv-scanner) é extração vazia; qualquer outro código, tempo esgotado ou processo morto é dependencies_scanner_failed, mesmo com JSON (exitCode, TestScannerExitCodesAndRelativeRoot).
  • Raiz relativa (--repo .): filepath.Abs antes de casar os caminhos do relatório.
  • Quando o scanner lista o pacote, a versão e o ecossistema dele são os consultados (alignWithScanner, TestScannerVersionAndEcosystemWin).
  • Severidade pelo vetor CVSS v3 quando o banco não traz uma (cvss.go, fórmula da especificação FIRST; v4 fica unknown, TestCVSSSeverity).
  • Registros do mesmo advisory em bancos diferentes viram um (mergeAliases, TestAliasesMerged).
  • dependencies.scanner só nome no PATH ou caminho absoluto; osv_url e deps_dev_url só https (http em loopback) (TestDependenciesSourcesAndScannerValidated).
  • Linhas do gate e avisos da revisão no catálogo internal/i18n (pt-BR e en), detalhe do inconclusivo redigido (TestDependencyLinesFollowLanguage, TestDependencyNoticesRedactDetail).
  • Dockerfile: o estágio do osv-scanner fixa GOPROXY, GOSUMDB, GOFLAGS=-mod=readonly e GOTOOLCHAIN=local; a integridade é a do checksum database do Go sobre as fontes, em vez de um sha256 de binário de release.