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: blockretorna 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
expiressó aceitaYYYY-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_idefile, 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).