Skip to content

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 in knowledge/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:

  1. AI recommendations (local-first).
  2. Automatic remediation generation.
  3. 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, aggregated severity, or the gate's exit-code decision. It runs after consensus, writes into new fields marked aiGenerated/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/Adviceresult.fixes[].artifactChanges (GitHub renders it as a suggested change); Referencesresult.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:

  1. --fix=suggest only — emits the diff; never applies (apply is not a default and requires an explicit flag/consent).
  2. 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.
  3. 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 + promptVersion in 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 :full or downloaded on demand (pinned by digest, checksum-verified, like the scanners in Dockerfile.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-model default is qwen2.5-coder:7b, guided by evals.

10. Phased roadmap (each phase is useful on its own)

Phase Delivery AI? Risk
0implemented 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
1implemented 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)
2implemented 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)
3implemented 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 advisor layer 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/EndLine is 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.