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,--outputnormalizado +0o600,idOSV validado +PathEscape, cache0o600+ 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 controlesC-####do kubescape para Kubernetes;tfsecautocorrelaciona comtrivypor 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
runCmdexecuta cada scanner comexec.CommandContext(ctx, bin, args...)eminternal/adapter/adapter.go. Os argumentos (incluindotarget.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 tiporepo; 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 -cou 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.CommandContextcom argv; nenhumsh -cno código; o tipo de alvo é resolvido porresolveTargetTypee o ref é sempre posicional. - Mitigação (presente) — argument injection. ✅ Resolvido. Um alvo iniciando com
-(ex.:--config=/etc/...) poderia ser interpretado como flag pelo scanner.runScanvalidatargetno boundary da CLI viavalidateTargetRef(cmd/quorum/scan.go) e recusa qualquer ref iniciando com-(mensagem sugere./-namepara 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(viaextraArgs/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.conftestavalia seu Rego de./policy(ou viaQUORUM_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),semgrepregrago.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.Unmarshaldo 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 comoTitle/Descriptionque 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;runCmdsó 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. ✅
runCmdcapa o stdout viacapWriterem 512 MiB (defaultMaxOutputBytes, ajustável porQUORUM_MAX_OUTPUT_BYTES). Ao ultrapassar o cap, o processo aborta com erro claro em vez de sofrer OOM — ocapWritercontinua drenando o pipe (retornalen(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.parsesó armazena oMatchde um secret finding após passar porredactSecretText(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/Locationna camadareport. - [ ] 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--outputera usado diretamente (os.WriteFile(output, ..., 0o644)) sem normalização, e legível por outros usuários do host. - Impacto. Em CI, um
--outputcontrolado por entrada (ex.: derivado de nome de branch/PR) podia sobrescrever arquivos graváveis pelo runner e expor findings (perm0644). - Probabilidade. Média (em pipelines que montam
--outputa partir de variáveis). - Severidade. Alto.
- Mitigação (presente).
emitaplicafilepath.Clean(output)(colapsa./e../), cria o diretório pai (0o755) e grava o relatório com perm0o600(não mais world-readable). O arquivo de métricas Prometheus (--metrics) segue o mesmoClean, mas grava0o644por conter apenas contagens não sensíveis destinadas a scrape. - Recomendação.
- [x] ~~Normalizar
--outputcomfilepath.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.fulle.goreleaser.yaml). - Descrição. O
Dockerfile.fullcombina três níveis de confiança para os 12 scanners: - Pinado por digest (imagem base):
aquasec/trivy@sha256:…,checkmarx/kics@sha256:…; basesgolang:1.26-alpine@sha256:…ealpine:3.20@sha256:…também pinadas. - Checksum-verificado (SHA256): Dockle, Kubescape (
KUBESCAPE_SHA256pinado, sem fallbackcurl | bash), Polaris, kube-score, tfsec, Terrascan, Regula e Conftest — cada um baixa o artefato +checksums.txte rodasha256sum -c. install.shda Anchore (resíduo): Grype e Syft viaraw.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 viapip 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, sobredist/checksums.txtdo 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 dosbom: truedo 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 (jobknowledgedorelease.yml, sobre um manifesto ordenadoknowledge.sha256). Ele embarca nas imagens, mas também é um artefato de dados autônomo que o consumidor pode verificar comgh 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) fazcosign verifyda imagem:fullantes de executá-la (verify: truepor padrão, via identity-regexp dorelease.yml); auto-monta/var/run/docker.sockemtype: image(evita falso-zero ao escanear imagem local); expõe inputs*-argsedocker-socket; a tag móvelv0(para pin do action) é avançada automaticamente portag-major.ymla cada release semver. THIRD_PARTY_NOTICES.mddocumenta as dependências de terceiros.- Gaps remanescentes. Grype/Syft ainda via
install.shde branch móvel; Checkov viapip installsem--require-hashes; ocosigninstalado peloaction.ymlusareleases/latest/download(mutável). - Recomendação.
- [x] ~~Pin por digest/checksum dos scanners baixados~~ — feito para 10 dos 12 (resta
grype/syft via
install.she checkov via pip). - [x] ~~Publicar SBOM da imagem
:fulle 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.shda Anchore por commit SHA;pip install --require-hashespara o Checkov. - [ ] Pinar a versão do
cosigninstalada noaction.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 porQUORUM_MAX_TARGET_BYTES); oWalkDirpara assim que o cap é cruzado, então repos normais pagam só uma passada leve destat. Alvos de imagem (sem árvore local) são pulados. - Cap de stdout:
capWriterlimita a saída de cada scanner a 512 MiB (ver R2). - Timeout por scanner (
--timeout, default 5m;PerScannerTimeno 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
--timeoutem CI. - [ ] Rodar a imagem com
--memory/--pids-limit/--read-onlyetmpfspara/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. ABaseURLé fixa (não controlável por flag), o que limita SSRF clássico, mas umidadversá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
idnão for validado. - Probabilidade. Baixa–Média.
- Severidade. Médio.
- Mitigação (presente).
--offlinedesliga totalmente o OSV (emrunScan,osvsó é instanciado quando!f.offline; usa apenas aliases locais do scanner + cache);BaseURLfixa; cliente com timeout de 8s,MaxRetries=2e backoff exponencial (base 200ms) sensível aoctx— 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 porurl.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
--offlinepara 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
0o600ao gravar (Putescreve o.tmpcomo0o600antes dorename) — não mais world-readable. - Versionamento de schema:
diskFormat.SchemaVersion(schemaVersion = 1).Opensó adota o arquivo quando ele faz parse E casa o schema EData != 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
0600no 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).
emitescreve com perm0o600(antes0644).- Redaction de valores de segredo: o
Matchde um secret finding do trivy só é persistido apósredactSecretText(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 marcaMatchcomo "sensitive: stored only after redaction". - Saída default é stdout (não persiste por padrão);
--min-severitye--quietreduzem ruído; baseline.quorumignorepermite 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
0600para--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
.quorumignoreinfluenciam correlação e supressão.resolveCrosswalkDirfaz fallback automático para/opt/quorum/crosswalkquando./crosswalknã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 (favorecefalse 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 mostracrosswalk=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.ymlfazcosign verifyantes 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 tocacorrelationKey,fingerprint, o score de confiança, a severidade agregada nem o gatefail-on. Sem--advicea saída é byte-idêntica. Três superfícies têm peso de segurança: - Egress (provedor remoto).
--advice-provider=remotechama 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=suggestsão reprodutíveis (temperature=0+ cache em disco porfingerprint+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=suggestpropõ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étricasquorum_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=localou--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=localpara alvos sensíveis; manter--offlinepara air-gap. - [ ] Rotacionar/escopar
QUORUM_ADVICE_API_KEY; nunca ecoar em log (action.ymla 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
--advicenão há IA alguma e LLM01–LLM10 são inertes. Com--advice-provider=local|remotea camada permanece presentation-only, então um modelo comprometido pode, no máximo, produzir texto de assessoria enganoso — não pode mudarcorrelationKey,fingerprint, confiança, severidade agregada nem o gatefail-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:fullrootless,--read-only,--cap-drop=ALL, sem--privileged. - [ ] Impor
--memory,--pids-limitetmpfsem/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 doaction.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
@sha256no pipeline, não pela tag:full(ov0do 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.jsonentre jobs não confiáveis (ou--cache ""). - [ ] Limitar scanners com
--scannersao necessário (least functionality / CM-7). - [ ] Ajustar
QUORUM_MAX_OUTPUT_BYTES/QUORUM_MAX_TARGET_BYTESsó 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/inputcheckov-args— nunca em log (oaction.ymlnão ecoa esses envs). - [ ] Manter a camada de assessoria desligada a menos que necessária; para alvos sensíveis
preferir
--advice-provider=locale nunca combinar assessoria remota com--fix; passarQUORUM_ADVICE_API_KEYvia inputadvice-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 eminternal/adapter/adapter.go(runCmd,capWriter,redactSecretText,extraArgs),internal/adapter/{trivy,grype,conftest}.goe 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 assessoriainternal/{enrich,rag,advisor,evals},knowledge/*.yaml+knowledge/owasp/corpus.yaml,Dockerfile.full,action.yml,.github/workflows/release.ymleDESIGN.md(§12, §14). - O
DESIGN.mdtraz 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 porvalidateTargetRef. - 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 tagv0baseiam-se emrelease.yml,action.ymleDockerfile.fulllidos 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 tocacorrelationKey/fingerprint/confiança/severidade agregada/o gatefail-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").