- 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.
8.6 KiB
8.6 KiB
Reachability Slice Schema
Last updated: 2025-12-22. Owner: Scanner Guild.
This document defines the Reachability Slice schema—a minimal, attestable proof unit that answers whether a vulnerable symbol is reachable from application entrypoints.
1. Overview
A slice is a focused subgraph extracted from a full reachability graph, containing only the nodes and edges relevant to answering a specific reachability query (e.g., "Is CVE-2024-1234's vulnerable function reachable?").
Key Properties
| Property | Description |
|---|---|
| Minimal | Contains only nodes/edges on paths between entrypoints and targets |
| Attestable | DSSE-signed with in-toto predicate format |
| Reproducible | Same inputs → same bytes (deterministic) |
| Content-addressed | Retrieved by BLAKE3 digest |
2. Schema Definition
2.1 DSSE Predicate Type
https://stellaops.dev/predicates/reachability-slice/v1
2.2 Full Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://stellaops.dev/schemas/reachability-slice.v1.schema.json",
"title": "Reachability Slice",
"type": "object",
"required": ["_type", "inputs", "query", "subgraph", "verdict", "manifest"],
"properties": {
"_type": {
"const": "https://stellaops.dev/predicates/reachability-slice/v1"
},
"inputs": { "$ref": "#/$defs/SliceInputs" },
"query": { "$ref": "#/$defs/SliceQuery" },
"subgraph": { "$ref": "#/$defs/SliceSubgraph" },
"verdict": { "$ref": "#/$defs/SliceVerdict" },
"manifest": { "$ref": "#/$defs/ScanManifest" }
},
"$defs": {
"SliceInputs": {
"type": "object",
"required": ["graphDigest", "binaryDigests"],
"properties": {
"graphDigest": { "type": "string", "pattern": "^blake3:[a-f0-9]{64}$" },
"binaryDigests": {
"type": "array",
"items": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" }
},
"sbomDigest": { "type": "string" },
"layerDigests": { "type": "array", "items": { "type": "string" } }
}
},
"SliceQuery": {
"type": "object",
"properties": {
"cveId": { "type": "string", "pattern": "^CVE-\\d{4}-\\d+$" },
"targetSymbols": { "type": "array", "items": { "type": "string" } },
"entrypoints": { "type": "array", "items": { "type": "string" } },
"policyHash": { "type": "string" }
}
},
"SliceSubgraph": {
"type": "object",
"required": ["nodes", "edges"],
"properties": {
"nodes": {
"type": "array",
"items": { "$ref": "#/$defs/SliceNode" }
},
"edges": {
"type": "array",
"items": { "$ref": "#/$defs/SliceEdge" }
}
}
},
"SliceNode": {
"type": "object",
"required": ["id", "symbol", "kind"],
"properties": {
"id": { "type": "string" },
"symbol": { "type": "string" },
"kind": { "enum": ["entrypoint", "intermediate", "target", "unknown"] },
"file": { "type": "string" },
"line": { "type": "integer" },
"purl": { "type": "string" },
"attributes": { "type": "object" }
}
},
"SliceEdge": {
"type": "object",
"required": ["from", "to", "confidence"],
"properties": {
"from": { "type": "string" },
"to": { "type": "string" },
"kind": { "enum": ["direct", "plt", "iat", "dynamic", "unknown"] },
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
"evidence": { "type": "string" },
"gate": { "$ref": "#/$defs/GateInfo" },
"observed": { "$ref": "#/$defs/ObservedInfo" }
}
},
"GateInfo": {
"type": "object",
"properties": {
"type": { "enum": ["feature_flag", "auth", "config", "admin_only"] },
"condition": { "type": "string" },
"satisfied": { "type": "boolean" }
}
},
"ObservedInfo": {
"type": "object",
"properties": {
"firstObserved": { "type": "string", "format": "date-time" },
"lastObserved": { "type": "string", "format": "date-time" },
"count": { "type": "integer" }
}
},
"SliceVerdict": {
"type": "object",
"required": ["status", "confidence"],
"properties": {
"status": { "enum": ["reachable", "unreachable", "unknown", "gated"] },
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
"reasons": { "type": "array", "items": { "type": "string" } },
"pathWitnesses": { "type": "array", "items": { "type": "string" } },
"unknownCount": { "type": "integer" },
"gatedPaths": { "type": "array", "items": { "$ref": "#/$defs/GateInfo" } }
}
},
"ScanManifest": {
"type": "object",
"required": ["analyzerVersion", "createdAt"],
"properties": {
"analyzerVersion": { "type": "string" },
"rulesetHash": { "type": "string" },
"feedVersions": { "type": "object" },
"createdAt": { "type": "string", "format": "date-time" },
"toolchain": { "type": "string" }
}
}
}
}
3. Verdict Status Definitions
| Status | Meaning | Confidence Range |
|---|---|---|
reachable |
Path exists from entrypoint to target | ≥0.7 |
unreachable |
No path found, no unknowns | ≥0.9 |
unknown |
Unknowns present on potential paths | 0.3–0.7 |
gated |
Path exists but gated by feature flag/auth | 0.5–0.8 |
Verdict Computation Rules
reachable := path_exists AND min(path_confidence) ≥ 0.7 AND unknown_edges = 0
unreachable := NOT path_exists AND unknown_edges = 0
gated := path_exists AND all_paths_gated AND gates_not_satisfied
unknown := unknown_edges > 0 OR min(path_confidence) < 0.5
4. Example Slice
{
"_type": "https://stellaops.dev/predicates/reachability-slice/v1",
"inputs": {
"graphDigest": "blake3:a1b2c3d4e5f6789012345678901234567890123456789012345678901234abcd",
"binaryDigests": ["sha256:deadbeef..."],
"sbomDigest": "sha256:cafebabe..."
},
"query": {
"cveId": "CVE-2024-1234",
"targetSymbols": ["openssl:EVP_PKEY_decrypt"],
"entrypoints": ["main", "http_handler"]
},
"subgraph": {
"nodes": [
{"id": "node:1", "symbol": "main", "kind": "entrypoint", "file": "/app/main.c", "line": 42},
{"id": "node:2", "symbol": "process_request", "kind": "intermediate", "file": "/app/handler.c", "line": 100},
{"id": "node:3", "symbol": "decrypt_data", "kind": "intermediate", "file": "/app/crypto.c", "line": 55},
{"id": "node:4", "symbol": "EVP_PKEY_decrypt", "kind": "target", "purl": "pkg:generic/openssl@3.0.0"}
],
"edges": [
{"from": "node:1", "to": "node:2", "kind": "direct", "confidence": 1.0},
{"from": "node:2", "to": "node:3", "kind": "direct", "confidence": 0.95},
{"from": "node:3", "to": "node:4", "kind": "plt", "confidence": 0.9}
]
},
"verdict": {
"status": "reachable",
"confidence": 0.9,
"reasons": ["Direct call path from main() to EVP_PKEY_decrypt()"],
"pathWitnesses": ["main → process_request → decrypt_data → EVP_PKEY_decrypt"]
},
"manifest": {
"analyzerVersion": "scanner.native:1.2.0",
"rulesetHash": "sha256:...",
"createdAt": "2025-12-22T10:00:00Z",
"toolchain": "iced-x86:1.21.0"
}
}
5. DSSE Envelope Format
Slices are wrapped in DSSE envelopes for attestation:
{
"payloadType": "application/vnd.in-toto+json",
"payload": "<base64-encoded slice JSON>",
"signatures": [
{
"keyid": "sha256:abc123...",
"sig": "<base64-encoded signature>"
}
]
}
6. Storage & Retrieval
CAS URI Format
cas://slices/blake3:<digest>
OCI Artifact Format
{
"mediaType": "application/vnd.stellaops.slice.v1+json",
"digest": "sha256:...",
"annotations": {
"org.stellaops.slice.cve": "CVE-2024-1234",
"org.stellaops.slice.verdict": "reachable"
}
}
7. Determinism Requirements
For reproducible slices:
- Node ordering: Sort by
idlexicographically - Edge ordering: Sort by
(from, to)tuple - Timestamps: Use UTC ISO-8601 with Z suffix
- Floating point: Round to 6 decimal places
- JSON serialization: No whitespace, sorted keys
8. Related Documentation
Created: 2025-12-22. See Sprint 3810 for implementation details.