@@ -9,6 +9,201 @@ The format is based on the regulated environment requirements:
99
1010---
1111
12+ ## [ 2026-04-22 20:30] - Presence-based auto-VEX (roadmap Phase 2)
13+
14+ ** Author:** Erick Bourgeois
15+
16+ ### Added
17+ - ` src/auto_vex_presence.rs ` (library module, ~ 180 LOC) + ` src/auto_vex_presence_tests.rs `
18+ (20 unit tests, 100% positive/negative/exception coverage per project rule):
19+ - ` compute_presence_vex ` — pure function that diffs Grype findings against
20+ SBOMs and the already-triaged CVE set, emitting
21+ ` not_affected + component_not_present ` statements for CVEs whose purl
22+ is absent from every SBOM.
23+ - ` build_document ` — wraps statements in the OpenVEX envelope.
24+ - ` load_triaged_from_vex_dir ` — reads hand-authored ` .vex/*.json ` and
25+ collects the set of ` vulnerability.name ` values (permissive on
26+ missing dir, strict on malformed JSON).
27+ - Typed deserializers for Grype JSON and CycloneDX SBOM subsets —
28+ tolerant of unknown upstream fields.
29+ - Output sorted by CVE id for deterministic, diffable artifacts.
30+ - ` src/bin/auto_vex_presence.rs ` — thin clap-driven CLI wrapping the
31+ library. Reads ` --grype-json ` , one or more ` --sbom ` files, a
32+ ` --vex-dir ` , a ` --product-purl ` , ` --id ` , optional ` --author ` and
33+ ` --timestamp ` , writes an OpenVEX JSON to ` --output ` or stdout.
34+ - ` Cargo.toml ` : ` [[bin]] auto-vex-presence ` entry.
35+ - ` Makefile ` : ` vex-auto-presence ` target (defaults ` GRYPE_JSON=grype.json `
36+ and ` SBOM_FILES=target/release/*.cdx.json docker-sbom-*.json ` ; both
37+ overridable). Added to ` .PHONY ` .
38+ - ` .github/workflows/build.yaml ` : two new jobs gated on
39+ ` github.event_name != 'pull_request' ` :
40+ - ` grype-triage ` — runs Grype against each image variant without VEX
41+ suppression, uploads per-variant JSON (pinned GRYPE_VERSION 0.87.0
42+ matching the existing ` grype ` job).
43+ - ` auto-vex-presence ` — builds the new bin, downloads both triage
44+ scans + Docker SBOMs, runs the bin once per variant, merges the
45+ per-variant outputs via ` vexctl merge ` , uploads
46+ ` vex-auto-presence ` artifact.
47+
48+ ### Changed
49+ - ` .github/workflows/build.yaml ` :
50+ - Docker SBOM generation (` Generate Docker SBOM for ${{ matrix.variant.name }} ` ):
51+ gate loosened from ` if: github.event_name == 'release' ` to
52+ ` if: github.event_name != 'pull_request' ` so push-to-main also
53+ produces SBOMs for auto-vex-presence to consume.
54+ - ` build-vex ` job: ` needs ` extended to include ` auto-vex-presence ` .
55+ Added a feature-flagged download step (` if: vars.AUTO_VEX_PRESENCE == '1' ` )
56+ and conditional merge inclusion. When the repo variable is
57+ ` AUTO_VEX_PRESENCE=1 ` , ` vex.auto-presence.json ` is merged alongside
58+ ` .vex/*.json ` ; otherwise hand-authored only. Artifact is produced
59+ regardless — only the merge is gated.
60+ - ` src/lib.rs ` : re-export ` pub mod auto_vex_presence ` .
61+ - ` docs/src/security/vex.md ` : CI flow section renumbered to 6 steps;
62+ added step 2 for auto-presence. Rewrote "Why we did not auto-generate
63+ statements" section as "What we automate, and what stays human" — the
64+ policy now distinguishes ` component_not_present ` (mechanical,
65+ automatable) from every other triage decision (human-authored).
66+ - ` .vex/README.md ` : added "Automated statements (roadmap Phase 2)"
67+ section explaining the review-artifact vs flag-gated-merge split.
68+
69+ ### Why
70+ Phase 2 of the automated VEX generation roadmap
71+ (` ~/dev/roadmaps/5spot-automated-vex-generation.md ` ). Most of the
72+ hand-authored statements under ` .vex/ ` exist because Grype flags
73+ CVEs on libc / glibc / zlib code paths that the Rust binary never
74+ reaches — i.e. the vulnerable component is present in the image
75+ filesystem but never invoked. A subset of those (plus most
76+ base-image CVEs going forward) can be suppressed mechanically by
77+ observing that the flagged package's purl is not in the SBOM at
78+ all: the SBOM is authoritative for what's in the product, so
79+ "component_not_present" is the one OpenVEX justification with a
80+ purely verifiable definition. Automating that specific case shrinks
81+ the ` .vex/ ` maintenance queue without compromising the trust model
82+ for any other justification.
83+
84+ The implementation is strictly SBOM-driven — no reachability analysis,
85+ no source-code inspection. Reachability is Phase 3.
86+
87+ ### Rollout / feature-flag
88+ Per the roadmap rollout plan, the auto-presence artifact is
89+ produced and uploaded on every push + release, but it is ** only
90+ merged into the signed VEX document when ` vars.AUTO_VEX_PRESENCE=1 `
91+ is set** at the repository or organization level. This gives
92+ maintainers at least one release of parallel-run validation: the
93+ artifact is visible for review, but consumers see only the
94+ hand-authored VEX until the flag is flipped. This matches the
95+ roadmap's explicit guidance ("Run in parallel with hand-authored VEX
96+ for one release; diff the auto-set against .vex/ and confirm no
97+ surprises before flipping it on").
98+
99+ ### Verification
100+ - ` cargo fmt --all -- --check ` : clean.
101+ - ` cargo clippy --all-targets --all-features -- -D warnings ` : clean.
102+ - ` cargo test --lib ` : 349 tests pass (329 pre-existing + 20 new for
103+ ` auto_vex_presence ` ).
104+ - Smoke test: ran the bin against a synthetic Grype JSON (3 matches),
105+ synthetic SBOM (covering 1 of the 3 purls), and the current ` .vex/ `
106+ directory (covering 1 of the 3 CVEs). The bin emitted exactly the
107+ one statement for the remaining CVE whose purl was absent, and
108+ ` vexctl merge ` of the auto-presence output + ` .vex/*.json `
109+ produced a 16-statement document with the auto statement correctly
110+ included.
111+
112+ ### Impact
113+ - [ ] Breaking change
114+ - [x] Requires cluster rollout (no — CI only)
115+ - [ ] Config change only
116+ - [x] Documentation only
117+
118+ * (Rollout note: when maintainers want to flip ` AUTO_VEX_PRESENCE=1 ` ,
119+ that's a repo-variable change in GitHub settings, no code deploy.)*
120+
121+ ---
122+
123+ ## [ 2026-04-22 19:45] - Replace Python VEX tooling with vexctl (roadmap Phase 1)
124+
125+ ** Author:** Erick Bourgeois
126+
127+ ### Removed
128+ - ` tools/ ` — entire directory deleted. Removed ` assemble_openvex.py ` ,
129+ ` validate_vex.py ` , ` validate-vex.sh ` , ` tests/assemble-openvex-tests.sh ` ,
130+ ` tests/validate-vex-tests.sh ` , and all 18 fixture directories under
131+ ` tests/fixtures/ ` . ~ 400 LOC of bespoke Python + shell tests replaced
132+ by upstream ` vexctl ` invocations.
133+
134+ ### Changed
135+ - ` .vex/*.toml ` → ` .vex/*.json ` (15 files): migrated every statement
136+ from the bespoke TOML dialect to the native OpenVEX v0.2.0 JSON
137+ shape. Each file is now a single-statement OpenVEX document that
138+ ` vexctl merge ` can consume directly; no translation layer.
139+ - ` Makefile ` : added ` VEXCTL_VERSION ?= 0.4.1 ` , ` vexctl-install ` target
140+ (brew on macOS, pinned GitHub release tarball with checksum
141+ verification on Linux; errors on other OSes), ` vex-validate `
142+ (parses every ` .vex/*.json ` via ` vexctl merge ` — successful parse
143+ is the validation), and ` vex-assemble ` (prints the merged document
144+ for local preview). ` .PHONY ` list updated.
145+ - ` .github/workflows/build.yaml ` :
146+ - ` validate-vex ` job: dropped Python setup + custom validator calls;
147+ now runs ` make vexctl-install ` then ` make vex-validate ` .
148+ - ` build-vex ` job: dropped Python setup + ` assemble_openvex.py ` ;
149+ now runs `vexctl merge --id <per-event-id > --author <actor >
150+ .vex/* .json` directly. Per-event ` @id` logic (release / push /
151+ PR) preserved verbatim. Cosign attestation, artifact upload,
152+ and ` attest-build-provenance ` steps unchanged.
153+ - PR ` paths: ` filter: removed ` tools/** ` (directory no longer
154+ exists); ` .vex/** ` retained.
155+ - ` .vex/README.md ` : rewritten to document native OpenVEX JSON
156+ authoring and ` make vex-validate ` / ` make vex-assemble ` instead of
157+ the old TOML schema and shell validator.
158+ - ` docs/src/security/vex.md ` : CI flow section updated — step 1
159+ ("validate") and step 2 ("assemble") now describe ` vexctl merge `
160+ behaviour; dropped the obsolete "cross-check with ` vexctl validate ` "
161+ step (vexctl has no ` validate ` subcommand). Maintainer-authoring
162+ section rewritten with the JSON shape in place of the TOML shape.
163+ - ` .claude/SKILL.md ` : release checklist item for VEX updated from the
164+ old three-script check to ` make vex-validate ` .
165+
166+ ### Why
167+ Phase 1 of the automated VEX generation roadmap
168+ (` ~/dev/roadmaps/5spot-automated-vex-generation.md ` ). The custom
169+ Python toolchain duplicated functionality that ` vexctl ` — the official
170+ OpenVEX CLI — provides upstream. Maintaining a bespoke TOML dialect,
171+ its validator, its assembler, and two fixture-based test suites
172+ added ~ 400 LOC of surface area with no offsetting benefit once we
173+ accept OpenVEX JSON as the authoring format. Replacing the whole
174+ stack with ` vexctl merge ` (which doubles as the validator via
175+ successful parse) removes the Python runtime from CI entirely,
176+ eliminates a class of drift between our validator and the upstream
177+ spec, and prepares the pipeline for Phases 2–3 (automated VEX
178+ generation from SBOM and call-graph reachability).
179+
180+ The TOML-over-JSON ergonomics hit is real but small: 15 files in
181+ ` .vex/ ` , each ~ 15 lines of JSON, with no inline comments. In
182+ exchange, future maintainers work with the same format upstream
183+ tooling speaks and any OpenVEX-aware tool can consume the source
184+ directory directly.
185+
186+ ### Verification
187+ - ` make vex-validate ` parses all 15 ` .vex/*.json ` successfully.
188+ - ` vexctl merge --id <x> --author <y> .vex/*.json ` produces a
189+ 15-statement document structurally identical to what the Python
190+ assembler would have produced (confirmed via key-by-key diff of
191+ ` @context ` , ` @id ` , ` author ` , ` version ` , and every statement's
192+ ` products ` , ` status ` , ` justification ` , ` impact_statement ` ,
193+ ` action_statement ` , ` timestamp ` ).
194+
195+ ### Impact
196+ - [ ] Breaking change
197+ - [ ] Requires cluster rollout
198+ - [ ] Config change only
199+ - [x] Documentation only
200+
201+ * (Not really "documentation only" — code deletion is substantive —
202+ but no runtime, CRD, or API-surface change. Closest pre-existing
203+ box.)*
204+
205+ ---
206+
12207## [ 2026-04-21 15:00] - Model emergency-reclaim in CALM — ADD retrofit
13208
14209** Author:** Erick Bourgeois
0 commit comments