Refactor code structure for improved readability and maintainability; optimize performance in key functions.
This commit is contained in:
@@ -1,190 +1,179 @@
|
||||
# Reachability Slice Schema
|
||||
# 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.
|
||||
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?").
|
||||
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 (for example, "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) |
|
||||
| **Attestable** | DSSE-signed with a dedicated slice predicate |
|
||||
| **Reproducible** | Same inputs -> same bytes (deterministic) |
|
||||
| **Content-addressed** | Retrieved by BLAKE3 digest |
|
||||
|
||||
---
|
||||
|
||||
## 2. Schema Definition
|
||||
## 2. Predicate Type & Schema
|
||||
|
||||
### 2.1 DSSE Predicate Type
|
||||
- Predicate type: `stellaops.dev/predicates/reachability-slice@v1`
|
||||
- JSON schema: `https://stellaops.dev/schemas/stellaops-slice.v1.schema.json`
|
||||
- DSSE payload type: `application/vnd.stellaops.slice.v1+json`
|
||||
|
||||
```
|
||||
https://stellaops.dev/predicates/reachability-slice/v1
|
||||
```
|
||||
---
|
||||
|
||||
### 2.2 Full Schema
|
||||
## 3. Schema Structure
|
||||
|
||||
```json
|
||||
### 3.1 ReachabilitySlice
|
||||
|
||||
```csharp
|
||||
public sealed record ReachabilitySlice
|
||||
{
|
||||
"$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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
[JsonPropertyName("_type")]
|
||||
public string Type { get; init; } = "stellaops.dev/predicates/reachability-slice@v1";
|
||||
|
||||
[JsonPropertyName("inputs")]
|
||||
public required SliceInputs Inputs { get; init; }
|
||||
|
||||
[JsonPropertyName("query")]
|
||||
public required SliceQuery Query { get; init; }
|
||||
|
||||
[JsonPropertyName("subgraph")]
|
||||
public required SliceSubgraph Subgraph { get; init; }
|
||||
|
||||
[JsonPropertyName("verdict")]
|
||||
public required SliceVerdict Verdict { get; init; }
|
||||
|
||||
[JsonPropertyName("manifest")]
|
||||
public required ScanManifest Manifest { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
### 3.2 SliceInputs
|
||||
|
||||
```csharp
|
||||
public sealed record SliceInputs
|
||||
{
|
||||
public required string GraphDigest { get; init; }
|
||||
public ImmutableArray<string> BinaryDigests { get; init; }
|
||||
public string? SbomDigest { get; init; }
|
||||
public ImmutableArray<string> LayerDigests { get; init; }
|
||||
}
|
||||
```
|
||||
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
|
||||
|
||||
### 3.3 SliceQuery
|
||||
|
||||
```csharp
|
||||
public sealed record SliceQuery
|
||||
{
|
||||
public string? CveId { get; init; }
|
||||
public ImmutableArray<string> TargetSymbols { get; init; }
|
||||
public ImmutableArray<string> Entrypoints { get; init; }
|
||||
public string? PolicyHash { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 SliceSubgraph, Nodes, Edges
|
||||
|
||||
```csharp
|
||||
public sealed record SliceSubgraph
|
||||
{
|
||||
public ImmutableArray<SliceNode> Nodes { get; init; }
|
||||
public ImmutableArray<SliceEdge> Edges { get; init; }
|
||||
}
|
||||
|
||||
public sealed record SliceNode
|
||||
{
|
||||
public required string Id { get; init; }
|
||||
public required string Symbol { get; init; }
|
||||
public required SliceNodeKind Kind { get; init; } // entrypoint | intermediate | target | unknown
|
||||
public string? File { get; init; }
|
||||
public int? Line { get; init; }
|
||||
public string? Purl { get; init; }
|
||||
public IReadOnlyDictionary<string, string>? Attributes { get; init; }
|
||||
}
|
||||
|
||||
public sealed record SliceEdge
|
||||
{
|
||||
public required string From { get; init; }
|
||||
public required string To { get; init; }
|
||||
public SliceEdgeKind Kind { get; init; } // direct | plt | iat | dynamic | unknown
|
||||
public double Confidence { get; init; }
|
||||
public string? Evidence { get; init; }
|
||||
public SliceGateInfo? Gate { get; init; }
|
||||
public ObservedEdgeMetadata? Observed { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
### 3.5 SliceVerdict
|
||||
|
||||
```csharp
|
||||
public sealed record SliceVerdict
|
||||
{
|
||||
public required SliceVerdictStatus Status { get; init; }
|
||||
public required double Confidence { get; init; }
|
||||
public ImmutableArray<string> Reasons { get; init; }
|
||||
public ImmutableArray<string> PathWitnesses { get; init; }
|
||||
public int UnknownCount { get; init; }
|
||||
public ImmutableArray<GatedPath> GatedPaths { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
`SliceVerdictStatus` values (snake_case):
|
||||
- `reachable`
|
||||
- `unreachable`
|
||||
- `unknown`
|
||||
- `gated`
|
||||
- `observed_reachable`
|
||||
|
||||
### 3.6 ScanManifest
|
||||
|
||||
`ScanManifest` is imported from `StellaOps.Scanner.Core` and includes required fields for reproducibility:
|
||||
|
||||
- `scanId`
|
||||
- `createdAtUtc`
|
||||
- `artifactDigest`
|
||||
- `scannerVersion`
|
||||
- `workerVersion`
|
||||
- `concelierSnapshotHash`
|
||||
- `excititorSnapshotHash`
|
||||
- `latticePolicyHash`
|
||||
- `deterministic`
|
||||
- `seed` (base64-encoded 32-byte seed)
|
||||
- `knobs` (string map)
|
||||
|
||||
`artifactPurl` is optional.
|
||||
|
||||
---
|
||||
|
||||
## 4. Example Slice
|
||||
## 4. Verdict Computation Rules
|
||||
|
||||
```
|
||||
reachable := path_exists AND min(path_confidence) > 0.7 AND unknown_edges == 0
|
||||
unreachable := NOT path_exists AND unknown_edges == 0
|
||||
unknown := otherwise
|
||||
```
|
||||
|
||||
`gated` and `observed_reachable` are reserved for feature-gate and runtime-observed paths (see Sprint 3830 and 3840).
|
||||
|
||||
---
|
||||
|
||||
## 5. Example Slice
|
||||
|
||||
```json
|
||||
{
|
||||
"_type": "https://stellaops.dev/predicates/reachability-slice/v1",
|
||||
"_type": "stellaops.dev/predicates/reachability-slice@v1",
|
||||
"inputs": {
|
||||
"graphDigest": "blake3:a1b2c3d4e5f6789012345678901234567890123456789012345678901234abcd",
|
||||
"binaryDigests": ["sha256:deadbeef..."],
|
||||
"sbomDigest": "sha256:cafebabe..."
|
||||
"binaryDigests": ["sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],
|
||||
"sbomDigest": "sha256:cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"
|
||||
},
|
||||
"query": {
|
||||
"cveId": "CVE-2024-1234",
|
||||
@@ -207,75 +196,42 @@ unknown := unknown_edges > 0 OR min(path_confidence) < 0.5
|
||||
"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"]
|
||||
"reasons": ["path_exists_high_confidence"],
|
||||
"pathWitnesses": ["main -> process_request -> decrypt_data -> EVP_PKEY_decrypt"],
|
||||
"unknownCount": 0
|
||||
},
|
||||
"manifest": {
|
||||
"analyzerVersion": "scanner.native:1.2.0",
|
||||
"rulesetHash": "sha256:...",
|
||||
"createdAt": "2025-12-22T10:00:00Z",
|
||||
"toolchain": "iced-x86:1.21.0"
|
||||
"scanId": "scan-1234",
|
||||
"createdAtUtc": "2025-12-22T10:00:00Z",
|
||||
"artifactDigest": "sha256:00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff",
|
||||
"artifactPurl": "pkg:generic/app@1.0.0",
|
||||
"scannerVersion": "scanner.native:1.2.0",
|
||||
"workerVersion": "scanner.worker:1.2.0",
|
||||
"concelierSnapshotHash": "sha256:1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff",
|
||||
"excititorSnapshotHash": "sha256:2222333344445555666677778888999900001111aaaabbbbccccddddeeeeffff",
|
||||
"latticePolicyHash": "sha256:3333444455556666777788889999000011112222aaaabbbbccccddddeeeeffff",
|
||||
"deterministic": true,
|
||||
"seed": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||
"knobs": { "maxDepth": "20" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. DSSE Envelope Format
|
||||
|
||||
Slices are wrapped in DSSE envelopes for attestation:
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
## 6. Determinism Requirements
|
||||
|
||||
For reproducible slices:
|
||||
|
||||
1. **Node ordering**: Sort by `id` lexicographically
|
||||
2. **Edge ordering**: Sort by `(from, to)` tuple
|
||||
3. **Timestamps**: Use UTC ISO-8601 with Z suffix
|
||||
4. **Floating point**: Round to 6 decimal places
|
||||
5. **JSON serialization**: No whitespace, sorted keys
|
||||
1. **Node ordering**: Sort by `id` (ordinal).
|
||||
2. **Edge ordering**: Sort by `from`, then `to`, then `kind`.
|
||||
3. **Strings**: Trim and de-duplicate lists (`targetSymbols`, `entrypoints`, `reasons`).
|
||||
4. **Timestamps**: Use UTC ISO-8601 with `Z` suffix.
|
||||
5. **JSON serialization**: Canonical JSON (sorted keys, no whitespace).
|
||||
|
||||
---
|
||||
|
||||
## 8. Related Documentation
|
||||
## 7. Related Documentation
|
||||
|
||||
- [Binary Reachability Schema](./binary-reachability-schema.md)
|
||||
- [RichGraph Contract](../contracts/richgraph-v1.md)
|
||||
|
||||
Reference in New Issue
Block a user