AUR-623 — Plugin agendado em repositório privado sem Code Security: achados viram issues
Problema
Decisão do dono (2026-10-14): em repositório privado sem GitHub Code Security não há aba Security para alertas de código; os achados do plugin agendado viram issues do próprio repositório, visíveis só a colaboradores. Em repositório público, nunca: só a aba Security. Até aqui (AUR-622) o job decidia o destino e falhava antes de rodar o plugin quando ele seria issues.
Desenho
| arquivo | responsabilidade |
|---|---|
internal/plugin/issues_findings.go |
Lê o SARIF do ingest. Ausente, malformado, execução sem sucesso ou categoria de outro plugin: inconclusivo. Redige de novo cada texto e deduplica por fingerprint. |
internal/plugin/issues_body.go |
Rótulo aurumcode-plugin:<nome>, marcador <!-- aurumcode-plugin:<nome>:<fingerprint> --> (fingerprint fora de [A-Za-z0-9._-]{1,128} vira sha256), lido só da primeira linha do corpo; título só com campos estruturados; corpo pt-BR com a descrição redigida num bloco de código (crase vira ˋ). Título acima de 256 caracteres encurta o caminho pela esquerda; corpo acima de 65536 corta a descrição com aviso, sempre do mesmo jeito: limite externo nunca derruba a sincronização. |
internal/plugin/issues_github.go |
Cliente HTTP mínimo (GITHUB_TOKEN, AURUMCODE_GITHUB_API_URL validado por config.GitHubAPIURL): visibilidade, rótulo, listagem paginada até página vazia (sem teto), criar, atualizar, fechar, comentar. Erro só com o código HTTP. |
internal/plugin/issues_ratelimit.go |
429 ou 403 do limitador (Retry-After, X-RateLimit-Remaining: 0): espera o pedido e reenvia (a escrita recusada não duplica), como o cliente de PR. |
internal/plugin/issues_sync.go |
Plano puro (abrir, atualizar, reabrir, fechar) e aplicação; issue criada sem o rótulo reprova (seria duplicada a cada varredura). Fechar deixa um comentário com <!-- aurumcode-plugin:<nome>:fechado -->. |
internal/plugin/issues_history.go |
Quem fechou: o ator do último evento closed (eventos e comentários paginados, sem teto). É o plugin se for o autor da issue ou o autor de um comentário-marcador posterior à última reabertura; outro ator = dispensada por pessoa (fica fechada, sem escrita). Eventos ilegíveis ou sem closed: reabre (falha fechada: achado presente nunca some sem decisão humana). Marcador forjado só pode levar a reabrir, nunca a esconder. |
internal/plugin/issues_run.go |
Ordem: SARIF local (inconclusivo: nenhuma chamada) → visibilidade (private/internal e private: true, senão recusa antes de escrever) → rótulo → listagem → plano. Saída só com contagens. |
cmd/aurumcode/cmd_plugin.go |
Verbo issues (só flags). |
.github/workflows/review.yml |
"Choose destination" grava destino; upload-sarif e a contagem só em security; "Sync issues" só em issues após ingest conclusivo; issues: write documentado para o chamador. |
demo/tutoriais/plugin-agendado/ |
Casos issues (três varreduras contra github-falso-issues.py) e issues-publico (recusa e inconclusivo sem chamada). |
Aceite
tests/acceptance/AUR-623.sh: AC-001..AC-005; MUT-001 (a checagem de
visibilidade aceitar qualquer repositório derruba AC-003) e MUT-002 (o SARIF
inconclusivo virar zero conclusivo, fechando issues, derruba AC-003).
TestAUR622WorkflowPluginsJob passou a exigir o if: do upload com o
destino security (antes, exatamente success()).
Pendências declaradas
- Sem execução contra o GitHub real neste card: o comportamento da API
(rótulo criado antes da issue,
state=allcom rótulo) segue a documentação pública e foi provado contra o GitHub falso. - Se a identidade do plugin mudar (outro token) e a nova fechar issues abertas pela antiga, o comentário-marcador da nova identidade, escrito logo antes do fechamento, a reconhece; uma pessoa que forja o marcador e fecha ela mesma faz a issue ser reaberta (ruído, nunca achado escondido).
- Repositório com Issues desativadas: a sincronização falha (código HTTP) a cada execução e nada é publicado em outro canal.
- Uma falha da API no meio da sincronização para na primeira escrita que falhou; a varredura seguinte converge do estado em que o repositório ficou.