Skip to content

Security

This section is the complete security analysis of Quorum (quorum-sec-scan, v0.8.3), a CLI/Docker consensus security scanning tool. Quorum does not scan artifacts directly: it orchestrates a pool of 12 OSS scanners (trivy, grype, checkov, kics, dockle, kubescape, polaris, kube-score, terrascan, tfsec, regula, conftest), parses the untrusted output of each one, reads arbitrary targets (images, repositories, manifests) and, optionally, queries OSV.dev over the network. This profile — a security tool that runs other tools and ingests untrusted data — concentrates the risk on four boundaries: shell-out, parsing, file I/O and network/supply chain.

As of v0.8.3 there is one more, strictly opt-in surface: the advisory layer (--advice). It is presentation-only — it never touches correlationKey, fingerprint, the confidence score, aggregated severity, or the fail-on gate; without --advice the output is byte-identical. The deterministic core still has no AI. Its optional remote provider is the only path that can send data off-host, and it is consent-gated (--advice-allow-egress) and blocked by --offline — so it gets its own boundary below.

The document models the threats over these boundaries, maps the product against the main frameworks (OWASP, MITRE, STRIDE, DREAD, LINDDUN, NIST, CIS, ISO 27001, PCI DSS, LGPD) and, for each risk, presents Description, Impact, Likelihood, Severity, Mitigation, Recommendation and suggested Tools. Where a framework does not apply to the CLI/Docker scope (MASVS, MITRE ATLAS — and OWASP LLM Top 10, which applies only when the opt-in AI layer is enabled), this is declared N/A with technical justification.

Cross-links: Architecture · Operations/CI · Supply Chain & Releases · AI · DESIGN.md (§12 CLI & Docker, §14 Risks).

Version note. This document was originally written for v0.2.3 and revised for v0.8.3 (revision 2026-07-04). Between those versions, the pool grew from 6 to 12 scanners, consensus was extended to MISCONFIG/IaC and K8S_POSTURE (via a crosswalk derived from real output), most of the hardening gaps this doc used to flag were closed (target - refused, --output normalized + 0o600, OSV id validated + PathEscape, cache 0o600 + schemaVersion, secret redaction, DoS caps, supply chain with attested SLSA + SBOM), and an opt-in advisory layer (--advice, presentation-only, off by default) was added. The history is preserved in the text of each risk.


1. Trust-boundary model

Quorum is a Go process that, starting from user/CI-controlled input, spawns subprocesses and consumes bytes it did not produce. All the analysis below is anchored to this model.

flowchart LR
  subgraph Untrusted["UNTRUSTED input"]
    T[Target: image / repo / k8s]
    OSVresp[OSV.dev response]
    SOUT[scanner stdout/stderr]
    CACHE[(aliases.json on disk)]
    BL[.quorumignore / crosswalk / Rego policy]
  end

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

  subgraph External["Subprocesses / network"]
    SCANNERS[12 scanners: trivy grype checkov kics dockle kubescape polaris kube-score terrascan tfsec regula conftest]
    OSV[(api.osv.dev)]
    AIEP[(AI endpoint: local Ollama / remote API -- only under --advice-provider)]
  end

  T --> SCAN --> ORCH --> ADP --> SCANNERS
  SCANNERS -- untrusted stdout --> 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 + output file + Prometheus metrics]

Trust boundaries:

# Boundary Untrusted input Where in the code
B1 Shell-out target.Ref, scanner names, flags, QUORUM_<NAME>_ARGS internal/adapter/adapter.go:runCmd (exec.CommandContext)
B2 Parsing JSON/SARIF stdout of the 12 scanners internal/adapter/*.go (json.Unmarshal)
B3 File I/O --output, --cache, --baseline, --crosswalk, --metrics, --advice-cache cmd/quorum/scan.go:emit, internal/cache/store.go
B4 Network OSV.dev response internal/alias/osv.go
B5 Supply chain scanner binaries, base image, knowledge pack + crosswalk Dockerfile.full, action.yml, release.yml, GHCR
B6 Resources target size (repo/image), stdout size cmd/quorum/scan.go:checkTargetSize, adapter.capWriter
B7 Advisory / AI (opt-in) LLM output; egress of the normalized finding to a remote endpoint internal/advisor, internal/rag, internal/enrich (only under --advice)

2. STRIDE per boundary

Boundary Spoofing Tampering Repudiation Info Disclosure DoS Elevation
B1 Shell-out ▲ (command injection)
B2 Parsing ▲ (forged output inflates/hides findings) ▲ (giant JSON — capped at 512 MiB)
B3 File I/O ▲ (path traversal/overwrite) ▲ (findings in wrong location)
B4 OSV network ▲ (DNS/MITM) ▲ (forged response) ▲ (leaks queried IDs) ▲ (latency — 8s timeout)
B5 Supply chain ▲ (fake image/binary) ▲ (trojanized binary/pack)
B6 Resources ▲ (zip/img bomb — 20 GiB target cap)
B7 Advisory/AI (opt-in) ▲ (rogue AI endpoint) ▲ (forged recommendation) ▲ (remote provider egress of the finding)

▲ = vector relevant to Quorum. Blanks indicate a vector not applicable to the CLI scope. Row B7 is inert unless --advice is set; the presentation-only design keeps it off the correlation/confidence path.


3. Risk matrix (DREAD)

DREAD scale per criterion 1–10; Score = mean; Severity derived: Critical ≥ 8, High 6–7.9, Medium 4–5.9, Low < 4.

ID Risk Damage Reprod. Exploit. Affected Discover. Score Sev State
R1 Command injection via target/flags 9 3 3 6 4 5.0 Medium ✅ Mitigated (argv, no shell + target - refused)
R2 Parsing malicious scanner output 6 7 5 7 5 6.0 High Partial (byte cap + redaction; SARIF sanitization pending)
R3 Path traversal / overwrite via --output 7 8 7 5 6 6.6 High ✅ Mitigated (Clean + 0o600)
R4 Supply chain (scanner binaries, mutable tag) 9 5 5 8 4 6.2 High ✅ Mostly mitigated (attested SLSA + SBOM + pin/checksum)
R5 DoS via giant target / zip bomb / img bomb 6 6 5 6 6 5.8 Medium ✅ Mitigated (20 GiB target cap + 512 MiB stdout cap + timeout)
R6 SSRF / ID exfiltration via OSV 5 4 3 5 5 4.4 Medium ✅ Mitigated (id validation + PathEscape; metadata leak remains)
R7 Cache poisoning (aliases.json) 5 6 5 5 4 5.0 Medium ✅ Mitigated (0o600 + schemaVersion; signing still absent)
R8 Findings exposure (sensitive report) 6 5 4 6 5 5.2 Medium ✅ Mitigated (0o600 + secret redaction; artifact classification is operational)
R9 Tampered crosswalk/baseline (false merge/suppress) 7 5 4 5 4 5.0 Medium Partial
R10 RCE in a trojanized scanner parser/dep 9 3 3 7 3 5.0 Medium Partial (mitigated by pin/checksum + SLSA)
R11 Advisory-layer egress / AI output handling (opt-in) 5 4 3 4 3 3.8 Low ✅ Mitigated by design (off by default; egress consent-gated + --offline-blocked; presentation-only)

Product principle that reduces class risk: "false split > false merge" — when in doubt, Quorum does not merge findings. That limits the damage of R2/R9 (a forged output tends to produce extra findings, not hide real ones). Consensus for MISCONFIG/IaC and K8S_POSTURE via crosswalk was derived from real output precisely to keep false split > false merge (the crosswalk covers AVD for AWS/Azure/GCP and the C-#### kubescape controls for Kubernetes; tfsec auto-correlates with trivy by emitting native AVD; RBAC stays single-engine because it needs cluster context).


4. Detailed risks

R1 — Command injection via target / flags (B1)

  • Description. runCmd executes each scanner with exec.CommandContext(ctx, bin, args...) in internal/adapter/adapter.go. The arguments (including target.Ref) are passed as an argv slice, with no intermediate shell (/bin/sh -c). There is no string interpolation in the command, so shell metacharacters (;, |, $(), backticks) in a target such as repo; rm -rf / are treated as a literal path, not as a command.
  • Impact. Arbitrary command execution in the Quorum process context (and, under Docker, inside the :full container).
  • Likelihood. Low — requires a regression that reintroduces sh -c, or a scanner that itself interprets the target as shell.
  • Severity. Medium (high impact, low likelihood in the current state).
  • Mitigation (present). exec.CommandContext with argv; no sh -c anywhere in the code; the target type is resolved by resolveTargetType and the ref is always positional.
  • Mitigation (present) — argument injection.Resolved. A target starting with - (e.g. --config=/etc/...) could be interpreted as a flag by the scanner. runScan validates target at the CLI boundary via validateTargetRef (cmd/quorum/scan.go) and refuses any ref starting with - (the message suggests ./-name for a literal path), so no adapter ever receives a leading-hyphen ref. This universal validation was chosen over injecting a per-scanner -- (which would break scanners without -- support).
  • Note — args passthrough. QUORUM_<NAME>_ARGS (via extraArgs/splitArgs) injects extra args into a scanner (e.g. QUORUM_CHECKOV_ARGS="--bc-api-key <key>" to unlock Prisma/Bridgecrew policies). This is operator-controlled (whoever runs the container/CLI), at the same trust level as the flags themselves — not untrusted third-party input. conftest evaluates your Rego from ./policy (or via QUORUM_CONFTEST_ARGS="--policy <dir>").
  • Recommendation.
  • [x] ~~Validate/normalize target.Ref (reject refs starting with -)~~ — done.
  • [ ] (Optional) Validate image reference format and path existence for repos.
  • [ ] Regression test ensuring no adapter uses sh -c.
  • Tools. gosec (G204), semgrep rule go.lang.security.audit.dangerous-exec, golangci-lint.

R2 — Parsing malicious scanner output (B2)

  • Description. Each of the 12 adapters runs json.Unmarshal on the scanner's stdout (e.g. trivy.parse, grype.parse, conftest.parse). A compromised scanner, or a target that induces the scanner to emit adversarial output, can (a) inflate findings, (b) inject content into fields such as Title/Description that later flow into SARIF, or (c) emit huge JSON.
  • Impact. Polluted report; potential XSS/HTML injection if the SARIF is rendered in a third-party UI (GitHub code scanning, etc.); memory consumption.
  • Likelihood. Medium — depends on compromising a scanner or a hostile target.
  • Severity. High.
  • Mitigation (present). Structured parsing into Go structs (unknown fields ignored), not eval; runCmd only treats exit≠0 as an error when stdout is empty (the "findings-found" convention), avoiding an exit code being mistaken for success; the false split > false merge principle limits hiding.
  • Mitigation (present) — memory DoS.runCmd caps stdout via capWriter at 512 MiB (defaultMaxOutputBytes, tunable via QUORUM_MAX_OUTPUT_BYTES). On exceeding the cap the process aborts with a clear error instead of OOMing — capWriter keeps draining the pipe (returns len(p)) so the child does not stall, and truncation is detected after the run.
  • Mitigation (present) — secrets. ✅ Fields carrying text near secrets are redacted: trivy.parse only stores a secret finding's Match after passing it through redactSecretText (keeps locating context, but only the first 4 chars of each long token survive — see R8).
  • Remaining gaps. No explicit sanitization/escaping of generic textual fields (Title/Description/Location) before emitting SARIF for third-party UIs; no schema validation of the output.
  • Recommendation.
  • [x] ~~io.LimitReader/byte cap on scanner stdout (memory DoS)~~ — done (capWriter, 512 MiB).
  • [ ] Sanitize/escape Title/Description/Location in the report layer.
  • [ ] Keep the per-adapter contract tests (internal/adapter/testdata, realdata_test.go) and add adversarial fixtures (giant fields, control unicode, malformed JSON).
  • Tools. go-fuzz/native fuzzing on the parsers, semgrep, SARIF validators.

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

  • Mitigated.
  • Description. Until v0.2.3, in cmd/quorum/scan.go:emit, the --output path was used directly (os.WriteFile(output, ..., 0o644)) without normalization, and readable by other host users.
  • Impact. In CI, an input-controlled --output (e.g. derived from a branch/PR name) could overwrite files writable by the runner and expose findings (perm 0644).
  • Likelihood. Medium (in pipelines that build --output from variables).
  • Severity. High.
  • Mitigation (present). emit applies filepath.Clean(output) (collapses ./ and ../), creates the parent directory (0o755) and writes the report with perm 0o600 (no longer world-readable). The Prometheus metrics file (--metrics) follows the same Clean but writes 0o644 because it holds only non-sensitive counts meant for scraping.
  • Recommendation.
  • [x] ~~Normalize --output with filepath.Clean~~ — done.
  • [x] ~~Reduce the report's permission~~ — done: 0o600.
  • [ ] (Optional) Confine output to an --output-dir/CWD when it comes from untrusted input.
  • [ ] (Optional) O_EXCL/atomic write when not an intentional overwrite.
  • Tools. gosec (G304 file path provided as taint), semgrep.

R4 — Scanner and image supply chain (B5)

  • Mostly mitigated (hardened in release.yml, Dockerfile.full and .goreleaser.yaml).
  • Description. Dockerfile.full combines three trust levels for the 12 scanners:
  • Digest-pinned (base image): aquasec/trivy@sha256:…, checkmarx/kics@sha256:…; the golang:1.26-alpine@sha256:… and alpine:3.20@sha256:… bases are pinned too.
  • Checksum-verified (SHA256): Dockle, Kubescape (KUBESCAPE_SHA256 pinned, no curl | bash fallback), Polaris, kube-score, tfsec, Terrascan, Regula and Conftest — each downloads the artifact + checksums.txt and runs sha256sum -c.
  • Anchore install.sh (residual): Grype and Syft via raw.githubusercontent.com/.../main/install.sh. The installer validates the binary's internal checksum, but the script itself comes from a moving branch (main). Checkov via pip install "checkov==<version>" without --require-hashes.
  • Impact. A trojanized binary running inside the trust boundary → RCE, exfiltration.
  • Likelihood. Low–Medium (reduced by the majority pin/checksum).
  • Severity. High (impact), but likelihood contained.
  • Mitigation (present).
  • :full image (linux/amd64) and :slim (amd64+arm64) published on GHCR, keyless-signed with cosign (OIDC) with retry against Fulcio/Rekor flakiness.
  • SLSA build-provenance attestation (actions/attest-build-provenance) of the image and, for the native binaries, over the GoReleaser dist/checksums.txtre-verified in the release itself (gh attestation verify, with retry).
  • Attested SPDX SBOM (actions/attest-sbom, via syft) for the image, on top of BuildKit's sbom: true; per-binary SBOMs via GoReleaser/syft.
  • Attested advisory knowledge pack. The advisory data — remediation templates and the digest-pinned OWASP corpus (knowledge/*.yaml, knowledge/owasp/corpus.yaml) plus the crosswalk — now gets its own SLSA build-provenance attestation each release (release.yml knowledge job, over a sorted knowledge.sha256 manifest). It ships inside the images but is also a standalone data artifact a consumer can verify with gh attestation verify knowledge/owasp/corpus.yaml.
  • Grype DB pre-cached in the image with GRYPE_DB_VALIDATE_AGE=false (the 1st scan runs offline; the bundled DB does not expire).
  • The composite GitHub Action (action.yml) runs cosign verify on the :full image before executing it (verify: true by default, via the release.yml identity-regexp); auto-mounts /var/run/docker.sock for type: image (avoids a false-zero when scanning a local image); exposes *-args and docker-socket inputs; the moving v0 tag (for action pinning) is advanced automatically by tag-major.yml on every semver release.
  • THIRD_PARTY_NOTICES.md documents third-party dependencies.
  • Remaining gaps. Grype/Syft still via a moving-branch install.sh; Checkov via pip install without --require-hashes; the cosign installed by action.yml uses releases/latest/download (mutable).
  • Recommendation.
  • [x] ~~Pin downloaded scanners by digest/checksum~~ — done for 10 of 12 (grype/syft via install.sh and checkov via pip remain).
  • [x] ~~Publish and verify the :full image SBOM in the release~~ — done (attested SPDX).
  • [x] ~~Attest the advisory knowledge pack~~ — done (knowledge job).
  • [x] ~~Verify the SLSA attestation~~ — done at release; replicate on the consumer (gh attestation verify / cosign verify-attestation).
  • [ ] Pin the Anchore install.sh by commit SHA; pip install --require-hashes for Checkov.
  • [ ] Pin the cosign version installed in action.yml.
  • Tools. cosign, slsa-verifier/gh attestation verify, syft (SBOM), trivy/grype on the image itself, pip-audit, Dependabot/Renovate with digest pinning.

R5 — DoS via giant target, zip bomb, image bomb (B6)

  • Mitigated.
  • Description. Quorum reads arbitrary targets and delegates reading to the scanners. A giant repo, an image with inflated layers, or a malicious compressed file (zip bomb) can exhaust the runner's CPU/memory/disk.
  • Impact. Pipeline hang; runner cost; OOM.
  • Likelihood. Medium.
  • Severity. Medium.
  • Mitigation (present).
  • Target size cap: checkTargetSize (cmd/quorum/scan.go) aborts early if a filesystem target (repo/k8s) exceeds 20 GiB (defaultMaxTargetBytes, tunable/disable-able via QUORUM_MAX_TARGET_BYTES); the WalkDir stops as soon as the cap is crossed, so normal repos only pay a light stat pass. Image targets (no local tree) are skipped.
  • Stdout cap: capWriter limits each scanner's output to 512 MiB (see R2).
  • Per-scanner timeout (--timeout, default 5m; PerScannerTime in the orchestrator) with parallel fan-out isolating each scanner; a version probe that distinguishes timeout / killed(OOM) / not-installed and produces an actionable message ("likely out of memory; raise the container's memory limit"); per-scanner status (ran|skipped|unavailable|error|timeout) makes the DoS visible instead of masking it as "0 findings". Principle "0 findings is not proof of safety".
  • Remaining gaps. The target cap is by total on-disk size, not by decompression depth (a small on-disk zip bomb can inflate inside the scanner); Quorum imposes no ulimit/cgroup of its own (it delegates to the container runtime).
  • Recommendation.
  • [x] ~~Byte cap on stdout and target size limit~~ — done (512 MiB / 20 GiB).
  • [ ] Document/recommend memory limits and --timeout in CI.
  • [ ] Run the image with --memory/--pids-limit/--read-only and tmpfs for /tmp.
  • Tools. Docker/K8s cgroup limits, timeout(1), runner monitor.

R6 — SSRF / exfiltration via OSV (B4)

  • URL-manipulation risk mitigated; passive metadata exfiltration to the provider remains (use --offline).
  • Description. The alias resolver queries https://api.osv.dev/v1/vulns/<id> (internal/alias/osv.go). The <id> comes from the scanner output. Until v0.2.3 it was concatenated into the URL path without sanitization. BaseURL is fixed (not flag-controllable), which limits classic SSRF, but an adversarial id could contain path/query characters.
  • Impact. (a) Passive exfiltration: the list of queried IDs reveals which vulnerabilities exist in the target to a third party (OSV.dev). (b) Residual URL-manipulation risk if id is not validated.
  • Likelihood. Low–Medium.
  • Severity. Medium.
  • Mitigation (present). --offline turns OSV off entirely (in runScan, osv is only instantiated when !f.offline; it then uses only scanner-local aliases + cache); fixed BaseURL; client with an 8s timeout, MaxRetries=2 and exponential backoff (200ms base) sensitive to ctx — graceful degradation: a network failure never breaks the scan (DESIGN §7); CVE is preferred locally before touching the network, reducing the query count.
  • Mitigation (present) — id validation. ✅ The id is validated against ^[A-Za-z][A-Za-z0-9._-]{0,127}$ (vulnIDPattern) and passes through url.PathEscape (defense in depth) before composing the URL; malformed ids are refused without touching the network ("osv: refusing to query malformed vuln id"). This prevents a forged id from injecting path/query segments (../, ?, /).
  • Remaining gaps. No explicit host allowlist beyond the constant; the query still leaks metadata (the list of IDs) to the provider.
  • Recommendation.
  • [x] ~~url.PathEscape(id) + validate the format before the request~~ — done.
  • [ ] Document --offline for air-gapped / sensitive-data environments.
  • [ ] Allow an internal OSV mirror via env, with a host allowlist.
  • Tools. gosec (G107 url taint), egress proxy allowlist, semgrep.

R7 — Cache poisoning of aliases.json (B3)

  • Mitigated as to permission and resilient parsing; integrity/signing still absent.
  • Description. internal/cache/store.go is a JSON KV at ~/.cache/quorum/aliases.json (atomic write via .tmp+rename). A corrupted/tampered file is silently degraded to an empty cache; an attacker with FS access could map a GHSA to the wrong CVE, affecting correlation.
  • Impact. Incorrect correlation (targeted false merge/split); localized obfuscation.
  • Likelihood. Low (requires access to the user/runner FS).
  • Severity. Medium.
  • Mitigation (present).
  • Perm 0o600 on write (Put writes the .tmp as 0o600 before rename) — no longer world-readable.
  • Schema versioning: diskFormat.SchemaVersion (schemaVersion = 1). Open only adopts the file when it parses AND matches the schema AND Data != nil; a legacy/incompatible/corrupt file degrades to an empty cache instead of a mis-parsed read (single rebuild, no migration).
  • Atomic write; the cache is only an optimization (it never breaks the scan); OSV is the arbiter when the cache lacks the key.
  • Remaining gaps. No per-entry integrity/signing; values trusted without cryptographic re-validation; no TTL.
  • Recommendation.
  • [x] ~~Perm 0600 on the cache file~~ — done.
  • [x] ~~Schema version~~ — done (schemaVersion).
  • [ ] In ephemeral CI, do not persist the cache between untrusted jobs, or use --cache "".
  • [ ] Per-entry TTL and checksum/signature.
  • Tools. FS permission control, gosec (G306 file perms).

R8 — Findings exposure (B3 / output)

  • Mitigated as to permission and secret values; artifact classification is an operational responsibility.
  • Description. The report (SARIF/JSON/XML) contains vulnerabilities, misconfigs and detected secrets (trivy/dockle emit secret findings). This output is sensitive data: it reveals the target's attack surface and may contain text near secrets.
  • Impact. Disclosure of security posture / exploitation clues to whoever reads the artifact.
  • Likelihood. Medium (CI artifacts are often public or broadly accessible).
  • Severity. Medium.
  • Mitigation (present).
  • emit writes with perm 0o600 (was 0644).
  • Secret-value redaction: a trivy secret finding's Match is only persisted after redactSecretText (internal/adapter/adapter.go) — long tokens ([A-Za-z0-9+/=_-]{12,}) become <4 chars>…REDACTED…, preserving locating context without leaking the value. The struct comment marks Match as "sensitive: stored only after redaction".
  • Default output is stdout (does not persist by default); --min-severity and --quiet reduce noise; the .quorumignore baseline can suppress known findings.
  • Remaining gaps. Redaction covers the long-token pattern; short/atypical secrets may escape; no automatic artifact-classification warning.
  • Recommendation.
  • [x] ~~Perm 0600 for --output~~ — done.
  • [x] ~~Redact/mask secret values~~ — done (redactSecretText).
  • [ ] Treat SARIF reports as restricted artifacts (not public by default in CI).
  • [ ] Document artifact retention/purging.
  • Tools. CI artifact-storage controls, DLP, ACL.

R9 — Tampered crosswalk / baseline (B3)

  • Description. The crosswalk (YAML rule→control) and .quorumignore influence correlation and suppression. resolveCrosswalkDir falls back automatically to /opt/quorum/crosswalk when ./crosswalk does not exist (and the flag was not passed). A tampered baseline can suppress real findings; a tampered crosswalk can force incorrect merges.
  • Impact. Hiding findings (improper suppression) or loss of granularity.
  • Likelihood. Low–Medium.
  • Severity. Medium.
  • Mitigation (present). Suppressions are always logged (filtered: N suppressed by baseline …); the false split > false merge principle + a crosswalk derived from real output (favoring false split) limit silent merges; the bundled crosswalk (aws.yaml, azure.yaml, gcp.yaml, k8s.yaml) is part of the signed — and now attested (R4) — image, and the log shows crosswalk=N rules.
  • Gaps. User baseline/crosswalk are neither signed nor reviewed; the directory fallback can mask an empty crosswalk.
  • Recommendation.
  • [ ] Version .quorumignore/crosswalk in the repo with mandatory review (CODEOWNERS).
  • [ ] Alert when 0 rules are loaded (there is already a crosswalk=N log).
  • [ ] Optional: sign/validate a custom crosswalk.
  • Tools. code review/CODEOWNERS, git history, PR policy.

R10 — RCE via a trojanized scanner parser/dependency (B5 ⟶ B2)

  • Description. Combination of R4+R2: if a scanner binary is trojanized, it runs inside the trust boundary and its output feeds the parsers. It is the worst chained case.
  • Impact. Full compromise of the container/runner.
  • Likelihood. Low.
  • Severity. Medium (high impact, low likelihood given the majority pin/checksum).
  • Mitigation (present). Digest pin (trivy/kics + bases) and SHA256 checksum of the other downloaded binaries (kubescape/polaris/kube-score/tfsec/terrascan/regula/conftest/dockle); cosign + SLSA + SBOM attested on the Quorum image; action.yml runs cosign verify before running; the stdout cap (512 MiB) limits the damage of a binary's adversarial output.
  • Recommendation. See R4 (close grype/syft/checkov) and run the image with least privilege (rootless, --read-only, --cap-drop=ALL, no --privileged).
  • Tools. cosign, slsa-verifier, container runtime hardening.

R11 — Advisory-layer egress / AI output handling (B7, opt-in)

  • Mitigated by design (off by default; the surface only exists under --advice).
  • Description. The advisory layer (--advice) is presentation-only: it attaches remediation templates, OWASP references and — when a provider is selected — a natural-language recommendation. It never touches correlationKey, fingerprint, the confidence score, aggregated severity, or the fail-on gate. Without --advice the output is byte-identical. Three surfaces carry security weight:
  • Egress (remote provider). --advice-provider=remote calls an external API (QUORUM_ADVICE_API_KEY). Because data leaves the host it is gated on explicit consent (--advice-allow-egress), blocked by --offline ("--advice-provider=remote is disabled by --offline") and it refuses --fix (that would upload source). Only the normalized finding (title, path, control) is sent — never source code. Phases 0 (deterministic templates/internal/enrich) and 2 (lexical/semantic RAG over the digest-pinned OWASP corpus, internal/rag) run fully local, no egress.
  • AI output handling. LLM output is untrusted text. It is treated as advisory only and each AI attachment is labeled "AI-generated, advisory only"; it never changes the gate. --advice-provider=local (e.g. Ollama) and --fix=suggest are reproducible (temperature=0 + an on-disk cache keyed by fingerprint+provider+model) and degrade gracefully: if the model is unreachable the report ships without AI advice and the scan never fails.
  • Verify-the-fix. --fix=suggest proposes a patch that must pass a re-scan: apply to a temp copy, re-scan with the same scanner, keep the suggestion only if the finding is gone and the file still parses. It never auto-applies.
  • Impact. (a) With the remote provider, the normalized finding is disclosed to an external API (metadata about the target's posture). (b) Forged/hallucinated AI text if the endpoint is rogue — but it is advisory-only and cannot flip the gate or the confidence.
  • Likelihood. Low (the whole layer is opt-in and off by default; remote additionally needs consent + is offline-blocked).
  • Severity. Low.
  • Mitigation (present). Off by default (byte-identical output without --advice); presentation-only (isolated from correlation/confidence/gate); remote egress consent-gated (--advice-allow-egress) and --offline-blocked; remote refuses --fix; only the normalized finding is sent, never source; AI attachments explicitly labeled; graceful degradation on an unreachable model; verify-the-fix never auto-applies; the knowledge pack is digest-pinned and attested (R4). New metrics quorum_advice_enriched, quorum_advice_provider, quorum_advice_fix{stage=proposed|verified} make the layer's behavior observable only under --advice.
  • Remaining gaps. The remote provider still discloses finding metadata to a third party by design (mitigate with --advice-provider=local or --offline); trust in the AI text is the operator's (it is advisory, never gating).
  • Recommendation.
  • [x] ~~Gate remote egress on explicit consent and block it under --offline~~ — done.
  • [x] ~~Keep the layer presentation-only and off by default~~ — done.
  • [ ] Prefer --advice-provider=local for sensitive targets; keep --offline for air-gap.
  • [ ] Rotate/scope QUORUM_ADVICE_API_KEY; never echo it in logs (action.yml forwards it via env, does not print it).
  • Tools. egress proxy allowlist, secret management for the API key, internal/evals harness (measures deterministic remediation coverage, OWASP reference relevance, and the verify-the-fix rate in CI). See AI and the design proposal 21-proposta-ia.md.

5. OWASP Top 10 (2021) — mapping to the CLI/Docker context

Category Applicability to Quorum Risk(s) State
A01 Broken Access Control Partial — no authz of its own; access = FS/CI runner R3, R7, R8 Depends on environment (0o600 perms)
A02 Cryptographic Failures OSV over HTTPS; 0o600 artifacts; secrets redacted R6, R8 OK (TLS + redaction)
A03 Injection Central — command/argument injection R1 ✅ Mitigated (argv, no shell; target - refused)
A04 Insecure Design false split > false merge, transparent status R2, R9 Strong by design
A05 Security Misconfiguration 0o600 perms (report+cache), crosswalk fallback, Docker run R3, R7, R8 ✅ Improved (permissions closed)
A06 Vulnerable & Outdated Components 12 scanners/base image bundled R4, R10 ✅ Mitigated (pin/checksum + SLSA/SBOM; grype/syft/checkov remain)
A07 Identification & Auth Failures N/A — no accounts/auth N/A
A08 Software & Data Integrity Failures cosign + SLSA + SBOM attested (image, binaries, knowledge pack); versioned cache; unsigned baseline R4, R7, R9, R11 ✅ Mostly mitigated
A09 Logging & Monitoring Failures Status/suppression logs (text/json); Prometheus metrics R9 OK (CLI scope)
A10 SSRF OSV.dev (fixed BaseURL; validated id + PathEscape); opt-in remote advice endpoint is consent-gated + --offline-blocked R6, R11 ✅ Mitigated (metadata leak remains)

6. OWASP API Security Top 10 (2023) — mapped to the CLI

Quorum exposes no REST/HTTP API (no server, no endpoints) — N/A by architecture. It is a client of the OSV.dev API (and, only under --advice-provider, of an AI endpoint). The API Top 10 applies by analogy to that consumption:

Item Applicability Note
API1 BOLA N/A No exposed objects/users
API2 Broken Authentication N/A OSV is a public endpoint without auth (the opt-in remote advice API uses QUORUM_ADVICE_API_KEY)
API3 BOPLA Partial Quorum trusts fields of the OSV response (R6) — treated as untrusted; AI output is advisory-only (R11)
API4 Unrestricted Resource Consumption ✅ Mitigated 8s timeout + MaxRetries=2 + backoff on the OSV client
API5 BFLA N/A No functions/levels
API6 Unrestricted Business Flows N/A
API7 SSRF ✅ Mitigated Fixed BaseURL; validated id + PathEscape (R6); metadata leak remains
API8 Security Misconfiguration Partial --offline for air-gap; remote advice is off + consent-gated (R11)
API9 Improper Inventory N/A A single known endpoint
API10 Unsafe Consumption of APIs ✅ Mitigated id validated/encoded, response treated as untrusted (R6); AI text labeled advisory-only (R11)

Future proposal (clearly separated): if Quorum ever exposes an API/daemon, this Top 10 becomes primary (authn/z, rate limit, BOLA). Today it is N/A by design.


7. OWASP ASVS 5.0 — controls applicable to the scope

Recommended target level: ASVS L1 (with L2 where applicable to a command-line tool).

ASVS chapter Relevant requirement State in Quorum
V1 Encoding/Sanitization Treat scanner output, OSV response and AI output as untrusted Partial (R2: cap + redaction done; SARIF sanitization pending; R6 id encoded; R11 AI advisory-only)
V2 Validation Validate target, --output, --log-format, OSV id ✅ Covered (R1/R3/R6); semantic image/path validation remains
V5 File Handling Path traversal in --output/cache --output (R3, Clean+0o600) and aliases.json cache (R7, 0o600+schema) mitigated
V8 Data Protection Findings/secrets at rest ✅ Mitigated (0o600 + redaction, R8)
V10 Communication TLS for OSV OK
V12 Secure Comms / Egress --offline, host allowlist, advisory egress consent Partial (R6: --offline ok; allowlist pending; R11: remote egress consent-gated + offline-blocked)
V14 Configuration Dependency pinning, build hardening ✅ Mostly mitigated (R4: pin/checksum + SLSA/SBOM + attested knowledge pack)
V50 (Authn) / V51 (Session) Authentication/session N/A — no users

8. OWASP SAMM — process maturity

SAMM function Practice Evidence in the project Target maturity
Governance Education & Guidance DESIGN.md §12/§14, this doc, GitHub Pages (MkDocs Material) 2
Design Threat Assessment STRIDE/DREAD in this doc 2
Design Security Requirements false split > false merge; 0 findings ≠ safe 2
Implementation Secure Build Dockerfile pin/checksum, GoReleaser, cosign + SLSA + SBOM + attested knowledge pack 3
Implementation Software Dependencies Pin/checksum of 10/12 scanners; grype/syft/checkov remain 2
Verification Security Testing Per-adapter contract tests; CI coverage; E2E; internal/evals harness 2→3
Verification Architecture Assessment Trust boundaries (§1) 2
Operations Incident Mgmt Transparent per-scanner status; Prometheus metrics 1→2 (define runbook)

9. OWASP MASVS — N/A

Quorum is not a mobile application: there is no iOS/Android app, no on-device storage, no mobile IPC, no WebView. All MASVS controls (STORAGE, CRYPTO, AUTH, NETWORK, PLATFORM, CODE, RESILIENCE) are N/A. The equivalent themes of protecting data at rest and in transit are covered by ASVS (§7) and by risks R6/R8.

10. OWASP LLM Top 10 — applies only when the opt-in AI layer is enabled

The Quorum core uses no AI/LLM: correlation and consensus are deterministic (correlationKey, Fingerprint = sha256(correlationKey), a confidence formula based on engine diversity/severity/authoritative confirmation). The advisory layer (--advice) is opt-in and off by default, and even then Phase 0 (curated templates, internal/enrich) and Phase 2 (RAG over the digest-pinned OWASP corpus, internal/rag) are deterministic and model-free. An actual LLM is queried only under --advice-provider=local|remote (internal/advisor). Therefore the LLM Top 10 does not apply to the deterministic core; when the AI layer is enabled, the relevant items are already addressed by the R11 design:

LLM risk Where it applies State when AI enabled
LLM01 Prompt Injection A hostile finding's text becomes part of the prompt Advisory-only output; never gates; temperature=0
LLM02 Insecure Output Handling LLM text flows into the report Labeled "AI-generated, advisory only"; not sanitized into the gate/confidence
LLM05 Supply Chain OWASP corpus / model Corpus is digest-pinned + attested (R4); local model runs on-host
LLM06 Sensitive Information Disclosure Remote provider egress Consent-gated (--advice-allow-egress) + --offline-blocked; only the normalized finding is sent, never source; --fix refused remotely (R11)
LLM10 Model Theft N/A Quorum ships no model of its own

Honest framing. Without --advice there is no AI at all and LLM01–LLM10 are inert. With --advice-provider=local|remote the layer stays presentation-only, so a compromised model can at most produce misleading advisory text — it cannot change correlationKey, fingerprint, confidence, aggregated severity, or the fail-on gate. See AI and 21-proposta-ia.md.


11. MITRE ATT&CK — relevant TTPs (attacker targets the pipeline)

Tactic Technique Relation to Quorum Mitigation
Initial Access T1195 Supply Chain Compromise Trojanized scanner binary / image (R4/R10) Pin/checksum, cosign + SLSA + SBOM + attested knowledge pack
Execution T1059 Command/Scripting Interpreter Shell-out (R1) argv, no shell; target - refused
Execution T1203 Exploitation for Client Execution Parser exploited by forged output (R2) structured parsing, 512 MiB cap, fuzzing
Persistence T1554 Compromise Host Software Binaries Replace scanner in PATH (:slim mode) signed :full image; controlled PATH
Defense Evasion T1027/T1070 Obfuscation/Indicator Removal Tampered baseline/crosswalk (R9) suppressions always logged
Discovery T1082/T1083 System/File Discovery Findings reveal surface (R8) 0o600 perms, redaction, restrict artifacts
Exfiltration T1041 Exfil over C2 / web IDs leaked via OSV (R6); finding metadata via remote advice (R11) --offline, validated id, egress allowlist; remote advice consent-gated + offline-blocked
Impact T1499 Endpoint DoS Giant target / zip bomb (R5) 20 GiB target cap, 512 MiB stdout cap, timeout

12. MITRE ATLAS — N/A (core); opt-in AI layer noted

ATLAS covers threats to machine-learning systems (model evasion, training-data poisoning, model extraction). The Quorum core has no ML/AI component (see §10), so no ATLAS technique applies to it. The poisoning relevant here is of the alias cache (R7), handled as classic data tampering, not ML data poisoning. When the opt-in AI layer is enabled (--advice-provider=local|remote), Quorum acts as a client of an external model rather than training/serving one, so ATLAS still barely applies — the residual concern (untrusted model output, provider egress) is covered by R11, and the RAG corpus is digest-pinned + attested (R4).


13. LINDDUN — privacy

Personal data processed by Quorum is minimal and incidental (no accounts; there may be metadata in paths/detected secrets). Mapping:

LINDDUN threat Applicability Risk Mitigation
Linkability Low Vuln IDs queried on OSV correlatable --offline
Identifiability Low Paths/secrets may contain personal data min-severity, redaction (redactSecretText)
Non-repudiation N/A No user actions to repudiate
Detectability Medium An OSV query reveals a vuln's existence (R6) --offline, internal mirror
Disclosure of info Medium Report/secrets exposed (R8); finding metadata to a remote advice provider (R11) 0o600 perms, redaction, artifact restriction; --advice-provider=local/--offline
Unawareness Low documentation (GitHub Pages)
Non-compliance Medium LGPD if findings contain personal data see §19

14. NIST Cybersecurity Framework (CSF 2.0)

Function State Evidence / gap
GOVERN Partial Principles and risks documented; incident runbook missing
IDENTIFY Good Trust boundaries, dependency inventory (Dockerfile), attested SBOM
PROTECT Good cosign + SLSA + SBOM + attested knowledge pack, --offline, 0o600 perms, DoS caps; grype/syft/checkov pin remains
DETECT Good (scope) Per-scanner status, 0 findings ≠ safe, suppressions logged, Prometheus metrics
RESPOND Initial Actionable messages (OOM/probe); no formal process
RECOVER N/A→Initial Stateless tool; image rebuild restores state

15. NIST SP 800-53 (selected controls)

Family/Control Application State
AC-3/AC-6 Least Privilege Run container rootless, --cap-drop=ALL Recommended (operational gap)
AU-2/AU-12 Audit Logging Execution/suppression logs (text/json); metrics OK (scope)
CM-7 Least Functionality --scanners limits the surface; advisory off by default OK
SA-12 Supply Chain Protection Pin/checksum + cosign + SLSA + SBOM + attested knowledge pack ✅ Mostly mitigated (R4)
SC-7 Boundary Protection Egress only to OSV; --offline; remote advice consent-gated + offline-blocked Partial (R6/R11)
SC-8 Transmission Confidentiality TLS OSV OK
SI-7 Software/Info Integrity Artifact signing/attestation (image, binaries, knowledge pack); versioned cache (0o600) ✅ Mitigated (R4/R7)
SI-10 Input Validation Validation of target/output/id/log-format ✅ Covered (R1/R3/R6)

16. CIS Controls v8 (relevant)

Control Application State
2 Inventory of Software Dockerfile lists scanners/versions; attested SBOM OK
4 Secure Configuration Container hardening, 0o600 perms ✅ Improved
7 Continuous Vuln Mgmt Quorum's purpose (consensus, 12 scanners) Core
8 Audit Log Mgmt Logs (text/json) + Prometheus metrics OK
16 Application Software Security This doc's SDL, contract tests, CI coverage, internal/evals Partial
18 Penetration Testing Parser fuzzing recommended Gap

17. ISO/IEC 27001:2022 (Annex A — relevant controls)

Control Theme State
A.5.23 Security in cloud services Use of OSV.dev; opt-in remote advice API Partial (--offline, consent-gated egress)
A.8.8 Technical vulnerability management End product Core
A.8.28 Secure coding argv, structured parsing, caps, redaction ✅ Improved
A.8.30 Outsourced/3rd-party 12 third-party scanners bundled ✅ Mostly mitigated (pin/checksum)
A.5.31 Legal requirements (LGPD) Data in findings See §19
A.8.16 Monitoring Per-scanner status + metrics OK

18. PCI DSS v4.0

Quorum stores/processes no cardholder data (no CHD/SAD). It is a compliance-support tool, not a system in CDE scope.

Requirement Relation State
Req 6.2/6.3 Secure software and patching Quorum helps evidence SCA/IaC/misconfig/K8s Support
Req 6.4.3 Software components Attested SBOM/pin ✅ Reinforced support
Req 11.3 Vulnerability detection Consensus scans in CI (12 scanners) Support
Req 12 Policies Integrate Quorum into the process Organizational
Others (1–4, 7–10) N/A — no CHD, no CDE network N/A

19. LGPD (Law 13.709/2018)

Quorum collects no personal data from users (no accounts, no user telemetry; --metrics writes only aggregated counts). Residual risk: personal data may appear incidentally in findings (paths, detected secrets/credentials, emails in code).

Aspect Situation Action
Direct collection of personal data None
Incidental personal data in reports Possible (R8) Treat the report as restricted (0o600); secret redaction
International transfer An OSV.dev query (USA) sends only validated vuln IDs, not personal data; the opt-in remote advice provider sends only the normalized finding, never source (R11) --offline for air-gap; --advice-provider=local
Legal basis / minimization Default output to stdout; --min-severity Retain the minimum
Data-subject rights N/A (no registration)

20. Present mitigations vs. gaps (executive summary)

quadrantChart
  title Mitigations: coverage vs. effort to close gaps
  x-axis "Low current coverage" --> "High current coverage"
  y-axis "Low effort" --> "High effort"
  quadrant-1 "Maintain"
  quadrant-2 "Invest"
  quadrant-3 "Quick wins"
  quadrant-4 "Plan"
  "argv no shell + target - (R1)": [0.88, 0.2]
  "cosign + SLSA + SBOM (R4)": [0.82, 0.55]
  "--offline + validated id (R6)": [0.85, 0.2]
  "transparent status (R5)": [0.85, 0.25]
  "--output Clean+0600 (R3)": [0.85, 0.2]
  "cache 0600 + schema (R7)": [0.8, 0.2]
  "secret redaction (R8)": [0.8, 0.25]
  "DoS caps 512MiB/20GiB (R5)": [0.82, 0.3]
  "advisory opt-in + egress gate (R11)": [0.86, 0.22]
  "pin grype/syft/checkov (R4)": [0.4, 0.45]
  "SARIF sanitization (R2)": [0.3, 0.4]

Already present (strengths): - Shell-out without sh -c — arguments via argv (exec.CommandContext); a target starting with - is refused (validateTargetRef). - Structured struct-based parsing + per-adapter contract tests (12 adapters) with versioned fixtures; 512 MiB stdout cap (capWriter); secret redaction. - --offline turns OSV off; OSV client with 8s timeout / retry / backoff and graceful degradation; id validated (vulnIDPattern) + url.PathEscape. - --output normalized (filepath.Clean) and written 0o600; aliases.json cache 0o600 + schemaVersion; 20 GiB target size cap (checkTargetSize). - Hardened distribution: keyless cosign (OIDC) with retry + SLSA build-provenance attestation + attested SPDX SBOM (image and per-binary) + an attested advisory knowledge pack (knowledge job), all re-verified in the release; action.yml runs cosign verify before running and auto-mounts the Docker socket for type: image; the moving v0 tag is advanced automatically by tag-major.yml. - Digest pin of trivy/kics + bases; SHA256 checksum of kubescape/polaris/kube-score/tfsec/terrascan/regula/conftest/dockle; pre-cached, non-expiring Grype DB. - Per-scanner timeout + version probe distinguishing timeout/OOM/absent; per-scanner status, "0 findings is not proof of safety", logged suppressions; text/json logs + Prometheus metrics (--metrics). - false split > false merge design + a crosswalk derived from real output limit hiding. - Advisory layer opt-in and presentation-only (byte-identical without --advice); remote egress consent-gated (--advice-allow-egress) + --offline-blocked; only the normalized finding is sent, never source; verify-the-fix never auto-applies; internal/evals measures coverage/relevance/fix-rate in CI.

Priority gaps (actionable backlog): - [ ] R4 Pin the Anchore install.sh (grype/syft) by commit SHA; pip install --require-hashes for Checkov; pin the cosign version in action.yml. - [ ] R2 Sanitize/escape textual fields (Title/Description/Location) before SARIF consumed by third-party UIs; add adversarial fixtures. - [ ] R5 Decompression-depth limit (beyond the on-disk size cap). - [ ] R6 Host allowlist / internal OSV mirror via env; document --offline. - [ ] R7 Per-entry cache TTL and checksum/signature; --no-cache in ephemeral CI. - [ ] R9 Sign/validate custom crosswalk and baseline; alert on 0 rules.


21. Operational hardening checklist (CI/Docker)

  • [ ] Run quorum:full rootless, --read-only, --cap-drop=ALL, no --privileged.
  • [ ] Impose --memory, --pids-limit and tmpfs on /tmp (complements the target/stdout cap, R5).
  • [ ] Restrict the runner's egress to api.osv.dev (or use --offline).
  • [ ] Verify the image with cosign verify (the action.yml default) and the SLSA/SBOM attestation on the consumer (gh attestation verify); verify the knowledge pack too (gh attestation verify knowledge/owasp/corpus.yaml).
  • [ ] Pin the image by @sha256 in the pipeline, not by the :full tag (the action's v0 is moving by design and advances on every release).
  • [ ] Treat SARIF reports (0o600) as restricted artifacts; define retention/purging.
  • [ ] Do not persist aliases.json between untrusted jobs (or --cache "").
  • [ ] Limit scanners with --scanners to what is needed (least functionality / CM-7).
  • [ ] Tune QUORUM_MAX_OUTPUT_BYTES / QUORUM_MAX_TARGET_BYTES only when a legitimate target requires it; keep the defaults (512 MiB / 20 GiB) otherwise.
  • [ ] Pass platform keys (e.g. Prisma/Bridgecrew) via QUORUM_CHECKOV_ARGS/the checkov-args input — never in logs (action.yml does not echo those envs).
  • [ ] Keep the advisory layer off unless needed; for sensitive targets prefer --advice-provider=local and never combine remote advice with --fix; pass QUORUM_ADVICE_API_KEY via the advice-api-key input, never in logs.

Assumptions

  • Analyzed version: v0.8.3 (revision 2026-07-04), from the code on main. Claims were verified in internal/adapter/adapter.go (runCmd, capWriter, redactSecretText, extraArgs), internal/adapter/{trivy,grype,conftest}.go and the remaining adapters (12 in total), cmd/quorum/scan.go (emit/runScan/validateTargetRef/checkTargetSize + the advisory flag wiring), internal/alias/osv.go (vulnIDPattern/PathEscape), internal/cache/store.go (schemaVersion/0o600), the advisory packages internal/{enrich,rag,advisor,evals}, knowledge/*.yaml + knowledge/owasp/corpus.yaml, Dockerfile.full, action.yml, .github/workflows/release.yml and DESIGN.md (§12, §14).
  • DESIGN.md carries the supply-chain discussion in §12 (CLI & Docker) and the risks in §14 (Risks and mitigations); both were addressed.
  • Confirmed that there is no intermediate shell in the shell-out (no sh -c); classic command injection (R1) is mitigated, and the argument injection residue (a target starting with -) was also closed by validateTargetRef.
  • The DREAD scores are qualitative estimates for prioritization, not empirical measurements; the "State" column reflects the code read in this revision (many items formerly "Gap/Partial" were promoted to "Mitigated").
  • Claims about cosign, SLSA, SBOM, the attested knowledge pack, GHCR :full/:slim, GoReleaser and the v0 tag are based on release.yml, action.yml and Dockerfile.full read directly (see also Supply Chain).
  • The advisory layer (--advice) is assumed opt-in and off by default and presentation-only: without it the output is byte-identical and no AI runs; it never touches correlationKey/fingerprint/confidence/aggregated severity/the fail-on gate. The remote provider is the only egress path and is consent-gated (--advice-allow-egress) and --offline-blocked; it sends only the normalized finding, never source. This framing is why the OWASP LLM Top 10 is treated as conditional (§10) rather than flatly N/A.
  • The SARIF report is assumed to be consumable by third-party UIs (GitHub code scanning), which motivates the remaining recommendation to sanitize textual fields (R2).
  • MISCONFIG/IaC and K8S_POSTURE consensus rests on a crosswalk derived from real output (favoring false split); RBAC stays single-engine (kubescape RBAC needs cluster context) — documented as a decision, not a gap.
  • Frameworks marked N/A (MASVS, its own REST API, and most of PCI DSS) are so for lack of, respectively, a mobile app, an HTTP server and cardholder data. MITRE ATLAS and the OWASP LLM Top 10 are N/A for the deterministic core and become partially relevant only when the opt-in AI layer is enabled — reassess if the product scope changes (see "Future proposals").