Files
git.stella-ops.org/docs/product-advisories/04-Dec-2025 - Designing Traceable Evidence in Security UX.md
2025-12-09 20:23:50 +02:00

15 KiB
Raw Blame History

Heres a compact, practical design you can drop into StellaOps to make findings provable and gating trustable—no mystery meat.


Prooflinked findings (reachability + receipts)

Why: “Reachable” ≠ just a label; it should ship with cryptographic receipts. Snyk popularized reachability (callgraph evidence to show a vuln is actually invoked), so lets mirror the affordance—but back it with proofs. (docs.snyk.io)

UI: for every finding, show a rightrail “Evidence” drawer with four artifacts:

  1. SBOM snippet (signed)

    • Minimal CycloneDX/ SPDX slice (component + version + file refs) wrapped as an intoto DSSE attestation (application/vnd.intoto+json). Verify with cosign. (in-toto)
  2. Callstack slice (reachability)

    • Small, humanreadable excerpt: entrypoint → vulnerable symbol, with file:line and hash of the static call graph node set. Status pill: Reachable, Potentially reachable, Unreachable (suppressed). (Snyks “reachability” term and behavior reference.) (docs.snyk.io)
  3. Attestation chain

    • Show the DSSE envelope summary (subject digest, predicate type) and verification status. Link: “Verify locally” -> cosign verify-attestation …. (Sigstore)
  4. Transparency receipt

    • Rekor inclusion proof (log index, UUID, checkpoint). Button: “Verify inclusion” -> rekor-cli verify …. (Sigstore)

Oneclick export:

  • “Export Evidence (.tar.gz)” bundling: SBOM slice, callstack JSON, DSSE attestation, Rekor proof JSON. (Helps audits and vendor handoffs.)

Dev notes:

  • Attestation predicates: start with SLSA provenance + custom stellaops.reachability/v1 (symbol list + calledges + source hashes). Use DSSE envelopes and publish to Rekor (or your mirror). (in-toto)

VEXgated policy UX (clear decisions, quick drilldowns)

Why: VEX exists to state why a product is or isnt 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 | NeedsVEX
  • Decision hash: SHA256 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/affected with justification). (first.org)

Diffaware override (with justification):

  • “Request override” opens a panel prefilled with the delta (changed components/paths). Require a signed justification (DSSEwrapped note + optional timeboxed TTL). Record to the transparency log (orglocal 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 Hats guidance is a good structural map. (redhatproductsecurity.github.io)

Bareminimum 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

  • decision enum, decision_hash, policy_version, inputs[] (digests), rule_id, explanation, vex_refs[]

CLI hooks

  • stella verify-evidence <findingId> → runs cosign verify-attestation + rekor-cli verify under the hood. (Sigstore)

Implementation tips (quick wins)

  • Start with readonly proofs: generate DSSE attestations for todays SBOM slices and publish to Rekor; wire the Evidence drawer before enforcing gates. (Sigstore)
  • Reachability MVP: static callgraph for .NET 10 (Roslyn analyzers) capturing symboltosink edges; label Potentially reachable when edges cross unknown reflection/dynamic boundaries; store the callstack slice in the predicate. (UX mirrors Snyks 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 appendonly log bundle for airgapped 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). Dont ship a UI label that cant 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 (intoto Statement + DSSE)

Dont invent your own signing format. Use:

  • intoto 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 intoto 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 its:

  • 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_status and 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):

    1. Human summary (“why you should care”)
    2. Evidence list (SBOM snippet, reachability path, VEX statement)
    3. Verification section (who signed, transparency receipt)
    4. 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 (dont 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 (intoto 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.