finish off sprint advisories and sprints
This commit is contained in:
237
docs/contracts/function-map-v1.md
Normal file
237
docs/contracts/function-map-v1.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# Function Map V1 Contract
|
||||
|
||||
> **Predicate Type:** `https://stella.ops/predicates/function-map/v1`
|
||||
> **DSSE Payload Type:** `application/vnd.stellaops.function-map.v1+json`
|
||||
> **Schema Version:** `1.0.0`
|
||||
|
||||
## Overview
|
||||
|
||||
A function map predicate declares the expected call paths for a service component, enabling verification of runtime behavior against static analysis. It follows the [in-toto attestation](https://github.com/in-toto/attestation) framework.
|
||||
|
||||
---
|
||||
|
||||
## Predicate Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "https://stella.ops/predicates/function-map/v1",
|
||||
"subject": {
|
||||
"purl": "pkg:oci/my-service@sha256:abc123...",
|
||||
"digest": { "sha256": "abc123..." }
|
||||
},
|
||||
"predicate": {
|
||||
"service": "my-backend",
|
||||
"build_id": "build-456",
|
||||
"expected_paths": [...],
|
||||
"coverage": {
|
||||
"min_observation_rate": 0.95,
|
||||
"window_seconds": 1800,
|
||||
"fail_on_unexpected": false
|
||||
},
|
||||
"generated_at": "2026-01-23T10:00:00Z",
|
||||
"generated_from": {
|
||||
"sbom_ref": "oci://registry/sbom@sha256:...",
|
||||
"static_analysis_ref": "oci://registry/analysis@sha256:..."
|
||||
},
|
||||
"generator": {
|
||||
"name": "stella-cli",
|
||||
"version": "2.0.0",
|
||||
"commit": "abc123"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Subject
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `purl` | string | Yes | Package URL of the subject artifact |
|
||||
| `digest` | object | Yes | Content digest (sha256, sha512, etc.) |
|
||||
|
||||
---
|
||||
|
||||
## Predicate Fields
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `service` | string | Yes | Service name for correlation |
|
||||
| `build_id` | string | No | Build identifier for provenance correlation |
|
||||
| `expected_paths` | array | Yes | List of expected call paths |
|
||||
| `coverage` | object | Yes | Coverage thresholds for verification |
|
||||
| `generated_at` | ISO 8601 | Yes | Generation timestamp |
|
||||
| `generated_from` | object | No | Source references (SBOM, static analysis) |
|
||||
| `generator` | object | No | Tool that generated the predicate |
|
||||
|
||||
---
|
||||
|
||||
## Expected Path
|
||||
|
||||
Each expected path represents a call chain starting from an entrypoint:
|
||||
|
||||
```json
|
||||
{
|
||||
"path_id": "path-001",
|
||||
"entrypoint": {
|
||||
"symbol": "handleRequest",
|
||||
"node_hash": "sha256:..."
|
||||
},
|
||||
"expected_calls": [
|
||||
{
|
||||
"symbol": "crypto_sign",
|
||||
"purl": "pkg:deb/libcrypto3@3.0.0",
|
||||
"node_hash": "sha256:...",
|
||||
"probe_types": ["uprobe"],
|
||||
"optional": false,
|
||||
"function_address": null,
|
||||
"binary_path": "/usr/lib/libcrypto.so.3"
|
||||
}
|
||||
],
|
||||
"path_hash": "sha256:...",
|
||||
"optional": false,
|
||||
"strict_ordering": false,
|
||||
"tags": ["crypto"]
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `path_id` | string | Yes | Unique path identifier |
|
||||
| `entrypoint` | object | Yes | Path entry point (symbol + node_hash) |
|
||||
| `expected_calls` | array | Yes | List of expected function calls |
|
||||
| `path_hash` | string | Yes | SHA-256(entrypoint \|\| sorted calls) |
|
||||
| `optional` | boolean | No | Whether this path is optional (default false) |
|
||||
| `strict_ordering` | boolean | No | Ordered sequence vs unordered set (default false) |
|
||||
| `tags` | array | No | Categorization tags (crypto, auth, network, etc.) |
|
||||
|
||||
---
|
||||
|
||||
## Expected Call
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `symbol` | string | Yes | Function name (demangled) |
|
||||
| `purl` | string | Yes | Package URL of the component containing this function |
|
||||
| `node_hash` | string | Yes | SHA-256(PURL + normalized symbol) |
|
||||
| `probe_types` | array | Yes | Acceptable probe types for observation |
|
||||
| `optional` | boolean | No | Whether this call is optional (default false) |
|
||||
| `function_address` | string | No | Address hint for probe attachment |
|
||||
| `binary_path` | string | No | Binary path for uprobe attachment |
|
||||
|
||||
### Probe Types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `kprobe` | Kernel function entry |
|
||||
| `kretprobe` | Kernel function return |
|
||||
| `uprobe` | User-space function entry |
|
||||
| `uretprobe` | User-space function return |
|
||||
| `tracepoint` | Kernel tracepoint |
|
||||
| `usdt` | User-space statically defined tracing |
|
||||
|
||||
---
|
||||
|
||||
## Coverage Thresholds
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `min_observation_rate` | double | 0.95 | Minimum fraction of paths that must be observed |
|
||||
| `window_seconds` | integer | 1800 | Observation window duration |
|
||||
| `fail_on_unexpected` | boolean | false | Whether unexpected symbols cause verification failure |
|
||||
|
||||
---
|
||||
|
||||
## Node Hash Recipe
|
||||
|
||||
Node hashes provide content-addressable identifiers for function calls, matching the [Witness V1](witness-v1.md) convention:
|
||||
|
||||
```
|
||||
node_hash = SHA-256(PURL + ":" + normalize(symbol))
|
||||
```
|
||||
|
||||
Where `normalize(symbol)`:
|
||||
1. Demangle C++/Rust symbols
|
||||
2. Strip leading underscores (platform convention)
|
||||
3. Lowercase the result
|
||||
4. Remove whitespace
|
||||
|
||||
### Path Hash Recipe
|
||||
|
||||
```
|
||||
path_hash = SHA-256(entrypoint.node_hash + ":" + sort(calls.map(c => c.node_hash)).join(":"))
|
||||
```
|
||||
|
||||
The path hash is independent of call ordering (sorted) unless `strict_ordering` is true, in which case calls are not sorted before hashing.
|
||||
|
||||
---
|
||||
|
||||
## Coverage Calculation Algorithm
|
||||
|
||||
```
|
||||
total_required = count(paths where optional == false)
|
||||
observed_required = count(paths where optional == false AND has_matching_observation)
|
||||
|
||||
observation_rate = observed_required / total_required
|
||||
= 0.0 if total_required == 0
|
||||
|
||||
verified = observation_rate >= coverage.min_observation_rate
|
||||
```
|
||||
|
||||
For each path, an observation "matches" when:
|
||||
- At least one observation has a `node_hash` matching any call in the path
|
||||
- The observation falls within the time window
|
||||
- The probe type is in the call's `probe_types` list
|
||||
|
||||
---
|
||||
|
||||
## Verification Algorithm
|
||||
|
||||
```
|
||||
VERIFY(predicate, observations, options):
|
||||
1. Filter observations to time window [now - window_seconds, now]
|
||||
2. For each required expected_path:
|
||||
a. For each expected_call in path:
|
||||
- Find observations matching node_hash AND probe_type
|
||||
- Mark call as "observed" if any match found
|
||||
b. Mark path as "covered" if entrypoint OR any call observed
|
||||
3. Compute observation_rate = covered_paths / required_paths
|
||||
4. Collect unexpected = observations not matching any expected call
|
||||
5. Collect missing = required calls with no matching observation
|
||||
6. verified = observation_rate >= min_observation_rate
|
||||
AND (NOT fail_on_unexpected OR unexpected.count == 0)
|
||||
7. Return result with breakdown, unexpected, missing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Media Types
|
||||
|
||||
| Usage | Media Type |
|
||||
|-------|-----------|
|
||||
| Function map predicate | `application/vnd.stella.function-map+json` |
|
||||
| DSSE-signed predicate | `application/vnd.dsse+json` |
|
||||
| Observations | `application/x-ndjson` |
|
||||
| Verification report | `application/vnd.stella.verification-report+json` |
|
||||
|
||||
---
|
||||
|
||||
## Observation Record (NDJSON)
|
||||
|
||||
Each line in an observations file:
|
||||
|
||||
```json
|
||||
{
|
||||
"observation_id": "obs-123",
|
||||
"node_hash": "sha256:...",
|
||||
"function_name": "crypto_sign",
|
||||
"probe_type": "uprobe",
|
||||
"observed_at": "2026-01-23T10:05:00Z",
|
||||
"observation_count": 42,
|
||||
"container_id": "abc123",
|
||||
"pod_name": "my-service-pod-xyz",
|
||||
"namespace": "production",
|
||||
"duration_microseconds": 150
|
||||
}
|
||||
```
|
||||
51
docs/contracts/sbom-volatile-fields.json
Normal file
51
docs/contracts/sbom-volatile-fields.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "SBOM Volatile Fields Contract",
|
||||
"description": "Authoritative list of SBOM fields stripped before canonicalization to ensure deterministic hashes. Referenced by SbomNormalizer.",
|
||||
"version": 1,
|
||||
"cyclonedx": {
|
||||
"strip": [
|
||||
{
|
||||
"path": "serialNumber",
|
||||
"scope": "root",
|
||||
"rationale": "UUID regenerated on every BOM creation; not content-derived."
|
||||
},
|
||||
{
|
||||
"path": "metadata.timestamp",
|
||||
"scope": "metadata",
|
||||
"rationale": "Generation timestamp varies per run; not content-derived."
|
||||
},
|
||||
{
|
||||
"path": "metadata.tools",
|
||||
"scope": "metadata",
|
||||
"rationale": "Tool name/version/vendor varies across scanner installs; does not reflect scanned content."
|
||||
},
|
||||
{
|
||||
"path": "metadata.authors",
|
||||
"scope": "metadata",
|
||||
"rationale": "Author identity varies per operator; does not affect component inventory."
|
||||
}
|
||||
],
|
||||
"specVersions": ["1.4", "1.5", "1.6", "1.7"]
|
||||
},
|
||||
"spdx": {
|
||||
"strip": [
|
||||
{
|
||||
"path": "creationInfo.created",
|
||||
"scope": "creationInfo",
|
||||
"rationale": "Timestamp of SPDX document creation; varies per run."
|
||||
},
|
||||
{
|
||||
"path": "creationInfo.creators",
|
||||
"scope": "creationInfo",
|
||||
"rationale": "Tool identifiers include version strings (e.g., 'Tool: syft-1.2.3'); varies across installs."
|
||||
},
|
||||
{
|
||||
"path": "creationInfo.licenseListVersion",
|
||||
"scope": "creationInfo",
|
||||
"rationale": "Tracks upstream SPDX license list version available at scan time; not content-derived."
|
||||
}
|
||||
],
|
||||
"specVersions": ["2.2", "2.3", "3.0", "3.0.1"]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user