add advisories
This commit is contained in:
@@ -0,0 +1,628 @@
|
||||
Here’s a tight, step‑through recipe for making every VEX statement **verifiably** tied to build evidence—using CycloneDX (SBOM), deterministic identifiers, and attestations (in‑toto/DSSE).
|
||||
|
||||
---
|
||||
|
||||
# 1) Build time: mint stable, content‑addressed IDs
|
||||
|
||||
* For every artifact (source, module, package, container layer), compute:
|
||||
|
||||
* `sha256` of canonical bytes
|
||||
* a **deterministic component ID**: `pkg:<ecosystem>/<name>@<version>?sha256=<digest>` (CycloneDX supports `bom-ref`; use this value as the `bom-ref`).
|
||||
* Emit SBOM (CycloneDX 1.6) with:
|
||||
|
||||
* `metadata.component` = the top artifact
|
||||
* each `components[].bom-ref` = the deterministic ID
|
||||
* `properties[]` for extras: build system run ID, git commit, tool versions.
|
||||
|
||||
**Example (SBOM fragment):**
|
||||
|
||||
```json
|
||||
{
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.6",
|
||||
"serialNumber": "urn:uuid:7b4f3f64-8f0b-4a7d-9b3f-7a0a2b6cf6a9",
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"component": {
|
||||
"type": "container",
|
||||
"name": "stellaops/scanner",
|
||||
"version": "1.2.3",
|
||||
"bom-ref": "pkg:docker/stellaops/scanner@1.2.3?sha256=7e1a...b9"
|
||||
}
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"type": "library",
|
||||
"name": "openssl",
|
||||
"version": "3.2.1",
|
||||
"purl": "pkg:apk/alpine/openssl@3.2.1-r0",
|
||||
"bom-ref": "pkg:apk/alpine/openssl@3.2.1-r0?sha256=2c0f...54e",
|
||||
"properties": [
|
||||
{"name": "build.git", "value": "ef3d9b4"},
|
||||
{"name": "build.run", "value": "gha-61241"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 2) Sign the SBOM as evidence
|
||||
|
||||
* Wrap the SBOM in **DSSE** and sign it (cosign or in‑toto).
|
||||
* Record to Rekor (or your offline mirror). Store the **log index**/UUID.
|
||||
|
||||
**Provenance note:** keep `{ sbomDigest, dsseSignature, rekorLogID }`.
|
||||
|
||||
---
|
||||
|
||||
# 3) Normalize vulnerability findings to the same IDs
|
||||
|
||||
* Your scanner should output findings where `affected.bom-ref` equals the component’s deterministic ID.
|
||||
* If using CVE/OSV, keep both the upstream ID and your local `bom-ref`.
|
||||
|
||||
**Finding (internal record):**
|
||||
|
||||
```json
|
||||
{
|
||||
"vulnId": "CVE-2024-12345",
|
||||
"affected": "pkg:apk/alpine/openssl@3.2.1-r0?sha256=2c0f...54e",
|
||||
"source": "grype@0.79.0",
|
||||
"introducedBy": "stellaops/scanner@1.2.3",
|
||||
"evidence": {"scanDigest": "sha256:aa1b..."}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 4) Issue VEX with deterministic targets
|
||||
|
||||
* Create a CycloneDX **VEX** doc where each `vulnerabilities[].affects[].ref` equals the SBOM `bom-ref`.
|
||||
* Use `analysis.justification` and `analysis.state` (`not_affected`, `affected`, `fixed`, `under_investigation`).
|
||||
* Add **tight reasons** (reachability, config, platform) and a **link back to evidence** via properties.
|
||||
|
||||
**VEX (CycloneDX) minimal:**
|
||||
|
||||
```json
|
||||
{
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.6",
|
||||
"version": 1,
|
||||
"vulnerabilities": [
|
||||
{
|
||||
"id": "CVE-2024-12345",
|
||||
"source": {"name": "NVD"},
|
||||
"analysis": {
|
||||
"state": "not_affected",
|
||||
"justification": "vulnerable_code_not_present",
|
||||
"response": ["will_not_fix"],
|
||||
"detail": "Linked OpenSSL feature set excludes the vulnerable cipher."
|
||||
},
|
||||
"affects": [
|
||||
{"ref": "pkg:apk/alpine/openssl@3.2.1-r0?sha256=2c0f...54e"}
|
||||
],
|
||||
"properties": [
|
||||
{"name": "evidence.sbomDigest", "value": "sha256:91f2...9a"},
|
||||
{"name": "evidence.rekorLogID", "value": "425c1d1e..."},
|
||||
{"name": "reachability.report", "value": "sha256:reacha..."},
|
||||
{"name": "policy.decision", "value": "TrustGate#R-17.2"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 5) Sign the VEX and anchor it
|
||||
|
||||
* Wrap the VEX in DSSE, sign, and (optionally) publish to Rekor (or your Proof‑Market mirror).
|
||||
* Now you can verify: **component digest ↔ SBOM bom‑ref ↔ VEX affects.ref ↔ signatures/log**.
|
||||
|
||||
---
|
||||
|
||||
# 6) Verifier flow (what your UI/CLI should do)
|
||||
|
||||
1. Load VEX → verify DSSE signature → (optional) Rekor inclusion.
|
||||
2. For each `affects.ref`, check there exists an SBOM component with the **exact same value**.
|
||||
3. Verify the SBOM signature and Rekor entry (hash of SBOM equals what VEX references in `properties.evidence.sbomDigest`).
|
||||
4. Cross‑check the running artifact/container digest matches the SBOM `metadata.component.bom-ref` (or OCI manifest digest).
|
||||
5. Render the decision with **explainable evidence** (links to proofs, reachability report hash, policy rule ID).
|
||||
|
||||
---
|
||||
|
||||
# 7) Attestation shapes (quick starters)
|
||||
|
||||
**DSSE envelope (JSON) around SBOM or VEX payload:**
|
||||
|
||||
```json
|
||||
{
|
||||
"payloadType": "application/vnd.cyclonedx+json;version=1.6",
|
||||
"payload": "BASE64(SBOM_OR_VEX_JSON)",
|
||||
"signatures": [
|
||||
{"keyid": "SHA256-PUBKEY", "sig": "BASE64(SIG)"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**in‑toto Statement for provenance → attach SBOM hash:**
|
||||
|
||||
```json
|
||||
{
|
||||
"_type": "https://in-toto.io/Statement/v1",
|
||||
"predicateType": "https://slsa.dev/provenance/v1",
|
||||
"subject": [{"name": "stellaops/scanner", "digest": {"sha256": "7e1a...b9"}}],
|
||||
"predicate": {
|
||||
"buildType": "stellaops/ci",
|
||||
"materials": [{"uri": "git+https://...#ef3d9b4"}],
|
||||
"metadata": {"buildInvocationID": "gha-61241"},
|
||||
"externalParameters": {"sbomDigest": "sha256:91f2...9a"}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 8) Practical guardrails (so it stays deterministic)
|
||||
|
||||
* **Never** generate `bom-ref` from mutable fields (like file paths). Use content digests + stable PURL.
|
||||
* Pin toolchains and normalize JSON (UTF‑8, sorted keys if you post‑hash).
|
||||
* Store `{ toolVersions, feed snapshots, policy set hash }` to replay decisions.
|
||||
* For containers, prefer `bom-ref = pkg:oci/<repo>@<digest>` PLUS layer evidence in `components[]`.
|
||||
|
||||
---
|
||||
|
||||
# 9) “Hello‑world” verification script (pseudo)
|
||||
|
||||
```bash
|
||||
# 1) Verify SBOM sig -> get sbomDigest
|
||||
cosign verify-blob --signature sbom.sig sbom.json
|
||||
|
||||
# 2) Verify VEX sig
|
||||
cosign verify-blob --signature vex.sig vex.json
|
||||
|
||||
# 3) Check that every VEX affects.ref exists in SBOM
|
||||
jq -r '.vulnerabilities[].affects[].ref' vex.json | while read ref; do
|
||||
jq -e --arg r "$ref" '.components[] | select(.["bom-ref"]==$r)' sbom.json >/dev/null
|
||||
done
|
||||
|
||||
# 4) Compare running image digest to SBOM metadata.component.bom-ref
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Where this fits in Stella Ops (quick wiring)
|
||||
|
||||
* **Sbomer**: emits CycloneDX with deterministic `bom-ref`s + DSSE sig.
|
||||
* **Scanner**: normalizes findings to `bom-ref`.
|
||||
* **Vexer**: produces/signed VEX; includes `properties` back to SBOM/reachability/policy.
|
||||
* **Authority/Verifier**: one click “Prove it” view → checks DSSE, Rekor, and `ref` equality.
|
||||
* **Proof Graph**: edge types: `produces(SBOM)`, `affects(VEX↔component)`, `signedBy`, `recordedAt(Rekor)`.
|
||||
|
||||
If you want, I can turn this into:
|
||||
|
||||
* a **.NET 10** helper lib for stable `bom-ref` generation,
|
||||
* a **CLI** that takes `sbom.json` + `vex.json` and runs the full verification,
|
||||
* or **fixtures** (golden SBOM/VEX/DSSE triplets) for your CI.
|
||||
Below is a developer-oriented blueprint you can hand to engineers as “How we build a verifiable SBOM→VEX chain”.
|
||||
|
||||
---
|
||||
|
||||
## 1. Objectives and Trust Model
|
||||
|
||||
**Goal:** Any VEX statement about a component must be:
|
||||
|
||||
1. **Precisely scoped** to one or more concrete artifacts.
|
||||
2. **Cryptographically linked** to the SBOM that defined those artifacts.
|
||||
3. **Replayable**: a third party can re-run verification and reach the same conclusion.
|
||||
4. **Auditable**: every step is backed by signatures and immutable logs (e.g., Rekor or internal ledger).
|
||||
|
||||
**Questions you must be able to answer deterministically:**
|
||||
|
||||
* “Which exact artifact does this VEX statement apply to?”
|
||||
* “Show me the SBOM where this artifact is defined, and prove it was not tampered with.”
|
||||
* “Prove that the VEX document I am looking at was authored and/or approved by the expected party.”
|
||||
|
||||
---
|
||||
|
||||
## 2. Canonical Identifiers: Non-Negotiable Foundation
|
||||
|
||||
You cannot build a verifiable chain without **stable, content-addressed IDs**.
|
||||
|
||||
### 2.1 Component IDs
|
||||
|
||||
For every component, choose a deterministic scheme:
|
||||
|
||||
* Base: PURL or URN, e.g.,
|
||||
`pkg:maven/org.apache.commons/commons-lang3@3.14.0`
|
||||
* Extend with content hash:
|
||||
`pkg:maven/org.apache.commons/commons-lang3@3.14.0?sha256=<digest>`
|
||||
* Use this value as the **CycloneDX `bom-ref`**.
|
||||
|
||||
**Developer rule:**
|
||||
|
||||
* `bom-ref` must be:
|
||||
|
||||
* Stable across SBOM regenerations for identical content.
|
||||
* Independent of local, ephemeral data (paths, build numbers).
|
||||
* Derived from canonical bytes (normalized archive/layer, not “whatever we saw on disk”).
|
||||
|
||||
### 2.2 Top-Level Artifact IDs
|
||||
|
||||
For images, archives, etc.:
|
||||
|
||||
* Prefer OCI-style naming:
|
||||
`pkg:oci/<repo>@sha256:<manifestDigest>`
|
||||
* Set this as `metadata.component.bom-ref` in the SBOM.
|
||||
|
||||
---
|
||||
|
||||
## 3. SBOM Generation Guidelines
|
||||
|
||||
### 3.1 Required Properties
|
||||
|
||||
When emitting a CycloneDX SBOM (1.5/1.6):
|
||||
|
||||
* `metadata.component`:
|
||||
|
||||
* `name`, `version`, `bom-ref`.
|
||||
* `components[]`:
|
||||
|
||||
* `name`, `version`, `purl` (if available), **`bom-ref`**.
|
||||
* `hashes[]`: include at least `SHA-256`.
|
||||
* `properties[]`:
|
||||
|
||||
* Build metadata:
|
||||
|
||||
* `build.gitCommit`
|
||||
* `build.pipelineRunId`
|
||||
* `build.toolchain` (e.g., `dotnet-10.0.100`, `maven-3.9.9`)
|
||||
* Optional:
|
||||
|
||||
* `provenance.statementDigest`
|
||||
* `scm.url`
|
||||
|
||||
Minimal JSON fragment:
|
||||
|
||||
```json
|
||||
{
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.6",
|
||||
"metadata": {
|
||||
"component": {
|
||||
"type": "container",
|
||||
"name": "example/api-gateway",
|
||||
"version": "1.0.5",
|
||||
"bom-ref": "pkg:oci/example/api-gateway@sha256:abcd..."
|
||||
}
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"type": "library",
|
||||
"name": "openssl",
|
||||
"version": "3.2.1",
|
||||
"purl": "pkg:apk/alpine/openssl@3.2.1-r0",
|
||||
"bom-ref": "pkg:apk/alpine/openssl@3.2.1-r0?sha256:1234...",
|
||||
"hashes": [
|
||||
{ "alg": "SHA-256", "content": "1234..." }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 SBOM Normalization
|
||||
|
||||
Developer directions:
|
||||
|
||||
* Normalize JSON before hashing/signing:
|
||||
|
||||
* Sorted keys, UTF-8, consistent whitespace.
|
||||
* Ensure SBOM generation is **deterministic** given the same:
|
||||
|
||||
* Inputs (image, source tree)
|
||||
* Tool versions
|
||||
* Settings/flags
|
||||
|
||||
---
|
||||
|
||||
## 4. Signing and Publishing the SBOM
|
||||
|
||||
### 4.1 DSSE Envelope
|
||||
|
||||
Wrap the raw SBOM bytes in a DSSE envelope and sign:
|
||||
|
||||
```json
|
||||
{
|
||||
"payloadType": "application/vnd.cyclonedx+json;version=1.6",
|
||||
"payload": "BASE64(SBOM_JSON)",
|
||||
"signatures": [
|
||||
{
|
||||
"keyid": "<KID>",
|
||||
"sig": "BASE64(SIGNATURE)"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Guidelines:
|
||||
|
||||
* Use a **dedicated signing identity** (keypair or KMS key) for SBOMs.
|
||||
* Publish signature and payload hash to:
|
||||
|
||||
* Rekor or
|
||||
* Your internal immutable log / ledger.
|
||||
|
||||
Persist:
|
||||
|
||||
* `sbomDigest = sha256(SBOM_JSON)`.
|
||||
* `sbomLogId` (Rekor UUID or internal ledger ID).
|
||||
|
||||
---
|
||||
|
||||
## 5. Vulnerability Findings → Normalized Targets
|
||||
|
||||
Your scanners (or imports from external scanners) must map findings onto **the same IDs used in the SBOM**.
|
||||
|
||||
### 5.1 Mapping Rule
|
||||
|
||||
For each finding:
|
||||
|
||||
* `vulnId`: CVE, GHSA, OSV ID, etc.
|
||||
* `affectedRef`: **exact `bom-ref`** from SBOM.
|
||||
* Optional: secondary keys (file path, package manager coordinates).
|
||||
|
||||
Example internal record:
|
||||
|
||||
```json
|
||||
{
|
||||
"vulnId": "CVE-2025-0001",
|
||||
"affectedRef": "pkg:apk/alpine/openssl@3.2.1-r0?sha256:1234...",
|
||||
"scanner": "grype@0.79.0",
|
||||
"sourceSbomDigest": "sha256:91f2...",
|
||||
"foundAt": "2025-12-09T12:34:56Z"
|
||||
}
|
||||
```
|
||||
|
||||
Developer directions:
|
||||
|
||||
* Build a **component index** keyed by `bom-ref` when ingesting SBOMs.
|
||||
* Any finding that cannot be mapped to a known `bom-ref` must be flagged:
|
||||
|
||||
* `status = "unlinked"` and either:
|
||||
|
||||
* dropped from VEX scope, or
|
||||
* fixed by improving normalization rules.
|
||||
|
||||
---
|
||||
|
||||
## 6. VEX Authoring Guidelines
|
||||
|
||||
Use CycloneDX VEX (or OpenVEX) with a strict mapping to SBOM `bom-ref`s.
|
||||
|
||||
### 6.1 Minimal VEX Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.6",
|
||||
"version": 1,
|
||||
"vulnerabilities": [
|
||||
{
|
||||
"id": "CVE-2025-0001",
|
||||
"source": { "name": "NVD" },
|
||||
"analysis": {
|
||||
"state": "not_affected",
|
||||
"justification": "vulnerable_code_not_in_execute_path",
|
||||
"response": ["will_not_fix"],
|
||||
"detail": "The vulnerable function is not reachable in this configuration."
|
||||
},
|
||||
"affects": [
|
||||
{ "ref": "pkg:apk/alpine/openssl@3.2.1-r0?sha256:1234..." }
|
||||
],
|
||||
"properties": [
|
||||
{ "name": "evidence.sbomDigest", "value": "sha256:91f2..." },
|
||||
{ "name": "evidence.sbomLogId", "value": "rekor:abcd-..." },
|
||||
{ "name": "policy.decisionId", "value": "TRUST-ALG-001#rule-7" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 Required Analysis Discipline
|
||||
|
||||
For each `(vulnId, affectedRef)`:
|
||||
|
||||
* `state` ∈ { `not_affected`, `affected`, `fixed`, `under_investigation` }.
|
||||
* `justification`:
|
||||
|
||||
* `vulnerable_code_not_present`
|
||||
* `vulnerable_code_not_in_execute_path`
|
||||
* `vulnerable_code_not_configured`
|
||||
* `vulnerable_code_cannot_be_controlled_by_adversary`
|
||||
* etc.
|
||||
* `detail`: **concrete explanation**, not generic text.
|
||||
* Reference back to SBOM and other proofs via `properties`.
|
||||
|
||||
Developer rules:
|
||||
|
||||
* Every `affects.ref` must match **exactly** a `bom-ref` in at least one SBOM.
|
||||
* VEX generator must fail if it cannot confirm this mapping.
|
||||
|
||||
---
|
||||
|
||||
## 7. Cryptographic Linking: SBOM ↔ VEX
|
||||
|
||||
To make the chain verifiable:
|
||||
|
||||
1. Compute `sbomDigest = sha256(SBOM_JSON)`.
|
||||
2. Inside each VEX vulnerability (or at top-level), include:
|
||||
|
||||
* `properties.evidence.sbomDigest = sbomDigest`
|
||||
* `properties.evidence.sbomLogId` if a transparency log is used.
|
||||
3. Sign the VEX document with DSSE:
|
||||
|
||||
* Separate key from SBOM key, or the same with different usage metadata.
|
||||
4. Optionally publish VEX DSSE to Rekor (or equivalent).
|
||||
|
||||
Resulting verification chain:
|
||||
|
||||
* Artifact digest → matches SBOM `metadata.component.bom-ref`.
|
||||
* SBOM `bom-ref`s → referenced by `vulnerabilities[].affects[].ref`.
|
||||
* VEX references SBOM by hash/log ID.
|
||||
* Both SBOM and VEX have valid signatures and log inclusion proofs.
|
||||
|
||||
---
|
||||
|
||||
## 8. Verifier Implementation Guidelines
|
||||
|
||||
You should implement a **verifier library** and then thin wrappers:
|
||||
|
||||
* CLI
|
||||
* API endpoint
|
||||
* UI “Prove it” button
|
||||
|
||||
### 8.1 Verification Steps (Algorithm)
|
||||
|
||||
Given: artifact digest, SBOM, VEX, signatures, logs.
|
||||
|
||||
1. **Verify SBOM DSSE signature.**
|
||||
2. **Verify VEX DSSE signature.**
|
||||
3. If using Rekor/log:
|
||||
|
||||
* Verify SBOM and VEX entries:
|
||||
|
||||
* log inclusion proof
|
||||
* payload hashes match local files.
|
||||
4. Confirm that:
|
||||
|
||||
* `artifactDigest` matches `metadata.component.bom-ref` or the indicated digest.
|
||||
5. Build a map of `bom-ref` from SBOM.
|
||||
6. For each VEX `affects.ref`:
|
||||
|
||||
* Ensure it exists in SBOM components.
|
||||
* Ensure `properties.evidence.sbomDigest == sbomDigest`.
|
||||
7. Compile per-component decisions:
|
||||
|
||||
For each component:
|
||||
|
||||
* List associated VEX records.
|
||||
* Derive effective state using a policy (e.g., most recent, highest priority source).
|
||||
|
||||
Verifier output should be **structured** (not just logs), e.g.:
|
||||
|
||||
```json
|
||||
{
|
||||
"artifact": "pkg:oci/example/api-gateway@sha256:abcd...",
|
||||
"sbomVerified": true,
|
||||
"vexVerified": true,
|
||||
"components": [
|
||||
{
|
||||
"bomRef": "pkg:apk/alpine/openssl@3.2.1-r0?sha256:1234...",
|
||||
"vulnerabilities": [
|
||||
{
|
||||
"id": "CVE-2025-0001",
|
||||
"state": "not_affected",
|
||||
"justification": "vulnerable_code_not_in_execute_path"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Data Model and Storage
|
||||
|
||||
A minimal relational / document model:
|
||||
|
||||
* `Artifacts`
|
||||
|
||||
* `id`
|
||||
* `purl`
|
||||
* `digest`
|
||||
* `bomRef` (top level)
|
||||
* `Sboms`
|
||||
|
||||
* `id`
|
||||
* `digest`
|
||||
* `dsseSignature`
|
||||
* `logId`
|
||||
* `rawJson`
|
||||
* `SbomComponents`
|
||||
|
||||
* `id`
|
||||
* `sbomId`
|
||||
* `bomRef` (unique per SBOM)
|
||||
* `purl`
|
||||
* `hash`
|
||||
* `VexDocuments`
|
||||
|
||||
* `id`
|
||||
* `digest`
|
||||
* `dsseSignature`
|
||||
* `logId`
|
||||
* `rawJson`
|
||||
* `VexEntries`
|
||||
|
||||
* `id`
|
||||
* `vexId`
|
||||
* `vulnId`
|
||||
* `affectedBomRef`
|
||||
* `state`
|
||||
* `justification`
|
||||
* `evidenceSbomDigest`
|
||||
* `policyDecisionId`
|
||||
|
||||
Guideline: store **raw JSON** plus an **indexed view** for efficient queries.
|
||||
|
||||
---
|
||||
|
||||
## 10. Testing: Golden Chains
|
||||
|
||||
Developers should maintain **golden fixtures** where:
|
||||
|
||||
* A known image or package → SBOM (JSON) → VEX (JSON) → DSSE envelopes → log entries.
|
||||
* For each fixture:
|
||||
|
||||
* A test harness runs the verifier.
|
||||
* Asserts:
|
||||
|
||||
* All signatures valid.
|
||||
* All `affects.ref` map to a SBOM `bom-ref`.
|
||||
* The final summarized decision for specific `(vulnId, bomRef)` pairs matches expectations.
|
||||
|
||||
Include negative tests:
|
||||
|
||||
* VEX referencing unknown `bom-ref` → verification error.
|
||||
* Mismatching `evidence.sbomDigest` → verification error.
|
||||
* Tampered SBOM or VEX → signature/log verification failure.
|
||||
|
||||
---
|
||||
|
||||
## 11. Operational Practices and Guardrails
|
||||
|
||||
Developer-facing rules of thumb:
|
||||
|
||||
1. **Never** generate `bom-ref` from mutable fields (paths, timestamps).
|
||||
2. Treat tool versions and feed snapshots as part of the “scan config”:
|
||||
|
||||
* Include hashes/versions in SBOM/VEX properties.
|
||||
3. Enforce **strict types** in code (e.g., enums for VEX states/justifications).
|
||||
4. Keep keys and signing policies separate per role:
|
||||
|
||||
* Build pipeline SBOM signer.
|
||||
* Security team VEX signer.
|
||||
5. Offer a single, stable API:
|
||||
|
||||
* `POST /verify`:
|
||||
|
||||
* Inputs: artifact digest (or image reference), SBOM+VEX or references.
|
||||
* Outputs: structured verification report.
|
||||
|
||||
---
|
||||
|
||||
If you want, next step I can do is sketch a small reference implementation outline (e.g., .NET 10 service with DTOs and verification pipeline) that you can drop directly into your codebase.
|
||||
Reference in New Issue
Block a user