Skip to content

release: v0.9.6 — finish the roadmap + comprehensive docs refresh#23

Merged
Metbcy merged 21 commits intomainfrom
release/v0.9.6
Apr 30, 2026
Merged

release: v0.9.6 — finish the roadmap + comprehensive docs refresh#23
Metbcy merged 21 commits intomainfrom
release/v0.9.6

Conversation

@Metbcy
Copy link
Copy Markdown
Owner

@Metbcy Metbcy commented Apr 30, 2026

v0.9.6 — finish the roadmap

Closes out every "Future candidates" entry from the v0.9.5 roadmap with
explicit dispositions (ship / non-goal / blocked-on-upstream / decided)
plus a comprehensive documentation refresh across every chapter.

Highlights

  • OCI attestation verification. bomdrift diff --before-attestation <oci-ref> --after-attestation <oci-ref> shells out to cosign verify- attestation --type=cyclonedx, verifies + decodes the in-toto envelope,
    feeds the verified SBOM into the standard parser. New
    --require-attestation flag refuses falling back to file inputs for
    production CI gates. New chapter docs/src/attestation.md.
  • External-process plugin system. --plugin <manifest.toml>
    (repeatable). JSON over stdin/stdout. Best-effort failures (timeout,
    non-zero exit, malformed output drop the offending plugin's findings;
    rest of report renders). New bomdrift.plugin SARIF rule with stable
    fingerprints. Worked example shipped at examples/plugins/banned- packages/. New chapter docs/src/plugins.md.
  • CLI calibration knobs for the three previously-hardcoded
    thresholds: --typosquat-similarity-threshold,
    --young-maintainer-days, --cache-ttl-hours. --debug-calibration
    rows now emit the active threshold so collected data reflects the
    real run.
  • CACHE_TTL_SECS unified. Previously duplicated in 4 modules.
    Single source of truth in src/enrich/cache.rs with
    effective_ttl_secs(Option<u64>) helper.
  • Comprehensive docs refresh. README rewritten with 11-row
    comparison table vs Socket / Snyk / Trivy / OSV-Scanner / Grype.
    cli-reference.md rewritten end-to-end — every flag documented and
    grouped by purpose with introduced-in annotations. architecture.md
    module map covers all 8 v0.7-v0.9.6 new modules. baseline.md schema
    reference. Per-enricher chapter audit + standardized subsections.
    SUMMARY.md reorganized.

Roadmap closures (v0.9.5 Future candidates → v0.9.6 dispositions)

Item Disposition
OCI artifact attestation Shipped (cosign shell-out)
Custom rules / plugin system Shipped (external-process, minimal)
Calibration backlog (3 hardcoded thresholds + dup TTL) Shipped (CLI knobs + unification)
Reachability Non-goal (pair with Endor / Snyk)
GraphQL maintainer-age Decided: REST stays
VEX vocab beyond OpenVEX 8 Spec-bound (we follow OpenVEX)
PyPI / crates.io maintainer-set-changed Blocked on upstream APIs

After this PR merges the "Future candidates" section is empty.

Process

Three sequential sub-agents on isolated worktrees / branch:

  • feat/v0.9.6-rust (parallel): 4 phases of Rust source changes
    (cache-TTL unify → calibration knobs → OCI attestation → plugin
    system core).
  • feat/v0.9.6-platform (parallel with rust): 4 phases of
    docs/examples/CI changes (roadmap cleanup → banned-packages example
    plugin → attestation docs chapter → plugins protocol docs chapter).
  • release/v0.9.6 (sequential after both): comprehensive
    10-phase documentation refresh, then this release-prep commit.

Both feature branches merged cleanly into release/v0.9.6 with zero
conflicts (disjoint file ownership). 420 tests green throughout.

Test status

  • 389 → 420 (+31). All green: cargo test --release ubuntu / macos /
    windows.
  • cargo clippy --all-targets --all-features --release -- -D warnings
    clean (Rust 1.88 toolchain pinned in CI by v0.9.5).
  • cargo fmt --all -- --check clean.

Scope notes

Stayed deferred (v1.0+ candidates):

  • PyPI / crates.io maintainer-set-changed (upstream API blockers).
  • WASM / sandboxed plugin model (current external-process model works;
    revisit if demand materializes).
  • Bitbucket / Azure DevOps action-side vex: / emit-vex: / plugin:
    inputs (CLI surface is broader than action surface).
  • Multi-major version-jump MIN_MAJOR_DELTA calibration knob (only
    remaining hardcoded threshold).

Post-merge checklist (maintainer)

  1. Tag v0.9.6 on the merge commit.
  2. Re-point the floating v1 tag to v0.9.6.
  3. Cut the GitHub Release with the cosign-signed archives.
  4. Delete release/v0.9.6, feat/v0.9.6-rust, feat/v0.9.6-platform
    branches.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

copilot and others added 20 commits April 29, 2026 19:08
Resolves every entry in docs/src/roadmap.md's Future candidates and
Calibration backlog sections per the v0.9.6 finish-the-roadmap plan:

- New Shipped (v0.9.6) section listing F1+A+B+C.
- Reachability moved to Non-goals as its own subsection (lifted from
  STATUS pair-with-Endor recommendation).
- GraphQL maintainer-age recorded under new "Investigated and decided"
  section with the per_page=1+Link rationale; decided REST stays.
- VEX vocabulary justification documented in vex.md; entry removed
  from roadmap.
- PyPI / crates.io maintainer-set-changed moved to new
  "Blocked on upstream" subsection with precise API gap.
- Calibration backlog collapsed to a single sentence pointing at the
  three new flags + config keys.
- cli-reference: documented --typosquat-similarity-threshold,
  --young-maintainer-days, --cache-ttl-hours.
- STATUS: added rows for plugin system + attestation verification.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Promote pub const CACHE_TTL_SECS in src/enrich/cache.rs as the only
definition. Replace the duplicated const in epss.rs, kev.rs, and
registry.rs with a use of crate::enrich::cache::CACHE_TTL_SECS.

Foundation for v0.9.6 calibration knobs (cache-ttl-hours override
needs to plumb through one place, not four).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reference plugin demonstrating bomdrift's external-process plugin
protocol. Reads banned.txt (one purl prefix per line, # comments
allowed), checks each Added or VersionChanged component's purl as a
prefix match, emits {kind, message, severity, rule_id} findings on
stdout per the v0.9.6 plugin protocol.

Includes:
- plugin.toml manifest (timeout_ms=5000, invoke_on=[added, version-changed])
- check-banned.sh bash + jq executable (bash -n clean)
- banned.txt with 6 illustrative entries (event-stream, coa, ctx,
  rustdecimal, etc.) + comments documenting why each was flagged
- README covering adaptation, CI wiring, performance (O(N x M)),
  and security (plugins are not sandboxed) notes

Smoke-tested against a matching, non-matching, and exact-version
purl envelope.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New docs/src/attestation.md covering the v0.9.6 OCI attestation flow:
overview, prerequisites, generating attestations (sketch), the four
new flags (--before-attestation, --after-attestation,
--cosign-identity, --cosign-issuer), --require-attestation hard mode,
explicit threat-model trust boundaries, self-managed Sigstore via
SIGSTORE_REKOR_URL etc, troubleshooting top failure modes, and what
is intentionally not in v0.9.6 (SPDX, in-process Sigstore client,
air-gap test coverage).

Linked from SUMMARY.md under the Output section.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New docs/src/plugins.md covering the v0.9.6 plugin system end to end:

- Overview + threat model (plugins are not sandboxed).
- Design rationale for shell-out vs WASM.
- TOML manifest schema (name, exec, timeout_ms, invoke_on, ...).
- Stdin/stdout JSON protocol with the full envelope shapes.
- SARIF mapping under the new bomdrift.plugin rule.
- Failure semantics table (non-zero, timeout, malformed JSON, ...).
- Windows timeout caveat.
- Worked-example pointer to examples/plugins/banned-packages/.
- Performance notes (sequential, N x P, fork costs).
- Security checklist (vet, pin, scrub env, mirror).
- v0.9.6 stability promise + protocol_version forward-compat plan.
- GitHub Actions wiring example.

Linked from SUMMARY.md under Operations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ays, cache TTL

Three new CLI flags + matching [diff] config keys:

- --typosquat-similarity-threshold <FLOAT> (0.0..=1.0, clap-validated)
- --young-maintainer-days <i64>           (>= 1, clap-validated)
- --cache-ttl-hours <u64>                 (>= 1, applies uniformly to
                                          OSV/EPSS/KEV/Registry caches)

New helper: cache::effective_ttl_secs(Option<u64>) -> u64. Single
source of truth for resolving the override-vs-default decision; threaded
through all four cache call sites.

New helpers in enrichers, preserving the v0.9 public surface:
- typosquat::enrich_with_threshold(cs, Option<f64>)
- maintainer::enrich_with(..., Option<i64>) — extended in-place
- osv::enrich_cached_with_ttl(cs, no_cache, Option<u64>)
- epss::enrich_with_ttl / kev::enrich_with_ttl
- registry::enrich(cs, recently_published_days, cache_ttl_hours)

Calibration tap now emits the *active* threshold (the override when
set, else the const default), so adopters running --debug-calibration
with custom knobs see what the enricher actually used.

Tests: clap rejection for out-of-range / non-numeric values; calibration
threshold-column reflects the override; typosquat findings drop when
threshold is raised; cache TTL helper conversion + override semantics.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…gistries

New `bomdrift::attestation` module wraps `cosign verify-attestation
--type=cyclonedx`, decodes the resulting in-toto DSSE envelope, and
extracts the embedded CycloneDX predicate as serialized JSON ready for
the standard parser pipeline.

CLI:
- --before-attestation <oci-ref>     (conflicts with positional 'before')
- --after-attestation <oci-ref>      (conflicts with positional 'after')
- --cosign-identity <regex>          (passed to --certificate-identity-regexp)
- --cosign-issuer <url>              (passed to --certificate-oidc-issuer)
- --require-attestation              (refuses local-file fallback)

The positional `before`/`after` paths become optional, gated by
`required_unless_present` on the matching attestation flag.

Cosign is treated as an optional runtime dep: when missing on PATH
we surface a clear error pointing at the install docs (no in-process
sigstore verification — that demands a Fulcio CA bundle + rekor
witness validation that's out of scope for v0.9.6).

`--debug-calibration` gains an `attestation|<oci>|verified|<identity>`
row so adopters can confirm the cert regex cosign accepted.

New direct dep: `base64 = "0.22"` (already transitively present via
ureq; promoted to direct so we own the version pin).

Tests: in-toto envelope parse (well-formed + per-line emission +
malformed payload variants); fake-cosign integration test prepends a
tempdir to PATH (serialized via clock::test_env_lock); cosign-not-on-PATH
error path; clap conflicts and --require-attestation gating.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Minimum-viable Phase C: bomdrift learns to load plugins via TOML
manifests, shell out to the plugin executable per added /
version-changed component, and merge plugin findings into the standard
Enrichment surface. No WASM, no sandbox, no language commitment —
plugins are whatever executable the author wants.

New `bomdrift::plugin` module:
- PluginManifest / InvokeEvent / PluginFinding / PluginSeverity types.
- load_manifest(path) — parses TOML, resolves relative `exec` against
  manifest dir, validates timeout_ms > 0.
- run_plugins(manifests, cs) — spawns child per (plugin, component, event),
  feeds JSON to stdin, polls try_wait() with 25ms tick + manifest
  timeout_ms, parses stdout JSON. Best-effort: timeout / non-zero exit /
  malformed JSON drops findings + emits stderr at BOMDRIFT_DEBUG=1.

Wire format ("v1", may evolve):
  stdin:  { protocol_version: 1, component, event, before }
  stdout: { findings: [{ kind, message, severity, rule_id }] }

CLI: --plugin <path> (repeatable). Manifests load eagerly so a typo
fails the run; runtime plugin failures stay best-effort.

Renderers gain a Plugin findings section:
- Markdown: subsection per plugin, severity-emoji-prefixed bullets.
- Term: [PLG] tag, severity-toned ANSI coloring.
- JSON: enrichment.plugin_findings array (already wired via #[serde]).
- SARIF: new bomdrift.plugin rule + properties.pluginName /
  findingKind / ruleId / purl. Stable partialFingerprints from
  sha256(plugin_name + component_purl + rule_id) — distinct per finding,
  byte-stable across runs. Severity maps info→note / warning→warning /
  error→error.

No new heavy deps. Timeouts use std::thread::sleep + Child::try_wait
in a loop (no wait-timeout crate needed for the v0.9.6 contract).

Tests: manifest parse (min + full + missing-exec + bad-enum-value);
happy invocation; timeout drops findings (well under sleep duration);
non-zero exit drops findings; malformed JSON drops findings; two
plugins merge correctly; fingerprint stability + distinctness;
SARIF emits one bomdrift.plugin result per finding with distinct
fingerprints; CLI end-to-end produces 'Plugin findings' in markdown
and bomdrift.plugin results in SARIF.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
….9.6-new section

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…-code-scanning + cross-link comment-bridges

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…dd introduced-in annotations

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… + test conventions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…bling, see-also

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ssions / Advanced

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Bump release-signing example to VERSION=v0.9.6
- Replace stale GitLab 'deferred to v0.8' note with v0.9 Worker bridge documentation
- Fix two anchor links in baseline.md to match actual headings (vex.md#emitting-vex, license-policy.md#suppression)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Bump Cargo.toml + Cargo.lock 0.9.5 → 0.9.6.
- CHANGELOG.md: v0.9.6 entry covering OCI attestation verification,
  external-process plugin system, CLI calibration knobs (typosquat
  similarity / young-maintainer-days / cache-ttl-hours), CACHE_TTL_SECS
  unification, the comprehensive documentation refresh across every
  chapter, and the roadmap cleanup that closes out every Future
  candidate from v0.9.5 with explicit dispositions.

420 tests, clippy + fmt clean, base64 promoted to direct dep
(transitive via ureq, now pinned).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 30, 2026

SBOM diff

Change Count
Added 0
Removed 0
Version changed 1
License changed 0

Version changed (1)

Show details
Ecosystem Name Before After
cargo bomdrift 0.9.5 0.9.6

False positive? Report it · Suppress a finding? Comment /bomdrift suppress <ID> (requires the comment-suppress sub-action) · Docs

Metbcy added a commit that referenced this pull request Apr 30, 2026
…code)

Every caller of `fn comp` is inside `#[cfg(unix)]` tests (the bash-script
plugin runners). Without the matching cfg on `comp` itself, Windows
test builds fail with `dead_code` under `-D warnings`.

Caught by ubuntu+macos passing while windows-latest failed on PR #23.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Metbcy added a commit that referenced this pull request Apr 30, 2026
…code)

Every caller of `fn comp` is inside `#[cfg(unix)]` tests (the bash-script
plugin runners). Without the matching cfg on `comp` itself, Windows
test builds fail with `dead_code` under `-D warnings`.

Caught by ubuntu+macos passing while windows-latest failed on PR #23.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…code)

Every caller of `fn comp` is inside `#[cfg(unix)]` tests (the bash-script
plugin runners). Without the matching cfg on `comp` itself, Windows
test builds fail with `dead_code` under `-D warnings`.

Caught by ubuntu+macos passing while windows-latest failed on PR #23.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Metbcy Metbcy merged commit ba4c5c7 into main Apr 30, 2026
9 checks passed
@Metbcy Metbcy deleted the release/v0.9.6 branch April 30, 2026 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant