Files
git.stella-ops.org/docs/reachability/slice-schema.md
StellaOps Bot df94136727 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.
2025-12-22 09:49:53 +02:00

8.6 KiB
Raw Blame History

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.30.7
gated Path exists but gated by feature flag/auth 0.50.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:

  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


Created: 2025-12-22. See Sprint 3810 for implementation details.