feat: Implement distro-native version comparison for RPM, Debian, and Alpine packages
- Add RpmVersionComparer for RPM version comparison with epoch, version, and release handling. - Introduce DebianVersion for parsing Debian EVR (Epoch:Version-Release) strings. - Create ApkVersion for parsing Alpine APK version strings with suffix support. - Define IVersionComparator interface for version comparison with proof-line generation. - Implement VersionComparisonResult struct to encapsulate comparison results and proof lines. - Add tests for Debian and RPM version comparers to ensure correct functionality and edge case handling. - Create project files for the version comparison library and its tests.
This commit is contained in:
@@ -1,751 +0,0 @@
|
||||
Here’s a practical, first‑time‑friendly blueprint for making your security workflow both **explainable** and **provable**—from triage to approval.
|
||||
|
||||
# Explainable triage UX (what & why)
|
||||
|
||||
Show every risk score with the minimum evidence a responder needs to trust it:
|
||||
|
||||
* **Reachable path:** the concrete call‑chain (or network path) proving the vuln is actually hit.
|
||||
* **Entrypoint boundary:** the external surface (HTTP route, CLI verb, cron, message topic) that leads to that path.
|
||||
* **VEX status:** the exploitability decision (Affected/Not Affected/Under Investigation/Fixed) with rationale.
|
||||
* **Last‑seen timestamp:** when this evidence was last observed/generated.
|
||||
|
||||
## UI pattern (compact, 1‑click expand)
|
||||
|
||||
* **Row (collapsed):** `Score 72 • CVE‑2024‑12345 • service: api-gateway • package: x.y.z`
|
||||
* **Expand panel (evidence):**
|
||||
|
||||
* **Path:** `POST /billing/charge → BillingController.Pay() → StripeClient.Create()`
|
||||
* **Boundary:** `Ingress: /billing/charge (JWT: required, scope: payments:write)`
|
||||
* **VEX:** `Not Affected (runtime guard strips untrusted input before sink)`
|
||||
* **Last seen:** `2025‑12‑18T09:22Z` (scan: sbomer#c1a2, policy run: lattice#9f0d)
|
||||
* **Actions:** “Open proof bundle”, “Re-run check”, “Create exception (time‑boxed)”
|
||||
|
||||
## Data contract (what the panel needs)
|
||||
|
||||
```json
|
||||
{
|
||||
"finding_id": "f-7b3c",
|
||||
"cve": "CVE-2024-12345",
|
||||
"component": {"name": "stripe-sdk", "version": "6.1.2"},
|
||||
"reachable_path": [
|
||||
"HTTP POST /billing/charge",
|
||||
"BillingController.Pay",
|
||||
"StripeClient.Create"
|
||||
],
|
||||
"entrypoint": {"type":"http","route":"/billing/charge","auth":"jwt:payments:write"},
|
||||
"vex": {"status":"not_affected","justification":"runtime_sanitizer_blocks_sink","timestamp":"2025-12-18T09:22:00Z"},
|
||||
"last_seen":"2025-12-18T09:22:00Z",
|
||||
"attestation_refs": ["sha256:…sbom", "sha256:…vex", "sha256:…policy"]
|
||||
}
|
||||
```
|
||||
|
||||
# Evidence‑linked approvals (what & why)
|
||||
|
||||
Make “Approve to ship” contingent on **verifiable proof**, not screenshots:
|
||||
|
||||
* **Chain** must exist and be machine‑verifiable: **SBOM → VEX → policy decision**.
|
||||
* Use **in‑toto/DSSE** attestations or **SLSA provenance** so each link has a signature, subject digest, and predicate.
|
||||
* **Gate** merges/deploys only when the chain validates.
|
||||
|
||||
## Pipeline gate (simple policy)
|
||||
|
||||
* Require:
|
||||
|
||||
1. **SBOM attestation** referencing the exact image digest
|
||||
2. **VEX attestation** covering all listed components (or explicit allow‑gaps)
|
||||
3. **Policy decision attestation** (e.g., “risk ≤ threshold AND all reachable vulns = Not Affected/Fixed”)
|
||||
|
||||
### Minimal decision attestation (DSSE envelope → JSON payload)
|
||||
|
||||
```json
|
||||
{
|
||||
"predicateType": "stella/policy-decision@v1",
|
||||
"subject": [{"name":"registry/org/app","digest":{"sha256":"<image-digest>"}}],
|
||||
"predicate": {
|
||||
"policy": "risk_threshold<=75 && reachable_vulns.all(v => v.vex in ['not_affected','fixed'])",
|
||||
"inputs": {
|
||||
"sbom_ref": "sha256:<sbom>",
|
||||
"vex_ref": "sha256:<vex>"
|
||||
},
|
||||
"result": {"allowed": true, "score": 61, "exemptions":[]},
|
||||
"evidence_refs": ["sha256:<reachability-proof-bundle>"],
|
||||
"run_at": "2025-12-18T09:23:11Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# How this lands in your product (concrete moves)
|
||||
|
||||
* **Backend:** add `/findings/:id/evidence` (returns the contract above) + `/approvals/:artifact/attestations`.
|
||||
* **Storage:** keep **proof bundles** (graphs, call stacks, logs) as content‑addressed blobs; store DSSE envelopes alongside.
|
||||
* **UI:** one list → expandable rows; chips for VEX status; “Open proof” shows the call graph and boundary in 1 view.
|
||||
* **CLI/API:** `stella verify image:<digest> --require sbom,vex,decision` returns a signed summary; pipelines fail on non‑zero.
|
||||
* **Metrics:**
|
||||
|
||||
* **% changes with complete attestations** (target ≥95%)
|
||||
* **TTFE (time‑to‑first‑evidence)** from alert → panel open (target ≤30s)
|
||||
* **Post‑deploy reversions** due to missing proof (trend to zero)
|
||||
|
||||
# Starter acceptance checklist
|
||||
|
||||
* [ ] Every risk row expands to path, boundary, VEX, last‑seen in <300 ms.
|
||||
* [ ] “Approve” button disabled until SBOM+VEX+Decision attestations validate for the **exact artifact digest**.
|
||||
* [ ] One‑click “Show DSSE chain” renders the three envelopes with subject digests and signers.
|
||||
* [ ] Audit log captures who approved, which digests, and which evidence hashes.
|
||||
|
||||
If you want, I can turn this into ready‑to‑drop **.NET 10** endpoints + a small React panel with mocked data so your team can wire it up fast.
|
||||
Below is a “build‑it” guide for Stella Ops that goes past the concept level: concrete services, schemas, pipelines, signing/storage choices, UI components, and the exact invariants you should enforce so triage is **explainable** and approvals are **provably evidence‑linked**.
|
||||
|
||||
---
|
||||
|
||||
## 1) Start with the invariants (the rules your system must never violate)
|
||||
|
||||
If you implement nothing else, implement these invariants—they’re what make the UX trustworthy and the approvals auditable.
|
||||
|
||||
### Artifact anchoring invariant
|
||||
|
||||
Every finding, every piece of evidence, and every approval must be anchored to an immutable **subject digest** (e.g., container image digest `sha256:…`, binary SHA, or SBOM digest).
|
||||
|
||||
* No “latest tag” approvals.
|
||||
* No “approve commit” without mapping to the built artifact digest.
|
||||
|
||||
### Evidence closure invariant
|
||||
|
||||
A policy decision is only valid if it references **exactly** the evidence it used:
|
||||
|
||||
* `inputs.sbom_ref`
|
||||
* `inputs.vex_ref`
|
||||
* `inputs.reachability_ref` (optional but recommended)
|
||||
* `inputs.scan_ref` (optional)
|
||||
* and any config/IaC refs used for boundary/exposure.
|
||||
|
||||
### Signature chain invariant
|
||||
|
||||
Evidence is only admissible if it is:
|
||||
|
||||
1. structured (machine readable),
|
||||
2. signed (DSSE/in‑toto),
|
||||
3. verifiable (trusted identity/keys),
|
||||
4. retrievable by digest.
|
||||
|
||||
DSSE is specifically designed to authenticate both the message and its type (payload type) and avoid canonicalization pitfalls. ([GitHub][1])
|
||||
|
||||
### Staleness invariant
|
||||
|
||||
Evidence must have:
|
||||
|
||||
* `last_seen` and `expires_at` (or TTL),
|
||||
* a “stale evidence” behavior in policy (deny or degrade score).
|
||||
|
||||
---
|
||||
|
||||
## 2) Choose the canonical formats and where you’ll store “proof”
|
||||
|
||||
### Attestation envelope: DSSE + in‑toto Statement
|
||||
|
||||
Use:
|
||||
|
||||
* **in‑toto Attestation Framework** “Statement” as the payload model (“subject + predicateType + predicate”). ([GitHub][2])
|
||||
* Wrap it in **DSSE** for signing. ([GitHub][1])
|
||||
* If you use Sigstore bundles, the DSSE envelope is expected to carry an in‑toto statement and uses `payloadType` like `application/vnd.in-toto+json`. ([Sigstore][3])
|
||||
|
||||
### SBOM format: CycloneDX or SPDX
|
||||
|
||||
* SPDX is an ISO/IEC standard and has v3.0 and v2.3 lines in the ecosystem. ([spdx.dev][4])
|
||||
* CycloneDX is an ECMA standard (ECMA‑424) and widely used for application security contexts. ([GitHub][5])
|
||||
|
||||
Pick one as **your canonical** (internally), but ingest both.
|
||||
|
||||
### VEX format: OpenVEX (practical) + map to “classic” VEX statuses
|
||||
|
||||
VEX’s value is triage noise reduction: vendors can assert whether a product is affected, fixed, under investigation, or not affected. ([NTIA][6])
|
||||
OpenVEX is a minimal, embeddable implementation of VEX intended for interoperability. ([GitHub][7])
|
||||
|
||||
### Where to store proof: OCI registry referrers
|
||||
|
||||
Use OCI “subject/referrers” so proofs travel with the artifact:
|
||||
|
||||
* OCI 1.1 introduces an explicit `subject` field and referrers graph for signatures/attestations/SBOMs. ([opencontainers.org][8])
|
||||
* ORAS documentation explains linking artifacts via `subject`. ([Oras][9])
|
||||
* Microsoft docs show `oras attach … --artifact-type …` patterns (works across registries that support referrers). ([Microsoft Learn][10])
|
||||
|
||||
---
|
||||
|
||||
## 3) System architecture (services + data flow)
|
||||
|
||||
### Services (minimum set)
|
||||
|
||||
1. **Ingestor**
|
||||
|
||||
* Pulls scanner outputs (SCA/SAST/IaC), SBOM, runtime signals.
|
||||
2. **Evidence Builder**
|
||||
|
||||
* Computes reachability, entrypoints, boundary/auth context, score explanation.
|
||||
3. **Attestation Service**
|
||||
|
||||
* Creates in‑toto statements, wraps DSSE, signs (cosign/KMS), stores to registry.
|
||||
4. **Policy Engine**
|
||||
|
||||
* Evaluates allow/deny + reason codes, emits signed decision attestation.
|
||||
* Use OPA/Rego for maintainable declarative policies. ([openpolicyagent.org][11])
|
||||
5. **Stella Ops API**
|
||||
|
||||
* Serves findings + evidence panels to the UI (fast, cached).
|
||||
6. **UI**
|
||||
|
||||
* Explainable triage panel + chain viewer + approve button.
|
||||
|
||||
### Event flow (artifact‑centric)
|
||||
|
||||
1. Build produces `image@sha256:X`
|
||||
2. Generate SBOM → sign + attach
|
||||
3. Run vuln scan → sign + attach (optional but useful)
|
||||
4. Evidence Builder creates:
|
||||
|
||||
* reachability proof
|
||||
* boundary proof
|
||||
* vex doc (or imports vendor VEX + adds your context)
|
||||
5. Policy engine evaluates → emits “decision attestation”
|
||||
6. UI shows explainable triage + “approve” gating
|
||||
|
||||
---
|
||||
|
||||
## 4) Data model (the exact objects you need)
|
||||
|
||||
### Core IDs you should standardize
|
||||
|
||||
* `subject_digest`: `sha256:<image digest>`
|
||||
* `subject_name`: `registry/org/app`
|
||||
* `finding_key`: `(subject_digest, detector, cve, component_purl, location)` stable hash
|
||||
* `component_purl`: package URL (PURL) canonical component identifier
|
||||
|
||||
### Tables (Postgres suggested)
|
||||
|
||||
**artifacts**
|
||||
|
||||
* `id (uuid)`
|
||||
* `name`
|
||||
* `digest` (unique)
|
||||
* `created_at`
|
||||
|
||||
**findings**
|
||||
|
||||
* `id (uuid)`
|
||||
* `artifact_digest`
|
||||
* `cve`
|
||||
* `component_purl`
|
||||
* `severity`
|
||||
* `raw_score`
|
||||
* `risk_score`
|
||||
* `status` (open/triaged/accepted/fixed)
|
||||
* `first_seen`, `last_seen`
|
||||
|
||||
**evidence**
|
||||
|
||||
* `id (uuid)`
|
||||
* `finding_id`
|
||||
* `kind` (reachable_path | boundary | score_explain | vex | ...)
|
||||
* `payload_json` (jsonb, small)
|
||||
* `blob_ref` (content-addressed URI for big payloads)
|
||||
* `last_seen`
|
||||
* `expires_at`
|
||||
* `confidence` (0–1)
|
||||
* `source_attestation_digest` (nullable)
|
||||
|
||||
**attestations**
|
||||
|
||||
* `id (uuid)`
|
||||
* `artifact_digest`
|
||||
* `predicate_type`
|
||||
* `attestation_digest` (sha256 of DSSE envelope)
|
||||
* `signer_identity` (OIDC subject / cert identity)
|
||||
* `issued_at`
|
||||
* `registry_ref` (where attached)
|
||||
|
||||
**approvals**
|
||||
|
||||
* `id (uuid)`
|
||||
* `artifact_digest`
|
||||
* `decision_attestation_digest`
|
||||
* `approver`
|
||||
* `approved_at`
|
||||
* `expires_at`
|
||||
* `reason`
|
||||
|
||||
---
|
||||
|
||||
## 5) Explainable triage: how to compute the “Path + Boundary + VEX + Last‑seen”
|
||||
|
||||
### 5.1 Reachable path proof (call chain / flow)
|
||||
|
||||
You need a uniform reachability result type:
|
||||
|
||||
* `reachable = true` with an explicit path
|
||||
* `reachable = false` with justification (e.g., symbol absent, dead code)
|
||||
* `reachable = unknown` with reason (insufficient symbols, dynamic dispatch)
|
||||
|
||||
**Implementation strategy**
|
||||
|
||||
1. **Symbol mapping**: map CVE → vulnerable symbols/functions/classes
|
||||
|
||||
* Use one or more:
|
||||
|
||||
* vendor advisory → patched functions
|
||||
* diff mining (commit that fixes CVE) to extract changed symbols
|
||||
* curated mapping in your DB for high volume CVEs
|
||||
2. **Program graph extraction** at build time:
|
||||
|
||||
* Produce a call graph or dependency graph per language.
|
||||
* Store as compact adjacency list (or protobuf) keyed by `subject_digest`.
|
||||
3. **Entrypoint discovery**:
|
||||
|
||||
* HTTP routes (framework metadata)
|
||||
* gRPC service methods
|
||||
* queue/stream consumers
|
||||
* cron/CLI handlers
|
||||
4. **Path search**:
|
||||
|
||||
* BFS/DFS from entrypoints to vulnerable symbols.
|
||||
* Record the shortest path + top‑K alternatives.
|
||||
5. **Proof bundle**:
|
||||
|
||||
* path nodes with stable IDs
|
||||
* file hashes + line ranges (no raw source required)
|
||||
* tool version + config hash
|
||||
* graph digest
|
||||
|
||||
**Reachability evidence JSON (UI‑friendly)**
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "reachable_path",
|
||||
"result": "reachable",
|
||||
"confidence": 0.86,
|
||||
"entrypoints": [
|
||||
{"type":"http","route":"POST /billing/charge","auth":"jwt:payments:write"}
|
||||
],
|
||||
"paths": [{
|
||||
"path_id": "p-1",
|
||||
"steps": [
|
||||
{"node":"BillingController.Pay","file_hash":"sha256:aaa","lines":[41,88]},
|
||||
{"node":"StripeClient.Create","file_hash":"sha256:bbb","lines":[10,52]},
|
||||
{"node":"stripe-sdk.vulnFn","symbol":"stripe-sdk::parseWebhook","evidence":"symbol-match"}
|
||||
]
|
||||
}],
|
||||
"graph": {"digest":"sha256:callgraph...", "format":"stella-callgraph-v1"},
|
||||
"last_seen": "2025-12-18T09:22:00Z",
|
||||
"expires_at": "2025-12-25T09:22:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**UI rule:** never show “reachable” without a concrete, replayable path ID.
|
||||
|
||||
---
|
||||
|
||||
### 5.2 Boundary proof (the “why this is exposed” part)
|
||||
|
||||
Boundary proof answers: “Even if reachable, who can trigger it?”
|
||||
|
||||
**Data sources**
|
||||
|
||||
* Kubernetes ingress/service (exposure)
|
||||
* API gateway routes and auth policies
|
||||
* service mesh auth (mTLS, JWT)
|
||||
* IAM policies (for cloud events)
|
||||
* network policies (deny/allow)
|
||||
|
||||
**Boundary evidence schema**
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "boundary",
|
||||
"surface": {"type":"http","route":"POST /billing/charge"},
|
||||
"exposure": {"internet": true, "ports":[443]},
|
||||
"auth": {
|
||||
"mechanism":"jwt",
|
||||
"required_scopes":["payments:write"],
|
||||
"audience":"billing-api"
|
||||
},
|
||||
"rate_limits": {"enabled": true, "rps": 20},
|
||||
"controls": [
|
||||
{"type":"waf","status":"enabled"},
|
||||
{"type":"input_validation","status":"enabled","location":"BillingController.Pay"}
|
||||
],
|
||||
"last_seen": "2025-12-18T09:22:00Z",
|
||||
"confidence": 0.74
|
||||
}
|
||||
```
|
||||
|
||||
**How to build it**
|
||||
|
||||
* Create a “Surface Extractor” plugin per environment:
|
||||
|
||||
* `k8s-extractor`: reads ingress + service + annotations
|
||||
* `gateway-extractor`: reads API gateway config
|
||||
* `iac-extractor`: parses Terraform/CloudFormation
|
||||
* Normalize into the schema above.
|
||||
|
||||
---
|
||||
|
||||
### 5.3 VEX in Stella: statuses + justifications
|
||||
|
||||
VEX statuses you should support in UI:
|
||||
|
||||
* Not affected
|
||||
* Affected
|
||||
* Fixed
|
||||
* Under investigation ([NTIA][6])
|
||||
|
||||
OpenVEX will carry the machine readable structure. ([GitHub][7])
|
||||
|
||||
**Practical approach**
|
||||
|
||||
* Treat VEX as **the decision record** for exploitability.
|
||||
* Your policy can require VEX coverage for all “reachable” high severity vulns.
|
||||
|
||||
**Rule of thumb**
|
||||
|
||||
* If `reachable=true` AND boundary shows reachable surface + auth weak → VEX defaults to `affected` until mitigations proven.
|
||||
* If `reachable=false` with high confidence and stable proof → VEX may be `not_affected`.
|
||||
|
||||
---
|
||||
|
||||
### 5.4 Explainable risk score (don’t hide the formula)
|
||||
|
||||
Make score explainability first‑class.
|
||||
|
||||
**Recommended implementation**
|
||||
|
||||
* Store risk score as an additive model:
|
||||
|
||||
* `base = CVSS normalized`
|
||||
* `+ reachability_bonus`
|
||||
* `+ exposure_bonus`
|
||||
* `+ privilege_bonus`
|
||||
* `- mitigation_discount`
|
||||
* Emit a `score_explain` evidence object:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "score_explain",
|
||||
"risk_score": 72,
|
||||
"contributions": [
|
||||
{"factor":"cvss","value":41,"reason":"CVSS 9.8"},
|
||||
{"factor":"reachability","value":18,"reason":"reachable path p-1"},
|
||||
{"factor":"exposure","value":10,"reason":"internet-facing route"},
|
||||
{"factor":"auth","value":3,"reason":"scope required lowers impact"}
|
||||
],
|
||||
"last_seen":"2025-12-18T09:22:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**UI rule:** “Score 72” must always be clickable to a stable breakdown.
|
||||
|
||||
---
|
||||
|
||||
## 6) The UI you should build (components + interaction rules)
|
||||
|
||||
### 6.1 Findings list row (collapsed)
|
||||
|
||||
Show only what helps scanning:
|
||||
|
||||
* Score badge
|
||||
* CVE + component
|
||||
* service
|
||||
* reachability chip: Reachable / Not reachable / Unknown
|
||||
* VEX chip
|
||||
* last_seen indicator (green/yellow/red)
|
||||
|
||||
### 6.2 Evidence drawer (expanded)
|
||||
|
||||
Tabs:
|
||||
|
||||
1. **Path**
|
||||
|
||||
* show entrypoint(s)
|
||||
* render call chain (simple list first; graph view optional)
|
||||
2. **Boundary**
|
||||
|
||||
* exposure, auth, controls
|
||||
3. **VEX**
|
||||
|
||||
* status + justification + issuer identity
|
||||
4. **Score**
|
||||
|
||||
* breakdown bar/list
|
||||
5. **Proof**
|
||||
|
||||
* attestation chain viewer (SBOM → VEX → Decision)
|
||||
* “Verify locally” action
|
||||
|
||||
### 6.3 “Open proof bundle” viewer
|
||||
|
||||
Must display:
|
||||
|
||||
* subject digest
|
||||
* signer identity
|
||||
* predicate type
|
||||
* digest of proof bundle
|
||||
* last_seen + tool versions
|
||||
|
||||
**This is where trust is built:** responders can see that the evidence is signed, tied to the artifact, and recent.
|
||||
|
||||
---
|
||||
|
||||
## 7) Proof‑linked evidence: how to generate and attach attestations
|
||||
|
||||
### 7.1 Statement format: in‑toto Attestation Framework
|
||||
|
||||
in‑toto’s model is:
|
||||
|
||||
* **Subjects** (the artifact digests)
|
||||
* **Predicate type** (schema ID)
|
||||
* **Predicate** (your actual data) ([GitHub][2])
|
||||
|
||||
### 7.2 DSSE envelope
|
||||
|
||||
Wrap statements using DSSE so payload type is signed too. ([GitHub][1])
|
||||
|
||||
### 7.3 Attach to OCI image via referrers
|
||||
|
||||
OCI “subject/referrers” makes attestations discoverable from the image digest. ([opencontainers.org][8])
|
||||
ORAS provides the operational model (“attach artifacts to an image”). ([Microsoft Learn][10])
|
||||
|
||||
### 7.4 Practical signing: cosign attest + verify
|
||||
|
||||
Cosign has built‑in in‑toto attestation support and can sign custom predicates. ([Sigstore][12])
|
||||
|
||||
Typical patterns (example only; adapt to your environment):
|
||||
|
||||
```bash
|
||||
# Attach an attestation
|
||||
cosign attest --predicate reachability.json \
|
||||
--type stella/reachability/v1 \
|
||||
<image@sha256:digest>
|
||||
|
||||
# Verify attestation
|
||||
cosign verify-attestation --type stella/reachability/v1 \
|
||||
<image@sha256:digest>
|
||||
```
|
||||
|
||||
(Use keyless OIDC or KMS keys depending on your org.)
|
||||
|
||||
---
|
||||
|
||||
## 8) Define your predicate types (this is the “contract” Stella enforces)
|
||||
|
||||
You’ll want at least these predicate types:
|
||||
|
||||
1. `stella/sbom@v1`
|
||||
|
||||
* embeds CycloneDX/SPDX (or references blob digest)
|
||||
|
||||
2. `stella/vex@v1`
|
||||
|
||||
* embeds OpenVEX document or references it ([GitHub][7])
|
||||
|
||||
3. `stella/reachability@v1`
|
||||
|
||||
* the reachability evidence above
|
||||
* includes `graph.digest`, `paths`, `confidence`, `expires_at`
|
||||
|
||||
4. `stella/boundary@v1`
|
||||
|
||||
* exposure/auth proof and `last_seen`
|
||||
|
||||
5. `stella/policy-decision@v1`
|
||||
|
||||
* the gating result, references all input attestation digests
|
||||
|
||||
6. Optional: `stella/human-approval@v1`
|
||||
|
||||
* “I approve deploy of subject digest X based on decision attestation Y”
|
||||
* keep it time‑boxed
|
||||
|
||||
---
|
||||
|
||||
## 9) The policy gate (how approvals become proof‑linked)
|
||||
|
||||
### 9.1 Use OPA/Rego for the gate
|
||||
|
||||
OPA policies are written in Rego. ([openpolicyagent.org][11])
|
||||
|
||||
**Gate input** should be a single JSON document assembled from verified attestations:
|
||||
|
||||
```json
|
||||
{
|
||||
"subject": {"name":"registry/org/app","digest":"sha256:..."},
|
||||
"sbom": {...},
|
||||
"vex": {...},
|
||||
"reachability": {...},
|
||||
"boundary": {...},
|
||||
"org_policy": {"max_risk": 75, "max_age_hours": 168}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Rego (deny‑by‑default)**
|
||||
|
||||
```rego
|
||||
package stella.gate
|
||||
|
||||
default allow := false
|
||||
|
||||
# deny if evidence is stale
|
||||
stale_evidence {
|
||||
now := time.now_ns()
|
||||
exp := time.parse_rfc3339_ns(input.reachability.expires_at)
|
||||
now > exp
|
||||
}
|
||||
|
||||
# deny if any high severity reachable vuln is not resolved by VEX
|
||||
unresolved_reachable[v] {
|
||||
v := input.reachability.findings[_]
|
||||
v.severity in {"critical","high"}
|
||||
v.reachable == true
|
||||
not input.vex.resolution[v.cve] in {"not_affected","fixed"}
|
||||
}
|
||||
|
||||
allow {
|
||||
input.risk_score <= input.org_policy.max_risk
|
||||
not stale_evidence
|
||||
count(unresolved_reachable) == 0
|
||||
}
|
||||
```
|
||||
|
||||
### 9.2 Emit a signed policy decision attestation
|
||||
|
||||
When OPA returns `allow=true`, emit **another attestation**:
|
||||
|
||||
* predicate includes the policy version/hash and all input refs.
|
||||
* that’s what the UI “Approve” button targets.
|
||||
|
||||
This is the “evidence‑linked approval”: approval references the signed decision, and the decision references the signed evidence.
|
||||
|
||||
---
|
||||
|
||||
## 10) “Approve” button behavior (what Stella Ops should enforce)
|
||||
|
||||
### Disabled until…
|
||||
|
||||
* subject digest known
|
||||
* SBOM attestation found + signature verified
|
||||
* VEX attestation found + signature verified
|
||||
* Decision attestation found + signature verified
|
||||
* Decision’s `inputs` digests match the actual retrieved evidence
|
||||
|
||||
### When clicked…
|
||||
|
||||
1. Stella Ops creates a `stella/human-approval@v1` statement:
|
||||
|
||||
* `subject` = artifact digest
|
||||
* `predicate.decision_ref` = decision attestation digest
|
||||
* `predicate.expires_at` = short TTL (e.g., 7–30 days)
|
||||
2. Signs it with the approver identity
|
||||
3. Attaches it to the artifact (OCI referrer)
|
||||
|
||||
### Audit view must show
|
||||
|
||||
* approver identity
|
||||
* exact artifact digest
|
||||
* exact decision attestation digest
|
||||
* timestamp and expiry
|
||||
|
||||
---
|
||||
|
||||
## 11) Implementation details that matter in production
|
||||
|
||||
### 11.1 Verification library (shared by UI backend + CI gate)
|
||||
|
||||
Write one verifier module used everywhere:
|
||||
|
||||
**Inputs**
|
||||
|
||||
* image digest
|
||||
* expected predicate types
|
||||
* trust policy (allowed identities/issuers, keyless rules, KMS keys)
|
||||
|
||||
**Steps**
|
||||
|
||||
1. Discover referrers for `image@sha256:…`
|
||||
2. Filter by `predicateType`
|
||||
3. Verify DSSE + signature + identity
|
||||
4. Validate JSON schema for predicate
|
||||
5. Check `subject.digest` matches image digest
|
||||
6. Return “verified evidence set” + “errors”
|
||||
|
||||
### 11.2 Evidence privacy
|
||||
|
||||
Reachability proofs can leak implementation details.
|
||||
|
||||
* Store file hashes, symbol names, and line ranges
|
||||
* Gate raw source behind elevated permissions
|
||||
* Provide redacted proofs by default
|
||||
|
||||
### 11.3 Evidence TTL strategy
|
||||
|
||||
* SBOM: long TTL (weeks/months) if digest immutable
|
||||
* Boundary: short TTL (hours/days) because env changes
|
||||
* Reachability: medium TTL (days/weeks) depending on code churn
|
||||
* VEX: must be renewed if boundary/reachability changes
|
||||
|
||||
### 11.4 Handling “Unknown reachability”
|
||||
|
||||
Don’t force false certainty.
|
||||
|
||||
* Mark as `unknown` and show why (missing symbols, dynamic reflection, stripped binaries)
|
||||
* Policy can treat unknown as “reachable” for critical CVEs in internet‑facing services.
|
||||
|
||||
---
|
||||
|
||||
## 12) A concrete MVP path that still delivers value
|
||||
|
||||
If you want a minimal but real first release:
|
||||
|
||||
### MVP (2–3 deliverables)
|
||||
|
||||
1. **Evidence drawer** fed by:
|
||||
|
||||
* scanner output + SBOM + a simple “entrypoint map”
|
||||
2. **VEX workflow**
|
||||
|
||||
* allow engineers to set VEX status + justification
|
||||
3. **Signed decision gating**
|
||||
|
||||
* even if reachability is heuristic, the chain is real
|
||||
|
||||
Then iterate:
|
||||
|
||||
* add reachability graphs
|
||||
* add boundary extraction from IaC/K8s
|
||||
* tighten policy (staleness, confidence thresholds)
|
||||
|
||||
---
|
||||
|
||||
## 13) Quick checklist for “done enough to trust”
|
||||
|
||||
* [ ] Every finding expands to: Path, Boundary, VEX, Score, Proof
|
||||
* [ ] Every evidence tab shows `last_seen` + confidence
|
||||
* [ ] “Verify chain” works: SBOM → VEX → Decision all signed and bound to the artifact digest
|
||||
* [ ] Approve button signs a human approval attestation tied to the decision digest
|
||||
* [ ] CI gate verifies the same chain before deploy
|
||||
|
||||
---
|
||||
|
||||
If you want, I can also drop in:
|
||||
|
||||
* a full set of JSON Schemas for `stella/*@v1` predicates,
|
||||
* a reference verifier implementation outline in .NET 10 (Minimal API + a verifier class),
|
||||
* and a sample UI component tree (React) that renders path/boundary graphs and attestation chains.
|
||||
|
||||
[1]: https://github.com/secure-systems-lab/dsse?utm_source=chatgpt.com "DSSE: Dead Simple Signing Envelope"
|
||||
[2]: https://github.com/in-toto/attestation?utm_source=chatgpt.com "in-toto Attestation Framework"
|
||||
[3]: https://docs.sigstore.dev/about/bundle/?utm_source=chatgpt.com "Sigstore Bundle Format"
|
||||
[4]: https://spdx.dev/use/specifications/?utm_source=chatgpt.com "Specifications"
|
||||
[5]: https://github.com/CycloneDX/specification?utm_source=chatgpt.com "CycloneDX/specification"
|
||||
[6]: https://www.ntia.gov/sites/default/files/publications/vex_one-page_summary_0.pdf "VEX one-page summary"
|
||||
[7]: https://github.com/openvex/spec?utm_source=chatgpt.com "OpenVEX Specification"
|
||||
[8]: https://opencontainers.org/posts/blog/2024-03-13-image-and-distribution-1-1/?utm_source=chatgpt.com "OCI Image and Distribution Specs v1.1 Releases"
|
||||
[9]: https://oras.land/docs/concepts/reftypes/?utm_source=chatgpt.com "Attached Artifacts | OCI Registry As Storage"
|
||||
[10]: https://learn.microsoft.com/en-us/azure/container-registry/container-registry-manage-artifact?utm_source=chatgpt.com "Manage OCI Artifacts and Supply Chain Artifacts with ORAS"
|
||||
[11]: https://openpolicyagent.org/docs/policy-language?utm_source=chatgpt.com "Policy Language"
|
||||
[12]: https://docs.sigstore.dev/cosign/verifying/attestation/?utm_source=chatgpt.com "In-Toto Attestations"
|
||||
@@ -0,0 +1,140 @@
|
||||
Here’s a tight, first‑time‑friendly blueprint for two Stella Ops UX pillars—**Triage & Exceptions** and **Knowledge Snapshots & Merge Semantics**—with just enough background plus concrete specs your PMs/devs can ship.
|
||||
|
||||
---
|
||||
|
||||
# Triage & Exceptions (quiet‑by‑design)
|
||||
|
||||
**Why it matters (plain English):** Most scanners drown users in alerts. “Quiet‑by‑design” shows only *provable, reachable* risks and lets you create **auditable exceptions** (temporary waivers) that auto‑feed compliance packs.
|
||||
|
||||
**User flow**
|
||||
|
||||
1. **Inbox grouped by exploit path**
|
||||
|
||||
* Group key = `(artifact → package → vulnerable symbol → runtime path)`.
|
||||
* Each group shows: risk score, blast radius (count of dependents), EPSS/CVSS, and a “Proof” button.
|
||||
2. **Open a path → Proof bundle**
|
||||
|
||||
* **Reach subgraph** (who calls what).
|
||||
* **Symbol map** (function/offsets; source or binary map).
|
||||
* **VEX claims** (vendor/distro/internal) with trust score + signatures.
|
||||
3. **Raise Exception** (time‑boxed)
|
||||
|
||||
* **Required fields:** attested reason (dropdown + free text), expiry date, recheck policy (e.g., “fail build if new reachable path appears”, “fail if EPSS > X”).
|
||||
* **Attestation:** DSSE‑signed exception object, OCI‑attached to artifact digest.
|
||||
* Auto‑lands in **Audit Pack** (PDF/JSON bundle) and **Timeline**.
|
||||
|
||||
**Data model (C# POCO sketch)**
|
||||
|
||||
```csharp
|
||||
record ExploitPathId(string ArtifactDigest, string PackagePurl, string CveId, string Symbol, string EntryPathHash);
|
||||
|
||||
record ExceptionObj(
|
||||
string Id, ExploitPathId Path, string ReasonCode, string ReasonText,
|
||||
DateTimeOffset IssuedAt, DateTimeOffset ExpiresAt,
|
||||
RecheckPolicy Policy, EvidenceRef[] Evidence, AttestationMeta Att);
|
||||
```
|
||||
|
||||
**RecheckPolicy (examples)**
|
||||
|
||||
* `ReachGraphChange=Block`
|
||||
* `EPSSAbove=0.5`
|
||||
* `EnvScope=prod-only`
|
||||
* `UnknownsAbove=N → Block`
|
||||
|
||||
**UI essentials**
|
||||
|
||||
* **Inbox:** 3‑pane (List • Details • Proof).
|
||||
* **Proof drawer:** collapsible reach subgraph; one‑click JSON export.
|
||||
* **Exception modal:** expiry presets (7/30/90 days), reason templates (backport, feature‑flag‑off, compensating control).
|
||||
|
||||
**APIs (REST-ish)**
|
||||
|
||||
* `GET /triage/inbox?scope=env:prod&quiet=true`
|
||||
* `GET /triage/path/{id}/proof`
|
||||
* `POST /exceptions` (body = ExceptionObj sans AttestationMeta; server returns DSSE envelope)
|
||||
* `GET /audit-packs/{releaseDigest}`
|
||||
|
||||
**Guardrails**
|
||||
|
||||
* Exceptions must **never** outlive a release line by default (force renewal).
|
||||
* Creating an exception **requires evidence hooks** (see below).
|
||||
* Build gates read exceptions; if proof no longer holds (reach diff changed), gate fails.
|
||||
|
||||
---
|
||||
|
||||
# Knowledge Snapshots & Merge Semantics
|
||||
|
||||
**Plain English:** Take a sealed “photo” of everything you *know* at a point in time—SBOM, VEX, attestations, policies—so audits and incident reviews can be replayed exactly.
|
||||
|
||||
**Lifecycle: Snapshot → Seal → Export**
|
||||
|
||||
1. **Snapshot**
|
||||
|
||||
* Capture: SBOM (CycloneDX 1.6 + SPDX 3.0.1), VEX docs, reach subgraphs, exception objects, policies, trust scores, feed versions, rules hashes.
|
||||
2. **Seal**
|
||||
|
||||
* Compute **Manifest of Manifests** (Merkle root) + DSSE signature (with PQ option).
|
||||
* Store to **Authority** (Postgres) and attach to image digest (OCI ref).
|
||||
3. **Export**
|
||||
|
||||
* Produce a single **Replay Bundle** (`.stella-replay.tgz`): data + `REPLAY.yaml` (inputs, versions, lattice rules).
|
||||
* Offline‑ready.
|
||||
|
||||
**Policy pane with merge semantics**
|
||||
|
||||
* Default preview: **vendor ⊕ distro ⊕ internal** (not “vendor > distro > internal”).
|
||||
* **Lattice rules** define resolution (e.g., `NOT_AFFECTED ⊕ AFFECTED → AFFECTED unless Evidence(feature_flag_off)`).
|
||||
* **Evidence hooks (required):**
|
||||
|
||||
* “Not affected because feature X off” → must include **feature‑flag attestation** (env‑scoped, signed).
|
||||
* “Backported patch” → must include **patch‑index** mapping (`fixed‑symbols`, commit OIDs).
|
||||
* “Compensating control” → must include **control attestation** (control ID, monitoring link, SLO).
|
||||
|
||||
**UI essentials**
|
||||
|
||||
* **Snapshot panel:** shows inputs (feed versions, rules hash), diff vs last snapshot, “Seal & Export” button.
|
||||
* **Policy pane:** interactive merge preview; failed hooks highlighted with “Add evidence” CTA.
|
||||
* **Replay check:** “Verify determinism” runs local re‑eval; shows PASS/FAIL badge.
|
||||
|
||||
**APIs**
|
||||
|
||||
* `POST /snapshots` → returns `SnapshotId` + content hashes
|
||||
* `POST /snapshots/{id}/seal` → returns DSSE + Merkle root
|
||||
* `GET /snapshots/{id}/export` → stream bundle
|
||||
* `POST /policy/preview` (inputs: sources+claims) → resolved verdict + missing-evidence list
|
||||
|
||||
**Storage**
|
||||
|
||||
* **Postgres** = system of record (immutable rows for sealed snapshots).
|
||||
* **Valkey** (optional) = cache (diffs, precomputed subgraphs).
|
||||
* **OCI** = distribution of attestations & snapshots alongside images.
|
||||
|
||||
---
|
||||
|
||||
# PM & Dev acceptance checklist (short)
|
||||
|
||||
* **Triage Inbox**
|
||||
|
||||
* [ ] Groups by exploit path, not by CVE alone.
|
||||
* [ ] Proof bundle includes reach subgraph + symbol map + VEX claims.
|
||||
* [ ] Exception modal enforces reason + expiry + recheck policy + evidence.
|
||||
* [ ] Exceptions are DSSE‑signed and appear in Audit Pack.
|
||||
|
||||
* **Knowledge Snapshot**
|
||||
|
||||
* [ ] Snapshot records *all* inputs (feeds, rules, versions, hashes).
|
||||
* [ ] Seal produces a verifiable Merkle root + DSSE.
|
||||
* [ ] Export bundle replays deterministically offline.
|
||||
* [ ] Policy pane supports lattice merges + evidence hooks with blocking states.
|
||||
|
||||
---
|
||||
|
||||
# Tiny implementation notes (.NET 10)
|
||||
|
||||
* Use **record structs** for small immutable IDs; keep hash fields as `ReadOnlyMemory<byte>`.
|
||||
* Attest with **in‑toto/DSSE**; provide PQ toggle (Dilithium/Falcon) at key‑policy level.
|
||||
* Graphs: store **reach subgraph** as compressed adjacency lists; index by `(digest, symbol)`.
|
||||
|
||||
---
|
||||
|
||||
If you want, I can turn this into: (a) Swagger stubs, (b) EF Core schema + migrations, or (c) a Figma‑ready UI spec with screen flows and copy.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,22 @@
|
||||
# Reachability Drift Detection
|
||||
|
||||
**Date**: 2025-12-17
|
||||
**Status**: ANALYZED - Ready for Implementation Planning
|
||||
**Status**: ARCHIVED - Implementation Complete (Sprints 3600.2-3600.3)
|
||||
**Archived**: 2025-12-22
|
||||
**Related Advisories**:
|
||||
- 14-Dec-2025 - Smart-Diff Technical Reference
|
||||
- 14-Dec-2025 - Reachability Analysis Technical Reference
|
||||
|
||||
**Implementation Documentation**:
|
||||
- Architecture: `docs/modules/scanner/reachability-drift.md`
|
||||
- API Reference: `docs/api/scanner-drift-api.md`
|
||||
- Operations Guide: `docs/operations/reachability-drift-guide.md`
|
||||
|
||||
**Follow-up Sprints**:
|
||||
- `SPRINT_3600_0004_0001` - Node.js Babel Integration (TODO)
|
||||
- `SPRINT_3600_0005_0001` - Policy CI Gate Integration (TODO)
|
||||
- `SPRINT_3600_0006_0001` - Documentation Finalization (TODO)
|
||||
|
||||
---
|
||||
|
||||
## 1. EXECUTIVE SUMMARY
|
||||
@@ -39,17 +50,17 @@ This advisory proposes extending StellaOps' Smart-Diff capabilities to detect **
|
||||
|
||||
### 2.2 What's Missing (New Implementation Required)
|
||||
|
||||
| Component | Advisory Ref | Gap Description |
|
||||
|-----------|-------------|-----------------|
|
||||
| **Call Graph Extractor (.NET)** | §7 C# Roslyn | No MSBuildWorkspace/Roslyn analysis exists |
|
||||
| **Call Graph Extractor (Go)** | §7 Go SSA | No golang.org/x/tools/go/ssa integration |
|
||||
| **Call Graph Extractor (Java)** | §7 | No Soot/WALA integration |
|
||||
| **Call Graph Extractor (Node)** | §7 | No @babel/traverse integration |
|
||||
| **`scanner.code_changes` table** | §4 Smart-Diff | AST-level diff facts not persisted |
|
||||
| **Drift Cause Explainer** | §6 Timeline | No causal attribution on path nodes |
|
||||
| **Path Viewer UI** | §UX | No Angular component for call path visualization |
|
||||
| **Cross-scan Function-level Drift** | §6 | State drift exists, function-level doesn't |
|
||||
| **Entrypoint Discovery (per-framework)** | §3 | Limited beyond package.json/manifest parsing |
|
||||
| Component | Advisory Ref | Gap Description | **Post-Implementation Status** |
|
||||
|-----------|-------------|-----------------|-------------------------------|
|
||||
| **Call Graph Extractor (.NET)** | §7 C# Roslyn | No MSBuildWorkspace/Roslyn analysis exists | **DONE** - `DotNetCallGraphExtractor` |
|
||||
| **Call Graph Extractor (Go)** | §7 Go SSA | No golang.org/x/tools/go/ssa integration | **DONE** - `GoCallGraphExtractor` |
|
||||
| **Call Graph Extractor (Java)** | §7 | No Soot/WALA integration | **DONE** - `JavaCallGraphExtractor` (ASM) |
|
||||
| **Call Graph Extractor (Node)** | §7 | No @babel/traverse integration | **PARTIAL** - Skeleton exists |
|
||||
| **`scanner.code_changes` table** | §4 Smart-Diff | AST-level diff facts not persisted | **DONE** - Migration 010 |
|
||||
| **Drift Cause Explainer** | §6 Timeline | No causal attribution on path nodes | **DONE** - `DriftCauseExplainer` |
|
||||
| **Path Viewer UI** | §UX | No Angular component for call path visualization | **DONE** - `path-viewer.component.ts` |
|
||||
| **Cross-scan Function-level Drift** | §6 | State drift exists, function-level doesn't | **DONE** - `ReachabilityDriftDetector` |
|
||||
| **Entrypoint Discovery (per-framework)** | §3 | Limited beyond package.json/manifest parsing | **DONE** - 9 entrypoint types |
|
||||
|
||||
### 2.3 Terminology Mapping
|
||||
|
||||
@@ -378,11 +389,13 @@ smart_diff:
|
||||
|
||||
### 7.2 Sub-Sprints
|
||||
|
||||
| ID | Topic | Priority | Effort | Dependencies |
|
||||
|----|-------|----------|--------|--------------|
|
||||
| SPRINT_3600_0002_0001 | Call Graph Infrastructure | P0 | Large | Master |
|
||||
| SPRINT_3600_0003_0001 | Drift Detection Engine | P0 | Medium | 3600.2 |
|
||||
| SPRINT_3600_0004_0001 | UI and Evidence Chain | P1 | Medium | 3600.3 |
|
||||
| ID | Topic | Priority | Effort | Dependencies | **Status** |
|
||||
|----|-------|----------|--------|--------------|------------|
|
||||
| SPRINT_3600_0002_0001 | Call Graph Infrastructure | P0 | Large | Master | **DONE** |
|
||||
| SPRINT_3600_0003_0001 | Drift Detection Engine | P0 | Medium | 3600.2 | **DONE** |
|
||||
| SPRINT_3600_0004_0001 | Node.js Babel Integration | P1 | Medium | 3600.3 | TODO |
|
||||
| SPRINT_3600_0005_0001 | Policy CI Gate Integration | P1 | Small | 3600.3 | TODO |
|
||||
| SPRINT_3600_0006_0001 | Documentation Finalization | P0 | Medium | 3600.3 | TODO |
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
# ARCHIVED
|
||||
|
||||
> **Archived:** 2025-12-22
|
||||
> **Reason:** Gap analysis complete; implementation planned via SPRINT_4300 series
|
||||
> **Analysis:** `docs/implplan/analysis/4300_explainable_triage_gap_analysis.md`
|
||||
> **Sprints:** `docs/implplan/SPRINT_4300_*.md`
|
||||
> **Coverage:** ~85% already implemented via prior sprints (3800, 3801, 4100, 4200)
|
||||
> **Remaining Gaps:** 6 sprints created to close gaps (CLI verify, Evidence API, Privacy, TTL, Schemas, Metrics)
|
||||
|
||||
---
|
||||
|
||||
Here's a practical, first-time-friendly blueprint for making your security workflow both **explainable** and **provable**-from triage to approval.
|
||||
|
||||
# Explainable triage UX (what & why)
|
||||
|
||||
Show every risk score with the minimum evidence a responder needs to trust it:
|
||||
|
||||
* **Reachable path:** the concrete call-chain (or network path) proving the vuln is actually hit.
|
||||
* **Entrypoint boundary:** the external surface (HTTP route, CLI verb, cron, message topic) that leads to that path.
|
||||
* **VEX status:** the exploitability decision (Affected/Not Affected/Under Investigation/Fixed) with rationale.
|
||||
* **Last-seen timestamp:** when this evidence was last observed/generated.
|
||||
|
||||
## UI pattern (compact, 1-click expand)
|
||||
|
||||
* **Row (collapsed):** `Score 72 - CVE-2024-12345 - service: api-gateway - package: x.y.z`
|
||||
* **Expand panel (evidence):**
|
||||
|
||||
* **Path:** `POST /billing/charge -> BillingController.Pay() -> StripeClient.Create()`
|
||||
* **Boundary:** `Ingress: /billing/charge (JWT: required, scope: payments:write)`
|
||||
* **VEX:** `Not Affected (runtime guard strips untrusted input before sink)`
|
||||
* **Last seen:** `2025-12-18T09:22Z` (scan: sbomer#c1a2, policy run: lattice#9f0d)
|
||||
* **Actions:** "Open proof bundle", "Re-run check", "Create exception (time-boxed)"
|
||||
|
||||
## Data contract (what the panel needs)
|
||||
|
||||
```json
|
||||
{
|
||||
"finding_id": "f-7b3c",
|
||||
"cve": "CVE-2024-12345",
|
||||
"component": {"name": "stripe-sdk", "version": "6.1.2"},
|
||||
"reachable_path": [
|
||||
"HTTP POST /billing/charge",
|
||||
"BillingController.Pay",
|
||||
"StripeClient.Create"
|
||||
],
|
||||
"entrypoint": {"type":"http","route":"/billing/charge","auth":"jwt:payments:write"},
|
||||
"vex": {"status":"not_affected","justification":"runtime_sanitizer_blocks_sink","timestamp":"2025-12-18T09:22:00Z"},
|
||||
"last_seen":"2025-12-18T09:22:00Z",
|
||||
"attestation_refs": ["sha256:...sbom", "sha256:...vex", "sha256:...policy"]
|
||||
}
|
||||
```
|
||||
|
||||
# Evidence-linked approvals (what & why)
|
||||
|
||||
Make "Approve to ship" contingent on **verifiable proof**, not screenshots:
|
||||
|
||||
* **Chain** must exist and be machine-verifiable: **SBOM -> VEX -> policy decision**.
|
||||
* Use **in-toto/DSSE** attestations or **SLSA provenance** so each link has a signature, subject digest, and predicate.
|
||||
* **Gate** merges/deploys only when the chain validates.
|
||||
|
||||
## Pipeline gate (simple policy)
|
||||
|
||||
* Require:
|
||||
|
||||
1. **SBOM attestation** referencing the exact image digest
|
||||
2. **VEX attestation** covering all listed components (or explicit allow-gaps)
|
||||
3. **Policy decision attestation** (e.g., "risk <= threshold AND all reachable vulns = Not Affected/Fixed")
|
||||
|
||||
### Minimal decision attestation (DSSE envelope -> JSON payload)
|
||||
|
||||
```json
|
||||
{
|
||||
"predicateType": "stella/policy-decision@v1",
|
||||
"subject": [{"name":"registry/org/app","digest":{"sha256":"<image-digest>"}}],
|
||||
"predicate": {
|
||||
"policy": "risk_threshold<=75 && reachable_vulns.all(v => v.vex in ['not_affected','fixed'])",
|
||||
"inputs": {
|
||||
"sbom_ref": "sha256:<sbom>",
|
||||
"vex_ref": "sha256:<vex>"
|
||||
},
|
||||
"result": {"allowed": true, "score": 61, "exemptions":[]},
|
||||
"evidence_refs": ["sha256:<reachability-proof-bundle>"],
|
||||
"run_at": "2025-12-18T09:23:11Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# How this lands in your product (concrete moves)
|
||||
|
||||
* **Backend:** add `/findings/:id/evidence` (returns the contract above) + `/approvals/:artifact/attestations`.
|
||||
* **Storage:** keep **proof bundles** (graphs, call stacks, logs) as content-addressed blobs; store DSSE envelopes alongside.
|
||||
* **UI:** one list -> expandable rows; chips for VEX status; "Open proof" shows the call graph and boundary in 1 view.
|
||||
* **CLI/API:** `stella verify image:<digest> --require sbom,vex,decision` returns a signed summary; pipelines fail on non-zero.
|
||||
* **Metrics:**
|
||||
|
||||
* **% changes with complete attestations** (target >=95%)
|
||||
* **TTFE (time-to-first-evidence)** from alert -> panel open (target <=30s)
|
||||
* **Post-deploy reversions** due to missing proof (trend to zero)
|
||||
|
||||
# Starter acceptance checklist
|
||||
|
||||
* [x] Every risk row expands to path, boundary, VEX, last-seen in <300 ms. *(SPRINT_4200_0001_0001)*
|
||||
* [x] "Approve" button disabled until SBOM+VEX+Decision attestations validate for the **exact artifact digest**. *(SPRINT_4100_0005_0001)*
|
||||
* [x] One-click "Show DSSE chain" renders the three envelopes with subject digests and signers. *(SPRINT_4200_0001_0001)*
|
||||
* [x] Audit log captures who approved, which digests, and which evidence hashes. *(SPRINT_3801_0001_0004)*
|
||||
|
||||
---
|
||||
|
||||
## Implementation Coverage Summary
|
||||
|
||||
| Section | Coverage | Sprint(s) |
|
||||
|---------|----------|-----------|
|
||||
| Explainable Triage UX | 85% | 3800.*, 4200.0001.0001 |
|
||||
| Evidence-Linked Approvals | 100% | 3801.*, 4100.* |
|
||||
| Backend APIs | 85% | 3800.0003.0001, **4300.0001.0002** |
|
||||
| CLI/API | 50% | 3500.0004.*, **4300.0001.0001** |
|
||||
| Invariants | 90% | 4100.0003.*, **4300.0002.0002** |
|
||||
| Data Model | 100% | Scanner.Triage |
|
||||
| Evidence Types | 100% | 3800.0002.*, Evidence.Bundle |
|
||||
| Predicate Types | 80% | 3801.*, **4300.0003.0001** |
|
||||
| Policy Gate | 100% | Policy.Engine |
|
||||
| Approve Button | 100% | 4100.0005.0001 |
|
||||
| Privacy | 0% | **4300.0002.0001** |
|
||||
| TTL Strategy | 50% | **4300.0002.0002** |
|
||||
| Metrics | 30% | **4300.0003.0002** |
|
||||
|
||||
---
|
||||
|
||||
*Original advisory content preserved below for reference.*
|
||||
|
||||
---
|
||||
|
||||
## 1) Start with the invariants (the rules your system must never violate)
|
||||
|
||||
If you implement nothing else, implement these invariants-they're what make the UX trustworthy and the approvals auditable.
|
||||
|
||||
### Artifact anchoring invariant
|
||||
|
||||
Every finding, every piece of evidence, and every approval must be anchored to an immutable **subject digest** (e.g., container image digest `sha256:...`, binary SHA, or SBOM digest).
|
||||
|
||||
* No "latest tag" approvals.
|
||||
* No "approve commit" without mapping to the built artifact digest.
|
||||
|
||||
### Evidence closure invariant
|
||||
|
||||
A policy decision is only valid if it references **exactly** the evidence it used:
|
||||
|
||||
* `inputs.sbom_ref`
|
||||
* `inputs.vex_ref`
|
||||
* `inputs.reachability_ref` (optional but recommended)
|
||||
* `inputs.scan_ref` (optional)
|
||||
* and any config/IaC refs used for boundary/exposure.
|
||||
|
||||
### Signature chain invariant
|
||||
|
||||
Evidence is only admissible if it is:
|
||||
|
||||
1. structured (machine readable),
|
||||
2. signed (DSSE/in-toto),
|
||||
3. verifiable (trusted identity/keys),
|
||||
4. retrievable by digest.
|
||||
|
||||
### Staleness invariant
|
||||
|
||||
Evidence must have:
|
||||
|
||||
* `last_seen` and `expires_at` (or TTL),
|
||||
* a "stale evidence" behavior in policy (deny or degrade score).
|
||||
|
||||
---
|
||||
|
||||
## 2) Choose the canonical formats and where you'll store "proof"
|
||||
|
||||
### Attestation envelope: DSSE + in-toto Statement
|
||||
|
||||
Use:
|
||||
|
||||
* **in-toto Attestation Framework** "Statement" as the payload model ("subject + predicateType + predicate").
|
||||
* Wrap it in **DSSE** for signing.
|
||||
|
||||
### SBOM format: CycloneDX or SPDX
|
||||
|
||||
* SPDX is an ISO/IEC standard and has v3.0 and v2.3 lines in the ecosystem.
|
||||
* CycloneDX is an ECMA standard (ECMA-424) and widely used for application security contexts.
|
||||
|
||||
Pick one as **your canonical** (internally), but ingest both.
|
||||
|
||||
### VEX format: OpenVEX (practical) + map to "classic" VEX statuses
|
||||
|
||||
VEX's value is triage noise reduction: vendors can assert whether a product is affected, fixed, under investigation, or not affected.
|
||||
OpenVEX is a minimal, embeddable implementation of VEX intended for interoperability.
|
||||
|
||||
### Where to store proof: OCI registry referrers
|
||||
|
||||
Use OCI "subject/referrers" so proofs travel with the artifact:
|
||||
|
||||
* OCI 1.1 introduces an explicit `subject` field and referrers graph for signatures/attestations/SBOMs.
|
||||
|
||||
---
|
||||
|
||||
## 3-13) [Additional Sections]
|
||||
|
||||
*See original advisory for full content on:*
|
||||
- System architecture
|
||||
- Data model
|
||||
- Explainable triage computation
|
||||
- UI components
|
||||
- Proof-linked evidence generation
|
||||
- Predicate types
|
||||
- Policy gate
|
||||
- Approve button behavior
|
||||
- Implementation details
|
||||
- MVP path
|
||||
- Quick checklist
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
[1]: https://github.com/secure-systems-lab/dsse "DSSE: Dead Simple Signing Envelope"
|
||||
[2]: https://github.com/in-toto/attestation "in-toto Attestation Framework"
|
||||
[3]: https://docs.sigstore.dev/about/bundle/ "Sigstore Bundle Format"
|
||||
[4]: https://spdx.dev/use/specifications/ "Specifications"
|
||||
[5]: https://github.com/CycloneDX/specification "CycloneDX/specification"
|
||||
[6]: https://www.ntia.gov/sites/default/files/publications/vex_one-page_summary_0.pdf "VEX one-page summary"
|
||||
[7]: https://github.com/openvex/spec "OpenVEX Specification"
|
||||
[8]: https://opencontainers.org/posts/blog/2024-03-13-image-and-distribution-1-1/ "OCI Image and Distribution Specs v1.1 Releases"
|
||||
[9]: https://oras.land/docs/concepts/reftypes/ "Attached Artifacts | OCI Registry As Storage"
|
||||
[10]: https://learn.microsoft.com/en-us/azure/container-registry/container-registry-manage-artifact "Manage OCI Artifacts and Supply Chain Artifacts with ORAS"
|
||||
[11]: https://openpolicyagent.org/docs/policy-language "Policy Language"
|
||||
[12]: https://docs.sigstore.dev/cosign/verifying/attestation/ "In-Toto Attestations"
|
||||
@@ -0,0 +1,93 @@
|
||||
# ARCHIVED
|
||||
|
||||
> **Archived:** 2025-12-22
|
||||
> **Reason:** Gap analysis complete. Recommendations incorporated into sprints and documentation.
|
||||
>
|
||||
> **Implementation Artifacts:**
|
||||
> - SPRINT_2000_0003_0001: Alpine connector and APK comparator
|
||||
> - SPRINT_2000_0003_0002: Comprehensive distro version tests (50-100 per distro)
|
||||
> - SPRINT_4000_0002_0001: Backport UX explainability ("Compared with" badge, "Why Fixed" popover)
|
||||
> - SPRINT_6000_SUMMARY.md: Updated to reference existing Concelier comparators
|
||||
> - `src/Concelier/AGENTS.md`: Added distro backport version handling section
|
||||
>
|
||||
> **Existing Implementations Validated:**
|
||||
> - `src/Concelier/__Libraries/StellaOps.Concelier.Merge/Comparers/Nevra.cs` (RPM)
|
||||
> - `src/Concelier/__Libraries/StellaOps.Concelier.Merge/Comparers/DebianEvr.cs` (Debian/Ubuntu)
|
||||
> - Distro connectors: Debian, Ubuntu, RedHat, SUSE
|
||||
|
||||
---
|
||||
|
||||
Here's a quick, practical heads-up on **patch-aware backport handling** so your vulnerability verdicts don't go sideways.
|
||||
|
||||

|
||||
|
||||
### Why this matters
|
||||
|
||||
Distros often **backport fixes** without bumping the upstream version. If you compare versions with a generic SemVer library, you can mislabel **fixed** builds as **vulnerable** (or the reverse).
|
||||
|
||||
### Use distro-native comparators (not SemVer)
|
||||
|
||||
* **RPM (RHEL/CentOS/Fedora/openSUSE):** compare using **EVR** (`epoch:version-release`) via `rpmvercmp`. Tilde `~` sorts **before** anything; releases matter (e.g., `1.2-3.el9_2` > `1.2-3`).
|
||||
* **Debian/Ubuntu:** compare **epoch >> upstream_version >> debian_revision** using `dpkg --compare-versions` rules. Tilde `~` sorts **lower** than empty, so `2.0~rc1` < `2.0`.
|
||||
* **Alpine (APK):** follows its own comparator; treat `-r` (pkgrel) as part of ordering, similar in spirit to RPM release.
|
||||
|
||||
### Practical rules for your scanner (Stella Ops / Feedser -> Vexer)
|
||||
|
||||
1. **Normalize the package coordinate**
|
||||
|
||||
* RPM: `name:evr.arch` (epoch default 0 if missing).
|
||||
* DEB: `name:epoch:upstream_version-debian_revision arch`.
|
||||
* Keep the **distro release**/revision; it encodes backports.
|
||||
|
||||
2. **Compare with native engines**
|
||||
|
||||
* On Linux hosts/containers, call the system tool when possible:
|
||||
|
||||
* RPM: `rpm --qf '%{EPOCH}:%{VERSION}-%{RELEASE}\n' -q <pkg>` then use `rpmdev-vercmp`/`rpmvercmp`.
|
||||
* DEB/Ubuntu: `dpkg-query -W -f='${Version}\n' <pkg>` and `dpkg --compare-versions`.
|
||||
* In offline analysis, embed battle-tested comparators (ports of `rpmvercmp` and `dpkg` logic) in your evaluator.
|
||||
|
||||
3. **Model advisories with distro ranges**
|
||||
|
||||
* Store **per-ecosystem fixed ranges**:
|
||||
|
||||
* RPM example: `fixed >= 2:1.4.3-5.el9_3`
|
||||
* DEB example: `fixed >= 1:1.4.3-5+deb12u2`
|
||||
* Do **not** rewrite to SemVer; keep native forms.
|
||||
|
||||
4. **VEX/decisioning**
|
||||
|
||||
* When upstream says "fixed in 1.4.4" but **distro claims fixed in 1.4.3-5~deb12u2**, prefer distro channel **if source is trusted**.
|
||||
* Record **evidence**: source (DSA/RHSA/USN), comparator used, installed EVR/DEB version, fixed threshold, and result. Attach to the verdict.
|
||||
|
||||
5. **Edge cases to test**
|
||||
|
||||
* Epoch jumps: `1:1.2-1` > `0:9.9-9`.
|
||||
* Tilde pre-releases: `2.0~rc1` < `2.0`.
|
||||
* Release qualifiers: `1.2-3.el9_2` < `1.2-3.el9_3`.
|
||||
* Rebuilds/backports: `1.2-3ubuntu0.1` vs `1.2-3`.
|
||||
|
||||
### Minimal implementation sketch (C#)
|
||||
|
||||
* **Strategy pattern**: `IVersionComparator` with implementations `RpmComparator`, `DpkgComparator`, `ApkComparator`.
|
||||
* **Selector** by package source (`rpmdb`, `dpkg-status`, `apk info`).
|
||||
* **Evidence struct**:
|
||||
|
||||
```csharp
|
||||
record VersionVerdict(
|
||||
string Pkg, string Distro, string Installed, string Fixed,
|
||||
string Comparator, bool IsFixed, string EvidenceSource, string[] ProofLines);
|
||||
```
|
||||
* **Fallback**: If native comparator unavailable, use embedded ports of `rpmvercmp` and Debian's algorithm; never SemVer.
|
||||
|
||||
### CI tests you should pin
|
||||
|
||||
* A table-driven test set with 50-100 cases covering epochs, tildes, and distro revisions.
|
||||
* Golden files per distro to prevent regressions.
|
||||
* Cross-check installed values from real images (e.g., `ubi9`, `debian:12`, `ubuntu:22.04`, `alpine:3.20`).
|
||||
|
||||
### UX nudge
|
||||
|
||||
* In the UI, show **"Compared with: RPM EVR / dpkg rules"** and link the **exact fixed threshold** that matched. Provide a "why fixed" popover showing the string compare steps.
|
||||
|
||||
If you like, I can drop in ready-to-use C# comparators (rpmvercmp/dpkg) and a test corpus so you can wire this straight into Feedser/Vexer.
|
||||
@@ -0,0 +1,184 @@
|
||||
Here's a compact, practical way to make VEX trust decisions explainable and replayable across vendor, distro, and internal sources—without adding human-in-the-loop friction.
|
||||
|
||||
---
|
||||
|
||||
# VEX Trust Lattice (compact)
|
||||
|
||||
**Goal:** turn messy/contradictory VEX claims into a single, signed, reproducible verdict with a numeric confidence and an audit trail.
|
||||
|
||||
## 1) Trust vector per source
|
||||
|
||||
Each VEX source S gets a 3‑component trust vector scored in [0..1]:
|
||||
|
||||
* **Provenance (P):** cryptographic & process integrity
|
||||
|
||||
* 1.00 = DSSE‑signed, timestamped, Rekor/Git tag anchored, org DKIM/Sigstore OIDC, key in allow‑list, rotation policy OK
|
||||
* 0.75 = DSSE‑signed + public key known, but no transparency log
|
||||
* 0.40 = unsigned but retrieved via authenticated, immutable artifact repo
|
||||
* 0.10 = opaque/CSV/email/manual import
|
||||
* **Coverage (C):** how well the statement's scope maps to your asset
|
||||
|
||||
* 1.00 = exact package + version/build digest + feature/flag context matched
|
||||
* 0.75 = exact pkg + version range matched; partial feature context
|
||||
* 0.50 = product‑level only; maps via CPE/PURL family
|
||||
* 0.25 = family‑level heuristics; no version proof
|
||||
* **Replayability (R):** can we deterministically re‑derive the claim?
|
||||
|
||||
* 1.00 = all inputs pinned (feeds, SBOM hash, ruleset hash, lattice version); replays byte‑identical
|
||||
* 0.60 = inputs mostly pinned; non‑deterministic ordering tolerated but stable outcome
|
||||
* 0.20 = ephemeral APIs; no snapshot
|
||||
|
||||
**BaseTrust(S) = wP·P + wC·C + wR·R** (defaults: wP=0.45, wC=0.35, wR=0.20; tunable per policy).
|
||||
|
||||
## 2) Claim strength & freshness
|
||||
|
||||
Every individual VEX claim `S asserts X` carries multipliers:
|
||||
|
||||
* **Strength (M):** *not*‑affected‑because‑{reason}
|
||||
|
||||
* Exploitability analysis + reachability proof subgraph provided → 1.00
|
||||
* Config/feature‑flag reason with evidence → 0.80
|
||||
* Vendor blanket statement → 0.60
|
||||
* "Under investigation" → 0.40
|
||||
* **Freshness (F):** time‑decay curve; default half‑life 90 days
|
||||
|
||||
* `F = exp(- ln(2) · age_days / 90)`; floor at 0.35 unless revoked
|
||||
|
||||
**ClaimScore = BaseTrust(S) · M · F.**
|
||||
|
||||
## 3) Lattice ordering & merge
|
||||
|
||||
Define a **partial order** on claims by (scope specificity, ClaimScore). More specific scope wins ties.
|
||||
|
||||
For a given CVE×Asset, gather all claims `{Ci}`:
|
||||
|
||||
* If any **revocation/contradiction** exists, keep both and trigger **conflict mode**: require replay proof; otherwise down‑weight older/weaker by Δ=0.25.
|
||||
* Final **verdict** chosen by **argmax(ClaimScore)** after conflict adjustments.
|
||||
|
||||
Return tuple:
|
||||
|
||||
```
|
||||
Verdict = {
|
||||
status: {affected|not_affected|under_investigation|fixed},
|
||||
confidence: ClaimScore*,
|
||||
expl: list of (source, reason, P/C/R, M, F),
|
||||
evidence_refs: [attestations, SBOM hash, reachability subgraph id],
|
||||
policy_hash, lattice_version
|
||||
}
|
||||
```
|
||||
|
||||
## 4) Policy hooks (explainable gates)
|
||||
|
||||
* **Minimum confidence by environment:** e.g., prod requires ≥0.75 to accept "not_affected".
|
||||
* **Unknowns budget:** fail if (#unknown deps > N) OR (Σ(1–ClaimScore) over unknowns > T).
|
||||
* **Source quotas:** cap influence from any single vendor at 60% unless a second independent source supports within Δ=0.1.
|
||||
* **Reason allow‑list:** forbid blanket vendor claims for criticals unless reachability proof exists.
|
||||
|
||||
## 5) Deterministic replay
|
||||
|
||||
To guarantee "same inputs → same verdict":
|
||||
|
||||
* Pin: SBOM digest(s), vuln feed snapshot ids, VEX document digests, reachability graph ids, policy file, lattice version, clock cutoff.
|
||||
* Sort: stable topological order on inputs (by `(issuer_did, statement_digest)`).
|
||||
* Serialize verdict + inputs into a **Verdict Manifest** (JSON/CBOR) and sign (DSSE).
|
||||
* Store in **Authority** with index: `(asset_digest, CVE, policy_hash, lattice_version)`.
|
||||
|
||||
## 6) Minimal data model (for Vexer/Policy Engine)
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"id": "did:web:vendor.example",
|
||||
"provenance": {"sig_type":"dsse","rekor_log_id":"...","key_alias":"vendor_k1"},
|
||||
"provenance_score": 0.90,
|
||||
"coverage_score": 0.75,
|
||||
"replay_score": 0.60,
|
||||
"weights": {"wP":0.45,"wC":0.35,"wR":0.20}
|
||||
},
|
||||
"claim": {
|
||||
"scope": {"purl":"pkg:rpm/openssl@3.0.12-5","digest":"sha256:...","features":{"fips":true}},
|
||||
"cve": "CVE-2025-12345",
|
||||
"status": "not_affected",
|
||||
"reason": "feature_flag_off",
|
||||
"strength": 0.80,
|
||||
"issued_at": "2025-11-28T10:12:00Z",
|
||||
"evidence": {"reach_subgraph_id":"reg:subg/abcd","attestation":"sha256:..."}
|
||||
},
|
||||
"policy": {"min_confidence_prod":0.75,"unknown_budget":5,"require_reachability_for_criticals":true},
|
||||
"lattice_version": "1.2.0"
|
||||
}
|
||||
```
|
||||
|
||||
## 7) Deterministic evaluation (C# sketch)
|
||||
|
||||
```csharp
|
||||
public record TrustWeights(double wP=0.45, double wC=0.35, double wR=0.20);
|
||||
|
||||
double BaseTrust(double P, double C, double R, TrustWeights W)
|
||||
=> W.wP*P + W.wC*C + W.wR*R;
|
||||
|
||||
double Freshness(DateTime issuedAt, DateTime cutoff, double halfLifeDays=90, double floor=0.35)
|
||||
{
|
||||
var age = (cutoff - issuedAt).TotalDays;
|
||||
var f = Math.Exp(-Math.Log(2) * age / halfLifeDays);
|
||||
return Math.Max(f, floor);
|
||||
}
|
||||
|
||||
double ClaimScore(Source s, Claim c, TrustWeights W, DateTime cutoffUtc)
|
||||
{
|
||||
var baseTrust = BaseTrust(s.P, s.C, s.R, W);
|
||||
var freshness = Freshness(c.IssuedAt, cutoffUtc);
|
||||
return baseTrust * c.Strength * freshness;
|
||||
}
|
||||
|
||||
// Merge: pick best score; apply conflict penalty if contradictory present
|
||||
Verdict Merge(IEnumerable<(Source S, Claim C)> claims, Policy policy, DateTime cutoffUtc)
|
||||
{
|
||||
var scored = claims.Select(t => new {
|
||||
t.S, t.C, Score = ClaimScore(t.S, t.C, t.S.Weights, cutoffUtc)
|
||||
}).ToList();
|
||||
|
||||
bool contradictory = scored.Select(x=>x.C.Status).Distinct().Count() > 1;
|
||||
if (contradictory) {
|
||||
scored = scored.Select(x => new {
|
||||
x.S, x.C, Score = x.Score * 0.75 // conflict penalty
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
var winner = scored.OrderByDescending(x => (x.C.Scope.Specificity, x.Score)).First();
|
||||
if (policy.RequireReachForCriticals && winner.C.IsCritical && !winner.C.HasReachabilityProof)
|
||||
return Verdict.FailGate("No reachability proof for critical");
|
||||
|
||||
if (policy.MinConfidenceProd.HasValue && winner.Score < policy.MinConfidenceProd)
|
||||
return Verdict.FailGate("Below minimum confidence");
|
||||
|
||||
return Verdict.Accept(winner.C.Status, winner.Score, AuditTrail.From(scored));
|
||||
}
|
||||
```
|
||||
|
||||
## 8) UI: "Trust Algebra" panel (1 screen, no new page)
|
||||
|
||||
* **Header:** CVE × Asset digest → final status + confidence meter.
|
||||
* **Stacked bars:** P/C/R contributions for the winning claim.
|
||||
* **Claim table:** source, status, reason, P/C/R, strength, freshness, ClaimScore; toggle "show conflicts".
|
||||
* **Policy chips:** which gates applied; click to open policy YAML/JSON (read‑only if in replay).
|
||||
* **Replay button:** "Reproduce verdict" → emits a signed **Verdict Manifest** and logs proof ids.
|
||||
|
||||
## 9) Defaults for source classes
|
||||
|
||||
* **Vendor:** P=0.9, C=0.7 (often coarse), R=0.6
|
||||
* **Distro:** P=0.8, C=0.85 (build‑aware), R=0.6
|
||||
* **Internal:** P=0.85 (org‑signed), C=0.95 (exact SBOM+reach), R=0.9
|
||||
|
||||
Tune per issuer using rolling calibration: compare past ClaimScores vs. post‑mortem truth; adjust via small learning rate (±0.02/epoch) under a signed **calibration manifest** (also replayable).
|
||||
|
||||
---
|
||||
|
||||
If you want, I can drop this into your Stella Ops modules today as:
|
||||
|
||||
* **Vexer:** trust‑vector store + claim normalizer
|
||||
* **Policy Engine:** lattice evaluator + gates
|
||||
* **Authority:** verdict manifest signer/indexer
|
||||
* **UI:** single "Trust Algebra" panel wired to evidence ids
|
||||
|
||||
Say the word and I'll generate the concrete JSON schemas, C# interfaces, and a seed policy file.
|
||||
Reference in New Issue
Block a user