Requisitos Não Funcionais¶
Este documento especifica os Requisitos Não Funcionais (RNF) do Quorum
(quorum-sec-scan), versão v0.8.3 (revisão de 2026-07-04). O Quorum é uma
ferramenta de consensus security scanning distribuída como CLI e imagem
Docker — sem frontend web, sem banco de dados, sem API REST e sem autenticação
de usuário. Ele orquestra um pool de 12 scanners OSS (Trivy, Grype, Checkov,
KICS, Dockle, Kubescape, Polaris, kube-score, Terrascan, tfsec, Regula e
Conftest), normaliza tudo para um modelo canônico (model.Finding), resolve
aliases de vulnerabilidade, correlaciona findings equivalentes, calcula um score
de confiança (consenso) e emite SARIF/JSON/XML.
O núcleo determinístico não tem IA. A partir da v0.8.3 existe uma camada de
aconselhamento opt-in (habilitada com --advice) que é apenas de
apresentação: ela nunca toca em correlationKey, Fingerprint, confidence,
severidade agregada ou no gate --fail-on. Sem --advice a saída é
byte-idêntica às releases anteriores, e todo anexo de IA é rotulado como
"AI-generated, advisory only". Veja 13-ia.md e
21-proposta-ia.md para o enquadramento completo; o impacto
dela nos RNF (determinismo, egresso, cadeia de suprimentos) é destacado ao longo
do texto abaixo.
Por ser um binário/imagem de execução efêmera (não um serviço de longa duração), vários RNF clássicos de produtos SaaS não se aplicam diretamente. Onde for o caso, o requisito é declarado explicitamente como N/A com justificativa técnica, e — quando agregar valor — acompanhado de uma Proposta futura claramente separada. A natureza arquitetural do produto está descrita em 01-visao-geral.md e 04-arquitetura.md; o modelo de dados, a matriz de correlação e a matemática do consenso estão em DESIGN.md.
Convenção de criticidade dos RNF: P0 = essencial (release bloqueia se violado), P1 = importante, P2 = desejável.
1. Panorama dos RNF¶
mindmap
root((RNF Quorum))
Performance
Fan-out paralelo 12 scanners
Timeout por scanner
Probe de versão 60s
Grype DB pré-cacheado
Caps de DoS (output/target)
Escalabilidade
CLI stateless
Escala horizontal nos runners
Disponibilidade
N/A servico
Distribuicao GHCR/Releases
Seguranca
Supply chain
Imagens assinadas cosign
SLSA provenance
SBOM SPDX atestado
Atestacao do knowledge pack
Pin por sha256
Sem segredos
Observabilidade
Logs stderr text/json
Metricas Prometheus
Status por scanner
Auditoria
Relatorio como evidencia
Fingerprints
Conformidade
LGPD
PCI DSS
ISO 27001
OWASP ASVS / Top 10
Resiliencia
Degradacao graciosa
Retry cosign
Grype DB nao expira
Continuidade
Stateless
Artefatos reproduziveis
| # | Categoria | Aplicabilidade | Prioridade | Resumo |
|---|---|---|---|---|
| 2 | Performance | Aplicável | P0 | Paralelismo, timeouts, DB pré-cacheado, caps de DoS |
| 3 | Escalabilidade | Aplicável | P1 | CLI stateless, escala horizontal em CI |
| 4 | Disponibilidade | Reinterpretado | P1 | Não é serviço; vale a distribuição |
| 5 | Segurança | Aplicável | P0 | Supply chain, assinaturas, SBOM, sem segredos |
| 6 | Observabilidade | Aplicável | P1 | Logs stderr (text/json), métricas Prometheus, status por scanner |
| 7 | Auditoria | Aplicável | P1 | Relatório como evidência, fingerprints |
| 8 | LGPD | Aplicável (por exclusão) | P1 | Sem PII processada |
| 9 | PCI DSS | N/A como produto | P2 | Pode auxiliar compliance |
| 10 | ISO 27001 | Mapeamento parcial | P2 | Controles aplicáveis ao binário/pipeline |
| 11 | OWASP ASVS | Subconjunto CLI | P1 | Controles relevantes a um CLI |
| 12 | OWASP Top 10 | Aplicável ao próprio artefato | P0 | Risco do binário/imagem, não do alvo |
| 13 | Resiliência | Aplicável | P0 | Degradação graciosa |
| 14 | Backup/DR/RTO/RPO | N/A (stateless) | P2 | Artefatos reproduzíveis |
| 15 | SLA/SLO/SLI | Definido | P1 | Uso em CI e pipeline de release |
2. Performance¶
O Quorum é dominado por I/O e por subprocessos (cada scanner é um binário
externo invocado via exec). O tempo de parede de um scan é, na prática,
max(tempo de cada scanner) + overhead de correlação/consenso, porque os
adapters rodam em paralelo. A camada de aconselhamento opt-in (--advice) roda
depois do pipeline determinístico e está desligada por padrão, então nunca
entra nesse caminho crítico a menos que seja explicitamente solicitada.
2.1 Paralelismo (fan-out)¶
O orquestrador dispara uma goroutine por adapter e agrega os resultados sob
mutex (internal/orchestrator/orchestrator.go, Run). São 12 adapters
registrados em internal/adapter (trivy, grype, checkov, kics, dockle,
kubescape, polaris, kube-score, terrascan, tfsec, regula, conftest); apenas os
que suportam o tipo de alvo entram no fan-out:
flowchart LR
T[target] --> O[Orchestrator.Run]
O -->|goroutine| A1[trivy.Run]
O -->|goroutine| A2[grype.Run]
O -->|goroutine| A3[checkov.Run]
O -->|goroutine| A4[kics.Run]
O -->|goroutine| A5[dockle.Run]
O -->|goroutine| A6[kubescape.Run]
O -->|goroutine| A7[polaris.Run]
O -->|goroutine| A8[kube-score.Run]
O -->|goroutine| A9[terrascan.Run]
O -->|goroutine| A10[tfsec.Run]
O -->|goroutine| A11[regula.Run]
O -->|goroutine| A12[conftest.Run]
A1 & A2 & A3 & A4 & A5 & A6 & A7 & A8 & A9 & A10 & A11 & A12 --> W[wg.Wait + mutex agrega]
W --> C[Correlator.Enrich]
C --> M[consensus.Merge]
M --> R[Reporter SARIF/JSON/XML]
Implicações de performance:
- Escalonamento por scanner mais lento, não pela soma. Adicionar um scanner rápido a um conjunto que já contém um lento não altera muito o tempo total. Com 12 scanners, o gargalo típico continua sendo os processos Python/pesados (Checkov, KICS) e não o número de adapters em si.
- A fase de correlação/consenso é in-process e CPU-leve (agrupamento por
correlationKeyem mapas,sha256por chave) — desprezível frente aos scanners. - A resolução de aliases pode adicionar latência de rede (OSV.dev). Mitigada
por cache local (
~/.cache/quorum/aliases.json) e desligável com--offline.
2.2 Timeout por scanner¶
Dois orçamentos de tempo distintos protegem o pipeline contra travamentos:
| Parâmetro | Default | Onde | Efeito ao estourar |
|---|---|---|---|
--timeout (PerScannerTime) |
5m | cmd/quorum/scan.go, Options.PerScannerTime |
Scanner marcado timeout; demais continuam |
ProbeTime (probe de versão) |
60s | orchestrator.defaultProbeTime |
Scanner marcado unavailable com diagnóstico |
O probe de versão de 60s é deliberadamente generoso: ferramentas pesadas (ex.: Checkov, um processo Python) demoram a inicializar a frio, especialmente quando os 12 scanners sobem ao mesmo tempo num runner com pouca memória. O orquestrador distingue três falhas do probe e emite mensagem acionável:
- timeout do probe → "tool too slow to start or resource-starved (give the
container more memory, or scope
--scanners)"; - killed (OOM) —
signal: killed→ "likely out of memory; raise the container's memory limit"; - não instalado → "not installed/available".
Um timeout de execução nunca derruba o scan inteiro: o status do scanner vira
timeout/error e o relatório registra isso explicitamente — "0 findings is
not proof of safety".
2.3 Grype DB pré-cacheado¶
A imagem :full (Dockerfile.full) congela a base de vulnerabilidades do Grype
em build time:
ENV GRYPE_DB_CACHE_DIR=/opt/grype/db \
GRYPE_DB_AUTO_UPDATE=false \
GRYPE_DB_VALIDATE_AGE=false
RUN grype db update && grype db status
Benefícios diretos de performance e resiliência:
- O primeiro scan funciona offline e não falha com "failed to load vulnerability db: database does not exist".
- Elimina o download da DB no caminho crítico de cada job de CI.
GRYPE_DB_VALIDATE_AGE=falseé obrigatório aqui: sem isso o Grype recusaria uma DB com mais de 5 dias (db.max-allowed-built-age) e falharia todo scan poucos dias após o build. A DB baked é aceita por design (não expira); a atualização é um rebuild ouGRYPE_DB_AUTO_UPDATE=trueem runtime (requer rede).- Trade-off documentado: a DB é congelada na data do build — a frescor é uma escolha de supply chain, não um crash.
2.4 Caps de DoS (limites de recurso)¶
Dois limites protegem o processo Quorum contra alvos/outputs patológicos (R2/R5), independentemente da confiabilidade do scanner:
| Cap | Default | Env de override | Comportamento ao estourar | Onde |
|---|---|---|---|---|
| Stdout por scanner | 512 MiB | QUORUM_MAX_OUTPUT_BYTES (bytes) |
aborta o adapter com erro (evita OOM ao bufferizar) | internal/adapter/adapter.go, runCmd/capWriter |
| Tamanho do alvo (repo/k8s) | 20 GiB | QUORUM_MAX_TARGET_BYTES (bytes; 0 desabilita) |
recusa cedo o scan com mensagem acionável | cmd/quorum/scan.go, checkTargetSize |
- O cap de output usa um
capWriterque corta a leitura assim que o teto é cruzado — um scanner que despeje gigabytes de JSON não derruba o processo. - O cap de alvo faz um
filepath.WalkDircom early-abort: assim que a soma dos arquivos regulares ultrapassa o teto, a varredura para (repos normais pagam só um passo leve destat). Alvos do tipo imagem (sem árvore local) são pulados.
2.5 Metas de performance (SLO de produto)¶
Metas-alvo; ainda não validadas por benchmark formal (ver Gaps).
| Métrica | Meta | Condição | Prioridade |
|---|---|---|---|
| Overhead do orquestrador (correlação+consenso+report) | ≤ 2s | ≤ 50k findings brutos | P1 |
| Tempo do probe de todos os scanners | ≤ 60s | imagem :full warm |
P0 |
| Latência de alias (cache hit) | ~0 (sem rede) | re-scan idempotente | P1 |
Tempo total de scan SCA (:full, imagem média) |
≤ tempo do scanner mais lento + 2s | runner 2 vCPU / 4 GB | P1 |
| Pico de memória do orquestrador (excluindo scanners) | ≤ 256 MB | ≤ 50k findings + caps de DoS ativos | P2 |
Checklist de performance:
- [ ] Rodar com
--offlineem CI quando a rede OSV não for desejada no caminho crítico. - [ ] Restringir
--scannersao necessário em runners pequenos (12 scanners no:full). - [ ] Persistir
~/.cache/quorum/aliases.jsonentre jobs de CI. - [ ] Garantir memória suficiente ao container
:full(Checkov/KICS são pesados). - [ ] Ajustar
QUORUM_MAX_OUTPUT_BYTES/QUORUM_MAX_TARGET_BYTESsó se um alvo legítimo exceder os defaults. - [ ] Manter
--advicedesligado (default) no caminho crítico; a camada de aconselhamento é pós-processamento e opcional.
3. Escalabilidade¶
3.1 CLI stateless¶
Cada execução do Quorum é autocontida e sem estado compartilhado:
- Nenhum estado é persistido entre execuções além de caches opcionais (aliases
em disco e — apenas sob
--advice— o cache de advice indexado porfingerprint+provider+model) que são idempotentes e reconstruíveis. - Não há servidor, sessão, fila ou coordenação entre processos.
- A saída (SARIF/JSON/XML) é função determinística das entradas (alvo +
scanners + crosswalk + baseline), com os
correlationKey/Fingerprintpuros. A camada de aconselhamento não altera isso: com--advicedesligado a saída é byte-idêntica e, mesmo ligada, os campos de correlação e consenso ficam intactos.
3.2 Escala horizontal nos runners de CI¶
flowchart TB
subgraph CI[Pipeline / CI fleet]
R1[Runner 1\nquorum scan repoA]
R2[Runner 2\nquorum scan repoB]
R3[Runner N\nquorum scan imageX]
end
R1 --> S1[(SARIF/JSON\nartefato)]
R2 --> S2[(SARIF/JSON\nartefato)]
R3 --> S3[(SARIF/JSON\nartefato)]
- A escala é embaraçosamente paralela: N execuções independentes em N runners, sem ponto central de contenção (exceto o registry/Releases na obtenção do artefato).
- O gargalo de escala é o recurso do runner (CPU/memória para os 12 scanners), não o Quorum em si.
- Dependência externa única na execução: OSV.dev para aliases — mitigável com
--offlineou cache compartilhado.
| Vetor de escala | Mecanismo | Limite |
|---|---|---|
| Mais repositórios/imagens | mais jobs/runners paralelos | capacidade da frota de CI |
| Mais scanners por scan | fan-out de goroutines (até 12) | memória do runner |
| Re-scans frequentes | cache de aliases | tamanho do cache (negligível) |
N/A: escalabilidade vertical de um serviço (auto-scaling de pods, sharding de banco, balanceamento de carga) — não há serviço de longa duração. Ver §4.
4. Disponibilidade¶
N/A como serviço. O Quorum não expõe endpoint, daemon ou processo de longa duração; portanto não há uptime de serviço a medir. O conceito é reinterpretado como disponibilidade da distribuição — a capacidade de obter e executar o artefato.
flowchart LR
Dev[Desenvolvedor / CI] -->|docker pull| GHCR[(GHCR\nghcr.io)]
Dev -->|download| REL[(GitHub Releases\nGoReleaser)]
Dev -->|api.osv.dev| OSV[(OSV.dev\nopcional)]
GHCR --> Run[quorum scan ...]
REL --> Run
OSV -. degradação graciosa .-> Run
| Dependência | Papel | Disponibilidade | Mitigação |
|---|---|---|---|
GHCR (ghcr.io) |
distribuição das imagens :full/:slim |
SLA do GitHub | pin por @sha256, mirror interno, cache de runner |
| GitHub Releases | binários nativos + checksums + assinaturas | SLA do GitHub | mirror interno, cópia versionada |
| OSV.dev | resolução de aliases (apenas VULN) | best-effort | --offline, cache local, degradação graciosa |
| Binários dos scanners | execução real | local (imagem :full, 12 scanners) ou PATH (:slim) |
:full já embute tudo |
Metas de disponibilidade de distribuição:
- [ ] Imagens publicadas de forma reprodutível a cada tag semver (
release.yml). - [ ] Possível operar 100% offline com
:full(DB do Grype pré-cacheada + crosswalk embutido +--offline). - [ ] Recomendação enterprise: espelhar as imagens em registry interno e pinar por digest.
Proposta futura (claramente separada): não há plano de transformar o Quorum em serviço hospedado; caso surja, esta seção passaria a definir uptime/SLA de serviço real.
5. Segurança¶
A superfície de segurança do Quorum tem duas frentes: (a) segurança do próprio artefato e da sua cadeia de suprimentos, e (b) higiene de execução (não manusear segredos). O alvo escaneado é dado de entrada — o Quorum só o lê.
5.1 Cadeia de suprimentos (supply chain)¶
flowchart LR
src[Código fonte\nGo 1.26] --> rel[release.yml]
rel --> img[Imagens :full / :slim]
rel --> bin[Binários GoReleaser]
rel --> kn[Knowledge pack + crosswalk]
img --> cosign[cosign sign --yes\nkeyless OIDC + retry]
bin --> cblob[cosign verify-blob\nchecksums.txt.sig]
img --> slsa1[attest-build-provenance\nSLSA v1]
img --> sbom1[attest-sbom\nSPDX atestado]
bin --> slsa2[attest-build-provenance\nsubject-checksums]
bin --> sbom2[GoReleaser + syft\nSBOM por binário]
kn --> slsa3[attest-build-provenance\nknowledge.sha256]
cosign --> ver[Verify no release + retry]
slsa1 --> ver
sbom1 --> ver
slsa2 --> ver
slsa3 --> ver
| Controle | Implementação | Evidência |
|---|---|---|
| Assinatura keyless de imagem | cosign sign via OIDC do GitHub (sem chaves), com retry (4 tentativas) |
release.yml (step "Sign image") |
| Atestação SLSA build-provenance | actions/attest-build-provenance@v2 (imagem por digest e binários por subject-checksums) |
release.yml |
| SBOM SPDX atestado | actions/attest-sbom@v2 para a imagem (syft SPDX-JSON) além do BuildKit sbom: true; GoReleaser+syft por binário |
release.yml, .goreleaser.yaml |
| Atestação do knowledge pack + crosswalk | actions/attest-build-provenance@v2 sobre knowledge.sha256 (todo arquivo do pack de aconselhamento + crosswalk); verificada no mesmo release |
release.yml (job knowledge) |
| Verificação no próprio release | gh attestation verify oci://…@DIGEST (com retry) falha o build se inválida |
release.yml (step "Verify provenance") |
| Assinatura de binários | checksums.txt + cosign verify-blob (.sig/.pem) |
README §Native binary |
| Pin de scanners por digest | FROM …@sha256: (Trivy, KICS, golang, alpine); Kubescape/tfsec/Terrascan/Regula/Conftest verificados por checksum SHA256 |
Dockerfile.full |
Bases pinadas por @sha256 |
imagens de build/runtime (golang:1.26-alpine, alpine:3.20) pinadas por digest |
Dockerfile.full |
| GitHub Action verifica antes de rodar | action.yml (composite) faz cosign-verify da :full por padrão (verify: true) |
action.yml |
| Trigger de release restrito | tags semver v[0-9]+.[0-9]+.[0-9]+ apenas |
release.yml |
Tag móvel v0/v0.x para pin do action |
avançada automaticamente a cada release semver; não dispara release | tag-major.yml, README §CI/CD |
THIRD_PARTY_NOTICES.md |
inventário de licenças de dependências/scanners | repositório |
Verificação esperada do consumidor (recomendado em produção):
cosign verify ghcr.io/martinez1991/quorum-sec-scan:full \
--certificate-identity-regexp \
"https://github.com/Martinez1991/quorum-sec-scan/.github/workflows/release.yml@.*" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
gh attestation verify oci://ghcr.io/martinez1991/quorum-sec-scan:full \
--repo Martinez1991/quorum-sec-scan
# Knowledge pack de aconselhamento (corpus OWASP pinado por digest + templates de remediação):
gh attestation verify knowledge/owasp/corpus.yaml \
--repo Martinez1991/quorum-sec-scan
Nota de risco honesta: o
Dockerfile.fullpina Trivy, KICS e as bases (golang/alpine) por@sha256, e verifica os demais scanners (Grype/Syft/Dockle/Kubescape/Polaris/kube-score/tfsec/Terrascan/Regula/Conftest) por checksum SHA256 dos artefatos oficiais de release. Converter todos para digest imutável de imagem é uma melhoria incremental (DESIGN §12). Ver Gaps.
5.2 Ausência de segredos¶
| Aspecto | Estado | Justificativa |
|---|---|---|
| Credenciais de usuário | inexistentes | sem contas/autenticação |
| Tokens de API próprios | inexistentes | OSV.dev é endpoint público sem chave |
| Passthrough de credencial de scanner | opt-in via QUORUM_<SCANNER>_ARGS (ex.: QUORUM_CHECKOV_ARGS --bc-api-key …) |
fornecido pelo operador, nunca ecoado nos logs (action.yml) |
| Chave do provedor de aconselhamento | apenas com --advice-provider=remote, via QUORUM_ADVICE_API_KEY (opt-in) |
encaminhada como env pelo action.yml; o remoto exige --advice-allow-egress, é bloqueado por --offline e recusa --fix (enviaria código-fonte) |
| Segredos em build/release | apenas GITHUB_TOKEN efêmero do runner |
escopo mínimo (contents, packages, id-token, attestations) |
| Persistência de segredos | nenhuma | nada é gravado além do cache de aliases (públicos) e do cache opt-in de advice (findings normalizados + texto de advice, sem código-fonte) |
| Detecção de segredos no alvo | via adapter trivy (TypeSecret) |
findings reportados, com o Match do Trivy redigido (redactSecretText), nunca persistidos fora do relatório |
Princípios de execução segura:
- [ ] O Quorum não solicita nem armazena credenciais.
- [ ] Logs vão para stderr e não devem conter segredos (o conteúdo de segredos detectados é redigido antes de sair no relatório).
- [ ] Permissões do workflow de release seguem least privilege.
- [ ] Imagens devem ser verificadas antes do uso (cosign + provenance + SBOM).
- [ ] O relatório é gravado com permissão restrita
0600e o caminho é normalizado comfilepath.Clean(emit,scan.go). - [ ] O provedor de aconselhamento remoto envia apenas o finding normalizado, nunca código-fonte; está desligado por padrão e exige consentimento explícito de egresso.
5.3 Modelo de ameaças resumido¶
| Ameaça | Vetor | Mitigação |
|---|---|---|
| Imagem adulterada | registry comprometido / tag mutável | assinatura cosign + SLSA + SBOM atestado + pin por digest |
| Binário de scanner trojanizado | upstream comprometido | pin por digest/checksum no build |
| Falso negativo silencioso | scanner não roda mas reporta 0 | status por scanner explícito (§6) |
| Bind mount malformado | -v errado escaneia /work vazio; imagem local invisível |
aviso na doc; action.yml auto-monta docker.sock em type: image |
| Argument injection | alvo iniciando com - interpretado como flag do scanner |
validateTargetRef recusa alvos que começam com - |
| DoS por output/alvo gigante | scanner despeja GBs / árvore patológica | caps QUORUM_MAX_OUTPUT_BYTES (512 MiB) e QUORUM_MAX_TARGET_BYTES (20 GiB) (§2.4) |
| Exfiltração de dados do alvo | rede em runtime | --offline; única chamada externa é OSV (somente IDs de vuln). O provedor de aconselhamento remoto está desligado por padrão, exige --advice-allow-egress e é bloqueado por --offline |
6. Observabilidade¶
Sendo um CLI efêmero, a observabilidade é local ao processo: logs para humanos ou máquinas em stderr, status de máquina no próprio relatório e, agora, métricas exportáveis para um coletor Prometheus.
6.1 Logs (stderr) — texto ou JSON¶
- Toda a telemetria de progresso vai para stderr (
scan.go,logf), deixando stdout limpo para o relatório quando não há--output. - A flag
--log-format text|json(defaulttext) escolhe o formato: text: linhas legíveis com prefixo[quorum];json: um objeto por linha com{ts, level, msg}(RFC3339 UTC), pronto para ingestão por um coletor de logs estruturados.--quiet/-qsuprime os logs de progresso e o sumário.- Eventos logados: alvo/tipo/contagem de regras do crosswalk, início/fim por scanner com duração, supressões de baseline/min-severity, escrita de métricas e o disparo do gate.
[quorum] target=. type=repo crosswalk=142 rules (./crosswalk) offline=false
[quorum] run trivy (0.71.2) ...
[quorum] done trivy: 17 findings in 4.2s
[quorum] skip grype: does not support target repo
[quorum] filtered: 3 suppressed by baseline (5 entries), 2 below min-severity medium
[quorum] metrics written to quorum.prom
[quorum] gate: found HIGH finding >= --fail-on high → exit 1
O mesmo evento em --log-format json:
6.2 Métricas Prometheus (--metrics)¶
A flag --metrics <arquivo> grava as métricas do scan em formato texto
Prometheus (internal/report/metrics.go, WriteMetrics), adequado a um
textfile collector do node_exporter ou a um Pushgateway — a forma canônica de
expor telemetria de um CLI sem processo de longa duração. O arquivo é gravado com
permissão 0644 (contagens não sensíveis, feitas para scraping).
Métricas emitidas:
| Métrica | Tipo | Labels | Significado |
|---|---|---|---|
quorum_scan_duration_seconds |
gauge | — | tempo total de parede do scan |
quorum_scanner_up |
gauge | scanner, status |
1 se rodou, 0 caso contrário |
quorum_scanner_findings |
gauge | scanner |
findings brutos por scanner (pré-consenso) |
quorum_scanner_duration_seconds |
gauge | scanner |
duração por scanner |
quorum_findings_after_consensus |
gauge | — | findings após o merge de consenso |
quorum_findings_total |
gauge | severity |
findings de consenso por severidade |
quorum_multi_detected |
gauge | — | findings corroborados por mais de um scanner |
Métricas de aconselhamento (emitidas apenas sob --advice, descrevendo só a
camada de aconselhamento — nunca alteram as métricas de consenso acima):
| Métrica | Tipo | Labels | Significado |
|---|---|---|---|
quorum_advice_enriched |
gauge | kind (remediation|references|recommendation) |
findings enriquecidos por cada fase de aconselhamento |
quorum_advice_provider |
gauge | provider |
provedor de aconselhamento em uso (none|local|remote) |
quorum_advice_fix |
gauge | stage (proposed|verified) |
taxa de verify-the-fix (verified/proposed) |
6.3 Status por scanner¶
Cada execução produz um ScannerRun (orchestrator.go) com status de máquina,
exposto no sumário (stderr), no relatório e nas métricas (quorum_scanner_up):
| Status | Significado |
|---|---|
ran |
rodou e produziu resultados (mesmo que 0) |
skipped |
scanner não suporta o tipo de alvo |
unavailable |
binário ausente / probe falhou (timeout/OOM/não instalado) |
error |
erro de execução do scanner |
timeout |
execução excedeu --timeout |
O sumário ao final imprime status, contagem por severidade, findings multi-detectados e a frase âncora "0 findings is not proof of safety — see scanner statuses above." Isso transforma o status em sinal observável, não em ruído.
6.4 Estado e propostas¶
| Capacidade | Estado | Observação |
|---|---|---|
| Logs em stderr | ✅ Implementado | prefixados, com --quiet |
| Log estruturado JSON em stderr | ✅ Implementado | --log-format json ({ts,level,msg}) |
| Status por scanner | ✅ Implementado | no sumário, no relatório e nas métricas |
| Duração por scanner | ✅ Implementado | ScannerRun.Duration |
| Métricas Prometheus (textfile/Pushgateway) | ✅ Implementado | --metrics <arquivo> |
| Métricas de aconselhamento | ✅ Implementado | apenas sob --advice (quorum_advice_*) |
| Traços OpenTelemetry | ⚠️ Proposta futura | sem processo de longa duração; não priorizado |
7. Auditoria¶
O relatório é a evidência de auditoria. Ele é reproduzível, carimba quem detectou o quê e fornece identidade estável por finding.
| Artefato de auditoria | Conteúdo | Onde |
|---|---|---|
| Status por scanner | ran/skipped/unavailable/error/timeout + versão + duração |
Result.Runs, relatório, métricas |
| Procedência da detecção | detectedBy[], detectionCount, confidence |
MergedFinding, SARIF properties |
| Identidade estável | Fingerprint = sha256(correlationKey) |
SARIF partialFingerprints["quorum/v1"], JSON fingerprint |
| Supressões | sempre logadas e contabilizadas, nunca descartadas em silêncio | baseline .quorumignore, filter.Apply |
| Versão dos scanners | ScannerVersion por finding / ScannerRun.Version |
relatório |
| Métricas do scan | contagens/durações em formato Prometheus | arquivo de --metrics |
Propriedades de auditoria:
- Reprodutibilidade: mesma entrada → mesmo
correlationKey/Fingerprint(função pura). Permite diff temporal de relatórios. Os anexos de aconselhamento opt-in (Remediation/References/Advice) são aditivos e claramente rotulados como "AI-generated, advisory only"; não alteram a identidade. - Rastreabilidade de supressão: um finding suprimido pelo baseline é reportado como suprimido (com a entrada que o suprimiu), não some.
- Cadeia de custódia do artefato: SARIF/JSON pode ser arquivado como evidência de gate de CI; assinaturas/provenance/SBOM (§5) atestam a origem do binário que o gerou.
Checklist de auditoria em CI:
- [ ] Arquivar o relatório (SARIF/JSON) como artefato do job.
- [ ] Arquivar o arquivo de
--metricsjunto ao relatório (telemetria histórica). - [ ] Versionar o
.quorumignore(cada supressão revisada e datada). - [ ] Reter a saída do sumário (status por scanner) junto ao artefato.
- [ ] Registrar a versão/digest da imagem Quorum usada.
8. LGPD (Lei Geral de Proteção de Dados)¶
Aplicável por exclusão — o Quorum não trata dados pessoais. A justificativa é técnica: os dados processados são metadados de software e infraestrutura, não dados de pessoas naturais.
| Categoria de dado | Processado? | Natureza |
|---|---|---|
| Identificadores de vulnerabilidade (CVE/GHSA/AVD) | Sim | dado técnico público |
| PURLs / versões de pacote | Sim | dado técnico |
| Caminhos de arquivo, recursos IaC/K8s | Sim | dado técnico do alvo |
| Conteúdo de segredos detectados (Trivy) | Possível | não é PII por definição; redigido e fica só no relatório do usuário |
| Dados pessoais (nome, CPF, e-mail, etc.) | Não | fora do escopo funcional |
Consequências:
- Sem PII ⇒ sem necessidade de base legal, consentimento, DPO ou DPIA atribuíveis ao Quorum como controlador/operador.
- A única chamada externa (OSV.dev) envia somente IDs de vulnerabilidade
(ex.:
CVE-2021-…) — não há dado pessoal no tráfego. Desligável com--offline. - Responsabilidade do usuário: se o alvo escaneado contiver PII (ex.: um segredo que seja um e-mail), o relatório gerado pode contê-la mesmo com a redação de segredos. O tratamento e a retenção desse relatório são responsabilidade de quem opera o Quorum, não do produto.
- O provedor de aconselhamento remoto (opt-in) envia apenas o finding
normalizado, não código-fonte; está desligado por padrão, exige consentimento
explícito de egresso e é bloqueado por
--offline.
N/A: direitos do titular, portabilidade, anonimização interna — não há base de dados pessoais sob controle do Quorum.
9. PCI DSS¶
N/A como produto. O Quorum não armazena, processa ou transmite dados de cartão (CHD/SAD) e não faz parte de um Cardholder Data Environment (CDE) por construção. Logo, não está no escopo do PCI DSS como sistema.
Entretanto, o Quorum pode auxiliar a conformidade de quem opera um CDE, como ferramenta de apoio aos requisitos abaixo:
| Requisito PCI DSS v4.0 | Como o Quorum auxilia |
|---|---|
| 6.2 / 6.3 — desenvolver software seguro e gerir vulnerabilidades | consenso de SCA (Trivy+Grype) com confidence para priorização |
| 6.3.1 — identificar vulnerabilidades por fontes reconhecidas | IDs CVE via OSV; confirmação autoritativa pesa no score |
| 6.3.2 — inventário de componentes (bespoke/3rd-party) | PURLs por finding; SBOM SPDX atestado da imagem :full |
| 6.4 / 6.5 — proteger aplicações e gerir mudanças | gate em CI por --fail-on; baseline auditável; policy-as-code (Conftest) |
| 11.3 — varreduras de vulnerabilidade | varredura automatizável em pipeline, evidência em SARIF |
O Quorum não substitui um ASV scan externo nem um pentest. É um controle compensatório/preventivo no SDLC, não uma atestação de conformidade.
10. ISO/IEC 27001 (Anexo A)¶
Mapeamento dos controles aplicáveis ao Quorum (como ferramenta e como cadeia de build). Controles organizacionais amplos ficam a cargo de quem adota.
| Controle (ISO 27001:2022) | Aplicabilidade | Como o Quorum endereça |
|---|---|---|
| A.8.8 — Gestão de vulnerabilidades técnicas | ✅ Direta | núcleo do produto: detecção + consenso + gate |
| A.8.25 — Ciclo de vida de desenvolvimento seguro | ✅ Apoia | integra-se ao SDLC via CI |
| A.8.28 — Codificação segura | ✅ Apoia | findings IaC/MISCONFIG/SECRET/policy-as-code acionáveis |
| A.8.29 — Testes de segurança em dev/aceite | ✅ Apoia | gate por exit code |
| A.8.30 — Desenvolvimento terceirizado | ⚙️ Parcial | scan de componentes 3rd-party (SCA) |
| A.8.31 — Separação de ambientes | ➖ Do adotante | execução isolada por job efêmero |
| A.8.15 — Logging | ⚙️ Parcial | logs stderr (text/json) + status por scanner + métricas |
| A.8.16 — Monitoramento | ⚙️ Parcial | métricas Prometheus (--metrics) |
| A.5.23 — Segurança em serviços cloud | ➖ Do adotante | N/A (sem serviço cloud próprio) |
| A.5.7 — Threat intelligence | ⚙️ Parcial | OSV/CVE como fonte de vulnerabilidade |
| Cadeia de suprimentos (A.5.19–A.5.23) | ✅ Própria | cosign + SLSA provenance + SBOM atestado + atestação do knowledge pack + pin por sha256/checksum (§5) |
Legenda: ✅ atende/apoia diretamente · ⚙️ parcial · ➖ responsabilidade do adotante.
11. OWASP ASVS (controles relevantes a um CLI)¶
A maioria dos capítulos do ASVS pressupõe uma aplicação web com sessão e autenticação — N/A aqui. Restam os controles aplicáveis a um binário/CLI:
| Domínio ASVS | Aplicabilidade | Estado no Quorum |
|---|---|---|
| V1 Arquitetura & threat modeling | ✅ | DESIGN.md + §5.3 deste doc |
| V5 Validação de entrada | ✅ | parsing de flags (cobra); validateTargetRef recusa -…; parsers de adapter com contract tests |
| V7 Tratamento de erros & logging | ✅ | erros acionáveis, logs em stderr (text/json), redação de segredos |
| V10 Código malicioso / integridade | ✅ | assinatura, provenance, SBOM, pin de dependências |
| V12 Arquivos & recursos | ⚙️ | lê alvo/crosswalk/baseline; relatório escrito com 0600 e filepath.Clean; caps de DoS de output/alvo |
| V14 Configuração | ✅ | defaults seguros (--offline opcional, crosswalk com fallback, aconselhamento desligado por padrão) |
| V2 Autenticação | ➖ N/A | sem autenticação |
| V3 Sessão | ➖ N/A | sem sessão |
| V4 Controle de acesso | ➖ N/A | sem multiusuário |
| V6 Criptografia armazenada | ➖ N/A | não armazena dado sensível |
| V8/V9 Dados/Comunicação | ⚙️ | OSV via HTTPS; aconselhamento remoto (opt-in) via HTTPS com consentimento explícito de egresso; sem dado sensível em trânsito |
| V13 APIs | ➖ N/A | não expõe API |
Checklist ASVS prática:
- [ ] Validar formato/severidade das flags e falhar com exit code
2em uso inválido. - [ ] Garantir que parsers de adapter tenham fixture/contract test versionado (cobertura no CI).
- [ ] Não emitir conteúdo de segredo em logs de progresso (redação ativa).
- [ ] Verificar integridade do binário/imagem antes de executar.
12. OWASP Top 10 (do próprio binário/imagem)¶
Escopo: riscos do artefato Quorum em si, não do alvo que ele escaneia (esse é o trabalho dos scanners). Mapeamento ao OWASP Top 10:2021.
| Risco | Relevância p/ o Quorum | Controle |
|---|---|---|
| A01 Broken Access Control | Baixa | sem controle de acesso (CLI local) |
| A02 Cryptographic Failures | Baixa | não guarda segredos; assinaturas via Sigstore; relatório 0600 |
| A03 Injection | Média | invoca scanners via exec (args, não shell); validateTargetRef bloqueia argument injection por - |
| A04 Insecure Design | Média | princípio "false split > false merge"; threat model em DESIGN |
| A05 Security Misconfiguration | Média | defaults seguros; auto-mount de docker.sock em type: image; crosswalk com fallback |
| A06 Vulnerable & Outdated Components | Alta | deps Go + 12 binários de scanner pinados; SBOM SPDX; o próprio Quorum deve ser escaneado |
| A07 Identification & Auth Failures | N/A | sem autenticação |
| A08 Software & Data Integrity Failures | Alta | cosign (com retry) + SLSA provenance + SBOM atestado + atestação do knowledge pack + pin por digest/checksum |
| A09 Logging & Monitoring Failures | Média | status por scanner + métricas evitam falso negativo silencioso |
| A10 SSRF | Baixa | única saída de rede é OSV.dev (host fixo); --offline desliga. O endpoint remoto de aconselhamento (opt-in) é configurado pelo usuário e gated por consentimento explícito de egresso |
Ações priorizadas:
- [ ] A06/A08: rebuild periódico para refrescar DB do Grype e bumps de versão de scanner.
- [ ] A08: converter os checksums restantes para
@sha256de imagem noDockerfile.full. - [ ] A03: manter invocação de scanners por args (sem shell), revisada em code review.
Nota sobre a camada de aconselhamento por IA: o OWASP LLM Top 10 só se aplica quando a camada de IA está habilitada (
--advice-provider=local|remote). O núcleo determinístico não tem IA; ver 13-ia.md.
13. Resiliência (degradação graciosa)¶
A resiliência é um princípio de design central: uma falha parcial degrada, não derruba, e a degradação é sempre visível.
flowchart TD
A[Scanner ausente] --> A1[status unavailable\nscan continua]
B[Probe lento / OOM] --> B1[status unavailable\nmensagem acionável]
C[Scanner estoura --timeout] --> C1[status timeout\ndemais continuam]
D[OSV.dev indisponível] --> D1[usa id original\ncache local; sem falhar]
E[Crosswalk ausente] --> E1[fallback /opt/quorum/crosswalk\nou finding unmapped]
F[Controle não mapeado] --> F1[finding isolado\nflag unmapped]
G[Output/alvo gigante] --> G1[cap de DoS aborta\ncom mensagem acionável]
H[cosign/OIDC instável no release] --> H1[retry 4x\ncom backoff]
I[Modelo de advice inacessível] --> I1[relatório sai sem advice de IA\nscan nunca falha]
| Falha | Comportamento | Fonte |
|---|---|---|
| Scanner não instalado | unavailable, scan prossegue |
orchestrator.runOne |
| Probe lento / OOM | unavailable com diagnóstico (memória/scope) |
orchestrator.runOne |
| Timeout de execução | timeout, outros scanners continuam |
orchestrator + --timeout |
| Rede OSV indisponível | aliases degradam para id original; nunca falha o scan | alias/DESIGN §7 |
:full sem rede |
DB do Grype pré-cacheada + crosswalk embutido | Dockerfile.full |
| DB do Grype envelhecida | GRYPE_DB_VALIDATE_AGE=false — DB não expira, scan não trava |
Dockerfile.full |
| Output de scanner gigante | abortado no cap de 512 MiB (QUORUM_MAX_OUTPUT_BYTES) |
adapter.runCmd |
| Alvo repo/k8s gigante | recusado no cap de 20 GiB (QUORUM_MAX_TARGET_BYTES) |
scan.checkTargetSize |
| Crosswalk default ausente | fallback automático para /opt/quorum/crosswalk |
resolveCrosswalkDir |
| Controle não mapeável | finding fica isolado, flag unmapped (não chuta merge) |
DESIGN §6 |
| Assinatura/verificação instável no release | retry (4 tentativas, backoff crescente) no cosign e no gh attestation verify |
release.yml |
| Modelo/endpoint de advice inacessível | degradação graciosa: relatório sai sem advice de IA, scan nunca falha | internal/advisor |
Invariante de resiliência: "0 findings is not proof of safety" — toda degradação aparece como status, evitando que uma falha vire um falso "tudo certo".
14. Backup, DR, RTO e RPO¶
N/A — o Quorum é stateless. Não há dado de produção sob sua custódia que possa ser perdido. A continuidade depende de artefatos reproduzíveis, não de backup/restore.
| Conceito | Aplicabilidade | Justificativa |
|---|---|---|
| Backup de dados | N/A | sem armazenamento persistente de estado de negócio |
| Disaster Recovery (DR) | N/A (serviço) | não há serviço para recuperar |
| RTO | ~minutos, reinterpretado | "recuperar" = re-executar o scan em outro runner |
| RPO | 0, reinterpretado | nada a perder; cada scan é recomputável da fonte |
O que substitui backup/DR:
- Reprodutibilidade: imagens pinadas por digest + binários assinados + provenance + SBOM atestado permitem reconstruir/obter exatamente o mesmo artefato.
- Mirror de distribuição: espelhar imagens/binários em registry interno protege contra indisponibilidade do GHCR/Releases.
- Cache de aliases: descartável; reconstruído a partir do OSV/scanner. Gravado
com permissão
0600eschemaVersionpara invalidação segura. (O cache opt-in de advice é igualmente descartável e reconstruível, indexado porfingerprint+provider+model.)
Único item recuperável de fato: o relatório arquivado como evidência (§7) — sua retenção/backup é responsabilidade do pipeline que o gera, não do Quorum.
Proposta futura (separada): se um modo com cache persistente compartilhado for adotado em larga escala, definir política de backup/retenção desse cache.
15. SLA / SLO / SLI¶
Não há SLA contratual de serviço (não é serviço). Definimos SLO/SLI para dois contextos operacionais reais: uso em CI e pipeline de release.
15.1 Uso em CI (o binário/imagem como dependência do pipeline)¶
| SLI | Definição | SLO alvo | Como medir |
|---|---|---|---|
| Confiabilidade do scan | % de jobs que terminam com exit 0/1 (sem 2) |
≥ 99% | exit codes do job |
| Determinismo | % de re-scans da mesma entrada com mesmo fingerprint set | 100% | diff de relatórios |
| Latência de overhead | overhead do orquestrador (sem scanners) | ≤ 2s p95 | sumário/Duration/métricas |
| Resiliência a falha de rede | scans concluídos com OSV indisponível | 100% (degrada) | execução --offline |
| Cobertura de execução | % de scanners suportados com status ran |
≥ 95% em :full |
status por scanner / quorum_scanner_up |
| Neutralidade do aconselhamento | % de scans em que --advice mantém a saída do núcleo (fingerprints/consenso) inalterada |
100% | byte-diff com/sem --advice |
15.2 Pipeline de release (publicação do artefato)¶
| SLI | Definição | SLO alvo | Como medir |
|---|---|---|---|
| Sucesso de publicação | % de tags semver que publicam imagens+binários | 100% | release.yml |
| Integridade verificada | % de releases com cosign+provenance+SBOM+knowledge pack verificados no próprio build | 100% | steps "Verify provenance" / attest-sbom / job knowledge |
| Reprodutibilidade do build | build determinístico (-trimpath, deps pinadas, bases por sha256) |
100% | Dockerfile.full/GoReleaser |
| Cobertura de testes | testes com -race -covermode=atomic a cada push/PR |
verde bloqueante | ci.yml (step "Test (race + coverage)") |
| Cobertura de evals do aconselhamento | cobertura de remediação, relevância de referências OWASP e taxa de verify-the-fix medidas em CI | acompanhada (sem modelo pesado) | harness internal/evals |
| Tempo de release | tag → imagens+binários publicados | ≤ 30 min | duração do workflow |
Distinção importante de SLA do produto vs. integridade: a disponibilidade de publicação depende do GitHub/GHCR (não controlado por nós), mas a integridade (assinatura + provenance + SBOM + knowledge pack verificados) é bloqueante no próprio release — um artefato com atestação quebrada falha o build. Instabilidade transitória do Sigstore/OIDC é absorvida por retry (§13), não por relaxamento da verificação.
Premissas¶
- Os números/metas em §2.5 e §15 são alvos de engenharia, ainda não validados por benchmark formal neste repositório.
- Assume-se que o consumidor em produção verifica imagem/binário (cosign +
gh attestation verify) e, idealmente, pina por digest — o produto fornece os meios (assinatura, provenance, SBOM atestado, atestação do knowledge pack), a aplicação é do adotante. - O
--offlineé o modo recomendado quando a política de CI proíbe egressos de rede; com isso a única dependência externa em runtime (OSV.dev) é eliminada e o provedor de aconselhamento remoto é totalmente bloqueado. - "Sem PII" pressupõe que o produto não é configurado para tratar dados pessoais; PII eventualmente presente no alvo é responsabilidade do operador do relatório (mesmo com a redação de segredos aplicada).
- A camada de aconselhamento (
--advice) é opt-in e desligada por padrão; quando desligada, a saída é byte-idêntica e o determinismo é preservado. Apenas o provedor remoto faz dados saírem do host, e somente sob consentimento explícito (--advice-allow-egress), enviando o finding normalizado, nunca código-fonte. - Mapeamentos de PCI DSS, ISO 27001, ASVS e OWASP Top 10 são interpretativos, baseados no comportamento as-is do código (v0.8.3), e não constituem atestação de conformidade.
- A disponibilidade da distribuição depende de serviços de terceiros (GitHub GHCR/Releases, OSV.dev), cujos SLAs não são controlados por este projeto.
- A imagem
:fullélinux/amd64(os 12 scanners embutidos são amd64); cenários arm64 usam:slim(amd64+arm64) com scanners no PATH, o que altera o perfil de performance/disponibilidade local. - Os defaults dos caps de DoS (512 MiB de output por scanner, 20 GiB de alvo) são dimensionados muito acima de qualquer caso real; alvos legítimos que os excedam exigem override explícito por env.
Lacunas conhecidas (gaps)¶
- Não há benchmark de performance versionado para sustentar os SLOs alvo.
- Nem todos os scanners no
Dockerfile.fullestão pinados por digest imutável de imagem (Trivy/KICS/bases sim; os demais por versão+checksum SHA256); endurecer todos para@sha256é uma melhoria incremental recomendada. - A observabilidade cobre logs (text/json), status por scanner e métricas
Prometheus (
--metrics, mais as métricas opt-inquorum_advice_*); traços OpenTelemetry permanecem proposta futura. - Não há mecanismo nativo de mirror/retenção de artefatos; é responsabilidade do adotante.