philipp.baumann/dse-security-reports
Watch
0
0
Fork
You've already forked dse-security-reports
0

feat(triage): state the WireGuard unit's IaC ownership and review its stale Trivy advisories #28

Merged
Philipp Baumann merged 2 commits from fm/dse-security-reports-wireguard-iac into main 2026-10-28 14:39:24 +00:00

Summary

The report described the WireGuard unit's software and role but never said its installation is declared and owned by configuration, and it carried the unit's scanner hits without saying where they come from or how to read them. This change states the dse-ansible declaration beside the verified host state, names the 2021 scanner entries together with the vendor's own explanation of why a correctly patched host still shows them, and dismisses CVE-2021-3695 in the scanner through a committed VEX document — while keeping every dismissal visible in both derivation lanes and in a new report table.

What changed

  • reports/defender-triage.typ: state the IaC declaration (pinned githubixx.ansible_role_wireguard 19.2.0, playbooks/playbook-wireguard.yaml with wg0 / 51820 / endpoint / 10.15.5.1/24 / ULA / MTU 1280 with MSS clamping, peers in host_vars, cutover runbook) beside the verified host state, and contrast it with the unowned packages-microsoft-com-prod; add the WireGuard Trivy passage (findings come from Trivy, Red Hat's FAQ as the mechanism authority, the mirror-image reading, the verified grub2 position, the epoch root cause, the checked-not-waved discipline, the kernel-exposure guard); add the data-driven «Abgewiesene Fundstellen (VEX)» table.
  • config/vex/alma-backported-fixes.vex.json: the committed VEX fallback for CVE-2021-3695 on the eight grub2 purls, status fixed, each statement carrying the erratum and the review horizon. The stale-advisory reading is scoped to the individually checked entries and states explicitly that it says nothing about the unit's wider Trivy set.
  • scripts/scan_trivy.sh: add --vex <file>, --vuln-severity-source redhat and --show-suppressed; record the dismissed findings (status, source, and the document's impact_statement) in the per-unit payload; document the VEX obstacle and the epoch root cause in the header; key the cache on a scanConfig signature (with a format= token) so a stale cache is re-scanned instead of silently reused.
  • scripts/derive_findings.py and src/dse_security_reports/derive.py: carry vexDismissed per evidence unit and the payload-level vex aggregate in both lanes, and expose the VEX/severity source in scanMeta.
  • tests/test_derive.py: cover the dismissal join, the vex aggregate and the new scan metadata.
  • AGENTS.md: record the scan-lane VEX/severity mechanism, the VEX obstacle, the epoch root cause and the anti-invisibility rule.

Behaviour change

The scan no longer reports CVE-2021-3695 on the WireGuard unit as a live finding; it records it as a dismissed entry, and the report shows that dismissal with its VEX status, its source document and the erratum that backs it. Severities follow Red Hat where Red Hat has an opinion. Nothing suppressed can become invisible: the dismissal survives the scan cache, both derivations and the render.

Evidence

The instructed vendor-VEX route does not work, and the obstacle was proven rather than assumed (the header of scripts/scan_trivy.sh records all of it):

  • --vex repo (aquasecurity/vexhub) parses and fetches, but holds no statement matching our pkg:rpm/alma/... products — 866 findings unchanged — and it writes its config to ~/.trivy/vex/repository.yaml, i.e. outside the worktree.
  • Red Hat's real VEX document for the CVE (security.access.redhat.com/data/csaf/v2/vex/2021/cve-2021-3695.json, category csaf_vex, fixed 194 / known_affected 22 / known_not_affected 58) is parsed but cannot match: its product_identification_helper carries neither purls nor CPEs, only Red Hat-internal product ids.
  • AlmaLinux publishes no CSAF/VEX feed (all probed endpoints 404).

So the VEX source is a stated fallback, not vendor data: it encodes the vendor's own decision (RHSA-2022:5099 fixed grub2 for RHEL 9 on 2022-06-16) against the exact installed purls, and pins them, so a grub2 upgrade stops matching and the finding returns for review.

Before/after on CVE-2021-3695 (ASP-DSE-PROD-WIREGUARD, offline, cached DB):

  • without VEX: 866 detections, the CVE present as HIGH on all eight grub2 packages;
  • with VEX: 858 detections, the CVE absent from the findings and present as eight fixed dismissals (config/vex/alma-backported-fixes.vex.json);
  • the same statement applies on the three other el9 units that share the build (4 dismissals each on DEV-MISC, 8 each on DEV-WORKBENCH and PROD-WORKBENCH); the four el10 units: 0.

Vendor severity source (--vuln-severity-source redhat): CVE-2021-3695 HIGH → MEDIUM; the unit's severity mix moves from 461 HIGH / 358 MEDIUM / 47 LOW to 224 HIGH / 471 MEDIUM / 163 LOW.

The root cause is ours, and it was found while proving the dismissal: scripts/build_cyclonedx_sbom.py builds the purl from rpm -qa, whose version string carries no RPM epoch, while the Alma fixed version does (1:2.06-27.el9_0.7.alma). trivy therefore compares epoch 0 against epoch 1 and reports the package although the installed release (126) is far newer than the fixed one (27). Suffixing the eight grub2 purls with ?epoch=1 removes all 200 grub2 findings of the unit with no suppression at all (866 → 666; all 200 removed entries are grub2, 8 packages × 25 CVEs, no additions). Carrying the epoch is the durable fix and needs scripts/ssh_evidence.sh to collect %{EPOCHNUM} — deliberately not done here, because it re-baselines the whole scan lane and needs a fresh evidence round.

Status of the other entries examined under the same reasoning (checked, not added to the VEX document — no blanket suppression):

CVE packages installed trivy "fixed" fix present with epoch honoured?
CVE-2021-25220 bind-libs, bind-license, bind-utils 9.16.23-40.el9_8.12 32:9.16.23-5.el9_1 yes (40 > 5)
CVE-2021-25220 dhcp-client, dhcp-common 4.4.2-20.b1.el9 12:4.4.2-17.b1.el9 yes (20 > 17)
CVE-2021-3507 / CVE-2021-3611 qemu-guest-agent 10.1.0-17.el9_8.5.alma.1 17:7.0.0-13.el9 yes (10.1.0 > 7.0.0)

Every one of them has the installed build above the vendor's fixed version once the epoch counts, so the same reading holds — but the decision to suppress them belongs to the captain, not to this change.

Validation (all on the final tree): just all ✓ (fixture lane; the empty-dismissal branch renders), just repro ✓ (byte-identical PDFs for both reports), just test ✓ (39 passed), just lint ✓, just py-crosscheck ✓ (both derivation lanes byte-identical). The fixture lane stays offline; the re-scan ran offline against the cached DB (--skip-db-update). The PDFs were re-rendered and re-published (defender-triage-2026-10-28-1517.pdf plus the bare alias, likewise for findings); previous copies were moved to artifacts/superseded/; the 2026-10-26 set was not touched, and the republished files are byte-identical to a fresh render from this tree.

Scope note: the instructed premise was corrected by the check

The brief asked the report to say that the unit's Trivy findings all date from 2021 and that CVE-2021-3695 was the only HIGH. The check disproved both, which is why the passage is scoped the way it is:

  • The cache read for this change (ASP-DSE-PROD-WIREGUARD, the pre-change copy under .local/scans-prev28/) holds 866 findings over 277 CVEs, 461 of them HIGH, by year 2021:39, 2022:175, 2023:128, 2024:112, 2025:109, 2026:303.
  • 24 of the 39 2021 entries are HIGH — CVE-2021-3695, CVE-2021-3696 and CVE-2021-3697 on the same grub2 packages among them.
  • Firstmate's independent count of the same file agreed on the decisive part (2021:39 findings, 24 HIGH, matching this check exactly) while reading a different DB state for 2026 (their totals 998 findings / 295 CVEs / 597 HIGH), so the 2026 difference is advisory-data drift between the two checkouts, not a disagreement about the 2021 set.

So the report names the checked entries, says each was verified individually rather than assumed to belong to a class, and states that the unit's wider Trivy set is large and largely unexamined — never that it has been dismissed.

Open items for the captain

  1. Durable epoch fix (recommended): collect %{EPOCHNUM} in scripts/ssh_evidence.sh and emit the ?epoch= qualifier in scripts/build_cyclonedx_sbom.py. That removes the whole class of false positives at the root and needs a fresh evidence round.
  2. The committed claim «ihre Trivy-Funde sind durchgehend mittelschwer» (PR 26/27, two places) does not survive the data: the unit's Trivy set holds 224 HIGH entries after the vendor-severity change, 461 before it. Flagged, not changed here — the wording is the captain's.
  3. Whether to extend the dismissal to the bind/dhcp/qemu entries in the table above.

Instructions-PR: none

## Summary The report described the WireGuard unit's software and role but never said its installation is declared and owned by configuration, and it carried the unit's scanner hits without saying where they come from or how to read them. This change states the dse-ansible declaration beside the verified host state, names the 2021 scanner entries together with the vendor's own explanation of why a correctly patched host still shows them, and dismisses CVE-2021-3695 in the scanner through a committed VEX document — while keeping every dismissal visible in both derivation lanes and in a new report table. ## What changed - `reports/defender-triage.typ`: state the IaC declaration (pinned `githubixx.ansible_role_wireguard` 19.2.0, `playbooks/playbook-wireguard.yaml` with wg0 / 51820 / endpoint / 10.15.5.1/24 / ULA / MTU 1280 with MSS clamping, peers in `host_vars`, cutover runbook) beside the verified host state, and contrast it with the unowned `packages-microsoft-com-prod`; add the WireGuard Trivy passage (findings come from Trivy, Red Hat's FAQ as the mechanism authority, the mirror-image reading, the verified grub2 position, the epoch root cause, the checked-not-waved discipline, the kernel-exposure guard); add the data-driven «Abgewiesene Fundstellen (VEX)» table. - `config/vex/alma-backported-fixes.vex.json`: the committed VEX fallback for CVE-2021-3695 on the eight grub2 purls, status `fixed`, each statement carrying the erratum and the review horizon. The stale-advisory reading is scoped to the individually checked entries and states explicitly that it says nothing about the unit's wider Trivy set. - `scripts/scan_trivy.sh`: add `--vex <file>`, `--vuln-severity-source redhat` and `--show-suppressed`; record the dismissed findings (status, source, and the document's `impact_statement`) in the per-unit payload; document the VEX obstacle and the epoch root cause in the header; key the cache on a `scanConfig` signature (with a `format=` token) so a stale cache is re-scanned instead of silently reused. - `scripts/derive_findings.py` and `src/dse_security_reports/derive.py`: carry `vexDismissed` per evidence unit and the payload-level `vex` aggregate in both lanes, and expose the VEX/severity source in `scanMeta`. - `tests/test_derive.py`: cover the dismissal join, the `vex` aggregate and the new scan metadata. - `AGENTS.md`: record the scan-lane VEX/severity mechanism, the VEX obstacle, the epoch root cause and the anti-invisibility rule. ## Behaviour change The scan no longer reports CVE-2021-3695 on the WireGuard unit as a live finding; it records it as a dismissed entry, and the report shows that dismissal with its VEX status, its source document and the erratum that backs it. Severities follow Red Hat where Red Hat has an opinion. Nothing suppressed can become invisible: the dismissal survives the scan cache, both derivations and the render. ## Evidence **The instructed vendor-VEX route does not work, and the obstacle was proven rather than assumed** (the header of `scripts/scan_trivy.sh` records all of it): - `--vex repo` (aquasecurity/vexhub) parses and fetches, but holds no statement matching our `pkg:rpm/alma/...` products — 866 findings unchanged — and it writes its config to `~/.trivy/vex/repository.yaml`, i.e. outside the worktree. - Red Hat's real VEX document for the CVE (`security.access.redhat.com/data/csaf/v2/vex/2021/cve-2021-3695.json`, category `csaf_vex`, fixed 194 / known_affected 22 / known_not_affected 58) is parsed but cannot match: its `product_identification_helper` carries **neither purls nor CPEs**, only Red Hat-internal product ids. - AlmaLinux publishes no CSAF/VEX feed (all probed endpoints 404). So the VEX source is **a stated fallback, not vendor data**: it encodes the vendor's own decision (RHSA-2022:5099 fixed grub2 for RHEL 9 on 2022-06-16) against the exact installed purls, and pins them, so a grub2 upgrade stops matching and the finding returns for review. **Before/after on CVE-2021-3695** (`ASP-DSE-PROD-WIREGUARD`, offline, cached DB): - without VEX: 866 detections, the CVE present as HIGH on all eight grub2 packages; - with VEX: 858 detections, the CVE absent from the findings and present as eight `fixed` dismissals (`config/vex/alma-backported-fixes.vex.json`); - the same statement applies on the three other el9 units that share the build (4 dismissals each on DEV-MISC, 8 each on DEV-WORKBENCH and PROD-WORKBENCH); the four el10 units: 0. **Vendor severity source** (`--vuln-severity-source redhat`): CVE-2021-3695 HIGH → MEDIUM; the unit's severity mix moves from 461 HIGH / 358 MEDIUM / 47 LOW to 224 HIGH / 471 MEDIUM / 163 LOW. **The root cause is ours, and it was found while proving the dismissal:** `scripts/build_cyclonedx_sbom.py` builds the purl from `rpm -qa`, whose version string carries **no RPM epoch**, while the Alma fixed version does (`1:2.06-27.el9_0.7.alma`). trivy therefore compares epoch 0 against epoch 1 and reports the package although the installed release (126) is far newer than the fixed one (27). Suffixing the eight grub2 purls with `?epoch=1` removes **all 200 grub2 findings of the unit with no suppression at all** (866 → 666; all 200 removed entries are grub2, 8 packages × 25 CVEs, no additions). Carrying the epoch is the durable fix and needs `scripts/ssh_evidence.sh` to collect `%{EPOCHNUM}` — deliberately not done here, because it re-baselines the whole scan lane and needs a fresh evidence round. **Status of the other entries examined under the same reasoning** (checked, **not** added to the VEX document — no blanket suppression): | CVE | packages | installed | trivy "fixed" | fix present with epoch honoured? | |---|---|---|---|---| | CVE-2021-25220 | bind-libs, bind-license, bind-utils | 9.16.23-40.el9_8.12 | 32:9.16.23-5.el9_1 | yes (40 > 5) | | CVE-2021-25220 | dhcp-client, dhcp-common | 4.4.2-20.b1.el9 | 12:4.4.2-17.b1.el9 | yes (20 > 17) | | CVE-2021-3507 / CVE-2021-3611 | qemu-guest-agent | 10.1.0-17.el9_8.5.alma.1 | 17:7.0.0-13.el9 | yes (10.1.0 > 7.0.0) | Every one of them has the installed build above the vendor's fixed version once the epoch counts, so the same reading holds — but the decision to suppress them belongs to the captain, not to this change. **Validation** (all on the final tree): `just all` ✓ (fixture lane; the empty-dismissal branch renders), `just repro` ✓ (byte-identical PDFs for both reports), `just test` ✓ (39 passed), `just lint` ✓, `just py-crosscheck` ✓ (both derivation lanes byte-identical). The fixture lane stays offline; the re-scan ran offline against the cached DB (`--skip-db-update`). The PDFs were re-rendered and re-published (`defender-triage-2026-10-28-1517.pdf` plus the bare alias, likewise for findings); previous copies were moved to `artifacts/superseded/`; the 2026-10-26 set was not touched, and the republished files are byte-identical to a fresh render from this tree. ## Scope note: the instructed premise was corrected by the check The brief asked the report to say that the unit's Trivy findings *all* date from 2021 and that CVE-2021-3695 was *the only* HIGH. The check disproved both, which is why the passage is scoped the way it is: - The cache read for this change (`ASP-DSE-PROD-WIREGUARD`, the pre-change copy under `.local/scans-prev28/`) holds **866 findings over 277 CVEs, 461 of them HIGH**, by year 2021:39, 2022:175, 2023:128, 2024:112, 2025:109, 2026:303. - **24 of the 39 2021 entries are HIGH** — CVE-2021-3695, CVE-2021-3696 and CVE-2021-3697 on the same grub2 packages among them. - Firstmate's independent count of the same file agreed on the decisive part (2021:39 findings, 24 HIGH, matching this check exactly) while reading a different DB state for 2026 (their totals 998 findings / 295 CVEs / 597 HIGH), so the 2026 difference is advisory-data drift between the two checkouts, not a disagreement about the 2021 set. So the report names the checked entries, says each was verified individually rather than assumed to belong to a class, and states that the unit's wider Trivy set is large and largely unexamined — never that it has been dismissed. ## Open items for the captain 1. **Durable epoch fix** (recommended): collect `%{EPOCHNUM}` in `scripts/ssh_evidence.sh` and emit the `?epoch=` qualifier in `scripts/build_cyclonedx_sbom.py`. That removes the whole class of false positives at the root and needs a fresh evidence round. 2. **The committed claim «ihre Trivy-Funde sind durchgehend mittelschwer»** (PR 26/27, two places) does not survive the data: the unit's Trivy set holds 224 HIGH entries after the vendor-severity change, 461 before it. Flagged, not changed here — the wording is the captain's. 3. Whether to extend the dismissal to the bind/dhcp/qemu entries in the table above. Instructions-PR: none
- State the dse-ansible declaration (pinned role, playbook, peers, cutover
  runbook) beside the verified host state, and contrast it with the
  Microsoft repository no configuration owns
- Name the 2021 Trivy entries on the unit and cite Red Hat's own FAQ as the
  authority for the version-based advisory comparison
- Record CVE-2021-3695 as a reviewed dismissal (Red Hat MODERATE, fixed for
  RHEL 9 in RHSA-2022:5099, installed el9_8 build years newer)
- Feed the scan a committed OpenVEX fallback and the vendor severity source,
  because no published VEX document can match our alma purls
- Carry every dismissed finding into both derivation lanes and render it as a
  reviewed dismissal, so a suppression never becomes invisible
- Keep the shared el9 kernel exposure out of the stale-advisory reading
- Record the root cause found while proving the dismissal: the SBOM purl omits
  the RPM epoch, so the 200 grub2 findings of the unit vanish with an
  `?epoch=` qualifier and no suppression at all
- Scope the passage to the named 2021 entries, each verified individually,
  with no all/only/class claim
- Bound it explicitly: nothing there speaks about the unit's wider Trivy set,
  which is large and largely unexamined
- Keep the VEX dismissal and the before/after proof unchanged
Philipp Baumann deleted branch fm/dse-security-reports-wireguard-iac 2026-10-28 14:39:24 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
philipp.baumann/dse-security-reports!28
No description provided.