Ir para o conteúdo

Segurança

Esta seção é a análise de segurança completa do Quorum (quorum-sec-scan, v0.8.3), uma ferramenta CLI/Docker de consensus security scanning. O Quorum não escaneia artefatos diretamente: ele orquestra um pool de 12 scanners OSS (trivy, grype, checkov, kics, dockle, kubescape, polaris, kube-score, terrascan, tfsec, regula, conftest), faz parse da saída não confiável de cada um, lê alvos arbitrários (imagens, repositórios, manifests) e, opcionalmente, consulta a OSV.dev pela rede. Esse perfil — uma ferramenta de segurança que executa outras ferramentas e ingere dados não confiáveis — concentra o risco em quatro fronteiras: shell-out, parsing, I/O de arquivos e rede/supply chain.

A partir da v0.8.3 há mais uma superfície, estritamente opt-in: a camada de assessoria (--advice). Ela é presentation-only — nunca toca correlationKey, fingerprint, o score de confiança, a severidade agregada nem o gate fail-on; sem --advice a saída é byte-idêntica. O núcleo determinístico continua sem IA. Seu provedor remoto opcional é o único caminho capaz de enviar dados para fora do host, e é gated por consentimento explícito (--advice-allow-egress) e bloqueado por --offline — por isso ganha uma fronteira própria abaixo.

O documento modela as ameaças sobre essas fronteiras, mapeia o produto contra os principais frameworks (OWASP, MITRE, STRIDE, DREAD, LINDDUN, NIST, CIS, ISO 27001, PCI DSS, LGPD) e, para cada risco, apresenta Descrição, Impacto, Probabilidade, Severidade, Mitigação, Recomendação e Ferramentas sugeridas. Onde um framework não se aplica ao escopo CLI/Docker (MASVS, MITRE ATLAS — e o OWASP LLM Top 10, que se aplica apenas quando a camada de IA opt-in está habilitada), declara-se N/A com justificativa técnica.

Cross-links: Arquitetura · Operação/CI · Supply Chain & Releases · IA · DESIGN.md (§12 CLI & Docker, §14 Riscos).

Nota de versão. Este documento foi originalmente escrito na v0.2.3 e revisado para a v0.8.3 (revisão 2026-07-04). Entre as duas versões, o pool cresceu de 6 para 12 scanners, o consenso passou a valer também para MISCONFIG/IaC e K8S_POSTURE (via crosswalk derivado de output real), a maioria dos gaps de hardening que este doc apontava foi fechada (target - recusado, --output normalizado + 0o600, id OSV validado + PathEscape, cache 0o600 + schemaVersion, redaction de segredos, caps de DoS, supply chain com SLSA + SBOM atestados), e foi adicionada uma camada de assessoria opt-in (--advice, presentation-only, desligada por padrão). O histórico é preservado no texto de cada risco.


1. Modelo de fronteiras de confiança

O Quorum é um processo Go que, a partir de entrada controlada pelo usuário/CI, dispara subprocessos e consome bytes que ele não produziu. Toda análise abaixo se ancora neste modelo.

flowchart LR
  subgraph Untrusted["Entrada NÃO confiável"]
    T[Alvo: imagem / repo / k8s]
    OSVresp[Resposta OSV.dev]
    SOUT[stdout/stderr dos scanners]
    CACHE[(aliases.json em disco)]
    BL[.quorumignore / crosswalk / policy Rego]
  end

  subgraph Quorum["Processo quorum (Go 1.26)"]
    SCAN[scan.go runScan + validateTargetRef + checkTargetSize]
    ORCH[orchestrator fan-out]
    ADP[adapter.runCmd exec.CommandContext + capWriter 512MiB]
    PARSE[parsers JSON/SARIF dos adapters + redaction]
    ALIAS[alias resolver + OSVClient]
    ADV[camada de assessoria: enrich / rag / advisor -- opt-in, presentation-only]
    EMIT[emit -> WriteFile 0o600 / stdout]
  end

  subgraph External["Subprocessos / rede"]
    SCANNERS[12 scanners: trivy grype checkov kics dockle kubescape polaris kube-score terrascan tfsec regula conftest]
    OSV[(api.osv.dev)]
    AIEP[(endpoint de IA: Ollama local / API remota -- apenas sob --advice-provider)]
  end

  T --> SCAN --> ORCH --> ADP --> SCANNERS
  SCANNERS -- stdout não confiável --> PARSE --> ALIAS
  ALIAS <--> CACHE
  ALIAS --> OSV
  OSV -- OSVresp --> ALIAS
  BL --> SCAN
  PARSE --> ADV
  ADV -. opt-in .-> AIEP
  ADV --> EMIT
  PARSE --> EMIT
  EMIT --> OUT[report.sarif/json/xml + arquivo de saída + métricas Prometheus]

Fronteiras de confiança (trust boundaries):

# Fronteira Entrada não confiável Onde no código
B1 Shell-out target.Ref, nomes de scanner, flags, QUORUM_<NAME>_ARGS internal/adapter/adapter.go:runCmd (exec.CommandContext)
B2 Parsing stdout JSON/SARIF dos 12 scanners internal/adapter/*.go (json.Unmarshal)
B3 I/O de arquivo --output, --cache, --baseline, --crosswalk, --metrics, --advice-cache cmd/quorum/scan.go:emit, internal/cache/store.go
B4 Rede resposta da OSV.dev internal/alias/osv.go
B5 Supply chain binários dos scanners, imagem base, knowledge pack + crosswalk Dockerfile.full, action.yml, release.yml, GHCR
B6 Recursos tamanho do alvo (repo/imagem), tamanho do stdout cmd/quorum/scan.go:checkTargetSize, adapter.capWriter
B7 Assessoria / IA (opt-in) saída do LLM; egress do finding normalizado para endpoint remoto internal/advisor, internal/rag, internal/enrich (apenas sob --advice)

2. STRIDE por fronteira

Fronteira Spoofing Tampering Repudiation Info Disclosure DoS Elevation
B1 Shell-out ▲ (injeção de comando)
B2 Parsing ▲ (saída forjada infla/oculta findings) ▲ (JSON gigante — capado a 512 MiB)
B3 I/O arquivo ▲ (path traversal/overwrite) ▲ (findings em local errado)
B4 Rede OSV ▲ (DNS/MITM) ▲ (resposta forjada) ▲ (vaza IDs consultados) ▲ (latência — timeout 8s)
B5 Supply chain ▲ (imagem/binário falso) ▲ (binário/pack trojanizado)
B6 Recursos ▲ (zip/img bomb — cap de alvo 20 GiB)
B7 Assessoria/IA (opt-in) ▲ (endpoint de IA malicioso) ▲ (recomendação forjada) ▲ (egress do finding pelo provedor remoto)

▲ = vetor relevante para o Quorum. Vazios indicam vetor não aplicável ao escopo CLI. A linha B7 é inerte a menos que --advice esteja ligado; o design presentation-only a mantém fora do caminho de correlação/confiança.


3. Matriz de risco (DREAD)

Escala DREAD por critério 1–10; Score = média; Severidade derivada: Crítico ≥ 8, Alto 6–7.9, Médio 4–5.9, Baixo < 4.

ID Risco Damage Reprod. Exploit. Affected Discover. Score Sev Estado
R1 Injeção de comando via target/flags 9 3 3 6 4 5.0 Médio ✅ Mitigado (argv sem shell + recusa target -)
R2 Parse de saída maliciosa do scanner 6 7 5 7 5 6.0 Alto Parcial (cap de bytes + redaction; sanitização SARIF pendente)
R3 Path traversal / overwrite via --output 7 8 7 5 6 6.6 Alto ✅ Mitigado (Clean + 0o600)
R4 Supply chain (binários dos scanners, tag mutável) 9 5 5 8 4 6.2 Alto ✅ Mitigado em grande parte (SLSA + SBOM atestados + pin/checksum)
R5 DoS por alvo gigante / zip bomb / img bomb 6 6 5 6 6 5.8 Médio ✅ Mitigado (cap de alvo 20 GiB + cap de stdout 512 MiB + timeout)
R6 SSRF / exfiltração de IDs via OSV 5 4 3 5 5 4.4 Médio ✅ Mitigado (validação + PathEscape do id; resta vazamento de metadados)
R7 Cache poisoning (aliases.json) 5 6 5 5 4 5.0 Médio ✅ Mitigado (0o600 + schemaVersion; assinatura ainda ausente)
R8 Exposição de findings (relatório sensível) 6 5 4 6 5 5.2 Médio ✅ Mitigado (0o600 + redaction de segredos; classificação do artefato é operacional)
R9 Crosswalk/baseline adulterado (false merge/suppress) 7 5 4 5 4 5.0 Médio Parcial
R10 RCE no parser/dep do scanner trojanizado 9 3 3 7 3 5.0 Médio Parcial (mitigado por pin/checksum + SLSA)
R11 Egress da camada de assessoria / handling da saída de IA (opt-in) 5 4 3 4 3 3.8 Baixo ✅ Mitigado por design (desligado por padrão; egress consentido + bloqueado por --offline; presentation-only)

Princípio de produto que reduz risco de classe: "false split > false merge" — na dúvida, o Quorum não funde findings. Isso limita o dano de R2/R9 (uma saída forjada tende a gerar findings extras, não a esconder findings reais). O consenso de MISCONFIG/IaC e K8S_POSTURE via crosswalk foi derivado de output real justamente para manter false split > false merge (o crosswalk cobre AVD para AWS/Azure/GCP e os controles C-#### do kubescape para Kubernetes; tfsec autocorrelaciona com trivy por emitir AVD nativo; RBAC segue single-engine por exigir contexto de cluster).


4. Riscos detalhados

R1 — Injeção de comando via target / flags (B1)

  • Descrição. O runCmd executa cada scanner com exec.CommandContext(ctx, bin, args...) em internal/adapter/adapter.go. Os argumentos (incluindo target.Ref) são passados como slice de argv, sem shell intermediário (/bin/sh -c). Não há interpolação de string em comando, então metacaracteres de shell (;, |, $(), backticks) num alvo do tipo repo; rm -rf / são tratados como um literal de caminho, não como comando.
  • Impacto. Execução arbitrária de comando no contexto do processo Quorum (e, em Docker, dentro do container :full).
  • Probabilidade. Baixa — exige uma regressão que reintroduza sh -c ou um scanner que ele próprio interprete o alvo como shell.
  • Severidade. Médio (alto impacto, baixa probabilidade no estado atual).
  • Mitigação (presente). exec.CommandContext com argv; nenhum sh -c no código; o tipo de alvo é resolvido por resolveTargetType e o ref é sempre posicional.
  • Mitigação (presente) — argument injection.Resolvido. Um alvo iniciando com - (ex.: --config=/etc/...) poderia ser interpretado como flag pelo scanner. runScan valida target no boundary da CLI via validateTargetRef (cmd/quorum/scan.go) e recusa qualquer ref iniciando com - (mensagem sugere ./-name para um path literal), de modo que nenhum adapter recebe um ref com hífen-líder. Optou-se por essa validação universal em vez de inserir -- por scanner (que quebraria scanners sem suporte a --).
  • Nota — passthrough de args. QUORUM_<NAME>_ARGS (via extraArgs/splitArgs) injeta args extras num scanner (ex.: QUORUM_CHECKOV_ARGS="--bc-api-key <key>" para destravar políticas Prisma/Bridgecrew). Isso é controlado pelo operador (quem roda o container/CLI), mesmo nível de confiança das próprias flags — não é uma entrada não confiável de terceiros. conftest avalia seu Rego de ./policy (ou via QUORUM_CONFTEST_ARGS="--policy <dir>").
  • Recomendação.
  • [x] ~~Validar/normalizar target.Ref (rejeitar refs que comecem com -)~~ — feito.
  • [ ] (Opcional) Validar formato de referência de imagem e existência de path para repo.
  • [ ] Teste de regressão garantindo que nenhum adapter use sh -c.
  • Ferramentas. gosec (G204), semgrep regra go.lang.security.audit.dangerous-exec, golangci-lint.

R2 — Parsing de saída maliciosa do scanner (B2)

  • Descrição. Cada um dos 12 adapters faz json.Unmarshal do stdout do scanner (ex.: trivy.parse, grype.parse, conftest.parse). Um scanner comprometido ou um alvo que induza o scanner a emitir saída adversária pode (a) inflar findings, (b) injetar conteúdo em campos como Title/Description que depois vão para o SARIF, ou (c) emitir JSON enorme.
  • Impacto. Relatório poluído; potencial XSS/HTML injection se o SARIF for renderizado em uma UI de terceiros (GitHub code scanning, etc.); consumo de memória.
  • Probabilidade. Média — depende de comprometer um scanner ou alvo hostil.
  • Severidade. Alto.
  • Mitigação (presente). Parsing estruturado por structs Go (campos desconhecidos ignorados), não eval; runCmd só trata exit≠0 como erro quando stdout está vazio (convenção "findings-found"), evitando que um exit code seja confundido com sucesso; princípio false split > false merge limita ocultação.
  • Mitigação (presente) — DoS de memória.runCmd capa o stdout via capWriter em 512 MiB (defaultMaxOutputBytes, ajustável por QUORUM_MAX_OUTPUT_BYTES). Ao ultrapassar o cap, o processo aborta com erro claro em vez de sofrer OOM — o capWriter continua drenando o pipe (retorna len(p)) para não travar o filho, e a truncagem é detectada após a execução.
  • Mitigação (presente) — segredos. ✅ Campos que carregam trechos próximos a segredos são redigidos: trivy.parse só armazena o Match de um secret finding após passar por redactSecretText (mantém contexto localizador, mas só os 4 primeiros chars de cada token longo sobrevivem — ver R8).
  • Gaps remanescentes. Sem sanitização/escape explícito de campos textuais genéricos (Title/Description/Location) antes de emitir SARIF para UIs de terceiros; sem validação de schema da saída.
  • Recomendação.
  • [x] ~~io.LimitReader/cap de bytes no stdout do scanner (DoS de memória)~~ — feito (capWriter, 512 MiB).
  • [ ] Sanitizar/escapar Title/Description/Location na camada report.
  • [ ] Manter os contract tests por adapter (internal/adapter/testdata, realdata_test.go) e adicionar fixtures adversárias (campos gigantes, unicode de controle, JSON malformado).
  • Ferramentas. go-fuzz/native fuzzing nos parsers, semgrep, SARIF validators.

R3 — Path traversal / overwrite via --output (B3)

  • Mitigado.
  • Descrição. Até a v0.2.3, em cmd/quorum/scan.go:emit, o caminho de --output era usado diretamente (os.WriteFile(output, ..., 0o644)) sem normalização, e legível por outros usuários do host.
  • Impacto. Em CI, um --output controlado por entrada (ex.: derivado de nome de branch/PR) podia sobrescrever arquivos graváveis pelo runner e expor findings (perm 0644).
  • Probabilidade. Média (em pipelines que montam --output a partir de variáveis).
  • Severidade. Alto.
  • Mitigação (presente). emit aplica filepath.Clean(output) (colapsa ./ e ../), cria o diretório pai (0o755) e grava o relatório com perm 0o600 (não mais world-readable). O arquivo de métricas Prometheus (--metrics) segue o mesmo Clean, mas grava 0o644 por conter apenas contagens não sensíveis destinadas a scrape.
  • Recomendação.
  • [x] ~~Normalizar --output com filepath.Clean~~ — feito.
  • [x] ~~Reduzir a permissão do relatório~~ — feito: 0o600.
  • [ ] (Opcional) Confinar a saída a um --output-dir/CWD quando vier de entrada não confiável.
  • [ ] (Opcional) O_EXCL/escrita atômica quando não for sobrescrita intencional.
  • Ferramentas. gosec (G304 file path provided as taint), semgrep.

R4 — Supply chain dos scanners e imagem (B5)

  • Mitigado em grande parte (endurecido em release.yml, Dockerfile.full e .goreleaser.yaml).
  • Descrição. O Dockerfile.full combina três níveis de confiança para os 12 scanners:
  • Pinado por digest (imagem base): aquasec/trivy@sha256:…, checkmarx/kics@sha256:…; bases golang:1.26-alpine@sha256:… e alpine:3.20@sha256:… também pinadas.
  • Checksum-verificado (SHA256): Dockle, Kubescape (KUBESCAPE_SHA256 pinado, sem fallback curl | bash), Polaris, kube-score, tfsec, Terrascan, Regula e Conftest — cada um baixa o artefato + checksums.txt e roda sha256sum -c.
  • install.sh da Anchore (resíduo): Grype e Syft via raw.githubusercontent.com/.../main/install.sh. O instalador valida o checksum interno do binário, mas o script em si vem de branch móvel (main). Checkov via pip install "checkov==<versão>" sem --require-hashes.
  • Impacto. Binário trojanizado executado dentro do trust boundary → RCE, exfiltração.
  • Probabilidade. Baixa–Média (reduzida pelo pin/checksum majoritário).
  • Severidade. Alto (impacto), mas probabilidade contida.
  • Mitigação (presente).
  • Imagem :full (linux/amd64) e :slim (amd64+arm64) publicadas no GHCR, assinadas keyless com cosign (OIDC) com retry contra flutuação de Fulcio/Rekor.
  • Atestação SLSA build-provenance (actions/attest-build-provenance) da imagem e, para os binários nativos, sobre dist/checksums.txt do GoReleaser — re-verificada no próprio release (gh attestation verify, com retry).
  • SBOM SPDX atestada (actions/attest-sbom, via syft) para a imagem, além do sbom: true do BuildKit; SBOMs por binário via GoReleaser/syft.
  • Knowledge pack de assessoria atestado. Os dados da camada de assessoria — templates de remediação e o corpus OWASP pinado por digest (knowledge/*.yaml, knowledge/owasp/corpus.yaml) mais o crosswalk — agora recebem sua própria atestação SLSA build-provenance a cada release (job knowledge do release.yml, sobre um manifesto ordenado knowledge.sha256). Ele embarca nas imagens, mas também é um artefato de dados autônomo que o consumidor pode verificar com gh attestation verify knowledge/owasp/corpus.yaml.
  • Grype DB pré-cacheado na imagem com GRYPE_DB_VALIDATE_AGE=false (a 1ª scan roda offline; o DB embalado não expira).
  • O GitHub Action composite (action.yml) faz cosign verify da imagem :full antes de executá-la (verify: true por padrão, via identity-regexp do release.yml); auto-monta /var/run/docker.sock em type: image (evita falso-zero ao escanear imagem local); expõe inputs *-args e docker-socket; a tag móvel v0 (para pin do action) é avançada automaticamente por tag-major.yml a cada release semver.
  • THIRD_PARTY_NOTICES.md documenta as dependências de terceiros.
  • Gaps remanescentes. Grype/Syft ainda via install.sh de branch móvel; Checkov via pip install sem --require-hashes; o cosign instalado pelo action.yml usa releases/latest/download (mutável).
  • Recomendação.
  • [x] ~~Pin por digest/checksum dos scanners baixados~~ — feito para 10 dos 12 (resta grype/syft via install.sh e checkov via pip).
  • [x] ~~Publicar SBOM da imagem :full e verificá-la no release~~ — feito (SPDX atestada).
  • [x] ~~Atestar o knowledge pack de assessoria~~ — feito (job knowledge).
  • [x] ~~Verificar a atestação SLSA~~ — feito no release; replicar no consumidor (gh attestation verify / cosign verify-attestation).
  • [ ] Fixar o install.sh da Anchore por commit SHA; pip install --require-hashes para o Checkov.
  • [ ] Pinar a versão do cosign instalada no action.yml.
  • Ferramentas. cosign, slsa-verifier/gh attestation verify, syft (SBOM), trivy/grype na própria imagem, pip-audit, Dependabot/Renovate com pin por digest.

R5 — DoS por alvo gigante, zip bomb, image bomb (B6)

  • Mitigado.
  • Descrição. O Quorum lê alvos arbitrários e delega a leitura aos scanners. Um repo gigante, uma imagem com camadas infladas ou um arquivo compactado malicioso (zip bomb) pode exaurir CPU/memória/disco do runner.
  • Impacto. Travamento do pipeline; custo de runner; OOM.
  • Probabilidade. Média.
  • Severidade. Médio.
  • Mitigação (presente).
  • Cap de tamanho de alvo: checkTargetSize (cmd/quorum/scan.go) aborta cedo se um alvo de filesystem (repo/k8s) exceder 20 GiB (defaultMaxTargetBytes, ajustável/ desabilitável por QUORUM_MAX_TARGET_BYTES); o WalkDir para assim que o cap é cruzado, então repos normais pagam só uma passada leve de stat. Alvos de imagem (sem árvore local) são pulados.
  • Cap de stdout: capWriter limita a saída de cada scanner a 512 MiB (ver R2).
  • Timeout por scanner (--timeout, default 5m; PerScannerTime no orchestrator) com fan-out paralelo isolando cada scanner; probe de versão que distingue timeout / killed(OOM) / não-instalado e gera mensagem acionável ("likely out of memory; raise the container's memory limit"); status por scanner (ran|skipped|unavailable|error|timeout) deixa o DoS visível em vez de mascarar como "0 findings". Princípio "0 findings is not proof of safety".
  • Gaps remanescentes. O cap de alvo é por tamanho total on-disk, não por profundidade de descompressão (uma zip bomb pequena no disco pode inflar dentro do scanner); o Quorum não impõe ulimit/cgroup próprio (delega ao runtime do container).
  • Recomendação.
  • [x] ~~Cap de bytes no stdout e limite de tamanho de alvo~~ — feito (512 MiB / 20 GiB).
  • [ ] Documentar/recomendar limites de memória e --timeout em CI.
  • [ ] Rodar a imagem com --memory/--pids-limit/--read-only e tmpfs para /tmp.
  • Ferramentas. limites de cgroup do Docker/K8s, timeout(1), monitor de runner.

R6 — SSRF / exfiltração via OSV (B4)

  • Risco de manipulação de URL mitigado; a exfiltração passiva de metadados ao provedor permanece (use --offline).
  • Descrição. O resolver de aliases consulta https://api.osv.dev/v1/vulns/<id> (internal/alias/osv.go). O <id> vem da saída do scanner. Até a v0.2.3 era concatenado na path da URL sem sanitização. A BaseURL é fixa (não controlável por flag), o que limita SSRF clássico, mas um id adversário poderia conter caracteres de path/query.
  • Impacto. (a) Exfiltração passiva: a lista de IDs consultados revela quais vulnerabilidades existem no alvo a um terceiro (OSV.dev). (b) Risco residual de manipulação de URL se id não for validado.
  • Probabilidade. Baixa–Média.
  • Severidade. Médio.
  • Mitigação (presente). --offline desliga totalmente o OSV (em runScan, osv só é instanciado quando !f.offline; usa apenas aliases locais do scanner + cache); BaseURL fixa; cliente com timeout de 8s, MaxRetries=2 e backoff exponencial (base 200ms) sensível ao ctx — degradação graciosa: falha de rede nunca quebra o scan (DESIGN §7); CVE é preferido localmente antes de tocar a rede, reduzindo nº de queries.
  • Mitigação (presente) — validação do id. ✅ O id é validado contra ^[A-Za-z][A-Za-z0-9._-]{0,127}$ (vulnIDPattern) e passa por url.PathEscape (defesa em profundidade) antes de compor a URL; ids malformados são recusados sem tocar a rede ("osv: refusing to query malformed vuln id"). Isso impede que um id forjado injete segmentos de path/query (../, ?, /).
  • Gaps remanescentes. Sem allowlist de host explícita além da constante; a consulta ainda vaza metadados (lista de IDs) ao provedor.
  • Recomendação.
  • [x] ~~url.PathEscape(id) + validar formato antes da requisição~~ — feito.
  • [ ] Documentar --offline para ambientes air-gapped / dados sensíveis.
  • [ ] Permitir mirror interno de OSV via env, com allowlist de host.
  • Ferramentas. gosec (G107 url taint), proxy egress allowlist, semgrep.

R7 — Cache poisoning de aliases.json (B3)

  • Mitigado quanto a permissão e parse resiliente; integridade/assinatura ainda ausente.
  • Descrição. internal/cache/store.go é um KV JSON em ~/.cache/quorum/aliases.json (escrita atômica via .tmp+rename). Um arquivo corrompido/adulterado é silenciosamente degradado para cache vazio; um atacante com acesso ao FS poderia mapear um GHSA para um CVE errado, afetando a correlação.
  • Impacto. Correlação incorreta (false merge/split direcionado); ofuscação localizada.
  • Probabilidade. Baixa (exige acesso ao FS do usuário/runner).
  • Severidade. Médio.
  • Mitigação (presente).
  • Perm 0o600 ao gravar (Put escreve o .tmp como 0o600 antes do rename) — não mais world-readable.
  • Versionamento de schema: diskFormat.SchemaVersion (schemaVersion = 1). Open só adota o arquivo quando ele faz parse E casa o schema E Data != nil; um arquivo legado/incompatível/corrompido degrada para cache vazio em vez de leitura mal-parseada (rebuild único, sem migração).
  • Escrita atômica; cache é só otimização (nunca quebra o scan); OSV é arbiter quando o cache não tem a chave.
  • Gaps remanescentes. Sem integridade/assinatura por entrada; valores confiados sem revalidação criptográfica; sem TTL.
  • Recomendação.
  • [x] ~~Perm 0600 no arquivo de cache~~ — feito.
  • [x] ~~Versão de schema~~ — feito (schemaVersion).
  • [ ] Em CI efêmero, não persistir cache entre jobs não confiáveis, ou usar --cache "".
  • [ ] TTL e checksum/assinatura por entrada.
  • Ferramentas. controle de permissão de FS, gosec (G306 file perms).

R8 — Exposição de findings (B3 / output)

  • Mitigado quanto a permissão e valores de segredo; classificação do artefato é responsabilidade operacional.
  • Descrição. O relatório (SARIF/JSON/XML) contém vulnerabilidades, misconfigs e secrets detectados (trivy/dockle emitem findings de segredo). Esse output é dado sensível: revela a superfície de ataque do alvo e pode conter trechos próximos a segredos.
  • Impacto. Divulgação de postura de segurança / pistas de exploração a quem ler o artefato.
  • Probabilidade. Média (artefatos de CI frequentemente são públicos ou amplamente acessíveis).
  • Severidade. Médio.
  • Mitigação (presente).
  • emit escreve com perm 0o600 (antes 0644).
  • Redaction de valores de segredo: o Match de um secret finding do trivy só é persistido após redactSecretText (internal/adapter/adapter.go) — tokens longos ([A-Za-z0-9+/=_-]{12,}) viram <4 chars>…REDACTED…, preservando contexto localizador sem vazar o valor. O comentário no struct marca Match como "sensitive: stored only after redaction".
  • Saída default é stdout (não persiste por padrão); --min-severity e --quiet reduzem ruído; baseline .quorumignore permite suprimir findings conhecidos.
  • Gaps remanescentes. Redaction cobre o padrão de token longo; segredos curtos/atípicos podem escapar; sem aviso automático de classificação do artefato.
  • Recomendação.
  • [x] ~~Perm 0600 para --output~~ — feito.
  • [x] ~~Redigir/mascarar valores de segredo~~ — feito (redactSecretText).
  • [ ] Tratar relatórios SARIF como artefatos restritos (não públicos por padrão em CI).
  • [ ] Documentar retenção/expurgo dos artefatos.
  • Ferramentas. controles de artifact storage do CI, DLP, ACL.

R9 — Crosswalk / baseline adulterado (B3)

  • Descrição. O crosswalk (YAML rule→control) e o .quorumignore influenciam correlação e supressão. resolveCrosswalkDir faz fallback automático para /opt/quorum/crosswalk quando ./crosswalk não existe (e a flag não foi passada). Um baseline adulterado pode suprimir findings reais; um crosswalk adulterado pode forçar merges incorretos.
  • Impacto. Ocultação de findings (supressão indevida) ou perda de granularidade.
  • Probabilidade. Baixa–Média.
  • Severidade. Médio.
  • Mitigação (presente). Supressões são sempre logadas (filtered: N suppressed by baseline …); princípio false split > false merge + crosswalk derivado de output real (favorece false split) limitam merge silencioso; o crosswalk bundled (aws.yaml, azure.yaml, gcp.yaml, k8s.yaml) é parte da imagem assinada — e agora atestada (R4) — e o log mostra crosswalk=N rules.
  • Gaps. Baseline/crosswalk do usuário não são assinados nem revisados; o fallback de diretório pode mascarar um crosswalk vazio.
  • Recomendação.
  • [ ] Versionar .quorumignore/crosswalk no repo com revisão obrigatória (CODEOWNERS).
  • [ ] Alertar quando 0 regras são carregadas (já há log de crosswalk=N).
  • [ ] Opcional: assinar/validar crosswalk customizado.
  • Ferramentas. code review/CODEOWNERS, git history, política de PR.

R10 — RCE via parser/dependência de scanner trojanizado (B5 ⟶ B2)

  • Descrição. Combinação de R4+R2: se um binário de scanner for trojanizado, ele roda dentro do trust boundary e sua saída alimenta os parsers. É o pior caso de cadeia.
  • Impacto. Comprometimento total do container/runner.
  • Probabilidade. Baixa.
  • Severidade. Médio (alto impacto, baixa probabilidade dado o pin/checksum majoritário).
  • Mitigação (presente). Pin por digest (trivy/kics + bases) e checksum SHA256 dos demais binários baixados (kubescape/polaris/kube-score/tfsec/terrascan/regula/conftest/dockle); cosign + SLSA + SBOM atestados na imagem Quorum; action.yml faz cosign verify antes de rodar; cap de stdout (512 MiB) limita o dano de saída adversária do binário.
  • Recomendação. Ver R4 (fechar grype/syft/checkov) e executar a imagem com privilégios mínimos (rootless, --read-only, --cap-drop=ALL, sem --privileged).
  • Ferramentas. cosign, slsa-verifier, runtime hardening do container.

R11 — Egress da camada de assessoria / handling da saída de IA (B7, opt-in)

  • Mitigado por design (desligado por padrão; a superfície só existe sob --advice).
  • Descrição. A camada de assessoria (--advice) é presentation-only: anexa templates de remediação, referências OWASP e — quando um provedor é selecionado — uma recomendação em linguagem natural. Ela nunca toca correlationKey, fingerprint, o score de confiança, a severidade agregada nem o gate fail-on. Sem --advice a saída é byte-idêntica. Três superfícies têm peso de segurança:
  • Egress (provedor remoto). --advice-provider=remote chama uma API externa (QUORUM_ADVICE_API_KEY). Como dados saem do host, é gated por consentimento explícito (--advice-allow-egress), bloqueado por --offline ("--advice-provider=remote is disabled by --offline") e recusa --fix (que faria upload de código). Só o finding normalizado (título, path, controle) é enviado — nunca código-fonte. As Fases 0 (templates determinísticos/internal/enrich) e 2 (RAG lexical/semântico sobre o corpus OWASP pinado por digest, internal/rag) rodam totalmente local, sem egress.
  • Handling da saída de IA. A saída do LLM é texto não confiável. É tratada como advisory only e cada anexo de IA é rotulado "AI-generated, advisory only"; nunca muda o gate. --advice-provider=local (ex.: Ollama) e --fix=suggest são reprodutíveis (temperature=0 + cache em disco por fingerprint+provider+model) e degradam graciosamente: se o modelo estiver inacessível, o relatório sai sem assessoria de IA e o scan nunca falha.
  • Verify-the-fix. --fix=suggest propõe um patch que deve passar por um re-scan: aplica numa cópia temporária, reescaneia com o mesmo scanner, mantém a sugestão só se o finding sumir e o arquivo ainda parsear. Nunca aplica automaticamente.
  • Impacto. (a) Com o provedor remoto, o finding normalizado é divulgado a uma API externa (metadados sobre a postura do alvo). (b) Texto de IA forjado/alucinado se o endpoint for malicioso — mas é advisory-only e não pode virar o gate nem a confiança.
  • Probabilidade. Baixa (toda a camada é opt-in e desligada por padrão; o remoto ainda exige consentimento + é bloqueado offline).
  • Severidade. Baixo.
  • Mitigação (presente). Desligado por padrão (saída byte-idêntica sem --advice); presentation-only (isolado de correlação/confiança/gate); egress remoto gated por consentimento (--advice-allow-egress) e bloqueado por --offline; o remoto recusa --fix; só o finding normalizado é enviado, nunca fonte; anexos de IA explicitamente rotulados; degradação graciosa se o modelo estiver inacessível; verify-the-fix nunca aplica automático; o knowledge pack é pinado por digest e atestado (R4). Novas métricas quorum_advice_enriched, quorum_advice_provider, quorum_advice_fix{stage=proposed|verified} tornam o comportamento da camada observável apenas sob --advice.
  • Gaps remanescentes. O provedor remoto ainda divulga metadados do finding a um terceiro por design (mitigue com --advice-provider=local ou --offline); a confiança no texto de IA é do operador (é advisory, nunca gating).
  • Recomendação.
  • [x] ~~Gated o egress remoto por consentimento explícito e bloqueá-lo sob --offline~~ — feito.
  • [x] ~~Manter a camada presentation-only e desligada por padrão~~ — feito.
  • [ ] Preferir --advice-provider=local para alvos sensíveis; manter --offline para air-gap.
  • [ ] Rotacionar/escopar QUORUM_ADVICE_API_KEY; nunca ecoar em log (action.yml a repassa via env, não imprime).
  • Ferramentas. proxy egress allowlist, gestão de segredos para a API key, harness internal/evals (mede cobertura de remediação determinística, relevância de referências OWASP e a taxa de verify-the-fix no CI). Ver IA e a proposta de design 21-proposta-ia.md.

5. OWASP Top 10 (2021) — mapeamento ao contexto CLI/Docker

Categoria Aplicabilidade ao Quorum Risco(s) Estado
A01 Broken Access Control Parcial — sem authz própria; acesso = FS/CI runner R3, R7, R8 Depende do ambiente (perms 0o600)
A02 Cryptographic Failures OSV via HTTPS; artefatos 0o600; segredos redigidos R6, R8 OK (TLS + redaction)
A03 Injection Central — command/argument injection R1 ✅ Mitigado (argv, sem shell; recusa target -)
A04 Insecure Design false split > false merge, status transparente R2, R9 Forte por design
A05 Security Misconfiguration Perms 0o600 (relatório+cache), fallback de crosswalk, Docker run R3, R7, R8 ✅ Melhorado (perms fechadas)
A06 Vulnerable & Outdated Components 12 scanners/imagem base bundled R4, R10 ✅ Mitigado (pin/checksum + SLSA/SBOM; resta grype/syft/checkov)
A07 Identification & Auth Failures N/A — sem contas/auth N/A
A08 Software & Data Integrity Failures cosign + SLSA + SBOM atestados (imagem, binários, knowledge pack); cache versionado; baseline não assinado R4, R7, R9, R11 ✅ Mitigado em grande parte
A09 Logging & Monitoring Failures Logs de status/supressão (text/json); métricas Prometheus R9 OK (escopo CLI)
A10 SSRF OSV.dev (BaseURL fixa; id validado + PathEscape); endpoint remoto de assessoria opt-in é gated por consentimento + bloqueado por --offline R6, R11 ✅ Mitigado (resta vazamento de metadados)

6. OWASP API Security Top 10 (2023) — mapeado ao CLI

O Quorum não expõe API REST/HTTP (sem servidor, sem endpoints) — N/A por arquitetura. É cliente da API OSV.dev (e, apenas sob --advice-provider, de um endpoint de IA). O Top 10 de API se aplica por analogia a esse consumo:

Item Aplicabilidade Observação
API1 BOLA N/A Sem objetos/usuários expostos
API2 Broken Authentication N/A OSV é endpoint público sem auth (a API remota de assessoria opt-in usa QUORUM_ADVICE_API_KEY)
API3 BOPLA Parcial Quorum confia em campos da resposta OSV (R6) — tratados como não confiáveis; saída de IA é advisory-only (R11)
API4 Unrestricted Resource Consumption ✅ Mitigado Timeout 8s + MaxRetries=2 + backoff no cliente OSV
API5 BFLA N/A Sem funções/níveis
API6 Unrestricted Business Flows N/A
API7 SSRF ✅ Mitigado BaseURL fixa; id validado + PathEscape (R6); resta vazamento de metadados
API8 Security Misconfiguration Parcial --offline para air-gap; assessoria remota desligada + gated por consentimento (R11)
API9 Improper Inventory N/A Um único endpoint conhecido
API10 Unsafe Consumption of APIs ✅ Mitigado id validado/encodado, resposta tratada como não confiável (R6); texto de IA rotulado advisory-only (R11)

Proposta futura (claramente separada): se o Quorum vier a expor uma API/daemon, este Top 10 passa a ser primário (authn/z, rate limit, BOLA). Hoje é N/A por design.


7. OWASP ASVS 5.0 — controles aplicáveis ao escopo

Nível alvo recomendado: ASVS L1 (com L2 onde aplicável a ferramenta de linha de comando).

Capítulo ASVS Requisito relevante Estado no Quorum
V1 Encoding/Sanitization Tratar saída de scanner, resposta OSV e saída de IA como não confiáveis Parcial (R2: cap + redaction feitos; sanitização SARIF pendente; R6 id encodado; R11 IA advisory-only)
V2 Validation Validar target, --output, --log-format, id OSV ✅ Coberto (R1/R3/R6); resta validação semântica de imagem/path
V5 File Handling Path traversal em --output/cache --output (R3, Clean+0o600) e cache aliases.json (R7, 0o600+schema) mitigados
V8 Data Protection Findings/secrets em repouso ✅ Mitigado (0o600 + redaction, R8)
V10 Communication TLS para OSV OK
V12 Secure Comms / Egress --offline, allowlist de host, consentimento de egress de assessoria Parcial (R6: --offline ok; allowlist pendente; R11: egress remoto gated + bloqueado offline)
V14 Configuration Pin de dependências, hardening de build ✅ Mitigado em grande parte (R4: pin/checksum + SLSA/SBOM + knowledge pack atestado)
V50 (Authn) / V51 (Session) Autenticação/sessão N/A — sem usuários

8. OWASP SAMM — maturidade do processo

Função SAMM Prática Evidência no projeto Maturidade alvo
Governance Education & Guidance DESIGN.md §12/§14, este doc, GitHub Pages (MkDocs Material) 2
Design Threat Assessment STRIDE/DREAD deste doc 2
Design Security Requirements Princípio false split > false merge; 0 findings ≠ safe 2
Implementation Secure Build Dockerfile pin/checksum, GoReleaser, cosign + SLSA + SBOM + knowledge pack atestado 3
Implementation Software Dependencies Pin/checksum de 10/12 scanners; resta grype/syft/checkov 2
Verification Security Testing Contract tests por adapter; cobertura no CI; E2E; harness internal/evals 2→3
Verification Architecture Assessment Trust boundaries (§1) 2
Operations Incident Mgmt Status transparente por scanner; métricas Prometheus 1→2 (definir runbook)

9. OWASP MASVS — N/A

O Quorum não é aplicação móvel: não há app iOS/Android, sem armazenamento em dispositivo, sem IPC móvel, sem WebView. Todos os controles MASVS (STORAGE, CRYPTO, AUTH, NETWORK, PLATFORM, CODE, RESILIENCE) são N/A. Os temas equivalentes de proteção de dados em repouso e rede são cobertos por ASVS (§7) e pelos riscos R6/R8.

10. OWASP LLM Top 10 — aplica-se apenas quando a camada de IA opt-in está habilitada

O núcleo do Quorum não usa IA/LLM: correlação e consenso são determinísticos (correlationKey, Fingerprint = sha256(correlationKey), fórmula de confiança baseada em diversidade de engines/severidade/confirmação autoritativa). A camada de assessoria (--advice) é opt-in e desligada por padrão, e mesmo então a Fase 0 (templates curados, internal/enrich) e a Fase 2 (RAG sobre o corpus OWASP pinado por digest, internal/rag) são determinísticas e sem modelo. Um LLM de fato só é consultado sob --advice-provider=local|remote (internal/advisor). Portanto o LLM Top 10 não se aplica ao núcleo determinístico; quando a camada de IA está habilitada, os itens relevantes já são endereçados pelo design do R11:

Risco LLM Onde se aplica Estado com IA habilitada
LLM01 Prompt Injection O texto de um finding hostil vira parte do prompt Saída advisory-only; nunca gating; temperature=0
LLM02 Insecure Output Handling Texto do LLM flui para o relatório Rotulado "AI-generated, advisory only"; não sanitizado para o gate/confiança
LLM05 Supply Chain Corpus OWASP / modelo Corpus pinado por digest + atestado (R4); modelo local roda on-host
LLM06 Sensitive Information Disclosure Egress do provedor remoto Gated por consentimento (--advice-allow-egress) + bloqueado por --offline; só o finding normalizado é enviado, nunca fonte; --fix recusado no remoto (R11)
LLM10 Model Theft N/A O Quorum não embarca modelo próprio

Enquadramento honesto. Sem --advice não há IA alguma e LLM01–LLM10 são inertes. Com --advice-provider=local|remote a camada permanece presentation-only, então um modelo comprometido pode, no máximo, produzir texto de assessoria enganoso — não pode mudar correlationKey, fingerprint, confiança, severidade agregada nem o gate fail-on. Ver IA e 21-proposta-ia.md.


11. MITRE ATT&CK — TTPs relevantes (atacante mira o pipeline)

Tática Técnica Relação com o Quorum Mitigação
Initial Access T1195 Supply Chain Compromise Binário de scanner / imagem trojanizada (R4/R10) Pin/checksum, cosign + SLSA + SBOM + knowledge pack atestado
Execution T1059 Command/Scripting Interpreter Shell-out (R1) argv sem shell; recusa target -
Execution T1203 Exploitation for Client Execution Parser explorado por saída forjada (R2) parsing estruturado, cap 512 MiB, fuzzing
Persistence T1554 Compromise Host Software Binaries Substituir scanner no PATH (modo :slim) imagem :full assinada; PATH controlado
Defense Evasion T1027/T1070 Obfuscation/Indicator Removal Baseline/crosswalk adulterado (R9) supressões sempre logadas
Discovery T1082/T1083 System/File Discovery Findings revelam superfície (R8) perms 0o600, redaction, restringir artefatos
Exfiltration T1041 Exfil over C2 / web IDs vazados via OSV (R6); metadados do finding via assessoria remota (R11) --offline, id validado, egress allowlist; assessoria remota gated + bloqueada offline
Impact T1499 Endpoint DoS Alvo gigante / zip bomb (R5) cap de alvo 20 GiB, cap de stdout 512 MiB, timeout

12. MITRE ATLAS — N/A (núcleo); camada de IA opt-in anotada

ATLAS cobre ameaças a sistemas de machine learning (evasão de modelo, envenenamento de dados de treino, extração de modelo). O núcleo do Quorum não possui componente de ML/IA (ver §10), portanto nenhuma técnica ATLAS se aplica a ele. O envenenamento relevante aqui é de cache de aliases (R7), tratado como tampering de dados clássico, não como ML data poisoning. Quando a camada de IA opt-in está habilitada (--advice-provider=local|remote), o Quorum atua como cliente de um modelo externo, não treina/serve um, então ATLAS ainda mal se aplica — a preocupação residual (saída de modelo não confiável, egress ao provedor) é coberta pelo R11, e o corpus RAG é pinado por digest + atestado (R4).


13. LINDDUN — privacidade

Dados pessoais processados pelo Quorum são mínimos e incidentais (não há contas; pode haver metadados em paths/segredos detectados). Mapeamento:

Ameaça LINDDUN Aplicabilidade Risco Mitigação
Linkability Baixa IDs de vuln consultados na OSV correlacionáveis --offline
Identifiability Baixa Paths/secrets podem conter dados pessoais min-severity, redaction (redactSecretText)
Non-repudiation N/A Sem ações de usuário a repudiar
Detectability Média Consulta OSV revela existência de vuln (R6) --offline, mirror interno
Disclosure of info Média Relatório/secrets expostos (R8); metadados do finding a um provedor de assessoria remoto (R11) perms 0o600, redaction, restrição de artefato; --advice-provider=local/--offline
Unawareness Baixa documentação (GitHub Pages)
Non-compliance Média LGPD se findings contiverem dado pessoal ver §19

14. NIST Cybersecurity Framework (CSF 2.0)

Função Estado Evidência / lacuna
GOVERN Parcial Princípios e riscos documentados; falta runbook de incidente
IDENTIFY Bom Trust boundaries, inventário de dependências (Dockerfile), SBOM atestada
PROTECT Bom cosign + SLSA + SBOM + knowledge pack atestado, --offline, perms 0o600, caps de DoS; resta pin de grype/syft/checkov
DETECT Bom (escopo) Status por scanner, 0 findings ≠ safe, supressões logadas, métricas Prometheus
RESPOND Inicial Mensagens acionáveis (OOM/probe); sem processo formal
RECOVER N/A→Inicial Ferramenta stateless; rebuild da imagem refaz estado

15. NIST SP 800-53 (controles selecionados)

Família/Controle Aplicação Estado
AC-3/AC-6 Least Privilege Rodar container rootless, --cap-drop=ALL Recomendado (gap operacional)
AU-2/AU-12 Audit Logging Logs de execução/supressão (text/json); métricas OK (escopo)
CM-7 Least Functionality --scanners limita superfície; assessoria desligada por padrão OK
SA-12 Supply Chain Protection Pin/checksum + cosign + SLSA + SBOM + knowledge pack atestado ✅ Mitigado em grande parte (R4)
SC-7 Boundary Protection Egress só para OSV; --offline; assessoria remota gated + bloqueada offline Parcial (R6/R11)
SC-8 Transmission Confidentiality TLS OSV OK
SI-7 Software/Info Integrity Assinatura/atestação de artefatos (imagem, binários, knowledge pack); cache versionado (0o600) ✅ Mitigado (R4/R7)
SI-10 Input Validation Validação de target/output/id/log-format ✅ Coberto (R1/R3/R6)

16. CIS Controls v8 (relevantes)

Control Aplicação Estado
2 Inventory of Software Dockerfile lista scanners/versões; SBOM atestada OK
4 Secure Configuration Hardening de container, perms 0o600 ✅ Melhorado
7 Continuous Vuln Mgmt É o propósito do Quorum (consenso, 12 scanners) Core
8 Audit Log Mgmt Logs (text/json) + métricas Prometheus OK
16 Application Software Security SDL deste doc, contract tests, cobertura no CI, internal/evals Parcial
18 Penetration Testing Fuzzing de parsers recomendado Gap

17. ISO/IEC 27001:2022 (Anexo A — controles relevantes)

Controle Tema Estado
A.5.23 Segurança em serviços cloud Uso de OSV.dev; API remota de assessoria opt-in Parcial (--offline, egress gated por consentimento)
A.8.8 Gestão de vulnerabilidades técnicas Produto-fim Core
A.8.28 Codificação segura argv, parsing estruturado, caps, redaction ✅ Melhorado
A.8.30 Outsourced/3rd-party 12 scanners terceiros bundled ✅ Mitigado em grande parte (pin/checksum)
A.5.31 Requisitos legais (LGPD) Dados em findings Ver §19
A.8.16 Monitoramento Status por scanner + métricas OK

18. PCI DSS v4.0

O Quorum não armazena/processa dados de cartão (não há CHD/SAD). É ferramenta de apoio à conformidade, não um sistema no escopo CDE.

Requisito Relação Estado
Req 6.2/6.3 Software seguro e patching Quorum ajuda a evidenciar SCA/IaC/misconfig/K8s Apoio
Req 6.4.3 Componentes de software SBOM atestada/pin ✅ Apoio reforçado
Req 11.3 Detecção de vulnerabilidades Scans de consenso em CI (12 scanners) Apoio
Req 12 Políticas Integrar Quorum ao processo Organizacional
Demais (1–4, 7–10) N/A — sem CHD, sem rede CDE N/A

19. LGPD (Lei 13.709/2018)

O Quorum não coleta dados pessoais de usuários (sem contas, sem telemetria de usuário; o --metrics grava apenas contagens agregadas). Risco residual: dados pessoais podem aparecer incidentalmente em findings (paths, segredos/credenciais detectados, e-mails em código).

Aspecto Situação Ação
Coleta direta de dado pessoal Nenhuma
Dado pessoal incidental em relatórios Possível (R8) Tratar relatório como restrito (0o600); redaction de segredos
Transferência internacional Consulta a OSV.dev (EUA) envia apenas IDs de vuln validados, não dado pessoal; o provedor remoto de assessoria opt-in envia apenas o finding normalizado, nunca fonte (R11) --offline para air-gap; --advice-provider=local
Base legal / minimização Saída default em stdout; --min-severity Reter o mínimo
Direitos do titular N/A (sem cadastro)

20. Mitigações presentes vs. gaps (resumo executivo)

quadrantChart
  title Mitigações: cobertura vs. esforco para fechar gaps
  x-axis "Baixa cobertura atual" --> "Alta cobertura atual"
  y-axis "Baixo esforco" --> "Alto esforco"
  quadrant-1 "Manter"
  quadrant-2 "Investir"
  quadrant-3 "Quick wins"
  quadrant-4 "Planejar"
  "argv sem shell + target - (R1)": [0.88, 0.2]
  "cosign + SLSA + SBOM (R4)": [0.82, 0.55]
  "--offline + id validado (R6)": [0.85, 0.2]
  "status transparente (R5)": [0.85, 0.25]
  "--output Clean+0600 (R3)": [0.85, 0.2]
  "cache 0600 + schema (R7)": [0.8, 0.2]
  "redaction de segredos (R8)": [0.8, 0.25]
  "caps de DoS 512MiB/20GiB (R5)": [0.82, 0.3]
  "assessoria opt-in + gate de egress (R11)": [0.86, 0.22]
  "pin grype/syft/checkov (R4)": [0.4, 0.45]
  "sanitizacao SARIF (R2)": [0.3, 0.4]

Já presentes (forças): - Shell-out sem sh -c — argumentos via argv (exec.CommandContext); target iniciando com - recusado (validateTargetRef). - Parsing estruturado por struct + contract tests por adapter (12 adapters) com fixtures versionadas; cap de stdout em 512 MiB (capWriter); redaction de segredos. - --offline desliga OSV; cliente OSV com timeout 8s / retry / backoff e degradação graciosa; id validado (vulnIDPattern) + url.PathEscape. - --output normalizado (filepath.Clean) e escrito 0o600; cache aliases.json 0o600 + schemaVersion; cap de tamanho de alvo em 20 GiB (checkTargetSize). - Distribuição endurecida: cosign keyless (OIDC) com retry + atestação SLSA build-provenance + SBOM SPDX atestada (imagem e por-binário) + knowledge pack de assessoria atestado (job knowledge), re-verificados no release; action.yml faz cosign verify antes de rodar e auto-monta o Docker socket em type: image; tag móvel v0 avançada automaticamente por tag-major.yml. - Pin por digest de trivy/kics + bases; checksum SHA256 de kubescape/polaris/kube-score/tfsec/ terrascan/regula/conftest/dockle; grype DB pré-cacheado e não-expirável. - Timeout por scanner + probe de versão que distingue timeout/OOM/ausente; status por scanner, "0 findings is not proof of safety", supressões logadas; logs text/json + métricas Prometheus (--metrics). - Design false split > false merge + crosswalk derivado de output real limitam ocultação. - Camada de assessoria opt-in e presentation-only (byte-idêntica sem --advice); egress remoto gated por consentimento (--advice-allow-egress) + bloqueado por --offline; só o finding normalizado é enviado, nunca fonte; verify-the-fix nunca aplica automático; internal/evals mede cobertura/relevância/taxa-de-fix no CI.

Gaps prioritários (backlog acionável): - [ ] R4 Fixar install.sh da Anchore (grype/syft) por commit SHA; pip install --require-hashes para o Checkov; pinar a versão do cosign no action.yml. - [ ] R2 Sanitizar/escapar campos textuais (Title/Description/Location) antes do SARIF consumido por UIs de terceiros; adicionar fixtures adversárias. - [ ] R5 Limite de profundidade de descompressão (além do cap de tamanho on-disk). - [ ] R6 Allowlist de host / mirror interno de OSV via env; documentar --offline. - [ ] R7 TTL e checksum/assinatura por entrada de cache; --no-cache em CI efêmero. - [ ] R9 Assinar/validar crosswalk e baseline customizados; alertar em 0 regras.


21. Checklist de hardening operacional (CI/Docker)

  • [ ] Executar quorum:full rootless, --read-only, --cap-drop=ALL, sem --privileged.
  • [ ] Impor --memory, --pids-limit e tmpfs em /tmp (complementa o cap de alvo/stdout, R5).
  • [ ] Restringir egress do runner a api.osv.dev (ou usar --offline).
  • [ ] Verificar a imagem com cosign verify (default do action.yml) e a atestação SLSA/SBOM no consumidor (gh attestation verify); verificar também o knowledge pack (gh attestation verify knowledge/owasp/corpus.yaml).
  • [ ] Pinar a imagem por @sha256 no pipeline, não pela tag :full (o v0 do action é móvel por design e avança a cada release).
  • [ ] Tratar relatórios SARIF (0o600) como artefatos restritos; definir retenção/expurgo.
  • [ ] Não persistir aliases.json entre jobs não confiáveis (ou --cache "").
  • [ ] Limitar scanners com --scanners ao necessário (least functionality / CM-7).
  • [ ] Ajustar QUORUM_MAX_OUTPUT_BYTES / QUORUM_MAX_TARGET_BYTES só quando um alvo legítimo exigir; manter os defaults (512 MiB / 20 GiB) caso contrário.
  • [ ] Passar chaves de plataforma (ex.: Prisma/Bridgecrew) via QUORUM_CHECKOV_ARGS/input checkov-args — nunca em log (o action.yml não ecoa esses envs).
  • [ ] Manter a camada de assessoria desligada a menos que necessária; para alvos sensíveis preferir --advice-provider=local e nunca combinar assessoria remota com --fix; passar QUORUM_ADVICE_API_KEY via input advice-api-key, nunca em log.

Premissas

  • Versão analisada: v0.8.3 (revisão 2026-07-04), a partir do código em main. Afirmações foram verificadas em internal/adapter/adapter.go (runCmd, capWriter, redactSecretText, extraArgs), internal/adapter/{trivy,grype,conftest}.go e demais adapters (12 no total), cmd/quorum/scan.go (emit/runScan/validateTargetRef/ checkTargetSize + o wiring das flags de assessoria), internal/alias/osv.go (vulnIDPattern/PathEscape), internal/cache/store.go (schemaVersion/0o600), os pacotes de assessoria internal/{enrich,rag,advisor,evals}, knowledge/*.yaml + knowledge/owasp/corpus.yaml, Dockerfile.full, action.yml, .github/workflows/release.yml e DESIGN.md (§12, §14).
  • O DESIGN.md traz a discussão de supply chain em §12 (CLI & Docker) e os riscos em §14 (Riscos e mitigações); tratei ambos.
  • Confirmado que não há shell intermediário no shell-out (sem sh -c); a injeção de comando clássica (R1) está mitigada, e o resíduo de argument injection (target iniciando com -) também foi fechado por validateTargetRef.
  • Os scores DREAD são estimativas qualitativas para priorização, não medições empíricas; a coluna "Estado" reflete o código lido nesta revisão (muitos itens antes "Gap/Parcial" foram promovidos a "Mitigado").
  • Afirmações sobre cosign, SLSA, SBOM, o knowledge pack atestado, GHCR :full/:slim, GoReleaser e tag v0 baseiam-se em release.yml, action.yml e Dockerfile.full lidos diretamente (ver também Supply Chain).
  • Assume-se a camada de assessoria (--advice) opt-in e desligada por padrão e presentation-only: sem ela a saída é byte-idêntica e nenhuma IA roda; ela nunca toca correlationKey/fingerprint/confiança/severidade agregada/o gate fail-on. O provedor remoto é o único caminho de egress e é gated por consentimento (--advice-allow-egress) e bloqueado por --offline; envia apenas o finding normalizado, nunca fonte. Esse enquadramento é o motivo de o OWASP LLM Top 10 ser tratado como condicional (§10) em vez de simplesmente N/A.
  • Assume-se que o relatório SARIF pode ser consumido por UIs de terceiros (GitHub code scanning), o que motiva a recomendação remanescente de sanitização de campos textuais (R2).
  • O consenso de MISCONFIG/IaC e K8S_POSTURE apoia-se em crosswalk derivado de output real (favorecendo false split); RBAC segue single-engine (kubescape RBAC exige contexto de cluster) — documentado como decisão, não como lacuna.
  • Frameworks marcados N/A (MASVS, API REST própria e a maioria do PCI DSS) o são por ausência, respectivamente, de app móvel, de servidor HTTP e de dados de cartão. MITRE ATLAS e o OWASP LLM Top 10 são N/A para o núcleo determinístico e passam a ser parcialmente relevantes apenas quando a camada de IA opt-in está habilitada — reavaliar caso o escopo do produto mude (vide "Propostas futuras").