|
| 1 | +# SBOM Diff as a Bounded Policy Review Artifact |
| 2 | + |
| 3 | +An SBOM diff can answer a useful, limited question: what dependency evidence |
| 4 | +changed between two inputs? It cannot decide whether a package is safe. |
| 5 | + |
| 6 | +`sbom-diff-and-risk` keeps those statements separate. The tool produces a |
| 7 | +deterministic component diff, local heuristic risk findings, and optional |
| 8 | +policy decisions. Each layer remains visible in JSON, Markdown, SARIF, summary, |
| 9 | +and policy-sidecar artifacts. |
| 10 | + |
| 11 | +## Diff evidence comes first |
| 12 | + |
| 13 | +The diff layer identifies added, removed, and changed components from |
| 14 | +CycloneDX, SPDX, or supported dependency manifests. Component identity and |
| 15 | +signature fields determine membership in those sets. They are not reputation |
| 16 | +signals. |
| 17 | + |
| 18 | +The local risk layer can attach bounded review buckets such as `new_package`, |
| 19 | +`major_upgrade`, `version_change_unclassified`, `unknown_license`, |
| 20 | +`suspicious_source`, and `not_evaluated`. The |
| 21 | +[risk model boundary](../../../docs/risk-model-boundary.md) defines the exact |
| 22 | +inputs for those buckets and the conclusions they do not support. |
| 23 | + |
| 24 | +For example, `new_package` means a component appears only in the after input. |
| 25 | +It does not mean the package is vulnerable or malicious. `not_evaluated` |
| 26 | +preserves an unanswered question instead of filling an evidence gap with a |
| 27 | +guess. |
| 28 | + |
| 29 | +## Policy is a separate decision layer |
| 30 | + |
| 31 | +A local policy can map diff or risk evidence to `pass`, `warn`, or `fail`. |
| 32 | +Policy findings expose stable explanation fields: |
| 33 | + |
| 34 | +- `decision_reason` |
| 35 | +- `policy_rule` |
| 36 | +- `severity_source` |
| 37 | +- `matched_threshold` |
| 38 | +- `observed_value` |
| 39 | + |
| 40 | +Those fields let a reviewer see whether a decision came from a configured |
| 41 | +threshold, a risk bucket, or a default policy severity. They do not upgrade the |
| 42 | +decision into a dependency safety verdict. The full contract is documented in |
| 43 | +[policy decision explainability](policy-decision-explainability.md) and the |
| 44 | +[JSON report schema](report-schema.md). |
| 45 | + |
| 46 | +## A small reproducible review surface |
| 47 | + |
| 48 | +The checked-in examples include before/after inputs, generated reports, summary |
| 49 | +JSON, policy sidecars, and bounded policy-decision fixtures for `pass`, `warn`, |
| 50 | +`fail`, and consumer-side `needs-review`. |
| 51 | + |
| 52 | +From `tools/sbom-diff-and-risk`, a reviewer can run: |
| 53 | + |
| 54 | +```bash |
| 55 | +python -m pip install -e .[dev] |
| 56 | +python scripts/regenerate-example-artifacts.py --check |
| 57 | +python scripts/regenerate-example-artifacts.py --check --only requirements |
| 58 | +``` |
| 59 | + |
| 60 | +These checks use committed no-network examples. They verify that the examples |
| 61 | +still match the code; they do not prove that a third-party dependency is safe. |
| 62 | +The ordered evidence route is in the [reviewer path](reviewer-path.md). |
| 63 | + |
| 64 | +## What remains a human decision |
| 65 | + |
| 66 | +The tool can make a policy choice explicit, but it cannot decide whether that |
| 67 | +policy fits a deployment context. Reviewers still need to ask whether a |
| 68 | +threshold is appropriate, whether a missing license blocks release, whether a |
| 69 | +source allowlist is justified, and whether an evidence gap should fail a gate |
| 70 | +or remain `needs-review`. |
| 71 | + |
| 72 | +The boundary is intentional: the artifact records what changed, which local |
| 73 | +rule matched, and why the configured policy responded. It does not resolve |
| 74 | +CVEs, inspect package contents for malware, infer exploitability, or certify a |
| 75 | +package as safe. |
0 commit comments