Pular para conteúdo

AUR-520: exceção aprovada pela segurança, com dono e validade

Contrato

exceptions (internal/config/exceptions.go) é uma lista, no mesmo config.yml do repositório ou da política central, de exceções já aprovadas para UM achado exato: repo (owner/repo), rule (um id de regra dinâmica <skill>#<slug> do AUR-519, ou qualquer outro rule_id/advisory id), path (caminho exato, nunca um glob), owner, reason e expires (YYYY-MM-DD). Os seis campos são obrigatórios; qualquer um ausente, ou uma expires malformada, é um erro de configuração antes de qualquer chamada ao modelo (AC-005).

O casamento (cmd/aurumcode/aur520.go, matchException) roda dentro da avaliação do gate (evaluateGate, policygate.go): um achado só é excepcionado quando repo, rule e path casam exatamente e a data de hoje (sempre normalizada para UTC, truncateToUTCDate) é menor ou igual à expires do exceção. Um achado excepcionado nunca entra no cálculo de Breach/Fail e aparece nas linhas publicadas como "aceito por exceção", com dono/motivo/validade (AC-001). Uma exceção vencida não aplica mais: o achado segue para a avaliação normal e a saída diz que a exceção venceu (AC-002). Repo/rule/path diferentes nunca casam (AC-003).

Sob política central, ApplyCentralPolicy (internal/config/central.go) governa Exceptions exatamente como já governa Rules/Ignore/Gate: só as exceções da política aplicam; toda exceção do repositório é descartada com um aviso nomeado (AC-004).

A identidade do repositório usada no casamento nunca vem do modelo ou do diff: no --pr é owner/repoName, já autenticado pela chamada à API (cmd/aurumcode/pr.go); no --base é lida do remoto origin do checkout local via localRepoIdentity (aur520.go), reaproveitando os mesmos leitores read-only do AUR-515 (originRemoteURL/ ownerRepoFromRemoteURL, aur515.go). Quando a identidade não pode ser confirmada, nenhuma exceção com repo declarado casa (toda ExceptionConfig.Repo é obrigatória e não-vazia por Validate, então "" nunca é igual a nada) — falha fechado por construção, sem um ramo especial no código de casamento.

Checklist de robustez

  • Uma exceção nunca transforma uma revisão inconclusiva em aprovação: gate.inconclusive: block retorna antes do laço de exceções/limiar (TestAUR520EvaluateGateInconclusiveBlockNeverAppliesException).
  • Uma exceção nunca aplica a uma regra/caminho/repositório diferente (comparação exata, TestAUR520MatchException*).
  • A validade não pode ser contornada por fuso horário ou truque de formato: a comparação normaliza ambos os lados para a data UTC (TestAUR520MatchExceptionTimezoneCannotBypassExpiry); o parser de expires só aceita YYYY-MM-DD, recusando RFC3339/offsets/outros formatos no carregamento da config (TestAUR520ExceptionConfigValidateRejectsMalformedExpires). MUT-001 (tests/acceptance/AUR-520.sh) prova isso também em caixa-preta: anular a comparação de validade faz AC-002 falhar (RED).
  • Um campo controlado pelo modelo (texto livre do achado: message, evidence, etc.) nunca faz um achado parecer excepcionado — só rule_id e file, os dois campos estruturados, entram no casamento (TestAUR520EvaluateGateIgnoresModelFreeTextFields).
  • Sem exceções configuradas, o comportamento é byte-idêntico ao AUR-519 (TestAUR520EvaluateGateNoExceptionsIsByteIdentical).

Estado desta entrega

Implementado e ligado de ponta a ponta em --base (runReview) e --pr (runPRReview), com testes de comportamento reais pelo comando (não só unitários) para AC-001 a AC-005 e o checklist acima. Não implementado: qualquer fluxo de aprovação fora deste arquivo de configuração, e exceção por regra inteira sem caminho (ambos os Non-goals do card).