21. Proposal — Advisory AI layer (opt-in)¶
Reference version: v0.8.3 — proposal from 2026-07-04.
Status: PHASES 0, 1, 2 AND 3 IMPLEMENTED. Phase 0 (
--advice: templates + OWASP references,internal/enrich) — deterministic, no AI. Phase 2 (RAG-as-artifact: OWASP corpus digest-pinned inknowledge/owasp/, deterministic lexical retrieval by default and semantic via embeddings when available,internal/rag) — also deterministic and runs under--advice. Phase 1 (--advice-provider=local+--fix=suggest: local LLM recommendation, grounded on the RAG corpus, plus a patch with verify-the-fix,internal/advisor) — opt-in, off by default. Phase 3 (--advice-provider=remote: external API, egress-gated) — opt-in, off by default, with the guardrails below. The core stays 100% deterministic and, without the flags, output is byte-identical.
Cross-links: 13. AI (as-is) · 05. Data model · 06. Interfaces (CLI) · 12. Security · DESIGN.md.
Scope of this proposal — three requested features:
- AI recommendations (local-first).
- Automatic remediation generation.
- RAG over OWASP documentation.
1. Anchor principle (non-negotiable)¶
Quorum's differentiator is being deterministic, auditable and air-gapped: the
same input produces the same Fingerprint = sha256(correlationKey), and the
gate's pass/fail decision is 100% reproducible. Generative AI is probabilistic.
So this entire proposal obeys a single rule:
The AI layer is advisory and optional. It NEVER touches
correlationKey,fingerprint,confidence, aggregatedseverity, or the gate's exit-code decision. It runs after consensus, writes into new fields markedaiGenerated/advisory, and can be removed without changing a single byte of the core.
Distribution corollary: local-first. For a tool that runs in air-gapped CI
and must not leak source code, the local provider is the default and the
remote one is the exceptional opt-in (with explicit egress, blockable by
--offline).
2. The three features — verdict¶
| Feature | Value | Main risk | How the proposal neutralizes it |
|---|---|---|---|
| 1. AI recommendations | High (triage/prioritization; scanner descriptions are terse) | Hallucination; non-determinism; prompt-injection via finding content | Local-first; temperature=0+seed; cache by fingerprint; advisory labeling |
| 2. Automatic remediation | Very high for IaC/K8s/Dockerfile | Wrong patch "that looks safe"; HCL that does not even parse | 2 levels (deterministic template + AI); verify-the-fix loop; --fix=suggest (never apply) |
| 3. RAG over OWASP | High (grounds 1 and 2 in a citable source) | A live vector DB clashes with "no DB, air-gapped, deterministic" | RAG-as-artifact: digest-pinned index, deterministic retrieval |
3. Architecture — where it fits¶
A new advisor stage runs after merge/consensus, over
[]model.MergedFinding, behind a pluggable interface in the spirit of the
adapters (internal/adapter) and the alias resolver (internal/alias).
flowchart LR
C["correlate + consensus<br/>(deterministic — core)"] --> R0["MergedFinding[]"]
R0 --> G{"--advice / --fix<br/>enabled?"}
G -- no --> OUT["report SARIF/JSON/XML<br/>(identical to today)"]
G -- yes --> ADV["advisor stage (optional)"]
subgraph ADV_INNER["advisor (never touches the core)"]
T["Level A: template<br/>remediation by canonicalControl"]
KB["OWASP retrieval<br/>(pinned artifact)"]
L["Level B: local LLM<br/>(temp=0, cache by fingerprint)"]
V["verify-the-fix<br/>re-scan of the fixed file"]
T --> L
KB --> L
L --> V
end
ADV --> OUT2["report + advice/remediation/references fields<br/>(marked aiGenerated/advisory)"]
classDef det fill:#dfd,stroke:#080;
classDef ai fill:#ffe8cc,stroke:#d90;
class C,R0,T,KB,V det;
class L ai;
Proposed interface (internal/advisor):
// Advisor produces advisory guidance for an already-correlated/scored finding.
// An error NEVER fails the scan (graceful degradation, like the OSV client §DESIGN 7).
type Advisor interface {
Advise(ctx context.Context, f model.MergedFinding, kb KnowledgeBase) (Advice, error)
}
// Implementations:
// nullAdvisor — default/off; returns an empty Advice.
// localAdvisor — OpenAI-compatible/Ollama endpoint on localhost.
// remoteAdvisor — opt-in; explicit egress; blocked by --offline.
New fields on MergedFinding (all omitempty, ignored by the gate):
Remediation *Remediation `json:"remediation,omitempty"` // Level A: deterministic template
Advice *Advice `json:"advice,omitempty"` // Level B: AI (aiGenerated:true, advisory:true)
References []DocRef `json:"references,omitempty"` // curated OWASP/CWE
Output mapping:
- SARIF:
Remediation/Advice→result.fixes[].artifactChanges(GitHub renders it as a suggested change);References→result.relatedLocations/help. All marked"AI-generated, advisory only"when it comes from Level B. - JSON: fields directly on the finding. XML: same, optional.
4. Feature 2 — Automatic remediation (two levels)¶
Scope (decided): only IaC / K8s / Dockerfile — where the fix is
well-scoped and Quorum already has Location.File/StartLine/EndLine. SCA/CVE is
out of auto-fix (dependency version bumps are risky and break builds); for SCA,
at most a text "upgrade pkg to ≥ X.Y.Z" as a recommendation, no patch.
Level A — deterministic (no AI), build first¶
Curated fix templates, indexed by canonicalControl (the crosswalk already
resolves the control). Examples:
| canonicalControl | Remediation template |
|---|---|
AVD-AWS-0088 (S3 without SSE) |
injects a server_side_encryption_configuration block |
C-0017 (readOnlyRootFilesystem) |
securityContext.readOnlyRootFilesystem: true |
IMG_HARDENING (Dockerfile without USER) |
adds USER <non-root> |
Properties: auditable, air-gapped, zero hallucination, reproducible. Covers the top-N controls. This layer alone already justifies a release.
Level B — AI (opt-in), for the long tail / contextualization¶
Uses the local LLM to generate a proposed patch against the real file (e.g. a finding without a template, or adapting the template to the specific HCL). Rules:
--fix=suggestonly — emits the diff; never applies (applyis not a default and requires an explicit flag/consent).- Verify-the-fix loop (the biggest anti-hallucination safeguard, and the face of the product): generate patch → re-scan the fixed file with the same scanner → only present it if (a) the finding disappeared and (b) the file still parses. A patch that fails this bar is discarded, not shown.
- Labeling
aiGenerated: true, advisory: true.
flowchart LR
F["finding + real file"] --> P["local LLM proposes patch"]
P --> AP["applies patch to a temporary copy"]
AP --> RS["re-scan (same scanner)"]
RS --> Q{"finding gone<br/>AND file parses?"}
Q -- yes --> SHOW["emit as suggested fix (verified)"]
Q -- no --> DROP["discard (not shown)"]
5. Feature 3 — RAG over OWASP (as-artifact, not as-service)¶
No live vector DB. Two layers:
Level A — curated deterministic map¶
Lookup table CWE / canonicalControl / category → OWASP references, in the same
pattern as crosswalk/*.yaml (versioned YAML, loaded as data). Examples:
| Key | OWASP reference |
|---|---|
CWE-311 (encryption) |
OWASP Cryptographic Failures / Transport Layer Security Cheat Sheet |
K8S_POSTURE |
OWASP Kubernetes Security Cheat Sheet |
CWE-284 (network/access) |
OWASP Access Control / Authorization Cheat Sheet |
100% deterministic, no embeddings, no model. Covers ~80% of the value.
Level B — semantic retrieval over a pinned artifact¶
OWASP corpus (ASVS, Cheat Sheets, Top 10, K8s/IaC) chunked + embedded at build-time into a read-only, versioned, digest-pinned index, distributed as:
- an artifact in
:full(alongside the crosswalk), or - a separate OCI artifact
quorum-knowledge@sha256:…, attested like the base images (SLSA + SBOM).
Retrieval in CI = k-NN over an immutable index → air-gap preserved and reproducible (the index does not change between runs). Embedding runs locally (same runtime as the LLM). No external calls.
License: OWASP content is CC-BY-SA → requires attribution in THIRD_PARTY_NOTICES.md and license preservation in the knowledge artifact.
6. Feature 1 — AI recommendations (reproducible via cache)¶
Natural-language recommendation/triage for an already-correlated finding
(priority, "why this matters here", next steps), grounded on the OWASP
References (Feature 3) and the template (Feature 2). Improvements that make it
auditable:
- Practical determinism:
temperature=0+ fixed seed + model pinned by digest. - Cache by
fingerprint + modelDigest + promptVersionin the same pattern as~/.cache/quorum/aliases.json(0600,schemaVersion). Same finding + same model → same text (served from cache). The LLM's non-determinism becomes cacheable and reproducible. - Labeling
"AI-generated, advisory only"on every output.
7. Flags and configuration¶
Consistent with the existing ones (--offline, --metrics, --log-format):
| Flag | Default | Effect |
|---|---|---|
--advice |
off |
enables the advisory layer (deterministic templates + OWASP references) |
--advice-provider |
none |
none | local | remote |
--advice-endpoint |
http://localhost:11434/v1 |
OpenAI-compatible base URL (local by default; for remote, e.g. https://api.openai.com/v1) |
--advice-model |
qwen2.5-coder:7b |
model id for --advice-provider=local |
--advice-embed-model |
nomic-embed-text |
embedding model for semantic OWASP retrieval / advise-index |
--advice-cache |
~/.cache/quorum/advice.json |
cache file for AI advice (keyed by fingerprint+model) |
--advice-max |
50 |
max findings sent to the AI provider per run (0 = no cap) |
--advice-allow-egress |
off |
consent to send findings off-host: required for --advice-provider=remote |
--fix |
off |
off | suggest (apply is never a default) |
--offline |
(existing) | blocks remote; local stays allowed (it is on-host) |
New subcommand quorum advise-index embeds the OWASP corpus (preserving the
digest pin); after that, scan --advice --advice-provider local auto-picks
semantic retrieval. With no flag enabled, output is byte-identical to today's.
8. Guardrails (expands 13-ia §6)¶
| Guardrail | Rule |
|---|---|
| Explicit opt-in | Everything behind a flag, off by default |
| Untouchable core | AI does not alter correlation, fingerprint, confidence, aggregated severity or exit code |
| Gate determinism | The pass/fail decision stays 100% deterministic |
| Local-first | The local provider is the reference on-host mode; remote needs opt-in and is blocked by --offline |
| Privacy | Send only the normalized finding to the model; remote requires --advice-allow-egress and refuses --fix (no source upload) |
| Anti prompt-injection | Finding content treated as data, sanitized/escaped; never as an instruction |
| Verify-the-fix | Every Level B patch is re-scanned; only shown if the finding is gone and the file parses |
| Labeling | Every model output marked "AI-generated, advisory only" |
| Reproducibility | temperature=0, seed, model pinned by digest, cache by fingerprint |
| Model supply chain | Model artifact and OWASP index pinned by digest and attested (like the base images @sha256, the grype DB, and the SLSA-attested knowledge pack) |
| Cost/limits | Timeout, retries, graceful fallback (OSV client pattern); AI down → report ships without advice, scan never fails |
| Evals | Evaluation suite (internal/evals: remediation coverage, OWASP reference relevance, verify-the-fix rate) runs in CI before promoting any feature to default |
Note — the OWASP LLM Top 10 stops being N/A. 13-ia §4 lists prompt-injection etc. as N/A because there is no AI in the core. When the advisory layer is enabled, that surface reopens: the model artifact becomes a supply-chain dependency (pin/attest) and finding content becomes a prompt-injection vector (sanitize). Reopen the compliance checklist of §5.
9. Model distribution (decided)¶
- The local model ships outside
:slim— only in:fullor downloaded on demand (pinned by digest, checksum-verified, like the scanners inDockerfile.full). The:slim(orchestrator-only, multi-arch) does not bloat. - Default target: a small quantized code-capable model (e.g. Qwen2.5-Coder 7B or
Llama 3.1 8B via Ollama). The shipped
--advice-modeldefault isqwen2.5-coder:7b, guided by evals.
10. Phased roadmap (each phase is useful on its own)¶
| Phase | Delivery | AI? | Risk |
|---|---|---|---|
| 0 ✅ implemented | Remediation templates by canonicalControl + control/category/type → OWASP map (Level A of 2 and 3). Flag --advice, package internal/enrich, knowledge/*.yaml |
No | Zero — 100% on-brand |
| 1 ✅ implemented | localAdvisor (OpenAI-compatible/Ollama): recommendations + patch under --fix=suggest with verify-the-fix, cache by fingerprint, labeling, graceful degradation. Package internal/advisor; flags --advice-provider/-endpoint/-model/-cache/-max, --fix |
Yes (local) | Low (opt-in, verified) |
| 2 ✅ implemented | RAG-as-artifact: OWASP corpus pinned by digest (knowledge/owasp/corpus.yaml), lexical retrieval (default, no model) + semantic via embeddings when available; attaches references and grounds the Phase 1 prompt. Subcommand advise-index, package internal/rag |
Lexical: no; semantic: local | Medium (weight/supply chain) |
| 3 ✅ implemented | remote provider opt-in: authenticated external API (QUORUM_ADVICE_API_KEY), explicit egress (--advice-allow-egress), blocked by --offline, refuses --fix (no source upload); only the normalized finding leaves the host. advisor.NewRemoteClient |
Yes (remote) | Medium (privacy) |
Observability: under --advice, Quorum exports quorum_advice_enriched{kind=remediation|references|recommendation},
quorum_advice_provider{provider}, and quorum_advice_fix{stage=proposed|verified}
(verified/proposed = the verify-the-fix rate).
Recommendation: start with Phase 0, which delivers the largest slice of value at zero risk and keeps the deterministic promise intact.
Assumptions¶
- The as-is state (no AI in the core) lives in 13-ia and remains the honest framing: the core has no AI, and this advisory layer is opt-in and off by default.
- The
advisorlayer runs strictly after consensus, over[]MergedFinding, without feeding back into the core — consistent with the current pipeline (04-arquitetura, 05-modelo-de-dados). - The model's
Location.File/StartLine/EndLineis sufficient to locate the span to fix in IaC/K8s/Dockerfile (verified in internal/model/model.go). - SCA/CVE was deliberately excluded from auto-fix (risk of breaking builds on dependency bumps); decision recorded in this proposal.
- The local model and the OWASP index are treated as supply-chain dependencies
(pin by digest + attest), the same treatment as the base images and the grype DB
(10-infraestrutura, 12-seguranca); the
knowledge pack now carries a SLSA build-provenance attestation each release
(verify with
gh attestation verify knowledge/owasp/corpus.yaml). - "RAG-as-artifact" assumes an immutable versioned index; any update to the OWASP corpus is a new pinned version, never a dynamic runtime fetch.