Refactor code structure for improved readability and maintainability; optimize performance in key functions.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# Benchmark Module Architecture
|
||||
# Benchmark Module Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -22,23 +22,23 @@ Establish verifiable, reproducible benchmarks that:
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Benchmark Module │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Corpus │ │ Harness │ │ Metrics │ │
|
||||
│ │ Manager │───▶│ Runner │───▶│ Calculator │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │Ground Truth │ │ Competitor │ │ Claims │ │
|
||||
│ │ Manifest │ │ Adapters │ │ Index │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
┌─────────────────────────────────────────────────────────────────â”
|
||||
│ Benchmark Module │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┠┌─────────────┠┌─────────────┠│
|
||||
│ │ Corpus │ │ Harness │ │ Metrics │ │
|
||||
│ │ Manager │───▶│ Runner │───▶│ Calculator │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────┠┌─────────────┠┌─────────────┠│
|
||||
│ │Ground Truth │ │ Competitor │ │ Claims │ │
|
||||
│ │ Manifest │ │ Adapters │ │ Index │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
@@ -233,42 +233,42 @@ public record ClaimVerification(
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
┌────────────────┐
|
||||
│ Corpus Images │
|
||||
│ (50+ images) │
|
||||
└───────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────┐ ┌────────────────┐
|
||||
│ Stella Ops Scan│ │ Trivy/Grype │
|
||||
│ │ │ Scan │
|
||||
└───────┬────────┘ └───────┬────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌────────────────┐ ┌────────────────┐
|
||||
│ Normalized │ │ Normalized │
|
||||
│ Findings │ │ Findings │
|
||||
└───────┬────────┘ └───────┬────────┘
|
||||
│ │
|
||||
└──────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ Ground Truth │
|
||||
│ Comparison │
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ Metrics │
|
||||
│ (P/R/F1) │
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ Claims Index │
|
||||
│ Update │
|
||||
└──────────────┘
|
||||
┌────────────────â”
|
||||
│ Corpus Images │
|
||||
│ (50+ images) │
|
||||
└───────┬────────┘
|
||||
│
|
||||
â–¼
|
||||
┌────────────────┠┌────────────────â”
|
||||
│ Stella Ops Scan│ │ Trivy/Grype │
|
||||
│ │ │ Scan │
|
||||
└───────┬────────┘ └───────┬────────┘
|
||||
│ │
|
||||
â–¼ â–¼
|
||||
┌────────────────┠┌────────────────â”
|
||||
│ Normalized │ │ Normalized │
|
||||
│ Findings │ │ Findings │
|
||||
└───────┬────────┘ └───────┬────────┘
|
||||
│ │
|
||||
└──────────┬───────────┘
|
||||
│
|
||||
â–¼
|
||||
┌──────────────â”
|
||||
│ Ground Truth │
|
||||
│ Comparison │
|
||||
└──────┬───────┘
|
||||
│
|
||||
â–¼
|
||||
┌──────────────â”
|
||||
│ Metrics │
|
||||
│ (P/R/F1) │
|
||||
└──────┬───────┘
|
||||
│
|
||||
â–¼
|
||||
┌──────────────â”
|
||||
│ Claims Index │
|
||||
│ Update │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
@@ -277,25 +277,25 @@ public record ClaimVerification(
|
||||
|
||||
```
|
||||
bench/competitors/
|
||||
├── corpus/
|
||||
│ ├── manifest.json # Corpus metadata
|
||||
│ ├── ground-truth/
|
||||
│ │ ├── alpine-3.18.json # Per-image ground truth
|
||||
│ │ ├── debian-bookworm.json
|
||||
│ │ └── ...
|
||||
│ └── images/
|
||||
│ ├── base-os/
|
||||
│ ├── applications/
|
||||
│ └── edge-cases/
|
||||
├── results/
|
||||
│ ├── 2025-12-22/
|
||||
│ │ ├── stellaops.json
|
||||
│ │ ├── trivy.json
|
||||
│ │ ├── grype.json
|
||||
│ │ └── comparison.json
|
||||
│ └── latest -> 2025-12-22/
|
||||
└── fixtures/
|
||||
└── adapters/ # Test fixtures for adapters
|
||||
├── corpus/
|
||||
│ ├── manifest.json # Corpus metadata
|
||||
│ ├── ground-truth/
|
||||
│ │ ├── alpine-3.18.json # Per-image ground truth
|
||||
│ │ ├── debian-bookworm.json
|
||||
│ │ └── ...
|
||||
│ └── images/
|
||||
│ ├── base-os/
|
||||
│ ├── applications/
|
||||
│ └── edge-cases/
|
||||
├── results/
|
||||
│ ├── 2025-12-22/
|
||||
│ │ ├── stellaops.json
|
||||
│ │ ├── trivy.json
|
||||
│ │ ├── grype.json
|
||||
│ │ └── comparison.json
|
||||
│ └── latest -> 2025-12-22/
|
||||
└── fixtures/
|
||||
└── adapters/ # Test fixtures for adapters
|
||||
```
|
||||
|
||||
---
|
||||
@@ -436,9 +436,10 @@ stella benchmark summary --format table|json|markdown
|
||||
|
||||
- [Claims Index](../../claims-index.md)
|
||||
- [Sprint 7000.0001.0001](../../implplan/SPRINT_7000_0001_0001_competitive_benchmarking.md)
|
||||
- [Testing Strategy](../../implplan/SPRINT_5100_SUMMARY.md)
|
||||
- [Testing Strategy](../../implplan/SPRINT_5100_0000_0000_epic_summary.md)
|
||||
|
||||
---
|
||||
|
||||
*Document Version*: 1.0.0
|
||||
*Created*: 2025-12-22
|
||||
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
# component_architecture_cli.md — **Stella Ops CLI** (2025Q4)
|
||||
|
||||
> Consolidates requirements captured in the Policy Engine, Policy Studio, Vulnerability Explorer, Export Center, and Notifications implementation plans and module guides.
|
||||
# component_architecture_cli.md — **Stella Ops CLI** (2025Q4)
|
||||
|
||||
> **Scope.** Implementation‑ready architecture for **Stella Ops CLI**: command surface, process model, auth (Authority/DPoP), integration with Scanner/Excititor/Concelier/Signer/Attestor, Buildx plug‑in management, offline kit behavior, packaging, observability, security posture, and CI ergonomics.
|
||||
> Consolidates requirements captured in the Policy Engine, Policy Studio, Vulnerability Explorer, Export Center, and Notifications implementation plans and module guides.
|
||||
|
||||
> **Scope.** Implementation‑ready architecture for **Stella Ops CLI**: command surface, process model, auth (Authority/DPoP), integration with Scanner/Excititor/Concelier/Signer/Attestor, Buildx plug‑in management, offline kit behavior, packaging, observability, security posture, and CI ergonomics.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Provide a **fast, deterministic, CI‑friendly** command‑line interface to drive Stella Ops workflows:
|
||||
**Mission.** Provide a **fast, deterministic, CI‑friendly** command‑line interface to drive Stella Ops workflows:
|
||||
|
||||
* Build‑time SBOM generation via **Buildx generator** orchestration.
|
||||
* Post‑build **scan/compose/diff/export** against **Scanner.WebService**.
|
||||
* Build‑time SBOM generation via **Buildx generator** orchestration.
|
||||
* Post‑build **scan/compose/diff/export** against **Scanner.WebService**.
|
||||
* **Policy** operations and **VEX/Vuln** data pulls (operator tasks).
|
||||
* **Verification** (attestation, referrers, signatures) for audits.
|
||||
* Air‑gapped/offline **kit** administration.
|
||||
* Air‑gapped/offline **kit** administration.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* CLI **never** signs; it only calls **Signer**/**Attestor** via backend APIs when needed (e.g., `report --attest`).
|
||||
* CLI **does not** store long‑lived credentials beyond OS keychain; tokens are **short** (Authority OpToks).
|
||||
* Heavy work (scanning, merging, policy) is executed **server‑side** (Scanner/Excititor/Concelier).
|
||||
* CLI **does not** store long‑lived credentials beyond OS keychain; tokens are **short** (Authority OpToks).
|
||||
* Heavy work (scanning, merging, policy) is executed **server‑side** (Scanner/Excititor/Concelier).
|
||||
|
||||
---
|
||||
|
||||
@@ -28,20 +28,20 @@
|
||||
|
||||
```
|
||||
src/
|
||||
├─ StellaOps.Cli/ # net10.0 (Native AOT) single binary
|
||||
├─ StellaOps.Cli.Core/ # verb plumbing, config, HTTP, auth
|
||||
├─ StellaOps.Cli.Plugins/ # optional verbs packaged as plugins
|
||||
├─ StellaOps.Cli.Tests/ # unit + golden-output tests
|
||||
└─ packaging/
|
||||
├─ msix / msi / deb / rpm / brew formula
|
||||
└─ scoop manifest / winget manifest
|
||||
├─ StellaOps.Cli/ # net10.0 (Native AOT) single binary
|
||||
├─ StellaOps.Cli.Core/ # verb plumbing, config, HTTP, auth
|
||||
├─ StellaOps.Cli.Plugins/ # optional verbs packaged as plugins
|
||||
├─ StellaOps.Cli.Tests/ # unit + golden-output tests
|
||||
└─ packaging/
|
||||
├─ msix / msi / deb / rpm / brew formula
|
||||
└─ scoop manifest / winget manifest
|
||||
```
|
||||
|
||||
**Language/runtime**: .NET 10 **Native AOT** for speed/startup; Linux builds use **musl** static when possible.
|
||||
|
||||
**Plug-in verbs.** Non-core verbs (Excititor, runtime helpers, future integrations) ship as restart-time plug-ins under `plugins/cli/**` with manifest descriptors. The launcher loads plug-ins on startup; hot reloading is intentionally unsupported. The inaugural bundle, `StellaOps.Cli.Plugins.NonCore`, packages the Excititor, runtime, and offline-kit command groups and publishes its manifest at `plugins/cli/StellaOps.Cli.Plugins.NonCore/`.
|
||||
|
||||
**OS targets**: linux‑x64/arm64, windows‑x64/arm64, macOS‑x64/arm64.
|
||||
**OS targets**: linux‑x64/arm64, windows‑x64/arm64, macOS‑x64/arm64.
|
||||
|
||||
---
|
||||
|
||||
@@ -53,19 +53,19 @@ src/
|
||||
|
||||
* `auth login`
|
||||
|
||||
* Modes: **device‑code** (default), **client‑credentials** (service principal).
|
||||
* Modes: **device‑code** (default), **client‑credentials** (service principal).
|
||||
* Produces **Authority** access token (OpTok) + stores **DPoP** keypair in OS keychain.
|
||||
* `auth status` — show current issuer, subject, audiences, expiry.
|
||||
* `auth logout` — wipe cached tokens/keys.
|
||||
* `auth status` — show current issuer, subject, audiences, expiry.
|
||||
* `auth logout` — wipe cached tokens/keys.
|
||||
|
||||
### 2.2 Build‑time SBOM (Buildx)
|
||||
### 2.2 Build‑time SBOM (Buildx)
|
||||
|
||||
* `buildx install` — install/update the **StellaOps.Scanner.Sbomer.BuildXPlugin** on the host.
|
||||
* `buildx verify` — ensure generator is usable.
|
||||
* `buildx build` — thin wrapper around `docker buildx build --attest=type=sbom,generator=stellaops/sbom-indexer` with convenience flags:
|
||||
* `buildx install` — install/update the **StellaOps.Scanner.Sbomer.BuildXPlugin** on the host.
|
||||
* `buildx verify` — ensure generator is usable.
|
||||
* `buildx build` — thin wrapper around `docker buildx build --attest=type=sbom,generator=stellaops/sbom-indexer` with convenience flags:
|
||||
|
||||
* `--attest` (request Signer/Attestor via backend post‑push)
|
||||
* `--provenance` pass‑through (optional)
|
||||
* `--attest` (request Signer/Attestor via backend post‑push)
|
||||
* `--provenance` pass‑through (optional)
|
||||
|
||||
### 2.3 Scanning & artifacts
|
||||
|
||||
@@ -73,119 +73,120 @@ src/
|
||||
|
||||
* Options: `--force`, `--wait`, `--view=inventory|usage|both`, `--format=cdx-json|cdx-pb|spdx-json`, `--attest` (ask backend to sign/log).
|
||||
* Streams progress; exits early unless `--wait`.
|
||||
* `diff image --old <digest> --new <digest> [--view ...]` — show layer‑attributed changes.
|
||||
* `export sbom <digest> [--view ... --format ... --out file]` — download artifact.
|
||||
* `report final <digest> [--policy-revision ... --attest]` — request PASS/FAIL report from backend (policy+vex) and optional attestation.
|
||||
* `diff image --old <digest> --new <digest> [--view ...]` — show layer‑attributed changes.
|
||||
* `export sbom <digest> [--view ... --format ... --out file]` — download artifact.
|
||||
* `sbom upload --file <path> --artifact <ref> [--format cyclonedx|spdx]` - BYOS upload into the scanner analysis pipeline (ledger join uses the SBOM digest).
|
||||
* `report final <digest> [--policy-revision ... --attest]` — request PASS/FAIL report from backend (policy+vex) and optional attestation.
|
||||
|
||||
### 2.4 Policy & data
|
||||
|
||||
* `policy get/set/apply` — fetch active policy, apply staged policy, compute digest.
|
||||
* `concelier export` — trigger/export canonical JSON or Trivy DB (admin).
|
||||
* `excititor export` — trigger/export consensus/raw claims (admin).
|
||||
* `policy get/set/apply` — fetch active policy, apply staged policy, compute digest.
|
||||
* `concelier export` — trigger/export canonical JSON or Trivy DB (admin).
|
||||
* `excititor export` — trigger/export consensus/raw claims (admin).
|
||||
|
||||
### 2.5 Verification
|
||||
|
||||
* `verify attestation --uuid <rekor-uuid> | --artifact <sha256> | --bundle <path>` — call **Attestor /verify** and print proof summary.
|
||||
* `verify referrers <digest>` — ask **Signer /verify/referrers** (is image Stella‑signed?).
|
||||
* `verify image-signature <ref|digest>` — standalone cosign verification (optional, local).
|
||||
* `verify attestation --uuid <rekor-uuid> | --artifact <sha256> | --bundle <path>` — call **Attestor /verify** and print proof summary.
|
||||
* `verify referrers <digest>` — ask **Signer /verify/referrers** (is image Stella‑signed?).
|
||||
* `verify image-signature <ref|digest>` — standalone cosign verification (optional, local).
|
||||
|
||||
### 2.6 Runtime (Zastava helper)
|
||||
|
||||
* `runtime policy test --image/-i <digest> [--file <path> --ns <name> --label key=value --json]` — ask backend `/policy/runtime` like the webhook would (accepts multiple `--image`, comma/space lists, or stdin pipelines).
|
||||
* `runtime policy test --image/-i <digest> [--file <path> --ns <name> --label key=value --json]` — ask backend `/policy/runtime` like the webhook would (accepts multiple `--image`, comma/space lists, or stdin pipelines).
|
||||
|
||||
### 2.7 Offline kit
|
||||
|
||||
* `offline kit pull` — fetch latest **Concelier JSON + Trivy DB + Excititor exports** as a tarball from a mirror.
|
||||
* `offline kit import <tar>` — upload the kit to on‑prem services (Concelier/Excititor).
|
||||
* `offline kit status` — list current seed versions.
|
||||
* `offline kit pull` — fetch latest **Concelier JSON + Trivy DB + Excititor exports** as a tarball from a mirror.
|
||||
* `offline kit import <tar>` — upload the kit to on‑prem services (Concelier/Excititor).
|
||||
* `offline kit status` — list current seed versions.
|
||||
|
||||
### 2.8 Utilities
|
||||
|
||||
* `config set/get` — endpoint & defaults.
|
||||
* `whoami` — short auth display.
|
||||
* `version` — CLI + protocol versions; release channel.
|
||||
* `config set/get` — endpoint & defaults.
|
||||
* `whoami` — short auth display.
|
||||
* `version` — CLI + protocol versions; release channel.
|
||||
|
||||
### 2.9 Aggregation-only guard helpers
|
||||
|
||||
* `sources ingest --dry-run --source <id> --input <path|uri> [--tenant ... --format table|json --output file]`
|
||||
|
||||
* Normalises documents (handles gzip/base64), posts them to the backend `aoc/ingest/dry-run` route, and exits non-zero when guard violations are detected.
|
||||
* Defaults to table output with ANSI colour; `--json`/`--output` produce deterministic JSON for CI pipelines.
|
||||
|
||||
* `aoc verify [--since <ISO8601|duration>] [--limit <count>] [--sources list] [--codes list] [--format table|json] [--export file] [--tenant id] [--no-color]`
|
||||
|
||||
* Replays guard checks against stored raw documents. Maps backend `ERR_AOC_00x` codes onto deterministic exit codes so CI can block regressions.
|
||||
* Supports pagination hints (`--limit`, `--since`), tenant scoping via `--tenant` or `STELLA_TENANT`, and JSON exports for evidence lockers.
|
||||
|
||||
### 2.10 Key management (file KMS support)
|
||||
|
||||
* `kms export --key-id <logicalId> --output <file> [--version <id>] [--force]`
|
||||
|
||||
* Decrypts the file-backed KMS store (passphrase supplied via `--passphrase`, `STELLAOPS_KMS_PASSPHRASE`, or interactive prompt) and writes a portable JSON bundle (`KmsKeyMaterial`) with key metadata and coordinates for offline escrow or replication.
|
||||
|
||||
* `kms import --key-id <logicalId> --input <file> [--version <override>]`
|
||||
|
||||
* Imports a previously exported bundle into the local KMS root (`kms/` by default), promotes the imported version to `Active`, and preserves existing versions by marking them `PendingRotation`. Prompts for the passphrase when not provided to keep automation password-safe.
|
||||
|
||||
Both subcommands honour offline-first expectations (no network access) and normalise relative roots via `--root` when operators mirror the credential store.
|
||||
|
||||
### 2.11 Advisory AI (RAG summaries)
|
||||
|
||||
* `advise run <summary|conflict|remediation> --advisory-key <id> [--artifact-id id] [--artifact-purl purl] [--policy-version v] [--profile profile] [--section name] [--force-refresh] [--timeout seconds]`
|
||||
|
||||
* Calls the Advisory AI service (`/v1/advisory-ai/pipeline/{task}` + `/outputs/{cacheKey}`) to materialise a deterministic plan, queue execution, and poll for the generated brief.
|
||||
* Renders plan metadata (cache key, prompt template, token budgets), guardrail results, provenance hashes/signatures, and citation list. Exit code is non-zero if guardrails block or the command times out.
|
||||
* Uses `STELLAOPS_ADVISORYAI_URL` when configured; otherwise it reuses the backend base address and adds `X-StellaOps-Scopes` (`advisory:run` + task scope) per request.
|
||||
* `--timeout 0` performs a single cache lookup (for CI flows that only want cached artefacts).
|
||||
|
||||
### 2.12 Decision evidence (new)
|
||||
|
||||
- `decision export`
|
||||
|
||||
* Parameters: `--cve`, `--product <purl or digest>`, `--scan-id <optional>`, `--output-dir`.
|
||||
* Pulls `decision.openvex.json`, `decision.dsse.json`, `rekor.txt`, and evidence metadata from Policy Engine and writes them into the `bench/findings/<CVE>/` layout defined in [docs/benchmarks/vex-evidence-playbook.md](../benchmarks/vex-evidence-playbook.md).
|
||||
* When `--sync` is set, uploads the bundle to Git (bench repo) with deterministic commit messages.
|
||||
|
||||
- `decision verify`
|
||||
|
||||
* Offline verifier that wraps `tools/verify.sh`/`verify.py` from the bench repo. Checks DSSE signature, optional Rekor inclusion, and recomputes digests for reachability/SBOM artifacts.
|
||||
* Supports `--from bench` (local path) and `--remote` (fetch via API). Exit codes align with `verify.sh` (0 success, 3 signature failure, 18 truncated evidence).
|
||||
|
||||
- `decision compare`
|
||||
|
||||
* Executes the benchmark harness against baseline scanners (Trivy/Syft/Grype/Snyk/Xray), capturing false-positive reduction, mean-time-to-decision, and reproducibility metrics into `results/summary.csv`.
|
||||
* Flags regressions when Stella Ops produces more false positives or slower MTTD than the configured target.
|
||||
|
||||
All verbs require scopes `policy.findings:read`, `signer.verify`, and (for Rekor lookups) `attestor.read`. They honour sealed-mode rules by falling back to offline verification only when Rekor/Signer endpoints are unreachable.
|
||||
|
||||
### 2.13 Air-gap guard
|
||||
|
||||
- CLI outbound HTTP flows (Authority auth, backend APIs, advisory downloads) route through `StellaOps.AirGap.Policy`. When sealed mode is active the CLI refuses commands that would require external egress and surfaces the shared `AIRGAP_EGRESS_BLOCKED` remediation guidance instead of attempting the request.
|
||||
|
||||
---
|
||||
|
||||
## 3) AuthN: Authority + DPoP
|
||||
### 2.9 Aggregation-only guard helpers
|
||||
|
||||
* `sources ingest --dry-run --source <id> --input <path|uri> [--tenant ... --format table|json --output file]`
|
||||
|
||||
* Normalises documents (handles gzip/base64), posts them to the backend `aoc/ingest/dry-run` route, and exits non-zero when guard violations are detected.
|
||||
* Defaults to table output with ANSI colour; `--json`/`--output` produce deterministic JSON for CI pipelines.
|
||||
|
||||
* `aoc verify [--since <ISO8601|duration>] [--limit <count>] [--sources list] [--codes list] [--format table|json] [--export file] [--tenant id] [--no-color]`
|
||||
|
||||
* Replays guard checks against stored raw documents. Maps backend `ERR_AOC_00x` codes onto deterministic exit codes so CI can block regressions.
|
||||
* Supports pagination hints (`--limit`, `--since`), tenant scoping via `--tenant` or `STELLA_TENANT`, and JSON exports for evidence lockers.
|
||||
|
||||
### 2.10 Key management (file KMS support)
|
||||
|
||||
* `kms export --key-id <logicalId> --output <file> [--version <id>] [--force]`
|
||||
|
||||
* Decrypts the file-backed KMS store (passphrase supplied via `--passphrase`, `STELLAOPS_KMS_PASSPHRASE`, or interactive prompt) and writes a portable JSON bundle (`KmsKeyMaterial`) with key metadata and coordinates for offline escrow or replication.
|
||||
|
||||
* `kms import --key-id <logicalId> --input <file> [--version <override>]`
|
||||
|
||||
* Imports a previously exported bundle into the local KMS root (`kms/` by default), promotes the imported version to `Active`, and preserves existing versions by marking them `PendingRotation`. Prompts for the passphrase when not provided to keep automation password-safe.
|
||||
|
||||
Both subcommands honour offline-first expectations (no network access) and normalise relative roots via `--root` when operators mirror the credential store.
|
||||
|
||||
### 2.11 Advisory AI (RAG summaries)
|
||||
|
||||
* `advise run <summary|conflict|remediation> --advisory-key <id> [--artifact-id id] [--artifact-purl purl] [--policy-version v] [--profile profile] [--section name] [--force-refresh] [--timeout seconds]`
|
||||
|
||||
* Calls the Advisory AI service (`/v1/advisory-ai/pipeline/{task}` + `/outputs/{cacheKey}`) to materialise a deterministic plan, queue execution, and poll for the generated brief.
|
||||
* Renders plan metadata (cache key, prompt template, token budgets), guardrail results, provenance hashes/signatures, and citation list. Exit code is non-zero if guardrails block or the command times out.
|
||||
* Uses `STELLAOPS_ADVISORYAI_URL` when configured; otherwise it reuses the backend base address and adds `X-StellaOps-Scopes` (`advisory:run` + task scope) per request.
|
||||
* `--timeout 0` performs a single cache lookup (for CI flows that only want cached artefacts).
|
||||
|
||||
### 2.12 Decision evidence (new)
|
||||
|
||||
- `decision export`
|
||||
|
||||
* Parameters: `--cve`, `--product <purl or digest>`, `--scan-id <optional>`, `--output-dir`.
|
||||
* Pulls `decision.openvex.json`, `decision.dsse.json`, `rekor.txt`, and evidence metadata from Policy Engine and writes them into the `bench/findings/<CVE>/` layout defined in [docs/benchmarks/vex-evidence-playbook.md](../benchmarks/vex-evidence-playbook.md).
|
||||
* When `--sync` is set, uploads the bundle to Git (bench repo) with deterministic commit messages.
|
||||
|
||||
- `decision verify`
|
||||
|
||||
* Offline verifier that wraps `tools/verify.sh`/`verify.py` from the bench repo. Checks DSSE signature, optional Rekor inclusion, and recomputes digests for reachability/SBOM artifacts.
|
||||
* Supports `--from bench` (local path) and `--remote` (fetch via API). Exit codes align with `verify.sh` (0 success, 3 signature failure, 18 truncated evidence).
|
||||
|
||||
- `decision compare`
|
||||
|
||||
* Executes the benchmark harness against baseline scanners (Trivy/Syft/Grype/Snyk/Xray), capturing false-positive reduction, mean-time-to-decision, and reproducibility metrics into `results/summary.csv`.
|
||||
* Flags regressions when Stella Ops produces more false positives or slower MTTD than the configured target.
|
||||
|
||||
All verbs require scopes `policy.findings:read`, `signer.verify`, and (for Rekor lookups) `attestor.read`. They honour sealed-mode rules by falling back to offline verification only when Rekor/Signer endpoints are unreachable.
|
||||
|
||||
### 2.13 Air-gap guard
|
||||
|
||||
- CLI outbound HTTP flows (Authority auth, backend APIs, advisory downloads) route through `StellaOps.AirGap.Policy`. When sealed mode is active the CLI refuses commands that would require external egress and surfaces the shared `AIRGAP_EGRESS_BLOCKED` remediation guidance instead of attempting the request.
|
||||
|
||||
---
|
||||
|
||||
## 3) AuthN: Authority + DPoP
|
||||
|
||||
### 3.1 Token acquisition
|
||||
|
||||
* **Device‑code**: the CLI opens an OIDC device code flow against **Authority**; the browser login is optional for service principals.
|
||||
* **Client‑credentials**: service principals use **private_key_jwt** or **mTLS** to get tokens.
|
||||
* **Device‑code**: the CLI opens an OIDC device code flow against **Authority**; the browser login is optional for service principals.
|
||||
* **Client‑credentials**: service principals use **private_key_jwt** or **mTLS** to get tokens.
|
||||
|
||||
### 3.2 DPoP key management
|
||||
|
||||
* On first login, the CLI generates an **ephemeral JWK** (Ed25519) and stores it in the **OS keychain** (Keychain/DPAPI/KWallet/Gnome Keyring).
|
||||
* Every request to backend services includes a **DPoP proof**; CLI refreshes tokens as needed.
|
||||
|
||||
### 3.3 Multi‑audience & scopes
|
||||
### 3.3 Multi‑audience & scopes
|
||||
|
||||
* CLI requests **audiences** as needed per verb:
|
||||
|
||||
* `scanner` for scan/export/report/diff
|
||||
* `signer` (indirect; usually backend calls Signer)
|
||||
* `attestor` for verify (requires `attestor.verify` scope; read-only verbs fall back to `attestor.read`)
|
||||
* `concelier`/`excititor` for admin verbs
|
||||
|
||||
CLI rejects verbs if required scopes are missing.
|
||||
* CLI requests **audiences** as needed per verb:
|
||||
|
||||
* `scanner` for scan/export/report/diff
|
||||
* `signer` (indirect; usually backend calls Signer)
|
||||
* `attestor` for verify (requires `attestor.verify` scope; read-only verbs fall back to `attestor.read`)
|
||||
* `concelier`/`excititor` for admin verbs
|
||||
|
||||
CLI rejects verbs if required scopes are missing.
|
||||
|
||||
---
|
||||
|
||||
@@ -198,10 +199,10 @@ CLI rejects verbs if required scopes are missing.
|
||||
|
||||
### 4.2 Streaming
|
||||
|
||||
* `scan` and `report` support **server‑sent JSON lines** (progress events).
|
||||
* `scan` and `report` support **server‑sent JSON lines** (progress events).
|
||||
* `--json` prints machine events; human mode shows compact spinners and crucial updates only.
|
||||
|
||||
### 4.3 Exit codes (CI‑safe)
|
||||
### 4.3 Exit codes (CI‑safe)
|
||||
|
||||
| Code | Meaning |
|
||||
| ---- | ------------------------------------------- |
|
||||
@@ -213,7 +214,7 @@ CLI rejects verbs if required scopes are missing.
|
||||
| 6 | Rate limited / quota exceeded |
|
||||
| 7 | Backend unavailable (retryable) |
|
||||
| 9 | Invalid arguments |
|
||||
| 11–17 | Aggregation-only guard violation (`ERR_AOC_00x`) |
|
||||
| 11–17 | Aggregation-only guard violation (`ERR_AOC_00x`) |
|
||||
| 18 | Verification truncated (increase `--limit`) |
|
||||
| 70 | Transport/authentication failure |
|
||||
| 71 | CLI usage error (missing tenant, invalid cursor) |
|
||||
@@ -222,7 +223,7 @@ CLI rejects verbs if required scopes are missing.
|
||||
|
||||
## 5) Configuration model
|
||||
|
||||
**Precedence:** CLI flags → env vars → config file → defaults.
|
||||
**Precedence:** CLI flags → env vars → config file → defaults.
|
||||
|
||||
**Config file**: `${XDG_CONFIG_HOME}/stellaops/config.yaml` (Windows: `%APPDATA%\StellaOps\config.yaml`)
|
||||
|
||||
@@ -257,9 +258,9 @@ Environment variables: `STELLAOPS_AUTHORITY`, `STELLAOPS_SCANNER_URL`, etc.
|
||||
|
||||
* `--attest=type=sbom,generator=stellaops/sbom-indexer`
|
||||
* `--label org.stellaops.request=sbom`
|
||||
* Post‑build: CLI optionally calls **Scanner.WebService** to **verify referrers**, **compose** image SBOMs, and **attest** via Signer/Attestor.
|
||||
* Post‑build: CLI optionally calls **Scanner.WebService** to **verify referrers**, **compose** image SBOMs, and **attest** via Signer/Attestor.
|
||||
|
||||
**Detection**: If Buildx or generator unavailable, CLI falls back to **post‑build scan** with a warning.
|
||||
**Detection**: If Buildx or generator unavailable, CLI falls back to **post‑build scan** with a warning.
|
||||
|
||||
---
|
||||
|
||||
@@ -273,27 +274,27 @@ Environment variables: `STELLAOPS_AUTHORITY`, `STELLAOPS_SCANNER_URL`, etc.
|
||||
## 8) Security posture
|
||||
|
||||
* **DPoP private keys** stored in **OS keychain**; metadata cached in config.
|
||||
* **No plaintext tokens** on disk; short‑lived **OpToks** held in memory.
|
||||
* **TLS**: verify backend certificates; allow custom CA bundle for on‑prem.
|
||||
* **No plaintext tokens** on disk; short‑lived **OpToks** held in memory.
|
||||
* **TLS**: verify backend certificates; allow custom CA bundle for on‑prem.
|
||||
* **Redaction**: CLI logs remove `Authorization`, DPoP headers, PoE tokens.
|
||||
* **Supply chain**: CLI distribution binaries are **cosign‑signed**; `stellaops version --verify` checks its own signature.
|
||||
* **Supply chain**: CLI distribution binaries are **cosign‑signed**; `stellaops version --verify` checks its own signature.
|
||||
|
||||
---
|
||||
|
||||
## 9) Observability
|
||||
|
||||
* `--verbose` adds request IDs, timings, and retry traces.
|
||||
* **Metrics** (optional, disabled by default): Prometheus text file exporter for local monitoring in long‑running agents.
|
||||
* **Structured logs** (`--json`): per‑event JSON lines with `ts`, `verb`, `status`, `latencyMs`.
|
||||
* **Metrics** (optional, disabled by default): Prometheus text file exporter for local monitoring in long‑running agents.
|
||||
* **Structured logs** (`--json`): per‑event JSON lines with `ts`, `verb`, `status`, `latencyMs`.
|
||||
|
||||
---
|
||||
|
||||
## 10) Performance targets
|
||||
|
||||
* Startup ≤ **20 ms** (AOT).
|
||||
* `scan image` request/response overhead ≤ **5 ms** (excluding server work).
|
||||
* Buildx wrapper overhead negligible (<1 ms).
|
||||
* Large artifact download (100 MB) sustained ≥ **80 MB/s** on local networks.
|
||||
* Startup ≤ **20 ms** (AOT).
|
||||
* `scan image` request/response overhead ≤ **5 ms** (excluding server work).
|
||||
* Buildx wrapper overhead negligible (<1 ms).
|
||||
* Large artifact download (100 MB) sustained ≥ **80 MB/s** on local networks.
|
||||
|
||||
---
|
||||
|
||||
@@ -301,7 +302,7 @@ Environment variables: `STELLAOPS_AUTHORITY`, `STELLAOPS_SCANNER_URL`, etc.
|
||||
|
||||
* **Unit tests**: argument parsing, config precedence, URL resolution, DPoP proof creation.
|
||||
* **Integration tests** (Testcontainers): mock Authority/Scanner/Attestor; CI pipeline with fake registry.
|
||||
* **Golden outputs**: verb snapshots for `--json` across OSes; kept in `tests/golden/…`.
|
||||
* **Golden outputs**: verb snapshots for `--json` across OSes; kept in `tests/golden/…`.
|
||||
* **Contract tests**: ensure API shapes match service OpenAPI; fail build if incompatible.
|
||||
|
||||
---
|
||||
@@ -311,9 +312,9 @@ Environment variables: `STELLAOPS_AUTHORITY`, `STELLAOPS_SCANNER_URL`, etc.
|
||||
**Human:**
|
||||
|
||||
```
|
||||
✖ Policy FAIL: 3 high, 1 critical (VEX suppressed 12)
|
||||
- pkg:rpm/openssl (CVE-2025-12345) — affected (vendor) — fixed in 3.0.14
|
||||
- pkg:npm/lodash (GHSA-xxxx) — affected — no fix
|
||||
✖ Policy FAIL: 3 high, 1 critical (VEX suppressed 12)
|
||||
- pkg:rpm/openssl (CVE-2025-12345) — affected (vendor) — fixed in 3.0.14
|
||||
- pkg:npm/lodash (GHSA-xxxx) — affected — no fix
|
||||
See: https://ui.internal/scans/sha256:...
|
||||
Exit code: 2
|
||||
```
|
||||
@@ -340,7 +341,7 @@ Exit code: 2
|
||||
|
||||
* Emits **CycloneDX Protobuf** directly to stdout when `export sbom --format cdx-pb --out -`.
|
||||
* Pipes to `jq`/`yq` cleanly in JSON mode.
|
||||
* Can act as a **credential helper** for scripts: `stellaops auth token --aud scanner` prints a one‑shot token for curl.
|
||||
* Can act as a **credential helper** for scripts: `stellaops auth token --aud scanner` prints a one‑shot token for curl.
|
||||
|
||||
---
|
||||
|
||||
@@ -348,16 +349,16 @@ Exit code: 2
|
||||
|
||||
* **Installers**: deb/rpm (postinst registers completions), Homebrew, Scoop, Winget, MSI/MSIX.
|
||||
* **Shell completions**: bash/zsh/fish/pwsh.
|
||||
* **Update channel**: `stellaops self-update` (optional) fetches cosign‑signed release manifest; corporate environments can disable.
|
||||
* **Update channel**: `stellaops self-update` (optional) fetches cosign‑signed release manifest; corporate environments can disable.
|
||||
|
||||
---
|
||||
|
||||
## 16) Security hard lines
|
||||
|
||||
* Refuse to print token values; redact Authorization headers in verbose output.
|
||||
* Disallow `--insecure` unless `STELLAOPS_CLI_ALLOW_INSECURE=1` set (double opt‑in).
|
||||
* Enforce **short token TTL**; refresh proactively when <30 s left.
|
||||
* Device‑code cache binding to **machine** and **user** (protect against copy to other machines).
|
||||
* Disallow `--insecure` unless `STELLAOPS_CLI_ALLOW_INSECURE=1` set (double opt‑in).
|
||||
* Enforce **short token TTL**; refresh proactively when <30 s left.
|
||||
* Device‑code cache binding to **machine** and **user** (protect against copy to other machines).
|
||||
|
||||
---
|
||||
|
||||
@@ -409,16 +410,16 @@ sequenceDiagram
|
||||
|
||||
## 18) Roadmap (CLI)
|
||||
|
||||
* `scan fs <path>` (local filesystem tree) → upload to backend for analysis.
|
||||
* `scan fs <path>` (local filesystem tree) → upload to backend for analysis.
|
||||
* `policy test --sbom <file>` (simulate policy results offline using local policy bundle).
|
||||
* `runtime capture` (developer mode) — capture small `/proc/<pid>/maps` for troubleshooting.
|
||||
* Pluggable output renderers for SARIF/HTML (admin‑controlled).
|
||||
* `runtime capture` (developer mode) — capture small `/proc/<pid>/maps` for troubleshooting.
|
||||
* Pluggable output renderers for SARIF/HTML (admin‑controlled).
|
||||
|
||||
---
|
||||
|
||||
## 19) Example CI snippets
|
||||
|
||||
**GitHub Actions (post‑build)**
|
||||
**GitHub Actions (post‑build)**
|
||||
|
||||
```yaml
|
||||
- name: Login (device code w/ OIDC broker)
|
||||
@@ -447,7 +448,7 @@ script:
|
||||
|
||||
## 20) Test matrix (OS/arch)
|
||||
|
||||
* Linux: ubuntu‑20.04/22.04/24.04 (x64, arm64), alpine (musl).
|
||||
* macOS: 13–15 (x64, arm64).
|
||||
* Linux: ubuntu‑20.04/22.04/24.04 (x64, arm64), alpine (musl).
|
||||
* macOS: 13–15 (x64, arm64).
|
||||
* Windows: 10/11, Server 2019/2022 (x64, arm64).
|
||||
* Docker engines: Docker Desktop, containerd‑based runners.
|
||||
* Docker engines: Docker Desktop, containerd‑based runners.
|
||||
|
||||
7
docs/modules/concelier/connectors.md
Normal file
7
docs/modules/concelier/connectors.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Concelier Connectors
|
||||
|
||||
This index lists Concelier connectors and links to their operational runbooks. For detailed procedures and alerting, see `docs/modules/concelier/operations/connectors/`.
|
||||
|
||||
| Connector | Source ID | Purpose | Ops Runbook |
|
||||
| --- | --- | --- | --- |
|
||||
| EPSS | `epss` | FIRST.org EPSS exploitation probability feed | `docs/modules/concelier/operations/connectors/epss.md` |
|
||||
53
docs/modules/concelier/operations/connectors/alpine.md
Normal file
53
docs/modules/concelier/operations/connectors/alpine.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Concelier Alpine SecDB Connector - Operations Runbook
|
||||
|
||||
_Last updated: 2025-12-22_
|
||||
|
||||
## 1. Overview
|
||||
The Alpine connector pulls JSON secdb feeds (main/community) for configured
|
||||
releases and maps CVE identifiers to APK version ranges. It preserves native
|
||||
APK versions and emits `rangeKind: apk` so downstream consumers keep distro
|
||||
semantics intact.
|
||||
|
||||
## 2. Configuration knobs (`concelier.yaml`)
|
||||
```yaml
|
||||
concelier:
|
||||
sources:
|
||||
alpine:
|
||||
baseUri: "https://secdb.alpinelinux.org/"
|
||||
releases:
|
||||
- "v3.18"
|
||||
- "v3.19"
|
||||
- "v3.20"
|
||||
repositories:
|
||||
- "main"
|
||||
- "community"
|
||||
maxDocumentsPerFetch: 20
|
||||
fetchTimeout: "00:00:45"
|
||||
requestDelay: "00:00:00"
|
||||
userAgent: "StellaOps.Concelier.Alpine/0.1 (+https://stella-ops.org)"
|
||||
```
|
||||
|
||||
### Recommendations
|
||||
- Keep `releases` to supported Alpine branches only; avoid stale branches in
|
||||
production unless you maintain a mirror.
|
||||
- Use `requestDelay` when running multiple source connectors on shared egress.
|
||||
|
||||
## 3. Default job schedule
|
||||
|
||||
| Job kind | Cron | Timeout | Lease |
|
||||
|----------|------|---------|-------|
|
||||
| `source:alpine:fetch` | `*/30 * * * *` | 5 minutes | 4 minutes |
|
||||
| `source:alpine:parse` | `7,37 * * * *` | 6 minutes | 4 minutes |
|
||||
| `source:alpine:map` | `12,42 * * * *` | 8 minutes | 4 minutes |
|
||||
|
||||
The cadence staggers fetch, parse, and map so each stage has a clean window to
|
||||
complete. Override via `concelier.jobs.definitions[...]` when coordinating
|
||||
multiple sources on the same scheduler.
|
||||
|
||||
## 4. Offline and air-gapped deployments
|
||||
- Mirror `secdb` JSON files into a local repository and point `baseUri` to the
|
||||
mirror host.
|
||||
- The connector allowlists only the `baseUri` host; update it to match the
|
||||
internal mirror host.
|
||||
- Keep fixtures and exported bundles deterministic by leaving the order of
|
||||
releases and repositories stable.
|
||||
49
docs/modules/concelier/operations/connectors/epss.md
Normal file
49
docs/modules/concelier/operations/connectors/epss.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Concelier EPSS Connector Operations
|
||||
|
||||
This playbook covers deployment and monitoring of the EPSS connector that ingests daily FIRST.org EPSS snapshots.
|
||||
|
||||
## 1. Prerequisites
|
||||
|
||||
- Network egress to `https://epss.empiricalsecurity.com/` (or a mirrored endpoint).
|
||||
- Updated `concelier.yaml` (or environment variables) with the EPSS source configuration:
|
||||
|
||||
```yaml
|
||||
concelier:
|
||||
sources:
|
||||
epss:
|
||||
baseUri: "https://epss.empiricalsecurity.com/"
|
||||
fetchCurrent: true
|
||||
catchUpDays: 7
|
||||
httpTimeout: "00:02:00"
|
||||
maxRetries: 3
|
||||
airgapMode: false
|
||||
bundlePath: "/var/stellaops/bundles/epss"
|
||||
```
|
||||
|
||||
## 2. Smoke Test (staging)
|
||||
|
||||
1. Restart Concelier workers after configuration changes.
|
||||
2. Trigger a full cycle:
|
||||
- CLI: `stella db jobs run source:epss:fetch --and-then source:epss:parse --and-then source:epss:map`
|
||||
- REST: `POST /jobs/run { "kind": "source:epss:fetch", "chain": ["source:epss:parse", "source:epss:map"] }`
|
||||
3. Verify document status transitions: `pending_parse` -> `pending_map` -> `mapped`.
|
||||
4. Confirm log entries for `Fetched EPSS snapshot` and parse/map summaries.
|
||||
|
||||
## 3. Monitoring
|
||||
|
||||
- **Meter**: `StellaOps.Concelier.Connector.Epss`
|
||||
- **Key counters**:
|
||||
- `epss.fetch.attempts`, `epss.fetch.success`, `epss.fetch.failures`, `epss.fetch.unchanged`
|
||||
- `epss.parse.rows`, `epss.parse.failures`
|
||||
- `epss.map.rows`
|
||||
- **Alert suggestions**:
|
||||
- `rate(epss_fetch_failures_total[15m]) > 0`
|
||||
- `rate(epss_map_rows_total[1h]) == 0` during business hours while other connectors are active
|
||||
|
||||
## 4. Airgap Mode
|
||||
|
||||
- Place snapshots in the bundle directory:
|
||||
- `epss_scores-YYYY-MM-DD.csv.gz`
|
||||
- Optional `manifest.json` listing `name`, `modelVersion`, `sha256`, and `rowCount`.
|
||||
- Set `airgapMode: true` and `bundlePath` to the directory or specific file.
|
||||
- The connector validates the manifest hash when present and logs warnings on mismatch.
|
||||
@@ -1,22 +1,23 @@
|
||||
# Graph architecture
|
||||
# Graph architecture
|
||||
|
||||
> Derived from Epic 5 – SBOM Graph Explorer; this section captures the core model, pipeline, and API expectations. Extend with diagrams as implementation matures.
|
||||
> Derived from Epic 5 – SBOM Graph Explorer; this section captures the core model, pipeline, and API expectations. Extend with diagrams as implementation matures.
|
||||
|
||||
## 1) Core model
|
||||
|
||||
- **Nodes:**
|
||||
- `Artifact` (application/image digest) with metadata (tenant, environment, labels).
|
||||
- `SBOM` (sbom digest, format, version/sequence, chain id).
|
||||
- `Component` (package/version, purl, ecosystem).
|
||||
- `File`/`Path` (source files, binary paths) with hash/time metadata.
|
||||
- `License` nodes linked to components and SBOM attestations.
|
||||
- `Advisory` and `VEXStatement` nodes linking to Concelier/Excititor records via digests.
|
||||
- `PolicyVersion` nodes representing signed policy packs.
|
||||
- **Edges:** directed, timestamped relationships such as `DEPENDS_ON`, `BUILT_FROM`, `DECLARED_IN`, `AFFECTED_BY`, `VEX_EXEMPTS`, `GOVERNS_WITH`, `OBSERVED_RUNTIME`. Each edge carries provenance (SRM hash, SBOM digest, policy run ID).
|
||||
- **Edges:** directed, timestamped relationships such as `DEPENDS_ON`, `BUILT_FROM`, `DECLARED_IN`, `AFFECTED_BY`, `VEX_EXEMPTS`, `GOVERNS_WITH`, `OBSERVED_RUNTIME`, `SBOM_VERSION_OF`, and `SBOM_LINEAGE_*`. Each edge carries provenance (SRM hash, SBOM digest, policy run ID).
|
||||
- **Overlays:** computed index tables providing fast access to reachability, blast radius, and differential views (e.g., `graph_overlay/vuln/{tenant}/{advisoryKey}`). Runtime endpoints emit overlays inline (`policy.overlay.v1`, `openvex.v1`) with deterministic overlay IDs (`sha256(tenant|nodeId|overlayKind)`) and sampled explain traces on policy overlays.
|
||||
|
||||
## 2) Pipelines
|
||||
|
||||
1. **Ingestion:** Cartographer/SBOM Service emit SBOM snapshots (`sbom_snapshot` events) captured by the Graph Indexer. Advisories/VEX from Concelier/Excititor generate edge updates, policy runs attach overlay metadata.
|
||||
1. **Ingestion:** Cartographer/SBOM Service emit SBOM snapshots (`sbom_snapshot` events) captured by the Graph Indexer. Ledger lineage references become `SBOM_VERSION_OF` + `SBOM_LINEAGE_*` edges. Advisories/VEX from Concelier/Excititor generate edge updates, policy runs attach overlay metadata.
|
||||
2. **ETL:** Normalises nodes/edges into canonical IDs, deduplicates, enforces tenant partitions, and writes to the graph store (planned: Neo4j-compatible or document + adjacency lists in Mongo).
|
||||
3. **Overlay computation:** Batch workers build materialised views for frequently used queries (impact lists, saved queries, policy overlays) and store as immutable blobs for Offline Kit exports.
|
||||
4. **Diffing:** `graph_diff` jobs compare two snapshots (e.g., pre/post deploy) and generate signed diff manifests for UI/CLI consumption.
|
||||
@@ -24,11 +25,12 @@
|
||||
|
||||
## 3) APIs
|
||||
|
||||
- `POST /graph/search` — NDJSON node tiles with cursor paging, tenant + scope guards.
|
||||
- `POST /graph/query` — NDJSON nodes/edges/stats/cursor with budgets (tiles/nodes/edges) and optional inline overlays (`includeOverlays=true`) emitting `policy.overlay.v1` and `openvex.v1` payloads; overlay IDs are `sha256(tenant|nodeId|overlayKind)`; policy overlay may include a sampled `explainTrace`.
|
||||
- `POST /graph/paths` — bounded BFS (depth ≤6) returning path nodes/edges/stats; honours budgets and overlays.
|
||||
- `POST /graph/diff` — compares `snapshotA` vs `snapshotB`, streaming node/edge added/removed/changed tiles plus stats; budget enforcement mirrors `/graph/query`.
|
||||
- `POST /graph/export` — async job producing deterministic manifests (`sha256`, size, format) for `ndjson/csv/graphml/png/svg`; download via `/graph/export/{jobId}`.
|
||||
- `POST /graph/search` — NDJSON node tiles with cursor paging, tenant + scope guards.
|
||||
- `POST /graph/query` — NDJSON nodes/edges/stats/cursor with budgets (tiles/nodes/edges) and optional inline overlays (`includeOverlays=true`) emitting `policy.overlay.v1` and `openvex.v1` payloads; overlay IDs are `sha256(tenant|nodeId|overlayKind)`; policy overlay may include a sampled `explainTrace`.
|
||||
- `POST /graph/paths` — bounded BFS (depth ≤6) returning path nodes/edges/stats; honours budgets and overlays.
|
||||
- `POST /graph/diff` — compares `snapshotA` vs `snapshotB`, streaming node/edge added/removed/changed tiles plus stats; budget enforcement mirrors `/graph/query`.
|
||||
- `POST /graph/export` — async job producing deterministic manifests (`sha256`, size, format) for `ndjson/csv/graphml/png/svg`; download via `/graph/export/{jobId}`.
|
||||
- `POST /graph/lineage` - returns SBOM lineage nodes/edges anchored by `artifactDigest` or `sbomDigest`, with optional relationship filters and depth limits.
|
||||
- Legacy: `GET /graph/nodes/{id}`, `POST /graph/query/saved`, `GET /graph/impact/{advisoryKey}`, `POST /graph/overlay/policy` remain in spec but should align to the NDJSON surfaces above as they are brought forward.
|
||||
|
||||
## 4) Storage considerations
|
||||
|
||||
@@ -111,7 +111,7 @@ Key notes:
|
||||
| **Authority Client** (`Authority/`) | Acquire tokens, enforce scopes, perform DPoP key rotation. | Only service identity uses `effective:write`. |
|
||||
| **DSL Compiler** (`Dsl/`) | Parse, canonicalise, IR generation, checksum caching. | Uses Roslyn-like pipeline; caches by `policyId+version+hash`. |
|
||||
| **Selection Layer** (`Selection/`) | Batch SBOM ↔ advisory ↔ VEX joiners; apply equivalence tables; support incremental cursors. | Deterministic ordering (SBOM → advisory → VEX). |
|
||||
| **Evaluator** (`Evaluation/`) | Execute IR with first-match semantics, compute severity/trust/reachability weights, record rule hits. | Stateless; all inputs provided by selection layer. |
|
||||
| **Evaluator** (`Evaluation/`) | Execute IR with first-match semantics, compute severity/trust/reachability weights, record rule hits, and emit a unified confidence score with factor breakdown (reachability/runtime/VEX/provenance/policy). | Stateless; all inputs provided by selection layer. |
|
||||
| **Signals** (`Signals/`) | Normalizes reachability, trust, entropy, uncertainty, runtime hits into a single dictionary passed to Evaluator; supplies default `unknown` values when signals missing. Entropy penalties are derived from Scanner `layer_summary.json`/`entropy.report.json` (K=0.5, cap=0.3, block at image opaque ratio > 0.15 w/ unknown provenance) and exported via `policy_entropy_penalty_value` / `policy_entropy_image_opaque_ratio`; SPL scope `entropy.*` exposes `penalty`, `image_opaque_ratio`, `blocked`, `warned`, `capped`, `top_file_opaque_ratio`. | Aligns with `signals.*` namespace in DSL. |
|
||||
| **Materialiser** (`Materialization/`) | Upsert effective findings, append history, manage explain bundle exports. | PostgreSQL transactions per SBOM chunk. |
|
||||
| **Orchestrator** (`Runs/`) | Change-stream ingestion, fairness, retry/backoff, queue writer. | Works with Scheduler Models DTOs. |
|
||||
|
||||
@@ -173,6 +173,11 @@ validationSchema: "https://stellaops.io/schemas/evidence/feature-flag/v1"
|
||||
| `Expired` | Evidence older than maxAge |
|
||||
| `InsufficientTrust` | Source trust score too low |
|
||||
|
||||
## Persistence
|
||||
|
||||
- Hook registry: `policy.evidence_hooks` with `max_age_seconds` and `min_trust_score`.
|
||||
- Evidence submissions: `policy.submitted_evidence` with `validation_status`, `reference`, and optional `dsse_envelope`.
|
||||
|
||||
## Submission Flow
|
||||
|
||||
```
|
||||
|
||||
@@ -180,6 +180,12 @@ Conditions can be scoped to specific environments:
|
||||
6. Update exception with recheck result
|
||||
```
|
||||
|
||||
## Persistence
|
||||
|
||||
- Recheck policy definitions are stored in `policy.recheck_policies` with `conditions` as JSONB.
|
||||
- Exceptions reference a policy through `policy.exceptions.recheck_policy_id`.
|
||||
- The latest evaluation snapshot is stored in `policy.exceptions.last_recheck_result` and `policy.exceptions.last_recheck_at`.
|
||||
|
||||
## Build Gate Integration
|
||||
|
||||
Recheck policies integrate with build gates:
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
- Does not perform scanning; consumes Scanner outputs or supplied SPDX/CycloneDX blobs.
|
||||
- Does not author verdicts/policy; supplies evidence and projections to Policy/Concelier/Graph.
|
||||
- Append-only SBOM versions; mutations happen via new versions, never in-place edits.
|
||||
- Owns the SBOM lineage ledger for versioned uploads, diffs, and retention pruning.
|
||||
|
||||
## 2) Project layout
|
||||
- `src/SbomService/StellaOps.SbomService` — REST API + event emitters + orchestrator integration.
|
||||
@@ -25,7 +26,7 @@
|
||||
The service now owns an idempotent spine that converts OCI images into SBOMs and provenance bundles with DSSE and in-toto. The flow is intentionally air-gap ready:
|
||||
|
||||
- **Extract** OCI manifest/layers (hash becomes `contentAddress`).
|
||||
- **Build SBOM** in CycloneDX 1.6 and/or SPDX 3.0.1; canonicalize JSON before hashing (`sbomHash`).
|
||||
- **Build SBOM** in CycloneDX 1.7 and/or SPDX 3.0.1; canonicalize JSON before hashing (`sbomHash`).
|
||||
- **Sign** outputs as DSSE envelopes; predicate uses in-toto Statement with SLSA Provenance v1.
|
||||
- **Publish** attestations optionally to a transparency backend: `rekor`, `local-merkle`, or `null` (no-op). Local Merkle log keeps proofs for later sync when online.
|
||||
|
||||
@@ -42,23 +43,41 @@ Operational rules:
|
||||
|
||||
## 3) APIs (first wave)
|
||||
- `GET /sbom/paths?purl=...&artifact=...&scope=...&env=...` — returns ordered paths with runtime_flag/blast_radius and nearest-safe-version hint; supports `cursor` pagination.
|
||||
- `GET /sbom/versions?artifact=...` — time-ordered SBOM version timeline for Advisory AI; include provenance and source bundle hash.
|
||||
- `GET /console/sboms` — Console catalog with filters (artifact, license, scope, asset tags), cursor pagination, evaluation metadata, immutable JSON projection for drawer views.
|
||||
- `GET /components/lookup?purl=...` — component neighborhood for global search/Graph overlays; returns caches hints + tenant enforcement.
|
||||
- `POST /entrypoints` / `GET /entrypoints` — manage entrypoint/service node overrides feeding Cartographer relevance; deterministic defaults when unset.
|
||||
- `GET /sboms/{snapshotId}/projection` — Link-Not-Merge v1 projection returning hashes plus asset metadata (criticality, owner, environment, exposure flags, tags) alongside package/component graph.
|
||||
- `GET /sbom/versions?artifact=...` – time-ordered SBOM version timeline for Advisory AI; include provenance and source bundle hash.
|
||||
- `POST /sbom/upload` – BYOS upload endpoint; validates/normalizes SPDX 2.3/3.0 or CycloneDX 1.4–1.6 and registers a ledger version.
|
||||
- `GET /sbom/ledger/history` – list version history for an artifact (cursor pagination).
|
||||
- `GET /sbom/ledger/point` – resolve the SBOM version at a specific timestamp.
|
||||
- `GET /sbom/ledger/range` – query versions within a time range.
|
||||
- `GET /sbom/ledger/diff` – component/version/license diff between two versions.
|
||||
- `GET /sbom/ledger/lineage` – parent/child lineage edges for an artifact chain.
|
||||
- `GET /console/sboms` – Console catalog with filters (artifact, license, scope, asset tags), cursor pagination, evaluation metadata, immutable JSON projection for drawer views.
|
||||
- `GET /components/lookup?purl=...` – component neighborhood for global search/Graph overlays; returns caches hints + tenant enforcement.
|
||||
- `POST /entrypoints` / `GET /entrypoints` – manage entrypoint/service node overrides feeding Cartographer relevance; deterministic defaults when unset.
|
||||
- `GET /sboms/{snapshotId}/projection` – Link-Not-Merge v1 projection returning hashes plus asset metadata (criticality, owner, environment, exposure flags, tags) alongside package/component graph.
|
||||
- `GET /internal/sbom/events` — internal diagnostics endpoint returning the in-memory event outbox for validation.
|
||||
- `POST /internal/sbom/events/backfill` — replays existing projections into the event stream; deterministic ordering, clock abstraction for tests.
|
||||
- `GET /internal/sbom/asset-events` — diagnostics endpoint returning emitted `sbom.asset.updated` envelopes for validation and air-gap parity checks.
|
||||
- `GET/POST /internal/orchestrator/sources` — list/register orchestrator ingest/index sources (deterministic seeds; idempotent on artifactDigest+sourceType).
|
||||
- `GET/POST /internal/orchestrator/control` — manage pause/throttle/backpressure signals per tenant; metrics emitted for control updates.
|
||||
- `GET/POST /internal/orchestrator/watermarks` — fetch/set backfill watermarks for reconciliation and deterministic replays.
|
||||
- `GET /internal/sbom/resolver-feed` — list resolver candidates (artifact, purl, version, paths, scope, runtime_flag, nearest_safe_version).
|
||||
- `POST /internal/sbom/resolver-feed/backfill` — clear and repopulate resolver feed from current projections.
|
||||
- `GET /internal/sbom/resolver-feed/export` — NDJSON export of resolver candidates for air-gap delivery.
|
||||
- `GET /internal/sbom/resolver-feed` – list resolver candidates (artifact, purl, version, paths, scope, runtime_flag, nearest_safe_version).
|
||||
- `POST /internal/sbom/resolver-feed/backfill` – clear and repopulate resolver feed from current projections.
|
||||
- `GET /internal/sbom/resolver-feed/export` – NDJSON export of resolver candidates for air-gap delivery.
|
||||
- `GET /internal/sbom/ledger/audit` – audit trail for ledger changes (created/pruned).
|
||||
- `GET /internal/sbom/analysis/jobs` – list analysis jobs triggered by BYOS uploads.
|
||||
- `POST /internal/sbom/retention/prune` – apply retention policy and emit audit entries.
|
||||
|
||||
## 3.1) Ledger + BYOS workflow (Sprint 4600)
|
||||
- Uploads are validated, normalized, and stored as ledger versions chained per artifact identity.
|
||||
- Diffs compare normalized component keys and surface version/license deltas with deterministic ordering.
|
||||
- Lineage is derived from parent version references and emitted for Graph lineage edges.
|
||||
- Lineage relationships include parent links plus build links (shared CI build IDs when provided).
|
||||
- Retention policy prunes old versions while preserving audit entries and minimum keep counts.
|
||||
- See `docs/modules/sbomservice/ledger-lineage.md` for request/response examples.
|
||||
- See `docs/modules/sbomservice/byos-ingestion.md` for supported formats and troubleshooting.
|
||||
|
||||
## 4) Ingestion & orchestrator integration
|
||||
- Ingest sources: Scanner pipeline (preferred) or uploaded SPDX 3.0.1/CycloneDX 1.6 bundles.
|
||||
- Ingest sources: Scanner pipeline (preferred) or uploaded SPDX 2.3/3.0 and CycloneDX 1.4–1.6 bundles.
|
||||
- Orchestrator: register SBOM ingest/index jobs; worker SDK emits artifact hash + job metadata; honor pause/throttle; report backpressure metrics; support watermark-based backfill for idempotent replays.
|
||||
- Idempotency: combine `(tenant, artifactDigest, sbomVersion)` as primary key; duplicate ingests short-circuit.
|
||||
|
||||
@@ -80,7 +99,7 @@ Operational rules:
|
||||
- Input validation: schema-validate incoming SBOMs; reject oversized/unsupported media types early.
|
||||
|
||||
## 8) Observability
|
||||
- Metrics: `sbom_projection_seconds`, `sbom_projection_size_bytes`, `sbom_projection_queries_total`, `sbom_paths_latency_seconds`, `sbom_paths_cache_hit_ratio`, `sbom_events_backlog`.
|
||||
- Metrics: `sbom_projection_seconds`, `sbom_projection_size_bytes`, `sbom_projection_queries_total`, `sbom_paths_latency_seconds`, `sbom_paths_cache_hit_ratio`, `sbom_events_backlog`, `sbom_ledger_uploads_total`, `sbom_ledger_diffs_total`, `sbom_ledger_retention_pruned_total`.
|
||||
- Tracing: ActivitySource `StellaOps.SbomService` (entrypoints, component lookup, console catalog, projections, events).
|
||||
- Traces: wrap ingest, projection build, and API handlers; propagate orchestrator job IDs.
|
||||
- Logs: structured, include tenant + artifact digest + sbomVersion; classify ingest failures (schema, storage, orchestrator, validation).
|
||||
@@ -90,6 +109,7 @@ Operational rules:
|
||||
- Enable PostgreSQL storage for `/console/sboms` and `/components/lookup` by setting `SbomService:PostgreSQL:ConnectionString` (env: `SBOM_SbomService__PostgreSQL__ConnectionString`).
|
||||
- Optional overrides: `SbomService:PostgreSQL:Schema`, `SbomService:PostgreSQL:CatalogTable`, `SbomService:PostgreSQL:ComponentLookupTable`; defaults are `sbom_service`, `sbom_catalog`, `sbom_component_neighbors`.
|
||||
- When the connection string is absent the service falls back to fixture JSON or deterministic in-memory seeds to keep air-gapped workflows alive.
|
||||
- Ledger retention settings (env prefix `SBOM_SbomService__Ledger__`): `MaxVersionsPerArtifact`, `MaxAgeDays`, `MinVersionsToKeep`.
|
||||
|
||||
## 10) Open questions / dependencies
|
||||
- Confirm orchestrator pause/backfill contract (shared with Runtime & Signals 140-series).
|
||||
@@ -97,3 +117,5 @@ Operational rules:
|
||||
- Publish canonical LNM v1 fixtures and JSON schemas for projections and asset metadata.
|
||||
|
||||
- See `docs/modules/sbomservice/api/projection-read.md` for `/sboms/{snapshotId}/projection` (LNM v1, tenant-scoped, hash-returning).
|
||||
- See `docs/modules/sbomservice/lineage-ledger.md` for ledger endpoints and lineage relationships.
|
||||
- See `docs/modules/sbomservice/retention-policy.md` for retention configuration and audit expectations.
|
||||
|
||||
33
docs/modules/sbomservice/byos-ingestion.md
Normal file
33
docs/modules/sbomservice/byos-ingestion.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# BYOS SBOM Ingestion
|
||||
|
||||
## Overview
|
||||
Bring-your-own SBOM (BYOS) uploads accept SPDX and CycloneDX JSON and register them in the SBOM ledger for analysis.
|
||||
|
||||
## Supported formats
|
||||
- CycloneDX JSON: 1.4, 1.5, 1.6
|
||||
- SPDX JSON: 2.3, 3.0
|
||||
|
||||
## Upload endpoint
|
||||
- `POST /sbom/upload` or `POST /api/v1/sbom/upload`
|
||||
- Required: `artifactRef`, plus `sbom` (JSON object) or `sbomBase64`.
|
||||
- Optional: `format` hint (`cyclonedx` or `spdx`) and `source` metadata.
|
||||
|
||||
Example:
|
||||
```json
|
||||
{
|
||||
"artifactRef": "acme/app:2.0",
|
||||
"sbom": { "spdxVersion": "SPDX-2.3", "packages": [] },
|
||||
"source": { "tool": "syft", "version": "1.9.0" }
|
||||
}
|
||||
```
|
||||
|
||||
## Validation notes
|
||||
- CycloneDX requires `bomFormat` and supported `specVersion`.
|
||||
- SPDX requires `spdxVersion` and a supported version number.
|
||||
- Quality scoring prefers components with PURL, version, and license metadata.
|
||||
|
||||
## Troubleshooting
|
||||
- **"sbom or sbomBase64 is required"**: include an SBOM payload in the request.
|
||||
- **"Unable to detect SBOM format"**: set `format` explicitly or include required root fields.
|
||||
- **Unsupported SBOM format/version**: ensure CycloneDX 1.4–1.6 or SPDX 2.3/3.0.
|
||||
- **Low quality scores**: include PURLs, versions, and license declarations where possible.
|
||||
41
docs/modules/sbomservice/ledger-lineage.md
Normal file
41
docs/modules/sbomservice/ledger-lineage.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# SBOM Lineage Ledger Guide
|
||||
|
||||
## Purpose
|
||||
- Track historical SBOM versions per artifact with deterministic diffs and lineage edges.
|
||||
- Support BYOS uploads for SPDX/CycloneDX inputs while preserving audit history.
|
||||
|
||||
## Upload workflow
|
||||
- Endpoint: `POST /sbom/upload` or `POST /api/v1/sbom/upload`.
|
||||
- Required fields: `artifactRef`, `sbom` (JSON object) or `sbomBase64`.
|
||||
|
||||
Example payload:
|
||||
```json
|
||||
{
|
||||
"artifactRef": "acme/app:1.0",
|
||||
"sbom": { "bomFormat": "CycloneDX", "specVersion": "1.6", "components": [] },
|
||||
"format": "cyclonedx",
|
||||
"source": { "tool": "syft", "version": "1.0.0" }
|
||||
}
|
||||
```
|
||||
|
||||
## Ledger queries
|
||||
- `GET /sbom/ledger/history?artifact=...&limit=...&cursor=...`
|
||||
- `GET /sbom/ledger/point?artifact=...&at=...`
|
||||
- `GET /sbom/ledger/range?artifact=...&start=...&end=...&limit=...&cursor=...`
|
||||
- `GET /sbom/ledger/diff?before=...&after=...`
|
||||
- `GET /sbom/ledger/lineage?artifact=...`
|
||||
|
||||
## Lineage semantics
|
||||
- Versions are chained per artifact; parent references create lineage edges.
|
||||
- Graph Indexer maps ledger lineage into:
|
||||
- `SBOM_VERSION_OF` edges from SBOM to artifact.
|
||||
- `SBOM_LINEAGE_*` edges (e.g., `SBOM_LINEAGE_PARENT`).
|
||||
|
||||
## Retention policy
|
||||
- Apply with `POST /internal/sbom/retention/prune`.
|
||||
- Settings: `SbomService:Ledger:MaxVersionsPerArtifact`, `MaxAgeDays`, `MinVersionsToKeep`.
|
||||
- Audit is available via `GET /internal/sbom/ledger/audit`.
|
||||
|
||||
## Determinism
|
||||
- Versions are ordered by sequence and UTC timestamps.
|
||||
- Diffs are ordered by component key for stable output.
|
||||
30
docs/modules/sbomservice/lineage-ledger.md
Normal file
30
docs/modules/sbomservice/lineage-ledger.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# SBOM lineage ledger
|
||||
|
||||
## Overview
|
||||
- Tracks immutable SBOM versions per artifact reference.
|
||||
- Exposes history, temporal queries, and deterministic diffs.
|
||||
- Emits lineage edges to support graph joins and audit trails.
|
||||
|
||||
## Endpoints
|
||||
- `GET /sbom/ledger/history?artifact=<ref>&limit=50&cursor=0`
|
||||
- `GET /sbom/ledger/point?artifact=<ref>&at=<iso8601>`
|
||||
- `GET /sbom/ledger/range?artifact=<ref>&start=<iso8601>&end=<iso8601>`
|
||||
- `GET /sbom/ledger/diff?before=<versionId>&after=<versionId>`
|
||||
- `GET /sbom/ledger/lineage?artifact=<ref>`
|
||||
|
||||
## Lineage relationships
|
||||
- `parent`: explicit parent version link (supplied at ingest).
|
||||
- `build`: versions emitted from the same CI build ID (from upload provenance).
|
||||
|
||||
## Example lineage response
|
||||
```json
|
||||
{
|
||||
"artifactRef": "example.com/app:1.2.3",
|
||||
"nodes": [{ "versionId": "v1", "sequenceNumber": 1, "digest": "sha256:..." }],
|
||||
"edges": [{ "fromVersionId": "v1", "toVersionId": "v2", "relationship": "build" }]
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
- Ledger storage is in-memory until PostgreSQL-backed persistence is wired.
|
||||
- Ordering is deterministic by sequence number, then timestamp.
|
||||
18
docs/modules/sbomservice/retention-policy.md
Normal file
18
docs/modules/sbomservice/retention-policy.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# SBOM ledger retention policy
|
||||
|
||||
## Purpose
|
||||
Retention keeps ledger history bounded while preserving audit trails for compliance.
|
||||
|
||||
## Configuration
|
||||
Settings are bound from `SbomService:Ledger` (env prefix `SBOM_SbomService__Ledger__`):
|
||||
- `MaxVersionsPerArtifact`: max ledger versions retained per artifact (default 50).
|
||||
- `MaxAgeDays`: prune versions older than N days (0 disables age pruning).
|
||||
- `MinVersionsToKeep`: minimum versions always retained per artifact.
|
||||
|
||||
## Operations
|
||||
- `POST /internal/sbom/retention/prune` applies retention rules and returns a summary.
|
||||
- `GET /internal/sbom/ledger/audit?artifact=<ref>` returns audit entries for create/prune actions.
|
||||
|
||||
## Guarantees
|
||||
- Audit entries are append-only and preserved even when versions are pruned.
|
||||
- Deterministic ordering is used when selecting versions to prune.
|
||||
@@ -1,20 +1,20 @@
|
||||
# component_architecture_scanner.md — **Stella Ops Scanner** (2025Q4)
|
||||
# component_architecture_scanner.md — **Stella Ops Scanner** (2025Q4)
|
||||
|
||||
> Aligned with Epic 6 – Vulnerability Explorer and Epic 10 – Export Center.
|
||||
> Aligned with Epic 6 – Vulnerability Explorer and Epic 10 – Export Center.
|
||||
|
||||
> **Scope.** Implementation‑ready architecture for the **Scanner** subsystem: WebService, Workers, analyzers, SBOM assembly (inventory & usage), per‑layer caching, three‑way diffs, artifact catalog (RustFS default + PostgreSQL, S3-compatible fallback), attestation hand‑off, and scale/security posture. This document is the contract between the scanning plane and everything else (Policy, Excititor, Concelier, UI, CLI).
|
||||
> **Scope.** Implementation‑ready architecture for the **Scanner** subsystem: WebService, Workers, analyzers, SBOM assembly (inventory & usage), per‑layer caching, three‑way diffs, artifact catalog (RustFS default + PostgreSQL, S3-compatible fallback), attestation hand‑off, and scale/security posture. This document is the contract between the scanning plane and everything else (Policy, Excititor, Concelier, UI, CLI).
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Produce **deterministic**, **explainable** SBOMs and diffs for container images and filesystems, quickly and repeatedly, without guessing. Emit two views: **Inventory** (everything present) and **Usage** (entrypoint closure + actually linked libs). Attach attestations through **Signer→Attestor→Rekor v2**.
|
||||
**Mission.** Produce **deterministic**, **explainable** SBOMs and diffs for container images and filesystems, quickly and repeatedly, without guessing. Emit two views: **Inventory** (everything present) and **Usage** (entrypoint closure + actually linked libs). Attach attestations through **Signer→Attestor→Rekor v2**.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* Scanner **does not** produce PASS/FAIL. The backend (Policy + Excititor + Concelier) decides presentation and verdicts.
|
||||
* Scanner **does not** keep third‑party SBOM warehouses. It may **bind** to existing attestations for exact hashes.
|
||||
* Core analyzers are **deterministic** (no fuzzy identity). Optional heuristic plug‑ins (e.g., patch‑presence) run under explicit flags and never contaminate the core SBOM.
|
||||
* Scanner **does not** keep third‑party SBOM warehouses. It may **bind** to existing attestations for exact hashes.
|
||||
* Core analyzers are **deterministic** (no fuzzy identity). Optional heuristic plug‑ins (e.g., patch‑presence) run under explicit flags and never contaminate the core SBOM.
|
||||
|
||||
---
|
||||
|
||||
@@ -22,41 +22,41 @@
|
||||
|
||||
```
|
||||
src/
|
||||
├─ StellaOps.Scanner.WebService/ # REST control plane, catalog, diff, exports
|
||||
├─ StellaOps.Scanner.Worker/ # queue consumer; executes analyzers
|
||||
├─ StellaOps.Scanner.Models/ # DTOs, evidence, graph nodes, CDX/SPDX adapters
|
||||
├─ StellaOps.Scanner.Storage/ # PostgreSQL repositories; RustFS object client (default) + S3 fallback; ILM/GC
|
||||
├─ StellaOps.Scanner.Queue/ # queue abstraction (Redis/NATS/RabbitMQ)
|
||||
├─ StellaOps.Scanner.Cache/ # layer cache; file CAS; bloom/bitmap indexes
|
||||
├─ StellaOps.Scanner.EntryTrace/ # ENTRYPOINT/CMD → terminal program resolver (shell AST)
|
||||
├─ StellaOps.Scanner.Analyzers.OS.[Apk|Dpkg|Rpm]/
|
||||
├─ StellaOps.Scanner.Analyzers.Lang.[Java|Node|Bun|Python|Go|DotNet|Rust|Ruby|Php]/
|
||||
├─ StellaOps.Scanner.Analyzers.Native.[ELF|PE|MachO]/ # PE/Mach-O planned (M2)
|
||||
├─ StellaOps.Scanner.Symbols.Native/ # NEW – native symbol reader/demangler (Sprint 401)
|
||||
├─ StellaOps.Scanner.CallGraph.Native/ # NEW – function/call-edge builder + CAS emitter
|
||||
├─ StellaOps.Scanner.Emit.CDX/ # CycloneDX (JSON + Protobuf)
|
||||
├─ StellaOps.Scanner.Emit.SPDX/ # SPDX 3.0.1 JSON
|
||||
├─ StellaOps.Scanner.Diff/ # image→layer→component three‑way diff
|
||||
├─ StellaOps.Scanner.Index/ # BOM‑Index sidecar (purls + roaring bitmaps)
|
||||
├─ StellaOps.Scanner.Tests.* # unit/integration/e2e fixtures
|
||||
└─ Tools/
|
||||
├─ StellaOps.Scanner.Sbomer.BuildXPlugin/ # BuildKit generator (image referrer SBOMs)
|
||||
└─ StellaOps.Scanner.Sbomer.DockerImage/ # CLI‑driven scanner container
|
||||
├─ StellaOps.Scanner.WebService/ # REST control plane, catalog, diff, exports
|
||||
├─ StellaOps.Scanner.Worker/ # queue consumer; executes analyzers
|
||||
├─ StellaOps.Scanner.Models/ # DTOs, evidence, graph nodes, CDX/SPDX adapters
|
||||
├─ StellaOps.Scanner.Storage/ # PostgreSQL repositories; RustFS object client (default) + S3 fallback; ILM/GC
|
||||
├─ StellaOps.Scanner.Queue/ # queue abstraction (Redis/NATS/RabbitMQ)
|
||||
├─ StellaOps.Scanner.Cache/ # layer cache; file CAS; bloom/bitmap indexes
|
||||
├─ StellaOps.Scanner.EntryTrace/ # ENTRYPOINT/CMD → terminal program resolver (shell AST)
|
||||
├─ StellaOps.Scanner.Analyzers.OS.[Apk|Dpkg|Rpm]/
|
||||
├─ StellaOps.Scanner.Analyzers.Lang.[Java|Node|Bun|Python|Go|DotNet|Rust|Ruby|Php]/
|
||||
├─ StellaOps.Scanner.Analyzers.Native.[ELF|PE|MachO]/ # PE/Mach-O planned (M2)
|
||||
├─ StellaOps.Scanner.Symbols.Native/ # NEW – native symbol reader/demangler (Sprint 401)
|
||||
├─ StellaOps.Scanner.CallGraph.Native/ # NEW – function/call-edge builder + CAS emitter
|
||||
├─ StellaOps.Scanner.Emit.CDX/ # CycloneDX (JSON + Protobuf)
|
||||
├─ StellaOps.Scanner.Emit.SPDX/ # SPDX 3.0.1 JSON
|
||||
├─ StellaOps.Scanner.Diff/ # image→layer→component three‑way diff
|
||||
├─ StellaOps.Scanner.Index/ # BOM‑Index sidecar (purls + roaring bitmaps)
|
||||
├─ StellaOps.Scanner.Tests.* # unit/integration/e2e fixtures
|
||||
└─ Tools/
|
||||
├─ StellaOps.Scanner.Sbomer.BuildXPlugin/ # BuildKit generator (image referrer SBOMs)
|
||||
└─ StellaOps.Scanner.Sbomer.DockerImage/ # CLI‑driven scanner container
|
||||
```
|
||||
|
||||
Per-analyzer notes (language analyzers):
|
||||
- `docs/modules/scanner/analyzers-java.md` — Java/Kotlin (Maven, Gradle, fat archives)
|
||||
- `docs/modules/scanner/dotnet-analyzer.md` — .NET (deps.json, NuGet, packages.lock.json, declared-only)
|
||||
- `docs/modules/scanner/analyzers-python.md` — Python (pip, Poetry, pipenv, conda, editables, vendored)
|
||||
- `docs/modules/scanner/analyzers-node.md` — Node.js (npm, Yarn, pnpm, multi-version locks)
|
||||
- `docs/modules/scanner/analyzers-bun.md` — Bun (bun.lock v1, dev classification, patches)
|
||||
- `docs/modules/scanner/analyzers-go.md` — Go (build info, modules)
|
||||
- `docs/modules/scanner/analyzers-java.md` — Java/Kotlin (Maven, Gradle, fat archives)
|
||||
- `docs/modules/scanner/dotnet-analyzer.md` — .NET (deps.json, NuGet, packages.lock.json, declared-only)
|
||||
- `docs/modules/scanner/analyzers-python.md` — Python (pip, Poetry, pipenv, conda, editables, vendored)
|
||||
- `docs/modules/scanner/analyzers-node.md` — Node.js (npm, Yarn, pnpm, multi-version locks)
|
||||
- `docs/modules/scanner/analyzers-bun.md` — Bun (bun.lock v1, dev classification, patches)
|
||||
- `docs/modules/scanner/analyzers-go.md` — Go (build info, modules)
|
||||
|
||||
Cross-analyzer contract (identity safety, evidence locators, container layout):
|
||||
- `docs/modules/scanner/language-analyzers-contract.md` — PURL vs explicit-key rules, evidence formats, bounded scanning
|
||||
- `docs/modules/scanner/language-analyzers-contract.md` — PURL vs explicit-key rules, evidence formats, bounded scanning
|
||||
|
||||
Semantic entrypoint analysis (Sprint 0411):
|
||||
- `docs/modules/scanner/semantic-entrypoint-schema.md` — Schema for intent, capabilities, threat vectors, and data boundaries
|
||||
- `docs/modules/scanner/semantic-entrypoint-schema.md` — Schema for intent, capabilities, threat vectors, and data boundaries
|
||||
|
||||
Analyzer assemblies and buildx generators are packaged as **restart-time plug-ins** under `plugins/scanner/**` with manifests; services must restart to activate new plug-ins.
|
||||
|
||||
@@ -64,15 +64,15 @@ Analyzer assemblies and buildx generators are packaged as **restart-time plug-in
|
||||
|
||||
The **Semantic Entrypoint Engine** enriches scan results with application-level understanding:
|
||||
|
||||
- **Intent Classification** — Infers application type (WebServer, Worker, CliTool, Serverless, etc.) from framework detection and entrypoint analysis
|
||||
- **Capability Detection** — Identifies system resource access patterns (network, filesystem, database, crypto)
|
||||
- **Threat Vector Inference** — Maps capabilities to potential attack vectors with CWE/OWASP references
|
||||
- **Data Boundary Mapping** — Tracks data flow boundaries with sensitivity classification
|
||||
- **Intent Classification** — Infers application type (WebServer, Worker, CliTool, Serverless, etc.) from framework detection and entrypoint analysis
|
||||
- **Capability Detection** — Identifies system resource access patterns (network, filesystem, database, crypto)
|
||||
- **Threat Vector Inference** — Maps capabilities to potential attack vectors with CWE/OWASP references
|
||||
- **Data Boundary Mapping** — Tracks data flow boundaries with sensitivity classification
|
||||
|
||||
Components:
|
||||
- `StellaOps.Scanner.EntryTrace/Semantic/` — Core semantic types and orchestrator
|
||||
- `StellaOps.Scanner.EntryTrace/Semantic/Adapters/` — Language-specific adapters (Python, Java, Node, .NET, Go)
|
||||
- `StellaOps.Scanner.EntryTrace/Semantic/Analysis/` — Capability detection, threat inference, boundary mapping
|
||||
- `StellaOps.Scanner.EntryTrace/Semantic/` — Core semantic types and orchestrator
|
||||
- `StellaOps.Scanner.EntryTrace/Semantic/Adapters/` — Language-specific adapters (Python, Java, Node, .NET, Go)
|
||||
- `StellaOps.Scanner.EntryTrace/Semantic/Analysis/` — Capability detection, threat inference, boundary mapping
|
||||
|
||||
Integration points:
|
||||
- `LanguageComponentRecord` includes semantic fields (`intent`, `capabilities[]`, `threatVectors[]`)
|
||||
@@ -88,8 +88,8 @@ CLI usage: `stella scan --semantic <image>` enables semantic analysis in output.
|
||||
- **Build-id capture**: read `.note.gnu.build-id` for every ELF, store hex build-id alongside soname/path, propagate into `SymbolID`/`code_id`, and expose it to SBOM + runtime joiners. If missing, fall back to file hash and mark source accordingly.
|
||||
- **PURL-resolved edges**: annotate call edges with the callee purl and `symbol_digest` so graphs merge with SBOM components. See `docs/reachability/purl-resolved-edges.md` for schema rules and acceptance tests.
|
||||
- **Symbol hints in evidence**: reachability union and richgraph payloads emit `symbol {mangled,demangled,source,confidence}` plus optional `code_block_hash` for stripped/heuristic functions; serializers clamp confidence to [0,1] and uppercase `source` (`DWARF|PDB|SYM|NONE`) for determinism.
|
||||
- **Unknowns emission**: when symbol → purl mapping or edge targets remain unresolved, emit structured Unknowns to Signals (see `docs/signals/unknowns-registry.md`) instead of dropping evidence.
|
||||
- **Hybrid attestation**: emit **graph-level DSSE** for every `richgraph-v1` (mandatory) and optional **edge-bundle DSSE** (≤512 edges) for runtime/init-root/contested edges or third-party provenance. Publish graph DSSE digests to Rekor by default; edge-bundle Rekor publish is policy-driven. CAS layout: `cas://reachability/graphs/{blake3}` for graph body, `.../{blake3}.dsse` for envelope, and `cas://reachability/edges/{graph_hash}/{bundle_id}[.dsse]` for bundles. Deterministic ordering before hashing/signing is required.
|
||||
- **Unknowns emission**: when symbol → purl mapping or edge targets remain unresolved, emit structured Unknowns to Signals (see `docs/signals/unknowns-registry.md`) instead of dropping evidence.
|
||||
- **Hybrid attestation**: emit **graph-level DSSE** for every `richgraph-v1` (mandatory) and optional **edge-bundle DSSE** (≤512 edges) for runtime/init-root/contested edges or third-party provenance. Publish graph DSSE digests to Rekor by default; edge-bundle Rekor publish is policy-driven. CAS layout: `cas://reachability/graphs/{blake3}` for graph body, `.../{blake3}.dsse` for envelope, and `cas://reachability/edges/{graph_hash}/{bundle_id}[.dsse]` for bundles. Deterministic ordering before hashing/signing is required.
|
||||
- **Deterministic call-graph manifest**: capture analyzer versions, feed hashes, toolchain digests, and flags in a manifest stored alongside `richgraph-v1`; replaying with the same manifest MUST yield identical node/edge sets and hashes (see `docs/reachability/lead.md`).
|
||||
|
||||
### 1.1 Queue backbone (Redis / NATS)
|
||||
@@ -121,10 +121,10 @@ scanner:
|
||||
|
||||
The DI extension (`AddScannerQueue`) wires the selected transport, so future additions (e.g., RabbitMQ) only implement the same contract and register.
|
||||
|
||||
**Runtime form‑factor:** two deployables
|
||||
**Runtime form‑factor:** two deployables
|
||||
|
||||
* **Scanner.WebService** (stateless REST)
|
||||
* **Scanner.Worker** (N replicas; queue‑driven)
|
||||
* **Scanner.Worker** (N replicas; queue‑driven)
|
||||
|
||||
---
|
||||
|
||||
@@ -134,30 +134,30 @@ The DI extension (`AddScannerQueue`) wires the selected transport, so future add
|
||||
* **RustFS** (default, offline-first) for SBOM artifacts; optional S3/MinIO compatibility retained for migration; **Object Lock** semantics emulated via retention headers; **ILM** for TTL.
|
||||
* **PostgreSQL** for catalog, job state, diffs, ILM rules.
|
||||
* **Queue** (Redis Streams/NATS/RabbitMQ).
|
||||
* **Authority** (on‑prem OIDC) for **OpToks** (DPoP/mTLS).
|
||||
* **Authority** (on‑prem OIDC) for **OpToks** (DPoP/mTLS).
|
||||
* **Signer** + **Attestor** (+ **Fulcio/KMS** + **Rekor v2**) for DSSE + transparency.
|
||||
|
||||
---
|
||||
|
||||
## 3) Contracts & data model
|
||||
|
||||
### 3.1 Evidence‑first component model
|
||||
### 3.1 Evidence‑first component model
|
||||
|
||||
**Nodes**
|
||||
|
||||
* `Image`, `Layer`, `File`
|
||||
* `Component` (`purl?`, `name`, `version?`, `type`, `id` — may be `bin:{sha256}`)
|
||||
* `Executable` (ELF/PE/Mach‑O), `Library` (native or managed), `EntryScript` (shell/launcher)
|
||||
* `Component` (`purl?`, `name`, `version?`, `type`, `id` — may be `bin:{sha256}`)
|
||||
* `Executable` (ELF/PE/Mach‑O), `Library` (native or managed), `EntryScript` (shell/launcher)
|
||||
|
||||
**Edges** (all carry **Evidence**)
|
||||
|
||||
* `contains(Image|Layer → File)`
|
||||
* `installs(PackageDB → Component)` (OS database row)
|
||||
* `declares(InstalledMetadata → Component)` (dist‑info, pom.properties, deps.json…)
|
||||
* `links_to(Executable → Library)` (ELF `DT_NEEDED`, PE imports)
|
||||
* `calls(EntryScript → Program)` (file:line from shell AST)
|
||||
* `attests(Rekor → Component|Image)` (SBOM/predicate binding)
|
||||
* `bound_from_attestation(Component_attested → Component_observed)` (hash equality proof)
|
||||
* `contains(Image|Layer → File)`
|
||||
* `installs(PackageDB → Component)` (OS database row)
|
||||
* `declares(InstalledMetadata → Component)` (dist‑info, pom.properties, deps.json…)
|
||||
* `links_to(Executable → Library)` (ELF `DT_NEEDED`, PE imports)
|
||||
* `calls(EntryScript → Program)` (file:line from shell AST)
|
||||
* `attests(Rekor → Component|Image)` (SBOM/predicate binding)
|
||||
* `bound_from_attestation(Component_attested → Component_observed)` (hash equality proof)
|
||||
|
||||
**Evidence**
|
||||
|
||||
@@ -211,17 +211,20 @@ migrations.
|
||||
All under `/api/v1/scanner`. Auth: **OpTok** (DPoP/mTLS); RBAC scopes.
|
||||
|
||||
```
|
||||
POST /scans { imageRef|digest, force?:bool } → { scanId }
|
||||
GET /scans/{id} → { status, imageDigest, artifacts[], rekor? }
|
||||
GET /sboms/{imageDigest} ?format=cdx-json|cdx-pb|spdx-json&view=inventory|usage → bytes
|
||||
GET /scans/{id}/ruby-packages → { scanId, imageDigest, generatedAt, packages[] }
|
||||
GET /scans/{id}/bun-packages → { scanId, imageDigest, generatedAt, packages[] }
|
||||
GET /diff?old=<digest>&new=<digest>&view=inventory|usage → diff.json
|
||||
POST /exports { imageDigest, format, view, attest?:bool } → { artifactId, rekor? }
|
||||
POST /reports { imageDigest, policyRevision? } → { reportId, rekor? } # delegates to backend policy+vex
|
||||
GET /catalog/artifacts/{id} → { meta }
|
||||
POST /scans { imageRef|digest, force?:bool } → { scanId }
|
||||
GET /scans/{id} → { status, imageDigest, artifacts[], rekor? }
|
||||
GET /sboms/{imageDigest} ?format=cdx-json|cdx-pb|spdx-json&view=inventory|usage → bytes
|
||||
POST /sbom/upload { artifactRef, sbom|sbomBase64, format?, source? } -> { sbomId, analysisJobId }
|
||||
GET /sbom/uploads/{sbomId} -> upload record + provenance
|
||||
GET /scans/{id}/ruby-packages → { scanId, imageDigest, generatedAt, packages[] }
|
||||
GET /scans/{id}/bun-packages → { scanId, imageDigest, generatedAt, packages[] }
|
||||
GET /diff?old=<digest>&new=<digest>&view=inventory|usage → diff.json
|
||||
POST /exports { imageDigest, format, view, attest?:bool } → { artifactId, rekor? }
|
||||
POST /reports { imageDigest, policyRevision? } → { reportId, rekor? } # delegates to backend policy+vex
|
||||
GET /catalog/artifacts/{id} → { meta }
|
||||
GET /healthz | /readyz | /metrics
|
||||
```
|
||||
See docs/modules/scanner/byos-ingestion.md for BYOS workflow, formats, and troubleshooting.
|
||||
|
||||
### Report events
|
||||
|
||||
@@ -233,13 +236,13 @@ When `scanner.events.enabled = true`, the WebService serialises the signed repor
|
||||
|
||||
### 5.1 Acquire & verify
|
||||
|
||||
1. **Resolve image** (prefer `repo@sha256:…`).
|
||||
1. **Resolve image** (prefer `repo@sha256:…`).
|
||||
2. **(Optional) verify image signature** per policy (cosign).
|
||||
3. **Pull blobs**, compute layer digests; record metadata.
|
||||
|
||||
### 5.2 Layer union FS
|
||||
|
||||
* Apply whiteouts; materialize final filesystem; map **file → first introducing layer**.
|
||||
* Apply whiteouts; materialize final filesystem; map **file → first introducing layer**.
|
||||
* Windows layers (MSI/SxS/GAC) planned in **M2**.
|
||||
|
||||
### 5.3 Evidence harvest (parallel analyzers; deterministic only)
|
||||
@@ -259,32 +262,32 @@ When `scanner.events.enabled = true`, the WebService serialises the signed repor
|
||||
|
||||
**B) Language ecosystems (installed state only)**
|
||||
|
||||
* **Java**: `META-INF/maven/*/pom.properties`, MANIFEST → `pkg:maven/...`
|
||||
* **Node**: `node_modules/**/package.json` → `pkg:npm/...`
|
||||
* **Bun**: `bun.lock` (JSONC text) + `node_modules/**/package.json` + `node_modules/.bun/**/package.json` (isolated linker) → `pkg:npm/...`; `bun.lockb` (binary) emits remediation guidance
|
||||
* **Python**: `*.dist-info/{METADATA,RECORD}` → `pkg:pypi/...`
|
||||
* **Go**: Go **buildinfo** in binaries → `pkg:golang/...`
|
||||
* **.NET**: `*.deps.json` + assembly metadata → `pkg:nuget/...`
|
||||
* **Java**: `META-INF/maven/*/pom.properties`, MANIFEST → `pkg:maven/...`
|
||||
* **Node**: `node_modules/**/package.json` → `pkg:npm/...`
|
||||
* **Bun**: `bun.lock` (JSONC text) + `node_modules/**/package.json` + `node_modules/.bun/**/package.json` (isolated linker) → `pkg:npm/...`; `bun.lockb` (binary) emits remediation guidance
|
||||
* **Python**: `*.dist-info/{METADATA,RECORD}` → `pkg:pypi/...`
|
||||
* **Go**: Go **buildinfo** in binaries → `pkg:golang/...`
|
||||
* **.NET**: `*.deps.json` + assembly metadata → `pkg:nuget/...`
|
||||
* **Rust**: crates only when **explicitly present** (embedded metadata or cargo/registry traces); otherwise binaries reported as `bin:{sha256}`.
|
||||
|
||||
> **Rule:** We only report components proven **on disk** with authoritative metadata. Lockfiles are evidence only.
|
||||
|
||||
**C) Native link graph**
|
||||
|
||||
* **ELF**: parse `PT_INTERP`, `DT_NEEDED`, RPATH/RUNPATH, **GNU symbol versions**; map **SONAMEs** to file paths; link executables → libs.
|
||||
* **PE/Mach‑O** (planned M2): import table, delay‑imports; version resources; code signatures.
|
||||
* **ELF**: parse `PT_INTERP`, `DT_NEEDED`, RPATH/RUNPATH, **GNU symbol versions**; map **SONAMEs** to file paths; link executables → libs.
|
||||
* **PE/Mach‑O** (planned M2): import table, delay‑imports; version resources; code signatures.
|
||||
* Map libs back to **OS packages** if possible (via file lists); else emit `bin:{sha256}` components.
|
||||
* The exported metadata (`stellaops.os.*` properties, license list, source package) feeds policy scoring and export pipelines
|
||||
directly – Policy evaluates quiet rules against package provenance while Exporters forward the enriched fields into
|
||||
directly – Policy evaluates quiet rules against package provenance while Exporters forward the enriched fields into
|
||||
downstream JSON/Trivy payloads.
|
||||
* **Reachability lattice**: analyzers + runtime probes emit `Evidence`/`Mitigation` records (see `docs/reachability/lattice.md`). The lattice engine joins static path evidence, runtime hits (EventPipe/JFR), taint flows, environment gates, and mitigations into `ReachDecision` documents that feed VEX gating and event graph storage.
|
||||
* Sprint 401 introduces `StellaOps.Scanner.Symbols.Native` (DWARF/PDB reader + demangler) and `StellaOps.Scanner.CallGraph.Native`
|
||||
* Sprint 401 introduces `StellaOps.Scanner.Symbols.Native` (DWARF/PDB reader + demangler) and `StellaOps.Scanner.CallGraph.Native`
|
||||
(function boundary detector + call-edge builder). These libraries feed `FuncNode`/`CallEdge` CAS bundles and enrich reachability
|
||||
graphs with `{code_id, confidence, evidence}` so Signals/Policy/UI can cite function-level justifications.
|
||||
|
||||
**D) EntryTrace (ENTRYPOINT/CMD → terminal program)**
|
||||
**D) EntryTrace (ENTRYPOINT/CMD → terminal program)**
|
||||
|
||||
* Read image config; parse shell (POSIX/Bash subset) with AST: `source`/`.` includes; `case/if`; `exec`/`command`; `run‑parts`.
|
||||
* Read image config; parse shell (POSIX/Bash subset) with AST: `source`/`.` includes; `case/if`; `exec`/`command`; `run‑parts`.
|
||||
* Resolve commands via **PATH** within the **built rootfs**; follow language launchers (Java/Node/Python) to identify the terminal program (ELF/JAR/venv script).
|
||||
* Record **file:line** and choices for each hop; output chain graph.
|
||||
* Unresolvable dynamic constructs are recorded as **unknown** edges with reasons (e.g., `$FOO` unresolved).
|
||||
@@ -293,11 +296,11 @@ When `scanner.events.enabled = true`, the WebService serialises the signed repor
|
||||
|
||||
Post-resolution, the `SemanticEntrypointOrchestrator` enriches entry trace results with semantic understanding:
|
||||
|
||||
* **Application Intent** — Infers the purpose (WebServer, CliTool, Worker, Serverless, BatchJob, etc.) from framework detection and command patterns.
|
||||
* **Capability Classes** — Detects capabilities (NetworkListen, DatabaseSql, ProcessSpawn, SecretAccess, etc.) via import/dependency analysis and framework signatures.
|
||||
* **Attack Surface** — Maps capabilities to potential threat vectors (SqlInjection, Xss, Ssrf, Rce, PathTraversal) with CWE IDs and OWASP Top 10 categories.
|
||||
* **Data Boundaries** — Traces I/O edges (HttpRequest, DatabaseQuery, FileInput, EnvironmentVar) with direction and sensitivity classification.
|
||||
* **Confidence Scoring** — Each inference carries a score (0.0–1.0), tier (Definitive/High/Medium/Low/Unknown), and reasoning chain.
|
||||
* **Application Intent** — Infers the purpose (WebServer, CliTool, Worker, Serverless, BatchJob, etc.) from framework detection and command patterns.
|
||||
* **Capability Classes** — Detects capabilities (NetworkListen, DatabaseSql, ProcessSpawn, SecretAccess, etc.) via import/dependency analysis and framework signatures.
|
||||
* **Attack Surface** — Maps capabilities to potential threat vectors (SqlInjection, Xss, Ssrf, Rce, PathTraversal) with CWE IDs and OWASP Top 10 categories.
|
||||
* **Data Boundaries** — Traces I/O edges (HttpRequest, DatabaseQuery, FileInput, EnvironmentVar) with direction and sensitivity classification.
|
||||
* **Confidence Scoring** — Each inference carries a score (0.0–1.0), tier (Definitive/High/Medium/Low/Unknown), and reasoning chain.
|
||||
|
||||
Language-specific adapters (`PythonSemanticAdapter`, `JavaSemanticAdapter`, `NodeSemanticAdapter`, `DotNetSemanticAdapter`, `GoSemanticAdapter`) recognize framework patterns:
|
||||
* **Python**: Django, Flask, FastAPI, Celery, Click/Typer, Lambda handlers
|
||||
@@ -316,7 +319,7 @@ See `docs/modules/scanner/operations/entrypoint-semantic.md` for full schema ref
|
||||
**E) Attestation & SBOM bind (optional)**
|
||||
|
||||
* For each **file hash** or **binary hash**, query local cache of **Rekor v2** indices; if an SBOM attestation is found for **exact hash**, bind it to the component (origin=`attested`).
|
||||
* For the **image** digest, likewise bind SBOM attestations (build‑time referrers).
|
||||
* For the **image** digest, likewise bind SBOM attestations (build‑time referrers).
|
||||
|
||||
### 5.4 Component normalization (exact only)
|
||||
|
||||
@@ -326,25 +329,25 @@ See `docs/modules/scanner/operations/entrypoint-semantic.md` for full schema ref
|
||||
### 5.5 SBOM assembly & emit
|
||||
|
||||
* **Per-layer SBOM fragments**: components introduced by the layer (+ relationships).
|
||||
* **Image SBOMs**: merge fragments; refer back to them via **CycloneDX BOM‑Link** (or SPDX ExternalRef).
|
||||
* **Image SBOMs**: merge fragments; refer back to them via **CycloneDX BOM‑Link** (or SPDX ExternalRef).
|
||||
* Emit both **Inventory** & **Usage** views.
|
||||
* When the native analyzer reports an ELF `buildId`, attach it to component metadata and surface it as `stellaops:buildId` in CycloneDX properties (and diff metadata). This keeps SBOM/diff output in lockstep with runtime events and the debug-store manifest.
|
||||
* Serialize **CycloneDX JSON** and **CycloneDX Protobuf**; optionally **SPDX 3.0.1 JSON**.
|
||||
* Build **BOM‑Index** sidecar: purl table + roaring bitmap; flag `usedByEntrypoint` components for fast backend joins.
|
||||
* Serialize **CycloneDX 1.7 JSON** and **CycloneDX 1.7 Protobuf**; optionally **SPDX 3.0.1 JSON-LD** (`application/spdx+json; version=3.0.1`) with legacy tag-value output (`text/spdx`) when enabled (1.6 accepted for ingest compatibility).
|
||||
* Build **BOM‑Index** sidecar: purl table + roaring bitmap; flag `usedByEntrypoint` components for fast backend joins.
|
||||
|
||||
The emitted `buildId` metadata is preserved in component hashes, diff payloads, and `/policy/runtime` responses so operators can pivot from SBOM entries → runtime events → `debug/.build-id/<aa>/<rest>.debug` within the Offline Kit or release bundle.
|
||||
The emitted `buildId` metadata is preserved in component hashes, diff payloads, and `/policy/runtime` responses so operators can pivot from SBOM entries → runtime events → `debug/.build-id/<aa>/<rest>.debug` within the Offline Kit or release bundle.
|
||||
|
||||
### 5.6 DSSE attestation (via Signer/Attestor)
|
||||
|
||||
* WebService constructs **predicate** with `image_digest`, `stellaops_version`, `license_id`, `policy_digest?` (when emitting **final reports**), timestamps.
|
||||
* Calls **Signer** (requires **OpTok + PoE**); Signer verifies **entitlement + scanner image integrity** and returns **DSSE bundle**.
|
||||
* **Attestor** logs to **Rekor v2**; returns `{uuid,index,proof}` → stored in `artifacts.rekor`.
|
||||
* **Attestor** logs to **Rekor v2**; returns `{uuid,index,proof}` → stored in `artifacts.rekor`.
|
||||
* **Hybrid reachability attestations**: graph-level DSSE (mandatory) plus optional edge-bundle DSSEs for runtime/init/contested edges. See [`docs/reachability/hybrid-attestation.md`](../../reachability/hybrid-attestation.md) for verification runbooks and Rekor guidance.
|
||||
* Operator enablement runbooks (toggles, env-var map, rollout guidance) live in [`operations/dsse-rekor-operator-guide.md`](operations/dsse-rekor-operator-guide.md) per SCANNER-ENG-0015.
|
||||
|
||||
---
|
||||
|
||||
## 6) Three‑way diff (image → layer → component)
|
||||
## 6) Three‑way diff (image → layer → component)
|
||||
|
||||
### 6.1 Keys & classification
|
||||
|
||||
@@ -360,7 +363,7 @@ B = components(imageNew, key)
|
||||
|
||||
added = B \ A
|
||||
removed = A \ B
|
||||
changed = { k in A∩B : version(A[k]) != version(B[k]) || origin changed }
|
||||
changed = { k in A∩B : version(A[k]) != version(B[k]) || origin changed }
|
||||
|
||||
for each item in added/removed/changed:
|
||||
layer = attribute_to_layer(item, imageOld|imageNew)
|
||||
@@ -372,13 +375,13 @@ Diffs are stored as artifacts and feed **UI** and **CLI**.
|
||||
|
||||
---
|
||||
|
||||
## 7) Build‑time SBOMs (fast CI path)
|
||||
## 7) Build‑time SBOMs (fast CI path)
|
||||
|
||||
**Scanner.Sbomer.BuildXPlugin** can act as a BuildKit **generator**:
|
||||
|
||||
* During `docker buildx build --attest=type=sbom,generator=stellaops/sbom-indexer`, run analyzers on the build context/output; attach SBOMs as OCI **referrers** to the built image.
|
||||
* Optionally request **Signer/Attestor** to produce **Stella Ops‑verified** attestation immediately; else, Scanner.WebService can verify and re‑attest post‑push.
|
||||
* Scanner.WebService trusts build‑time SBOMs per policy, enabling **no‑rescan** for unchanged bases.
|
||||
* Optionally request **Signer/Attestor** to produce **Stella Ops‑verified** attestation immediately; else, Scanner.WebService can verify and re‑attest post‑push.
|
||||
* Scanner.WebService trusts build‑time SBOMs per policy, enabling **no‑rescan** for unchanged bases.
|
||||
|
||||
---
|
||||
|
||||
@@ -420,26 +423,26 @@ scanner:
|
||||
|
||||
## 9) Scale & performance
|
||||
|
||||
* **Parallelism**: per‑analyzer concurrency; bounded directory walkers; file CAS dedupe by sha256.
|
||||
* **Parallelism**: per‑analyzer concurrency; bounded directory walkers; file CAS dedupe by sha256.
|
||||
* **Distributed locks** per **layer digest** to prevent duplicate work across Workers.
|
||||
* **Registry throttles**: per‑host concurrency budgets; exponential backoff on 429/5xx.
|
||||
* **Registry throttles**: per‑host concurrency budgets; exponential backoff on 429/5xx.
|
||||
* **Targets**:
|
||||
|
||||
* **Build‑time**: P95 ≤ 3–5 s on warmed bases (CI generator).
|
||||
* **Post‑build delta**: P95 ≤ 10 s for 200 MB images with cache hit.
|
||||
* **Emit**: CycloneDX Protobuf ≤ 150 ms for 5k components; JSON ≤ 500 ms.
|
||||
* **Diff**: ≤ 200 ms for 5k vs 5k components.
|
||||
* **Build‑time**: P95 ≤ 3–5 s on warmed bases (CI generator).
|
||||
* **Post‑build delta**: P95 ≤ 10 s for 200 MB images with cache hit.
|
||||
* **Emit**: CycloneDX Protobuf ≤ 150 ms for 5k components; JSON ≤ 500 ms.
|
||||
* **Diff**: ≤ 200 ms for 5k vs 5k components.
|
||||
|
||||
---
|
||||
|
||||
## 10) Security posture
|
||||
|
||||
* **AuthN**: Authority‑issued short OpToks (DPoP/mTLS).
|
||||
* **AuthN**: Authority‑issued short OpToks (DPoP/mTLS).
|
||||
* **AuthZ**: scopes (`scanner.scan`, `scanner.export`, `scanner.catalog.read`).
|
||||
* **mTLS** to **Signer**/**Attestor**; only **Signer** can sign.
|
||||
* **No network fetches** during analysis (except registry pulls and optional Rekor index reads).
|
||||
* **Sandboxing**: non‑root containers; read‑only FS; seccomp profiles; disable execution of scanned content.
|
||||
* **Release integrity**: all first‑party images are **cosign‑signed**; Workers/WebService self‑verify at startup.
|
||||
* **Sandboxing**: non‑root containers; read‑only FS; seccomp profiles; disable execution of scanned content.
|
||||
* **Release integrity**: all first‑party images are **cosign‑signed**; Workers/WebService self‑verify at startup.
|
||||
|
||||
---
|
||||
|
||||
@@ -451,8 +454,8 @@ scanner:
|
||||
* `scanner.layer_cache_hits_total`, `scanner.file_cas_hits_total`
|
||||
* `scanner.artifact_bytes_total{format}`
|
||||
* `scanner.attestation_latency_seconds`, `scanner.rekor_failures_total`
|
||||
* `scanner_analyzer_golang_heuristic_total{indicator,version_hint}` — increments whenever the Go analyzer falls back to heuristics (build-id or runtime markers). Grafana panel: `sum by (indicator) (rate(scanner_analyzer_golang_heuristic_total[5m]))`; alert when the rate is ≥ 1 for 15 minutes to highlight unexpected stripped binaries.
|
||||
* **Tracing**: spans for acquire→union→analyzers→compose→emit→sign→log.
|
||||
* `scanner_analyzer_golang_heuristic_total{indicator,version_hint}` — increments whenever the Go analyzer falls back to heuristics (build-id or runtime markers). Grafana panel: `sum by (indicator) (rate(scanner_analyzer_golang_heuristic_total[5m]))`; alert when the rate is ≥ 1 for 15 minutes to highlight unexpected stripped binaries.
|
||||
* **Tracing**: spans for acquire→union→analyzers→compose→emit→sign→log.
|
||||
* **Audit logs**: DSSE requests log `license_id`, `image_digest`, `artifactSha256`, `policy_digest?`, Rekor UUID on success.
|
||||
|
||||
---
|
||||
@@ -461,12 +464,12 @@ scanner:
|
||||
|
||||
* **Analyzer contracts:** see `language-analyzers-contract.md` for cross-analyzer identity safety, evidence locators, and container layout rules. Per-analyzer docs: `analyzers-java.md`, `dotnet-analyzer.md`, `analyzers-python.md`, `analyzers-node.md`, `analyzers-bun.md`, `analyzers-go.md`. Implementation: `docs/implplan/SPRINT_0408_0001_0001_scanner_language_detection_gaps_program.md`.
|
||||
|
||||
* **Determinism:** given same image + analyzers → byte‑identical **CDX Protobuf**; JSON normalized.
|
||||
* **OS packages:** ground‑truth images per distro; compare to package DB.
|
||||
* **Lang ecosystems:** sample images per ecosystem (Java/Node/Python/Go/.NET/Rust) with installed metadata; negative tests w/ lockfile‑only.
|
||||
* **Native & EntryTrace:** ELF graph correctness; shell AST cases (includes, run‑parts, exec, case/if).
|
||||
* **Diff:** layer attribution against synthetic two‑image sequences.
|
||||
* **Performance:** cold vs warm cache; large `node_modules` and `site‑packages`.
|
||||
* **Determinism:** given same image + analyzers → byte‑identical **CDX Protobuf**; JSON normalized.
|
||||
* **OS packages:** ground‑truth images per distro; compare to package DB.
|
||||
* **Lang ecosystems:** sample images per ecosystem (Java/Node/Python/Go/.NET/Rust) with installed metadata; negative tests w/ lockfile‑only.
|
||||
* **Native & EntryTrace:** ELF graph correctness; shell AST cases (includes, run‑parts, exec, case/if).
|
||||
* **Diff:** layer attribution against synthetic two‑image sequences.
|
||||
* **Performance:** cold vs warm cache; large `node_modules` and `site‑packages`.
|
||||
* **Security:** ensure no code execution from image; fuzz parser inputs; path traversal resistance on layer extract.
|
||||
|
||||
---
|
||||
@@ -474,16 +477,16 @@ scanner:
|
||||
## 13) Failure modes & degradations
|
||||
|
||||
* **Missing OS DB** (files exist, DB removed): record **files**; do **not** fabricate package components; emit `bin:{sha256}` where unavoidable; flag in evidence.
|
||||
* **Unreadable metadata** (corrupt dist‑info): record file evidence; skip component creation; annotate.
|
||||
* **Unreadable metadata** (corrupt dist‑info): record file evidence; skip component creation; annotate.
|
||||
* **Dynamic shell constructs**: mark unresolved edges with reasons (env var unknown) and continue; **Usage** view may be partial.
|
||||
* **Registry rate limits**: honor backoff; queue job retries with jitter.
|
||||
* **Signer refusal** (license/plan/version): scan completes; artifact produced; **no attestation**; WebService marks result as **unverified**.
|
||||
|
||||
---
|
||||
|
||||
## 14) Optional plug‑ins (off by default)
|
||||
## 14) Optional plug‑ins (off by default)
|
||||
|
||||
* **Patch‑presence detector** (signature‑based backport checks). Reads curated function‑level signatures from advisories; inspects binaries for patched code snippets to lower false‑positives for backported fixes. Runs as a sidecar analyzer that **annotates** components; never overrides core identities.
|
||||
* **Patch‑presence detector** (signature‑based backport checks). Reads curated function‑level signatures from advisories; inspects binaries for patched code snippets to lower false‑positives for backported fixes. Runs as a sidecar analyzer that **annotates** components; never overrides core identities.
|
||||
* **Runtime probes** (with Zastava): when allowed, compare **/proc/<pid>/maps** (DSOs actually loaded) with static **Usage** view for precision.
|
||||
|
||||
---
|
||||
@@ -506,14 +509,14 @@ scanner:
|
||||
|
||||
## 17) Roadmap (Scanner)
|
||||
|
||||
* **M2**: Windows containers (MSI/SxS/GAC analyzers), PE/Mach‑O native analyzer, deeper Rust metadata.
|
||||
* **M2**: Buildx generator GA (certified external registries), cross‑registry trust policies.
|
||||
* **M3**: Patch‑presence plug‑in GA (opt‑in), cross‑image corpus clustering (evidence‑only; not identity).
|
||||
* **M2**: Windows containers (MSI/SxS/GAC analyzers), PE/Mach‑O native analyzer, deeper Rust metadata.
|
||||
* **M2**: Buildx generator GA (certified external registries), cross‑registry trust policies.
|
||||
* **M3**: Patch‑presence plug‑in GA (opt‑in), cross‑image corpus clustering (evidence‑only; not identity).
|
||||
* **M3**: Advanced EntryTrace (POSIX shell features breadth, busybox detection).
|
||||
|
||||
---
|
||||
|
||||
### Appendix A — EntryTrace resolution (pseudo)
|
||||
### Appendix A — EntryTrace resolution (pseudo)
|
||||
|
||||
```csharp
|
||||
ResolveEntrypoint(ImageConfig cfg, RootFs fs):
|
||||
@@ -544,9 +547,9 @@ ResolveEntrypoint(ImageConfig cfg, RootFs fs):
|
||||
return Unknown(reason)
|
||||
```
|
||||
|
||||
### Appendix A.1 — EntryTrace Explainability
|
||||
### Appendix A.1 — EntryTrace Explainability
|
||||
|
||||
### Appendix A.0 — Replay / Record mode
|
||||
### Appendix A.0 — Replay / Record mode
|
||||
|
||||
- WebService ships a **RecordModeService** that assembles replay manifests (schema v1) with policy/feed/tool pins and reachability references, then writes deterministic input/output bundles to the configured object store (RustFS default, S3/Minio fallback) under `replay/<head>/<digest>.tar.zst`.
|
||||
- Bundles contain canonical manifest JSON plus inputs (policy/feed/tool/analyzer digests) and outputs (SBOM, findings, optional VEX/logs); CAS URIs follow `cas://replay/...` and are attached to scan snapshots as `ReplayArtifacts`.
|
||||
@@ -567,12 +570,12 @@ EntryTrace emits structured diagnostics and metrics so operators can quickly und
|
||||
|
||||
Diagnostics drive two metrics published by `EntryTraceMetrics`:
|
||||
|
||||
- `entrytrace_resolutions_total{outcome}` — resolution attempts segmented by outcome (`resolved`, `partiallyresolved`, `unresolved`).
|
||||
- `entrytrace_unresolved_total{reason}` — diagnostic counts keyed by reason.
|
||||
- `entrytrace_resolutions_total{outcome}` — resolution attempts segmented by outcome (`resolved`, `partiallyresolved`, `unresolved`).
|
||||
- `entrytrace_unresolved_total{reason}` — diagnostic counts keyed by reason.
|
||||
|
||||
Structured logs include `entrytrace.path`, `entrytrace.command`, `entrytrace.reason`, and `entrytrace.depth`, all correlated with scan/job IDs. Timestamps are normalized to UTC (microsecond precision) to keep DSSE attestations and UI traces explainable.
|
||||
|
||||
### Appendix B — BOM‑Index sidecar
|
||||
### Appendix B — BOM‑Index sidecar
|
||||
|
||||
```
|
||||
struct Header { magic, version, imageDigest, createdAt }
|
||||
|
||||
33
docs/modules/scanner/byos-ingestion.md
Normal file
33
docs/modules/scanner/byos-ingestion.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# BYOS SBOM ingestion
|
||||
|
||||
## Overview
|
||||
- Accepts external SBOMs and runs them through validation, normalization, and analysis triggers.
|
||||
- Stores the SBOM artifact in the scanner object store and records provenance metadata.
|
||||
- Emits a deterministic analysis job id tied to the upload metadata.
|
||||
|
||||
## API
|
||||
- `POST /api/v1/sbom/upload`
|
||||
- `GET /api/v1/sbom/uploads/{sbomId}`
|
||||
|
||||
Example request:
|
||||
```json
|
||||
{
|
||||
"artifactRef": "example.com/app:1.0",
|
||||
"sbomBase64": "<base64>",
|
||||
"format": "cyclonedx",
|
||||
"source": { "tool": "syft", "version": "1.0.0" }
|
||||
}
|
||||
```
|
||||
|
||||
## Supported formats
|
||||
- CycloneDX JSON 1.4-1.6 (`bomFormat`, `specVersion`)
|
||||
- SPDX JSON 2.3 (`spdxVersion`)
|
||||
- SPDX JSON 3.0 (structural checks only; schema validation pending)
|
||||
|
||||
## CLI
|
||||
`stella sbom upload --file sbom.json --artifact example.com/app:1.0`
|
||||
|
||||
## Troubleshooting
|
||||
- Missing format: ensure `bomFormat` (CycloneDX) or `spdxVersion` (SPDX).
|
||||
- Unsupported versions: CycloneDX must be 1.4-1.6; SPDX must be 2.3 or 3.0.
|
||||
- Empty component lists are accepted but reduce quality scores.
|
||||
@@ -1,73 +1,42 @@
|
||||
# Reachability Drift Detection - Architecture
|
||||
# Reachability Drift Detection - Architecture
|
||||
|
||||
**Module:** Scanner
|
||||
**Version:** 1.0
|
||||
**Status:** Implemented (Sprint 3600.2-3600.3)
|
||||
**Status:** Implemented (core drift engine + API; Node Babel integration pending)
|
||||
**Last Updated:** 2025-12-22
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview
|
||||
|
||||
Reachability Drift Detection tracks function-level reachability changes between scans to identify when code modifications create new paths to vulnerable sinks or mitigate existing risks. This enables security teams to:
|
||||
Reachability Drift Detection tracks function-level reachability changes between scans. It highlights when code changes create new paths to sensitive sinks or remove existing paths, producing deterministic evidence for triage and VEX workflows.
|
||||
|
||||
- **Detect regressions** when previously unreachable vulnerabilities become exploitable
|
||||
- **Validate fixes** by confirming vulnerable code paths are removed
|
||||
- **Prioritize triage** based on actual exploitability rather than theoretical risk
|
||||
- **Automate VEX** by generating evidence-backed justifications
|
||||
Key outcomes:
|
||||
- Detect regressions when previously unreachable sinks become reachable.
|
||||
- Validate mitigations when reachable sinks become unreachable.
|
||||
- Provide deterministic evidence for audit and policy decisions.
|
||||
|
||||
---
|
||||
|
||||
## 2. Key Concepts
|
||||
|
||||
### 2.1 Call Graph
|
||||
|
||||
A directed graph representing function/method call relationships in source code:
|
||||
|
||||
- **Nodes**: Functions, methods, lambdas with metadata (file, line, visibility)
|
||||
- **Edges**: Call relationships with call kind (direct, virtual, delegate, reflection, dynamic)
|
||||
- **Entrypoints**: Public-facing functions (HTTP handlers, CLI commands, message consumers)
|
||||
- **Sinks**: Security-sensitive APIs (command execution, SQL, file I/O, deserialization)
|
||||
A directed graph of function calls:
|
||||
- Nodes: functions, methods, lambdas with file and line metadata.
|
||||
- Edges: call relationships (direct, virtual, dynamic).
|
||||
- Entrypoints: public handlers (HTTP, CLI, background services).
|
||||
- Sinks: security-sensitive APIs from the sink registry.
|
||||
|
||||
### 2.2 Reachability Analysis
|
||||
|
||||
Multi-source BFS traversal from entrypoints to determine which sinks are exploitable:
|
||||
|
||||
```
|
||||
Entrypoints (HTTP handlers, CLI)
|
||||
│
|
||||
▼ BFS traversal
|
||||
[Application Code]
|
||||
│
|
||||
▼
|
||||
Sinks (exec, query, writeFile)
|
||||
│
|
||||
▼
|
||||
Reachable = TRUE if path exists
|
||||
```
|
||||
Multi-source traversal from entrypoints to sinks to determine exploitability.
|
||||
|
||||
### 2.3 Drift Detection
|
||||
|
||||
Compares reachability between two scans (base vs head):
|
||||
|
||||
| Transition | Direction | Risk Impact |
|
||||
|------------|-----------|-------------|
|
||||
| Unreachable → Reachable | `became_reachable` | **Increased** - New exploit path |
|
||||
| Reachable → Unreachable | `became_unreachable` | **Decreased** - Mitigation applied |
|
||||
Compares reachability between base and head scans:
|
||||
- `became_reachable`: risk increased (new path to sink).
|
||||
- `became_unreachable`: risk decreased (path removed or mitigated).
|
||||
|
||||
### 2.4 Cause Attribution
|
||||
|
||||
Explains *why* drift occurred by correlating with code changes:
|
||||
|
||||
| Cause Kind | Description | Example |
|
||||
|------------|-------------|---------|
|
||||
| `guard_removed` | Conditional check removed | `if (!authorized)` deleted |
|
||||
| `guard_added` | New conditional blocks path | Added null check |
|
||||
| `new_public_route` | New entrypoint created | Added `/api/admin` endpoint |
|
||||
| `visibility_escalated` | Internal → Public | Method made public |
|
||||
| `dependency_upgraded` | Library update changed behavior | lodash 4.x → 5.x |
|
||||
| `symbol_removed` | Function deleted | Removed vulnerable helper |
|
||||
| `unknown` | Cannot determine | Multiple simultaneous changes |
|
||||
Explains why drift happened by correlating code changes with paths.
|
||||
|
||||
---
|
||||
|
||||
@@ -75,36 +44,15 @@ Explains *why* drift occurred by correlating with code changes:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Scan["Scan Execution"]
|
||||
A[Source Code] --> B[Call Graph Extractor]
|
||||
B --> C[CallGraphSnapshot]
|
||||
end
|
||||
|
||||
subgraph Analysis["Drift Analysis"]
|
||||
C --> D[Reachability Analyzer]
|
||||
D --> E[ReachabilityResult]
|
||||
|
||||
F[Base Scan Graph] --> G[Drift Detector]
|
||||
E --> G
|
||||
H[Code Changes] --> G
|
||||
G --> I[ReachabilityDriftResult]
|
||||
end
|
||||
|
||||
subgraph Output["Output"]
|
||||
I --> J[Path Compressor]
|
||||
J --> K[Compressed Paths]
|
||||
I --> L[Cause Explainer]
|
||||
L --> M[Drift Causes]
|
||||
|
||||
K --> N[Storage/API]
|
||||
M --> N
|
||||
end
|
||||
|
||||
subgraph Integration["Integration"]
|
||||
N --> O[Policy Gates]
|
||||
N --> P[VEX Emission]
|
||||
N --> Q[Web UI]
|
||||
end
|
||||
A[Source or binary] --> B[Call graph extractor]
|
||||
B --> C[CallGraphSnapshot]
|
||||
C --> D[Reachability analyzer]
|
||||
D --> E[ReachabilityResult]
|
||||
C --> F[Code change extractor]
|
||||
E --> G[ReachabilityDriftDetector]
|
||||
F --> G
|
||||
G --> H[ReachabilityDriftResult]
|
||||
H --> I[Storage + API]
|
||||
```
|
||||
|
||||
---
|
||||
@@ -113,259 +61,109 @@ flowchart TD
|
||||
|
||||
### 4.1 Call Graph Extractors
|
||||
|
||||
Per-language AST analysis producing `CallGraphSnapshot`:
|
||||
Registered extractors are configured in `CallGraphServiceCollectionExtensions`.
|
||||
|
||||
| Language | Extractor | Technology | Status |
|
||||
|----------|-----------|------------|--------|
|
||||
| .NET | `DotNetCallGraphExtractor` | Roslyn semantic model | **Done** |
|
||||
| Java | `JavaCallGraphExtractor` | ASM bytecode analysis | **Done** |
|
||||
| Go | `GoCallGraphExtractor` | golang.org/x/tools SSA | **Done** |
|
||||
| Python | `PythonCallGraphExtractor` | Python AST | **Done** |
|
||||
| Node.js | `NodeCallGraphExtractor` | Babel (planned) | Skeleton |
|
||||
| PHP | `PhpCallGraphExtractor` | php-parser | **Done** |
|
||||
| Ruby | `RubyCallGraphExtractor` | parser gem | **Done** |
|
||||
|
||||
**Location:** `src/Scanner/__Libraries/StellaOps.Scanner.CallGraph/Extraction/`
|
||||
| Language | Extractor | Status | Notes |
|
||||
|---|---|---|---|
|
||||
| .NET | `DotNetCallGraphExtractor` | Registered | Roslyn semantic model. |
|
||||
| Node.js | `NodeCallGraphExtractor` | Registered (placeholder) | Trace-based fallback; Babel integration pending (Sprint 3600.0004). |
|
||||
| Java | `JavaCallGraphExtractor` | Library present, not wired | Register extractor to enable. |
|
||||
| Go | `GoCallGraphExtractor` | Library present, not wired | Register extractor to enable. |
|
||||
| Python | `PythonCallGraphExtractor` | Library present, not wired | Register extractor to enable. |
|
||||
| PHP | `PhpCallGraphExtractor` | Library present, not wired | Register extractor to enable. |
|
||||
| Ruby | `RubyCallGraphExtractor` | Library present, not wired | Register extractor to enable. |
|
||||
| JavaScript | `JavaScriptCallGraphExtractor` | Library present, not wired | Register extractor to enable. |
|
||||
| Bun | `BunCallGraphExtractor` | Library present, not wired | Register extractor to enable. |
|
||||
| Deno | `DenoCallGraphExtractor` | Library present, not wired | Register extractor to enable. |
|
||||
| Binary | `BinaryCallGraphExtractor` | Library present, not wired | Native call edge extraction. |
|
||||
|
||||
### 4.2 Reachability Analyzer
|
||||
|
||||
Multi-source BFS from entrypoints to sinks:
|
||||
|
||||
```csharp
|
||||
public sealed class ReachabilityAnalyzer
|
||||
{
|
||||
public ReachabilityResult Analyze(CallGraphSnapshot graph);
|
||||
}
|
||||
|
||||
public record ReachabilityResult
|
||||
{
|
||||
ImmutableHashSet<string> ReachableNodes { get; }
|
||||
ImmutableArray<string> ReachableSinks { get; }
|
||||
ImmutableDictionary<string, ImmutableArray<string>> ShortestPaths { get; }
|
||||
}
|
||||
```
|
||||
|
||||
**Location:** `src/Scanner/__Libraries/StellaOps.Scanner.CallGraph/Analysis/`
|
||||
Located in `src/Scanner/__Libraries/StellaOps.Scanner.CallGraph/Analysis/`.
|
||||
|
||||
### 4.3 Drift Detector
|
||||
`ReachabilityDriftDetector` compares base and head snapshots and produces `ReachabilityDriftResult` with compressed paths.
|
||||
|
||||
Compares base and head graphs:
|
||||
|
||||
```csharp
|
||||
public sealed class ReachabilityDriftDetector
|
||||
{
|
||||
public ReachabilityDriftResult Detect(
|
||||
CallGraphSnapshot baseGraph,
|
||||
CallGraphSnapshot headGraph,
|
||||
IReadOnlyList<CodeChangeFact> codeChanges);
|
||||
}
|
||||
```
|
||||
|
||||
**Location:** `src/Scanner/__Libraries/StellaOps.Scanner.ReachabilityDrift/Services/`
|
||||
|
||||
### 4.4 Path Compressor
|
||||
|
||||
Reduces full paths to key nodes for storage/display:
|
||||
|
||||
```
|
||||
Full Path (20 nodes):
|
||||
entrypoint → A → B → C → ... → X → Y → sink
|
||||
|
||||
Compressed Path:
|
||||
entrypoint → [changed: B] → [changed: X] → sink
|
||||
(intermediateCount: 17)
|
||||
```
|
||||
|
||||
**Location:** `src/Scanner/__Libraries/StellaOps.Scanner.ReachabilityDrift/Services/PathCompressor.cs`
|
||||
|
||||
### 4.5 Cause Explainer
|
||||
|
||||
Correlates drift with code changes:
|
||||
|
||||
```csharp
|
||||
public sealed class DriftCauseExplainer
|
||||
{
|
||||
public DriftCause Explain(...);
|
||||
public DriftCause ExplainUnreachable(...);
|
||||
}
|
||||
```
|
||||
|
||||
**Location:** `src/Scanner/__Libraries/StellaOps.Scanner.ReachabilityDrift/Services/DriftCauseExplainer.cs`
|
||||
### 4.4 Path Compressor and Cause Explainer
|
||||
- `PathCompressor` reduces paths to key nodes and optionally includes full paths.
|
||||
- `DriftCauseExplainer` correlates changes to explain why drift happened.
|
||||
|
||||
---
|
||||
|
||||
## 5. Language Support Matrix
|
||||
|
||||
| Feature | .NET | Java | Go | Python | Node.js | PHP | Ruby |
|
||||
|---------|------|------|-------|--------|---------|-----|------|
|
||||
| Function extraction | Yes | Yes | Yes | Yes | Partial | Yes | Yes |
|
||||
| Call edge extraction | Yes | Yes | Yes | Yes | Partial | Yes | Yes |
|
||||
| HTTP entrypoints | ASP.NET | Spring | net/http | Flask/Django | Express* | Laravel | Rails |
|
||||
| gRPC entrypoints | Yes | Yes | Yes | Yes | No | No | No |
|
||||
| CLI entrypoints | Yes | Yes | Yes | Yes | Partial | Yes | Yes |
|
||||
| Sink detection | Yes | Yes | Yes | Yes | Partial | Yes | Yes |
|
||||
|
||||
*Requires Sprint 3600.4 completion
|
||||
| Capability | .NET | Node.js | Others (Java/Go/Python/PHP/Ruby/JS/Bun/Deno/Binary) |
|
||||
|---|---|---|---|
|
||||
| Call graph extraction | Supported | Placeholder | Library present, not wired |
|
||||
| Entrypoint detection | Supported | Partial | Library present, not wired |
|
||||
| Sink detection | Supported | Partial | Library present, not wired |
|
||||
|
||||
---
|
||||
|
||||
## 6. Storage Schema
|
||||
|
||||
### 6.1 PostgreSQL Tables
|
||||
Migrations are in `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Postgres/Migrations/`.
|
||||
|
||||
**call_graph_snapshots:**
|
||||
```sql
|
||||
CREATE TABLE call_graph_snapshots (
|
||||
id UUID PRIMARY KEY,
|
||||
tenant_id UUID NOT NULL,
|
||||
scan_id TEXT NOT NULL,
|
||||
language TEXT NOT NULL,
|
||||
graph_digest TEXT NOT NULL,
|
||||
node_count INT NOT NULL,
|
||||
edge_count INT NOT NULL,
|
||||
entrypoint_count INT NOT NULL,
|
||||
sink_count INT NOT NULL,
|
||||
extracted_at TIMESTAMPTZ NOT NULL,
|
||||
snapshot_json JSONB NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
**reachability_drift_results:**
|
||||
```sql
|
||||
CREATE TABLE reachability_drift_results (
|
||||
id UUID PRIMARY KEY,
|
||||
tenant_id UUID NOT NULL,
|
||||
base_scan_id TEXT NOT NULL,
|
||||
head_scan_id TEXT NOT NULL,
|
||||
language TEXT NOT NULL,
|
||||
newly_reachable_count INT NOT NULL,
|
||||
newly_unreachable_count INT NOT NULL,
|
||||
detected_at TIMESTAMPTZ NOT NULL,
|
||||
result_digest TEXT NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
**drifted_sinks:**
|
||||
```sql
|
||||
CREATE TABLE drifted_sinks (
|
||||
id UUID PRIMARY KEY,
|
||||
tenant_id UUID NOT NULL,
|
||||
drift_result_id UUID NOT NULL REFERENCES reachability_drift_results(id),
|
||||
sink_node_id TEXT NOT NULL,
|
||||
symbol TEXT NOT NULL,
|
||||
sink_category TEXT NOT NULL,
|
||||
direction TEXT NOT NULL,
|
||||
cause_kind TEXT NOT NULL,
|
||||
cause_description TEXT NOT NULL,
|
||||
compressed_path JSONB NOT NULL,
|
||||
associated_vulns JSONB
|
||||
);
|
||||
```
|
||||
|
||||
**code_changes:**
|
||||
```sql
|
||||
CREATE TABLE code_changes (
|
||||
id UUID PRIMARY KEY,
|
||||
tenant_id UUID NOT NULL,
|
||||
scan_id TEXT NOT NULL,
|
||||
base_scan_id TEXT NOT NULL,
|
||||
language TEXT NOT NULL,
|
||||
file TEXT NOT NULL,
|
||||
symbol TEXT NOT NULL,
|
||||
change_kind TEXT NOT NULL,
|
||||
details JSONB,
|
||||
detected_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
### 6.2 Valkey Caching
|
||||
|
||||
```
|
||||
stella:callgraph:{scan_id}:{lang}:{digest} → Compressed CallGraphSnapshot
|
||||
stella:callgraph:{scan_id}:{lang}:reachable → Set of reachable sink IDs
|
||||
stella:callgraph:{scan_id}:{lang}:paths:{sink} → Shortest path to sink
|
||||
```
|
||||
|
||||
TTL: Configurable (default 24h)
|
||||
Circuit breaker: 5 failures → 30s timeout
|
||||
Core tables:
|
||||
- `call_graph_snapshots`: `scan_id`, `language`, `graph_digest`, `extracted_at`, `node_count`, `edge_count`, `entrypoint_count`, `sink_count`, `snapshot_json`.
|
||||
- `reachability_results`: `scan_id`, `language`, `graph_digest`, `result_digest`, `computed_at`, `reachable_node_count`, `reachable_sink_count`, `result_json`.
|
||||
- `code_changes`: `scan_id`, `base_scan_id`, `language`, `node_id`, `file`, `symbol`, `change_kind`, `details`, `detected_at`.
|
||||
- `reachability_drift_results`: `base_scan_id`, `head_scan_id`, `language`, `newly_reachable_count`, `newly_unreachable_count`, `detected_at`, `result_digest`.
|
||||
- `drifted_sinks`: `drift_result_id`, `sink_node_id`, `sink_category`, `direction`, `cause_kind`, `cause_description`, `compressed_path`, `associated_vulns`.
|
||||
- `material_risk_changes`: extended with `base_scan_id`, `cause`, `cause_kind`, `path_nodes`, `associated_vulns` for drift attachments.
|
||||
|
||||
---
|
||||
|
||||
## 7. API Endpoints
|
||||
## 7. Cache and Determinism
|
||||
|
||||
If the call graph cache is enabled (`CallGraph:Cache`), cached keys follow this pattern:
|
||||
- `callgraph:graph:{scanId}:{language}`
|
||||
- `callgraph:reachability:{scanId}:{language}`
|
||||
|
||||
Determinism is enforced by stable ordering and deterministic IDs (see `DeterministicIds`).
|
||||
|
||||
---
|
||||
|
||||
## 8. API Endpoints
|
||||
|
||||
Base path: `/api/v1`
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| GET | `/scans/{scanId}/drift` | Get drift results for a scan |
|
||||
| GET | `/drift/{driftId}/sinks` | List drifted sinks (paginated) |
|
||||
| POST | `/scans/{scanId}/compute-reachability` | Trigger reachability computation |
|
||||
| GET | `/scans/{scanId}/reachability/components` | List components with reachability |
|
||||
| GET | `/scans/{scanId}/reachability/findings` | Get reachable vulnerable sinks |
|
||||
| GET | `/scans/{scanId}/reachability/explain` | Explain why a sink is reachable |
|
||||
|---|---|---|
|
||||
| GET | `/scans/{scanId}/drift` | Get or compute drift results for a scan. |
|
||||
| GET | `/drift/{driftId}/sinks` | List drifted sinks (paged). |
|
||||
| POST | `/scans/{scanId}/compute-reachability` | Trigger reachability computation. |
|
||||
| GET | `/scans/{scanId}/reachability/components` | List components with reachability. |
|
||||
| GET | `/scans/{scanId}/reachability/findings` | List findings with reachability. |
|
||||
| GET | `/scans/{scanId}/reachability/explain` | Explain reachability for a CVE and PURL. |
|
||||
|
||||
See: `docs/api/scanner-drift-api.md`
|
||||
See `docs/api/scanner-drift-api.md` for details.
|
||||
|
||||
---
|
||||
|
||||
## 8. Integration Points
|
||||
## 9. Integration Points
|
||||
|
||||
### 8.1 Policy Module
|
||||
|
||||
Drift results feed into policy gates for CI/CD blocking:
|
||||
|
||||
```yaml
|
||||
smart_diff:
|
||||
gates:
|
||||
- condition: "delta_reachable > 0 AND is_kev = true"
|
||||
action: block
|
||||
```
|
||||
|
||||
### 8.2 VEX Emission
|
||||
|
||||
Automatic VEX candidate generation on drift:
|
||||
|
||||
| Drift Direction | VEX Status | Justification |
|
||||
|-----------------|------------|---------------|
|
||||
| became_unreachable | `not_affected` | `vulnerable_code_not_in_execute_path` |
|
||||
| became_reachable | — | Requires manual review |
|
||||
|
||||
### 8.3 Attestation
|
||||
|
||||
DSSE-signed drift attestations:
|
||||
|
||||
```json
|
||||
{
|
||||
"_type": "https://in-toto.io/Statement/v1",
|
||||
"predicateType": "stellaops.dev/predicates/reachability-drift@v1",
|
||||
"predicate": {
|
||||
"baseScanId": "abc123",
|
||||
"headScanId": "def456",
|
||||
"newlyReachable": [...],
|
||||
"newlyUnreachable": [...],
|
||||
"resultDigest": "sha256:..."
|
||||
}
|
||||
}
|
||||
```
|
||||
- Policy gates: planned in `SPRINT_3600_0005_0001_policy_ci_gate_integration.md`.
|
||||
- VEX candidate emission: planned alongside policy gates.
|
||||
- Attestation: `StellaOps.Scanner.ReachabilityDrift.Attestation` provides DSSE signing utilities (integration is optional).
|
||||
|
||||
---
|
||||
|
||||
## 9. Performance Characteristics
|
||||
## 10. Performance Characteristics (Targets)
|
||||
|
||||
| Metric | Target | Notes |
|
||||
|--------|--------|-------|
|
||||
| Graph extraction (100K LOC) | < 60s | Per language |
|
||||
| Reachability analysis | < 5s | BFS traversal |
|
||||
| Drift detection | < 10s | Graph comparison |
|
||||
| Memory usage | < 2GB | Large projects |
|
||||
| Cache hit improvement | 10x | Valkey lookup vs recompute |
|
||||
|---|---|---|
|
||||
| Call graph extraction (100K LOC) | < 60s | Per language extractor. |
|
||||
| Reachability analysis | < 5s | BFS traversal on trimmed graphs. |
|
||||
| Drift detection | < 10s | Graph comparison and compression. |
|
||||
| Cache hit improvement | 10x | Valkey cache vs recompute. |
|
||||
|
||||
---
|
||||
|
||||
## 10. References
|
||||
## 11. References
|
||||
|
||||
- **Implementation Sprints:**
|
||||
- `docs/implplan/SPRINT_3600_0002_0001_call_graph_infrastructure.md`
|
||||
- `docs/implplan/SPRINT_3600_0003_0001_drift_detection_engine.md`
|
||||
- **API Reference:** `docs/api/scanner-drift-api.md`
|
||||
- **Operations Guide:** `docs/operations/reachability-drift-guide.md`
|
||||
- **Original Advisory:** `docs/product-advisories/archived/17-Dec-2025 - Reachability Drift Detection.md`
|
||||
- **Source Code:** `src/Scanner/__Libraries/StellaOps.Scanner.ReachabilityDrift/`
|
||||
- `docs/implplan/archived/SPRINT_3600_0002_0001_call_graph_infrastructure.md`
|
||||
- `docs/implplan/archived/SPRINT_3600_0003_0001_drift_detection_engine.md`
|
||||
- `docs/api/scanner-drift-api.md`
|
||||
- `docs/operations/reachability-drift-guide.md`
|
||||
- `docs/product-advisories/archived/17-Dec-2025 - Reachability Drift Detection.md`
|
||||
- `src/Scanner/__Libraries/StellaOps.Scanner.ReachabilityDrift/`
|
||||
|
||||
72
docs/modules/vexhub/architecture.md
Normal file
72
docs/modules/vexhub/architecture.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# VexHub Architecture
|
||||
|
||||
> **Scope.** Architecture and operational contract for the VexHub aggregation service that normalizes, validates, and distributes VEX statements with deterministic, offline-friendly outputs.
|
||||
|
||||
## 1) Purpose
|
||||
VexHub collects VEX statements from multiple upstream sources, validates and normalizes them, detects conflicts, and exposes a distribution API for internal services and external tools (Trivy/Grype). It is the canonical aggregation layer that feeds VexLens trust scoring and Policy Engine decisioning.
|
||||
|
||||
## 2) Responsibilities
|
||||
- Scheduled ingestion of upstream VEX sources (connectors + mirrored feeds).
|
||||
- Canonical normalization to OpenVEX-compatible structures.
|
||||
- Validation pipeline (schema + signature/provenance checks).
|
||||
- Conflict detection and provenance capture.
|
||||
- Distribution API for CVE/PURL/source queries and bulk exports.
|
||||
|
||||
Non-goals: policy decisioning (Policy Engine), consensus computation (VexLens), raw ingestion guardrails (Excititor AOC).
|
||||
|
||||
## 3) Component Model
|
||||
- **VexHub.WebService**: Minimal API host for distribution endpoints and admin controls.
|
||||
- **VexHub.Worker**: Background workers for ingestion schedules and validation pipelines.
|
||||
- **Normalization Pipeline**: Canonicalizes statements, deduplicates, and links provenance.
|
||||
- **Validation Pipeline**: Schema validation (OpenVEX/CycloneDX/CSAF) and signature checks.
|
||||
- **Storage**: PostgreSQL schema `vexhub` for normalized statements, provenance, conflicts, and export cursors.
|
||||
|
||||
## 4) Data Model (Draft)
|
||||
- `vexhub.statement`
|
||||
- `id`, `source_id`, `vuln_id`, `product_key`, `status`, `justification`, `timestamp`, `statement_hash`
|
||||
- `vexhub.provenance`
|
||||
- `statement_id`, `issuer`, `signature_valid`, `signature_ref`, `source_uri`, `ingested_at`
|
||||
- `vexhub.conflict`
|
||||
- `vuln_id`, `product_key`, `statement_ids[]`, `detected_at`, `reason`
|
||||
- `vexhub.export_cursor`
|
||||
- `source_id`, `last_exported_at`, `snapshot_hash`
|
||||
|
||||
All tables must include `tenant_id`, UTC timestamps, and deterministic ordering keys.
|
||||
|
||||
## 5) API Surface (Draft)
|
||||
- `GET /api/v1/vex/cve/{cve-id}`
|
||||
- `GET /api/v1/vex/package/{purl}`
|
||||
- `GET /api/v1/vex/source/{source-id}`
|
||||
- `GET /api/v1/vex/export` (bulk OpenVEX feed)
|
||||
- `GET /api/v1/vex/index` (vex-index.json)
|
||||
|
||||
Responses are deterministic: stable ordering by `timestamp DESC`, then `source_id ASC`, then `statement_hash ASC`.
|
||||
|
||||
## 6) Determinism & Offline Posture
|
||||
- Ingestion runs against frozen snapshots where possible; all outputs include `snapshot_hash`.
|
||||
- Canonical JSON serialization with stable key ordering.
|
||||
- No network egress outside configured connectors (sealed mode supported).
|
||||
- Bulk exports are immutable and content-addressed.
|
||||
|
||||
## 7) Security & Auth
|
||||
- API access requires Authority scopes (`vexhub.read`, `vexhub.admin`).
|
||||
- Signature verification follows issuer registry rules; failures are surfaced as metadata, not silent drops.
|
||||
- Rate limiting enforced at API gateway and per-client tokens.
|
||||
|
||||
## 8) Observability
|
||||
- Metrics: `vexhub_ingest_total`, `vexhub_validation_failures_total`, `vexhub_conflicts_total`, `vexhub_export_duration_seconds`.
|
||||
- Logs: include `tenant_id`, `source_id`, `statement_hash`, and `trace_id`.
|
||||
- Traces: spans for ingestion, normalization, validation, export.
|
||||
|
||||
## 9) Integration Points
|
||||
- **Excititor**: upstream connectors provide source payloads and trust hints.
|
||||
- **VexLens**: consumes normalized statements and provenance for trust scoring and consensus.
|
||||
- **Policy Engine**: reads VexLens consensus results; VexHub provides external distribution.
|
||||
- **UI**: VEX conflict studio consumes conflict API once available.
|
||||
|
||||
## 10) Testing Strategy
|
||||
- Unit tests for normalization and validation pipelines.
|
||||
- Integration tests with Postgres for ingestion and API outputs.
|
||||
- Determinism tests comparing repeated exports with identical inputs.
|
||||
|
||||
*Last updated: 2025-12-22.*
|
||||
Reference in New Issue
Block a user