15 KiB
Here’s a compact, practical design you can drop into Stella Ops to make findings provable and gating trustable—no mystery meat.
Proof‑linked findings (reachability + receipts)
Why: “Reachable” ≠ just a label; it should ship with cryptographic receipts. Snyk popularized reachability (call‑graph evidence to show a vuln is actually invoked), so let’s mirror the affordance—but back it with proofs. (docs.snyk.io)
UI: for every finding, show a right‑rail “Evidence” drawer with four artifacts:
-
SBOM snippet (signed)
- Minimal CycloneDX/ SPDX slice (component + version + file refs) wrapped as an in‑toto DSSE attestation (
application/vnd.in‑toto+json). Verify with cosign. (in-toto)
- Minimal CycloneDX/ SPDX slice (component + version + file refs) wrapped as an in‑toto DSSE attestation (
-
Call‑stack slice (reachability)
- Small, human‑readable excerpt: entrypoint → vulnerable symbol, with file:line and hash of the static call graph node set. Status pill:
Reachable,Potentially reachable,Unreachable (suppressed). (Snyk’s “reachability” term and behavior reference.) (docs.snyk.io)
- Small, human‑readable excerpt: entrypoint → vulnerable symbol, with file:line and hash of the static call graph node set. Status pill:
-
Attestation chain
- Show the DSSE envelope summary (subject digest, predicate type) and verification status. Link: “Verify locally” ->
cosign verify-attestation …. (Sigstore)
- Show the DSSE envelope summary (subject digest, predicate type) and verification status. Link: “Verify locally” ->
-
Transparency receipt
- Rekor inclusion proof (log index, UUID, checkpoint). Button: “Verify inclusion” ->
rekor-cli verify …. (Sigstore)
- Rekor inclusion proof (log index, UUID, checkpoint). Button: “Verify inclusion” ->
One‑click export:
- “Export Evidence (.tar.gz)” bundling: SBOM slice, call‑stack JSON, DSSE attestation, Rekor proof JSON. (Helps audits and vendor hand‑offs.)
Dev notes:
- Attestation predicates: start with SLSA provenance + custom
stellaops.reachability/v1(symbol list + call‑edges + source hashes). Use DSSE envelopes and publish to Rekor (or your mirror). (in-toto)
VEX‑gated policy UX (clear decisions, quick drill‑downs)
Why: VEX exists to state why a product is or isn’t affected—use it to drive gates, not just annotate. Support CSAF/OpenVEX now. (OASIS Open Documentation)
Gate banner (top of finding list / CI run):
- Status chip: Block | Allow | Needs‑VEX
- Decision hash: SHA‑256 over (policy version + inputs’ digests) → deterministic, auditable runs.
- Links to inputs: Scans, SBOM, Attestations, VEX.
- “Why blocked?” expands to the exact lattice rule hit + referenced VEX statement (
status: not_affected/affectedwith justification). (first.org)
Diff‑aware override (with justification):
- “Request override” opens a panel pre‑filled with the delta (changed components/paths). Require a signed justification (DSSE‑wrapped note + optional time‑boxed TTL). Record to the transparency log (org‑local Rekor mirror is fine). (Sigstore)
VEX ingestion:
- Accept CSAF VEX and OpenVEX; normalize into a single internal model (product tree ↔ component purls, status + rationale). Red Hat’s guidance is a good structural map. (redhatproductsecurity.github.io)
Bare‑minimum schema & API (so your team can build now)
Evidence object (per finding)
sbom_snippet_attestation(DSSE)reachability_proof{ entrypoint, frames[], file_hashes[], graph_digest }attestation_chain[](DSSE summaries)transparency_receipt{ logIndex, uuid, inclusionProof, checkpoint }
Gate decision
decisionenum,decision_hash,policy_version,inputs[](digests),rule_id,explanation,vex_refs[]
CLI hooks
stella verify-evidence <findingId>→ runscosign verify-attestation+rekor-cli verifyunder the hood. (Sigstore)
Implementation tips (quick wins)
- Start with read‑only proofs: generate DSSE attestations for today’s SBOM slices and publish to Rekor; wire the Evidence drawer before enforcing gates. (Sigstore)
- Reachability MVP: static call‑graph for .NET 10 (Roslyn analyzers) capturing symbol‑to‑sink edges; label Potentially reachable when edges cross unknown reflection/dynamic boundaries; store the call‑stack slice in the predicate. (UX mirrors Snyk’s concept so devs “get it”.) (docs.snyk.io)
- VEX first class: parse CSAF/OpenVEX, show the raw “justification” inline on hover, and let gates consume it. (OASIS Open Documentation)
- Make it verifiable offline: keep a Rekor mirror or signed append‑only log bundle for air‑gapped clients; surface inclusion proofs the same way. (Sigstore now even ships public datasets for analysis/mirroring patterns.) (openssf.org)
If you want, I can turn this into: (1) a .NET 10 DTO/record set, (2) Angular component stubs for the drawer and banner, and (3) a tiny cosign/rekor verification wrapper for your CI.
Below are developer-facing guidelines for designing traceable evidence in security UX—so every “this is vulnerable / reachable / blocked” claim can be verified, reproduced, and audited.
1) Start from a hard rule: every UI assertion must map to evidence
Define a small set of “claim types” your UI will ever display (examples):
- “Component X@version Y is present”
- “CVE-… matches component X”
- “CVE-… is reachable from entrypoint E”
- “This build was produced by pipeline P from commit C”
- “Gate blocked because policy R + VEX says affected”
For each claim type, require a minimum evidence bundle (see section 6). Don’t ship a UI label that can’t be backed by artifacts + a verifier.
2) Bind evidence to immutable subjects (digests first, names second)
Traceability collapses if identifiers drift.
Do:
- Identify the “subject” using content digests (e.g.,
sha256) and stable package identifiers (purl, coordinates). - Keep component names/versions as metadata, not the primary key.
- Track “subject sets” (multi-arch images, multi-file builds) explicitly.
This matches the supply-chain attestation model where a statement binds evidence to a particular subject. (GitHub)
3) Use a standard evidence envelope (in‑toto Statement + DSSE)
Don’t invent your own signing format. Use:
- in‑toto Statement v1 as the inner statement (subject + predicateType + predicate). (GitHub)
- DSSE as the signing envelope. (GitHub)
Minimal shape:
// DSSE envelope (outer)
{
"payloadType": "application/vnd.in-toto+json",
"payload": "<base64(in-toto Statement)>",
"signatures": [{ "sig": "<base64(signature)>" }]
}
Sigstore bundles expect this payload type and an in‑toto statement in the payload. (Sigstore)
For build provenance, prefer the SLSA provenance predicate (predicateType: https://slsa.dev/provenance/v1). (SLSA)
4) Make verification a first-class UX action (not a hidden “trust me”)
In the UI, every claim should have:
- Verification status:
Verified,Unverified,Failed verification,Expired/Outdated - Verifier details: who signed, what policy verified, what log entry proves transparency
- A “Verify locally” copy button with exact commands (developers love this)
Example direction (image attestations):
cosign verify-attestation …is explicitly designed to verify attestations and check transparency log claims. (GitHub)- Use transparency inclusion verification as part of the “Verified” state. (Sigstore)
UX tip: Default to a friendly summary (“Signed by CI key from Org X; logged to transparency; subject sha256:…”) and progressively disclose raw JSON.
5) Prefer “receipts” over screenshots: transparency logs + bundles
Traceable evidence is strongest when it’s:
- Signed (authenticity + integrity)
- Publicly/append-only recorded (non-repudiation)
- Exportable (audits, incident response, vendor escalation)
If you use Sigstore:
- Publish to Rekor and store/ship a Sigstore bundle that includes the DSSE envelope and log proofs. (Sigstore)
- In UX, show: log index/UUID + “inclusion proof present”.
6) Define minimum evidence bundles per feature (practical templates)
A) “Component is present”
Minimum evidence:
-
SBOM fragment (SPDX/CycloneDX) that includes the component identity and where it came from.
- SPDX 3.x explicitly models SBOM as a collection describing a package/system. (SPDX)
-
Signed attestation for the SBOM artifact.
B) “Vulnerability match”
Minimum evidence:
- The matching rule details (CPE/purl/range) + scanner identity/version
- Signed vulnerability report attestation (or signed scan output)
C) “Reachable vulnerability”
Minimum evidence:
- A call path: entrypoint → frames → vulnerable symbol
- A hash/digest of the call graph slice (so the path is tamper-evident)
- Tool info + limitations (reflection/dynamic dispatch uncertainty)
This mirrors how reachability is typically explained: determine whether vulnerable functions are used by building a call graph and reasoning about reachability. (Snyk User Docs)
D) “Not affected” via VEX
Minimum evidence:
- The VEX statement (OpenVEX/CSAF) + signer
- Justification for
not_affected(OpenVEX requires justification or an impact statement for not_affected). (GitHub) - If using CSAF VEX, include
product_statusand related required fields. (docs.oasis-open.org) - Align to minimum requirements guidance (CISA). (CISA)
E) “Gate decision: blocked/allowed”
Minimum evidence:
- Inputs digests (SBOM digest, scan attestation digests, VEX doc digests)
- Policy version + rule id
- A deterministic decision hash over (policy + input digests)
UX: Let users open a “Decision details” panel that shows exactly which VEX statement and which rule caused the block.
7) Build evidence UX around progressive disclosure + copyability
Recommended layout patterns:
-
Finding header: severity + status + “Verified” badge
-
Evidence drawer (right panel):
- Human summary (“why you should care”)
- Evidence list (SBOM snippet, reachability path, VEX statement)
- Verification section (who signed, transparency receipt)
- Raw artifacts (download / copy JSON)
Avoid: forcing users to leave the app to “trust” you. Provide the artifacts and verification steps inline.
8) Handle uncertainty explicitly (don’t overclaim)
Reachability and exploitability often have gray areas.
- Use a three-state model:
Reachable,Potentially reachable,Not reachable (with reason). - Make the reason machine-readable (so policies can use it) and human-readable (so devs accept it).
- If the analysis is approximate (reflection, native calls), show “Why uncertain” and what would tighten it (e.g., runtime trace, config constraints).
9) Security & privacy: evidence is sensitive
Evidence can leak:
- internal source paths
- dependency structure
- environment details
- user data
Guidelines:
- Minimize: store only the smallest slice needed (e.g., call-stack slice, not whole graph).
- Redact: secrets, usernames, absolute paths; replace with stable file hashes.
- Access-control: evidence visibility should follow least privilege; treat it like production logs.
- Retention: use TTL for volatile evidence; keep signed receipts longer.
10) Developer checklist (ship-ready)
Before you ship a “traceable evidence” feature, verify you have:
- Stable subject identifiers (digests + purls)
- Standard envelope (in‑toto Statement + DSSE) (GitHub)
- Provenance attestation (SLSA provenance where applicable) (SLSA)
- Transparency receipt (Rekor/bundle) and UX that surfaces it (Sigstore)
- “Verify locally” commands in UI (cosign verify-attestation, etc.) (GitHub)
- VEX ingestion + justification handling for
not_affected(GitHub) - Clear uncertainty states (esp. reachability) (Snyk User Docs)
- Exportable evidence bundle (for audits/incidents)
If you tell me your stack (e.g., .NET 10 + Angular, or Go + React) and where you store artifacts (OCI registry, S3, etc.), I can propose a concrete evidence object schema + UI component contract that fits your architecture.