Skip to content

release: v0.9.0 — interoperability + breadth#21

Merged
Metbcy merged 8 commits intomainfrom
release/v0.9.0
Apr 29, 2026
Merged

release: v0.9.0 — interoperability + breadth#21
Metbcy merged 8 commits intomainfrom
release/v0.9.0

Conversation

@Metbcy
Copy link
Copy Markdown
Owner

@Metbcy Metbcy commented Apr 29, 2026

v0.9.0 — interoperability + breadth

This release lands seven feature themes plus the v0.8 OSV-cache
aliases follow-up. Per-phase commits keep the diff reviewable.

Themes

  1. VEX consume (--vex <path>) — OpenVEX 0.2.0 + CycloneDX VEX
    1.6 statements suppress / annotate findings. Synthetic-id
    convention covers non-CVE finding kinds. (feat(vex): consume…)
  2. VEX emit (--emit-vex <path>) — OpenVEX 0.2.0 doc with
    explicit per-entry vex_status. Critically, baseline-suppressed
    entries default to under_investigation; not_affected is
    opt-in only. Byte-deterministic with SOURCE_DATE_EPOCH.
  3. Full SPDX expression evaluator via spdx = "0.10". Replaces
    the v0.8 atomic+glob matcher. Deprecates allow_ambiguous.
  4. Multi-SCM--platform bitbucket and --platform azure-devops, env auto-detect (BITBUCKET_BUILD_NUMBER,
    TF_BUILD), drop-in pipeline templates with READMEs.
  5. Registry-metadata enrichers — npm/PyPI/crates.io. Three new
    finding kinds (recently-published, deprecated,
    maintainer-set-changed), three new SARIF rules with stable
    fingerprints, --no-registry opt-out and
    --recently-published-days <N> knob.
  6. GitLab comment-driven suppress with five-guard Cloudflare
    Worker bridge (security-reviewed: webhook secret, event filter,
    project allowlist, commenter access_level >= 30, MR-context
    guard rejecting fork-MR exfiltration). New
    bomdrift baseline add --from-comment <BODY> flag.
  7. Explicit non-goals doc — README + STATUS + roadmap call out
    reachability, tarball static analysis, auto-fix PRs, container
    scans, SAST/secrets, risk-score dashboards, and closed-source
    advisory feeds as out of scope. Pair-with table recommends
    complementary tools.

Parallel fix from K

  • OSV cache schema extended with aliases so cache hits no
    longer drop alias data. Old entries without the field migrate
    gracefully (empty vec).

What stayed deferred (and why)

  • Per-exception SPDX allow/deny — WITH <exception> parses and the
    base license is checked, but per-exception granularity is a v1.0
    ask. Documented in docs/src/license-policy.md.
  • PyPI / crates.io maintainer-set-changed — blocked on per-version
    maintainer data in upstream APIs. Documented in
    docs/src/enrichers/registry.md.
  • Bitbucket / Azure DevOps comment-driven suppress — only the diff
    path ships in v0.9; comment-suppress is GitHub-only (and GitLab
    via the bridge).
  • OpenVEX vocabulary extensions — bomdrift uses the spec's 8
    justification values verbatim.

Stats

  • Tests: 343 → 369.
  • New deps: spdx = "0.10". time and sha2 already present from
    v0.8.

Maintainer-side checklist (do NOT do these from the agent)

  • Tag v0.9.0 after merge.
  • Re-point the v1 floating tag to the new release.
  • Cut a Release note pointing at the CHANGELOG entry.
  • Prune release/v0.9.0 after the tag is pushed.

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

bomdrift and others added 8 commits April 29, 2026 14:11
New `--vex <path>` flag (repeatable) on `bomdrift diff`. Each file is
auto-detected as OpenVEX 0.2.0 or CycloneDX VEX 1.6.

- Statements with status `not_affected` / `fixed` suppress matching
  findings (counted in the new "Suppressed by VEX" markdown summary
  row).
- `under_investigation` annotates with a `VEX:` badge in markdown and
  `properties.vexStatus` in SARIF without suppressing.
- `affected` annotates as a no-op badge.

Match keys: `(VulnRef.id OR alias, purl_with_version)`. For non-CVE
finding kinds (typosquat, version-jump, maintainer-age,
license-violation), bomdrift defines a synthetic ID convention
(`bomdrift.<kind>:<purl>:<discriminator>`) so VEX statements can target
those too. Documented in `docs/src/vex.md`.

Multi-file precedence is first-write-wins on `(vuln_id, product)`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New `--emit-vex <path>` flag on `bomdrift diff`. Writes a single
OpenVEX 0.2.0 JSON-LD doc covering:

- baseline-suppressed findings (status from each entry's optional
  `vex_status`, defaulting to `under_investigation` — baseline ≠
  not_affected, never auto-promoted);
- un-suppressed findings (status `affected`, with `status_notes`
  describing the bomdrift finding kind).

Baseline schema extended with optional `vex_status`
(`not_affected|affected|fixed|under_investigation`) and
`vex_justification` on object-form entries. Plain string-form entries
are unchanged.

New `[diff] vex_author` and `[diff] vex_default_justification`
config keys plus `--vex-author` / `--vex-default-justification`
CLI flags. `vex_author` falls back to repo_url, then to `bomdrift`.

Statements are sorted by `(vulnerability.name, products[0].@id)` and
the timestamp uses `clock::now()` so the emitted doc is byte-
deterministic in CI when `SOURCE_DATE_EPOCH` is set.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds `spdx = "0.10"` and replaces the v0.8 atomic+glob matcher with
proper SPDX evaluation:

- `(MIT OR Apache-2.0)` with `allow=[MIT]` permits because the
  licensee can pick MIT.
- `(MIT AND GPL-3.0-only)` with `deny=[GPL-3.0-only]` violates.
- `(GPL-3.0-only OR MIT) AND BSD-3-Clause` with allow `[MIT,
  BSD-3-Clause]` and deny `[GPL-3.0-only]` violates: deny wins
  because a resolution path could pick GPL.
- `Apache-2.0 WITH LLVM-exception` parses cleanly; the base license
  is checked. Per-exception allow/deny is informational only — that
  granularity is deferred to v1.0.

Non-SPDX strings ("Custom", vendor-specific spellings) fall back to
the v0.8 atomic+glob path so user-authored policies keep working.
`NOASSERTION` / `OTHER` / empty stay ambiguous (fail-closed).

`allow_ambiguous` is deprecated: a one-time stderr warning fires
when set. The flag still works on the fallback path. Removal in v1.0.

GNU licenses are special-cased: `spdx` strips `-only`/`-or-later`
into a flag, so we expand candidate names back so user-authored
policies that contain the original spelling match.

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

Extends `cli::Platform` and `markdown::Platform` with two new
variants. The exhaustive `From<cli::Platform> for markdown::Platform`
match keeps the two enums in lockstep at compile time.

- `--platform bitbucket` / `--platform azure-devops` flag values.
- Auto-detection via `BITBUCKET_BUILD_NUMBER` and `TF_BUILD` envs.
- `BITBUCKET_GIT_HTTP_ORIGIN` and `BUILD_REPOSITORY_URI` honored as
  `--repo-url` fallbacks.
- Footer URLs:
  - Bitbucket: `/issues/new` + `bomdrift baseline add` suppress hint.
  - Azure DevOps: `/_workitems/create?templateName=false-positive` +
    `bomdrift baseline add` suppress hint.
- Drop-in pipeline templates with READMEs:
  - `examples/bitbucket-pipelines/`
  - `examples/azure-devops/`
- New docs chapters `bitbucket.md` and `azure-devops.md` linked
  from SUMMARY; CLI reference updated.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ed/deprecated/maintainer-set-changed findings

New `src/enrich/registry.rs` module with three best-effort fetchers
mirroring the OSV / EPSS / KEV pattern:

- npm: `https://registry.npmjs.org/<pkg>` (URL-encoded `@scope/name`)
  — extracts `time`, per-version `deprecated`, per-version
  `maintainers[]`.
- PyPI: `https://pypi.org/pypi/<pkg>/json` — extracts `info.yanked`,
  classifiers, `releases[].upload_time_iso_8601`.
- crates.io: `https://crates.io/api/v1/crates/<name>` (UA required)
  — extracts `crate.updated_at`, `versions[].yanked`,
  `versions[].published_at`.

Three new finding kinds wired through Enrichment, JSON, markdown,
SARIF (`bomdrift.recently-published`, `bomdrift.deprecated`,
`bomdrift.maintainer-set-changed` with stable
partialFingerprints), and the calibration tap.

Disk cache at `<XDG_CACHE>/bomdrift/registry/<eco>/<pkg>.json`,
24h TTL, atomic temp-file + rename. Best-effort: any failure mode
(timeout, parse error, unsupported ecosystem) returns no findings —
diff rendering is never blocked.

New flags / config:
- `--no-registry` and `[diff] no_registry = true` to skip entirely.
- `--recently-published-days <N>` and
  `[diff] recently_published_days = N` to tune the threshold (default
  14 days).
- `--fail-on recently-published` / `--fail-on deprecated` exit-2
  thresholds.

Maintainer-set-changed is npm-only (PyPI / crates.io don't expose a
clean per-version maintainer view); fires for VersionChanged
components.

Address parallel v0.8 follow-up: extend OSV cache schema to carry
`aliases` so cache hits don't lose alias data. Old entries without
the field deserialize with an empty vec for graceful migration.

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

New `bomdrift baseline add --from-comment <BODY>` flag. Parses the
raw note body, extracts the first `/bomdrift suppress <ID>[ reason:
<text>]` directive, validates the ID (GHSA/CVE/MAL/OSV), and either
appends the suppression or exits non-zero with a clear stderr
message so a misconfigured webhook bridge fails loudly.

The grammar mirrors comment-suppress/entrypoint.sh's; a
cross-reference comment in both files notes the lockstep contract.

examples/gitlab-ci/comment-bridge/ ships a Cloudflare Worker
reference implementation enforcing five guards:
  1. Webhook secret (constant-time X-Gitlab-Token compare).
  2. Event-type filter (Note Hook only).
  3. Project-ID allowlist.
  4. Commenter access_level >= 30.
  5. MR-context guard (rejects fork-MR exfiltration).

The bridge triggers a GitLab pipeline that runs
`bomdrift baseline add --from-comment "$BOMDRIFT_NOTE_BODY"` and
pushes the change. suppress.gitlab-ci.yml is updated to handle both
the manual (BOMDRIFT_SUPPRESS_ID) and bridge (BOMDRIFT_NOTE_BODY)
paths.

Includes a Vercel/Netlify/Lambda port note and a deployment guide
with a documented threat model.

gitlab-ci.md gains an Advanced: Comment-driven suppression section
with the trade-off statement up front; baseline.md covers the new
--from-comment flag.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
README gains a fleshed-out Non-goals section covering reachability,
tarball static analysis, auto-fix PR generation, continuous
monitoring, container scans, SAST/secrets, risk-score dashboards,
and closed-source advisory feeds — each with a one-line rationale.
A Pair with... table summarizes recommended complementary tools.

STATUS adopts the same content (shorter form) as 'Out-of-scope by
design' alongside the now-shipped v0.9 capability rows.

docs/src/roadmap.md is reorganized: v0.9 items move to Shipped, v0.8
gains a recap, and the Future candidates list is updated with
items the v0.9 phases didn't cover (per-exception SPDX granularity,
PyPI/crates.io maintainer-set, OpenVEX vocabulary).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Bump Cargo.toml + Cargo.lock to 0.9.0.
- Bump README / quickstart / issue template references from v0.8.0
  to v0.9.0.
- Add the 0.9.0 CHANGELOG entry covering Phases G–L2 with explicit
  Scope notes for what stayed deferred (per-exception SPDX
  allow/deny, PyPI/crates.io maintainer sets, Bitbucket/Azure DevOps
  comment-suppress, OpenVEX vocabulary extensions).

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

SBOM diff

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

Added (1)

Show details
Ecosystem Name Version
cargo spdx 0.10.9

Version changed (1)

Show details
Ecosystem Name Before After
cargo bomdrift 0.8.0 0.9.0

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

@Metbcy Metbcy merged commit bc8eef4 into main Apr 29, 2026
8 checks passed
@Metbcy Metbcy deleted the release/v0.9.0 branch April 29, 2026 21:53
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