save progress

This commit is contained in:
StellaOps Bot
2026-01-03 00:47:24 +02:00
parent 3f197814c5
commit ca578801fd
319 changed files with 32478 additions and 2202 deletions

View File

@@ -0,0 +1,250 @@
# Dashboard Data Flow
## Overview
The Dashboard Data Flow describes how StellaOps aggregates security posture data from multiple sources and presents it to users through the Console UI. The dashboard provides real-time visibility into vulnerability counts, policy compliance, scan status, and risk trends across all managed assets.
**Business Value**: Operators gain immediate visibility into their security posture without querying multiple systems.
## Actors
| Actor | Type | Role |
|-------|------|------|
| Operator | Human | Views dashboard, triggers actions |
| Console (Web UI) | System | Renders dashboard components |
| Gateway | Service | Routes and authenticates requests |
| Platform Service | Service | Aggregates data from modules |
| Scanner | Service | Provides scan results and SBOM data |
| Policy Engine | Service | Provides policy verdicts |
| Concelier | Service | Provides advisory data |
| VexLens | Service | Provides VEX consensus data |
## Prerequisites
- User authenticated via Authority (OAuth/OIDC)
- Tenant context established via `X-Tenant-Id` header
- At least one scan completed for data to display
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Dashboard Data Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌────────┐ ┌─────────┐ ┌─────────┐ ┌──────────┐
│Operator│ │ Console │ │ Gateway │ │ Platform │
└───┬────┘ └────┬────┘ └────┬────┘ └────┬─────┘
│ │ │ │
│ Open Dashboard│ │ │
│───────────────>│ │ │
│ │ │ │
│ │ GET /api/v1/dashboard │
│ │ Authorization: Bearer {jwt} │
│ │ X-Tenant-Id: {tenant} │
│ │───────────────>│ │
│ │ │ │
│ │ │ Validate JWT │
│ │ │ Extract claims │
│ │ │───────┐ │
│ │ │ │ │
│ │ │<──────┘ │
│ │ │ │
│ │ │ Forward with │
│ │ │ X-User-Id │
│ │ │───────────────>│
│ │ │ │
│ │ │ │ ┌─────────┐
│ │ │ │ │ Scanner │
│ │ │ │ └────┬────┘
│ │ │ │ │
│ │ │ │ Query scan stats
│ │ │ │──────>│
│ │ │ │ │
│ │ │ │<──────│
│ │ │ │ │
│ │ │ │ ┌────────┐
│ │ │ │ │ Policy │
│ │ │ │ └───┬────┘
│ │ │ │ │
│ │ │ │ Query verdicts
│ │ │ │─────>│
│ │ │ │ │
│ │ │ │<─────│
│ │ │ │ │
│ │ │ │ ┌──────────┐
│ │ │ │ │ Concelier│
│ │ │ │ └────┬─────┘
│ │ │ │ │
│ │ │ │ Query advisories
│ │ │ │──────>│
│ │ │ │ │
│ │ │ │<──────│
│ │ │ │
│ │ │ Aggregated │
│ │ │ Dashboard DTO │
│ │ │<───────────────│
│ │ │ │
│ │ 200 OK │ │
│ │ {dashboard} │ │
│ │<───────────────│ │
│ │ │ │
│ Render widgets │ │ │
│<───────────────│ │ │
│ │ │ │
```
## Step-by-Step
### 1. User Opens Dashboard
- Operator navigates to Console dashboard
- Browser loads Angular SPA from CDN/static files
### 2. Authentication Check
- Console checks for valid JWT in local storage
- If expired, redirects to Authority login flow
- If valid, proceeds with API calls
### 3. Dashboard API Request
```http
GET /api/v1/dashboard HTTP/1.1
Host: gateway.stellaops.local
Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...
X-Tenant-Id: acme-corp
Accept: application/json
```
### 4. Gateway Processing
- Validates JWT signature against Authority JWKS
- Extracts tenant from claims or header
- Applies rate limiting and ABAC rules
- Adds internal headers: `X-User-Id`, `X-User-Email`
### 5. Platform Service Aggregation
Platform Service fans out to multiple modules in parallel:
| Query | Module | Endpoint |
|-------|--------|----------|
| Scan statistics | Scanner | `GET /internal/stats?tenant={id}` |
| Policy verdicts | Policy | `GET /internal/verdicts/summary?tenant={id}` |
| Advisory counts | Concelier | `GET /internal/advisories/counts?tenant={id}` |
| VEX coverage | VexLens | `GET /internal/vex/coverage?tenant={id}` |
### 6. Data Aggregation
Platform Service combines responses into dashboard DTO:
```json
{
"summary": {
"total_images": 1247,
"images_scanned_24h": 89,
"critical_vulns": 12,
"high_vulns": 145,
"policy_violations": 3
},
"trends": {
"vuln_trend_7d": [-5, -2, 0, +3, -1, -4, -2],
"scan_volume_7d": [78, 92, 85, 89, 76, 81, 89]
},
"top_vulns": [
{"cve": "CVE-2024-1234", "severity": "critical", "affected_images": 8}
],
"policy_status": {
"compliant": 1198,
"non_compliant": 49,
"pending": 0
}
}
```
### 7. Response Delivery
- Platform returns aggregated DTO
- Gateway forwards to Console
- Console renders dashboard widgets
## Data Contracts
### Request Headers
| Header | Required | Description |
|--------|----------|-------------|
| `Authorization` | Yes | Bearer JWT token |
| `X-Tenant-Id` | Yes | Tenant identifier |
| `Accept` | No | `application/json` (default) |
### Response Schema
```typescript
interface DashboardResponse {
summary: {
total_images: number;
images_scanned_24h: number;
critical_vulns: number;
high_vulns: number;
medium_vulns: number;
low_vulns: number;
policy_violations: number;
};
trends: {
vuln_trend_7d: number[];
scan_volume_7d: number[];
};
top_vulns: Array<{
cve: string;
severity: 'critical' | 'high' | 'medium' | 'low';
affected_images: number;
}>;
policy_status: {
compliant: number;
non_compliant: number;
pending: number;
};
last_updated: string; // ISO-8601
}
```
## Error Handling
| Error | HTTP Status | Recovery |
|-------|-------------|----------|
| Invalid JWT | 401 | Redirect to login |
| Tenant not found | 404 | Show tenant selection |
| Module timeout | 504 | Partial dashboard with stale data indicator |
| Rate limited | 429 | Exponential backoff retry |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `dashboard_request_total` | Counter | `tenant`, `status` |
| `dashboard_latency_seconds` | Histogram | `tenant` |
| `dashboard_module_latency_seconds` | Histogram | `module` |
### Trace Context
```
dashboard-request
├── gateway-auth-check
├── platform-aggregate
│ ├── scanner-stats-query
│ ├── policy-verdicts-query
│ ├── concelier-advisories-query
│ └── vexlens-coverage-query
└── response-serialize
```
### Key Log Events
| Event | Level | Fields |
|-------|-------|--------|
| `dashboard.request` | INFO | `tenant_id`, `user_id` |
| `dashboard.module_timeout` | WARN | `module`, `timeout_ms` |
| `dashboard.complete` | INFO | `tenant_id`, `latency_ms` |
## Related Flows
- [Scan Submission Flow](02-scan-submission-flow.md) - How scans that feed the dashboard are created
- [Policy Evaluation Flow](04-policy-evaluation-flow.md) - How policy verdicts are computed
- [Risk Score Dashboard Flow](18-risk-score-dashboard-flow.md) - Detailed risk scoring

View File

@@ -0,0 +1,411 @@
# Scan Submission Flow
## Overview
The Scan Submission Flow describes the complete lifecycle of a container image scan from initial submission through SBOM generation, vulnerability matching, policy evaluation, and result storage. This is the core workflow that produces security verdicts for container images.
**Business Value**: Automated, deterministic scanning ensures every container image is evaluated against the latest advisories and policies before deployment.
## Actors
| Actor | Type | Role |
|-------|------|------|
| Developer/CI | Human/System | Submits scan request |
| CLI / API Client | System | Initiates scan via API |
| Gateway | Service | Routes and authenticates |
| Scheduler | Service | Queues and dispatches work |
| Scanner | Service | Analyzes image and generates SBOM |
| Concelier | Service | Provides advisory data |
| VexLens | Service | Provides VEX statements |
| Policy | Service | Evaluates policy rules |
| Attestor | Service | Signs scan results |
| Notify | Service | Sends notifications |
## Prerequisites
- Valid API credentials (JWT or API key)
- Container registry accessible (or image pulled locally)
- Registry credentials configured (if private registry)
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Scan Submission Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌───────┐ ┌─────────┐ ┌─────────┐ ┌───────────┐ ┌─────────┐
│ CLI │ │ Gateway │ │Scheduler│ │ Scanner │ │ Policy │
└───┬───┘ └────┬────┘ └────┬────┘ └─────┬─────┘ └────┬────┘
│ │ │ │ │
│ POST /api/v1/scans │ │ │
│ {image: "..."} │ │ │
│───────────>│ │ │ │
│ │ │ │ │
│ │ Validate │ │ │
│ │ + Enqueue │ │ │
│ │────────────>│ │ │
│ │ │ │ │
│ 202 Accepted │ │ │
│ {scan_id: "..."} │ │ │
│<───────────│ │ │ │
│ │ │ │ │
│ │ │ Dispatch │ │
│ │ │─────────────>│ │
│ │ │ │ │
│ │ │ │ Pull image │
│ │ │ │──────┐ │
│ │ │ │ │ │
│ │ │ │<─────┘ │
│ │ │ │ │
│ │ │ │ Extract │
│ │ │ │ layers │
│ │ │ │──────┐ │
│ │ │ │ │ │
│ │ │ │<─────┘ │
│ │ │ │ │
│ │ │ │ Run 11 │
│ │ │ │ analyzers │
│ │ │ │──────┐ │
│ │ │ │ │ │
│ │ │ │<─────┘ │
│ │ │ │ │
│ │ │ │ Generate │
│ │ │ │ SBOM │
│ │ │ │──────┐ │
│ │ │ │ │ │
│ │ │ │<─────┘ │
│ │ │ │ │
│ │ │ │ ┌──────────┐│
│ │ │ │ │Concelier ││
│ │ │ │ └────┬─────┘│
│ │ │ │ │ │
│ │ │ │ Match vulns │
│ │ │ │──────>│ │
│ │ │ │ │ │
│ │ │ │<──────│ │
│ │ │ │ │
│ │ │ │ ┌─────────┐ │
│ │ │ │ │ VexLens │ │
│ │ │ │ └────┬────┘ │
│ │ │ │ │ │
│ │ │ │ Get VEX │
│ │ │ │──────>│ │
│ │ │ │ │ │
│ │ │ │<──────│ │
│ │ │ │ │
│ │ │ │ Request │
│ │ │ │ verdict │
│ │ │ │─────────────>│
│ │ │ │ │
│ │ │ │ K4 lattice │
│ │ │ │ evaluation │
│ │ │ │<─────────────│
│ │ │ │ │
│ │ │ │ ┌─────────┐ │
│ │ │ │ │ Attestor│ │
│ │ │ │ └────┬────┘ │
│ │ │ │ │ │
│ │ │ │ Sign SBOM │
│ │ │ │──────>│ │
│ │ │ │ │ │
│ │ │ │<──────│ │
│ │ │ │ │
│ │ │ Complete │ │
│ │ │<─────────────│ │
│ │ │ │ │
│ │ │ ┌────────┐ │ │
│ │ │ │ Notify │ │ │
│ │ │ └───┬────┘ │ │
│ │ │ │ │ │
│ │ │ Send │ │ │
│ │ │──────> │ │
│ │ │ │ │
```
## Step-by-Step
### 1. Scan Request Submission
**CLI Command:**
```bash
stellaops scan docker.io/library/nginx:1.25
```
**API Request:**
```http
POST /api/v1/scans HTTP/1.1
Host: gateway.stellaops.local
Authorization: Bearer {jwt}
X-Tenant-Id: acme-corp
Content-Type: application/json
```
### 2. Gateway Processing
- Validates JWT and extracts claims
- Applies rate limiting
- Validates request schema
- Forwards to Scheduler
### 3. Scheduler Queuing
- Creates scan job record in `scheduler.jobs` table
- Assigns priority based on tenant tier
- Returns scan ID immediately (async pattern)
**Response:**
```json
{
"scan_id": "scan-7f3a9b2c-1234-5678-abcd-ef0123456789",
"status": "queued",
"estimated_wait": "PT30S",
"status_url": "/api/v1/scans/scan-7f3a9b2c-1234-5678-abcd-ef0123456789"
}
```
### 4. Scanner Dispatch
Scheduler dispatches to available Scanner worker:
```json
{
"job_id": "scan-7f3a9b2c-...",
"image_ref": "docker.io/library/nginx:1.25",
"resolved_digest": "sha256:abc123...",
"analyzers": ["os", "dotnet", "java", "node", "python", "go", "rust", "php", "ruby", "deno", "binary"],
"options": {...}
}
```
### 5. Image Analysis
Scanner performs multi-stage analysis:
| Stage | Duration | Description |
|-------|----------|-------------|
| Pull | 5-30s | Fetch image manifest and layers |
| Extract | 2-10s | Unpack layer tarballs |
| OS Detection | <1s | Identify base OS (Alpine, Debian, etc.) |
| Analyzer Fan-out | 10-60s | Run 11 parallel language analyzers |
| Dependency Resolution | 5-20s | Resolve transitive dependencies |
| SBOM Assembly | 1-5s | Merge results into unified SBOM |
### 6. Vulnerability Matching
Scanner queries Concelier for matching advisories:
```http
POST /internal/match HTTP/1.1
Content-Type: application/json
```
Response includes matched CVEs with affected version ranges.
### 7. VEX Application
Scanner queries VexLens for applicable VEX statements:
```http
POST /internal/vex/apply HTTP/1.1
Content-Type: application/json
```
VEX statements modify vulnerability status (e.g., `not_affected`, `fixed`).
### 8. Policy Evaluation
Scanner requests policy verdict from Policy engine:
```http
POST /internal/evaluate HTTP/1.1
Content-Type: application/json
```
Policy engine applies K4 lattice logic and returns verdict:
```json
{
"verdict": "FAIL",
"confidence": 0.92,
"violations": [
{
"rule": "no-critical-reachable",
"cve": "CVE-2024-1234",
"message": "Critical CVE with reachable code path"
}
]
}
```
### 9. Attestation
Scanner requests DSSE attestation from Attestor:
```http
POST /internal/attest HTTP/1.1
Content-Type: application/json
```
### 10. Result Storage
Scanner stores results:
| Store | Data |
|-------|------|
| PostgreSQL `scanner.scans` | Scan metadata and verdict |
| PostgreSQL `scanner.findings` | Individual vulnerability findings |
| RustFS `blobs/{sha256}/` | SBOM document |
| RustFS `attestations/{sha256}/` | DSSE envelope |
| Valkey `scan:{digest}` | Cache for quick lookup |
### 11. Notification
Scheduler triggers Notify service for configured channels:
```json
{
"event": "scan.complete",
"scan_id": "scan-7f3a9b2c-...",
"verdict": "FAIL",
"channels": ["slack", "webhook"]
}
```
## Data Contracts
### Scan Request Schema
```typescript
interface ScanRequest {
image: string; // Container image reference
options?: {
analyzers?: string[]; // List or "all"
sbom_format?: 'spdx-3.0' | 'cyclonedx-1.6';
policy_set?: string; // Policy set name
attestation?: boolean;
labels?: Record<string, string>;
};
}
```
### Scan Result Schema
```typescript
interface ScanResult {
scan_id: string;
image: string;
digest: string;
status: 'queued' | 'running' | 'completed' | 'failed';
started_at: string;
completed_at?: string;
verdict: 'PASS' | 'FAIL' | 'WARN' | 'PENDING';
confidence: number;
summary: {
critical: number;
high: number;
medium: number;
low: number;
unknown: number;
};
sbom_url: string;
attestation_url?: string;
findings_url: string;
}
```
## Error Handling
| Error | HTTP Status | Recovery |
|-------|-------------|----------|
| Image not found | 404 | Verify image reference and credentials |
| Registry auth failed | 401 | Re-configure registry credentials |
| Analyzer timeout | 504 | Retry with increased timeout |
| Policy set not found | 400 | Verify policy set exists |
| Attestation signing failed | 500 | Check Signer service health |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `scan_submitted_total` | Counter | `tenant` |
| `scan_completed_total` | Counter | `tenant`, `verdict` |
| `scan_duration_seconds` | Histogram | `tenant`, `image_size` |
| `scan_analyzer_duration_seconds` | Histogram | `analyzer` |
| `scan_findings_total` | Counter | `severity` |
### Trace Context
```
scan-submission
├── gateway-auth
├── scheduler-enqueue
└── scanner-execute
├── image-pull
├── layer-extract
├── analyzer-dotnet
├── analyzer-java
├── analyzer-node
├── ...
├── concelier-match
├── vexlens-apply
├── policy-evaluate
└── attestor-sign
```
## Related Flows
- [SBOM Generation Flow](03-sbom-generation-flow.md) - Detailed SBOM creation
- [Policy Evaluation Flow](04-policy-evaluation-flow.md) - K4 lattice details
- [CI/CD Gate Flow](10-cicd-gate-flow.md) - Pipeline integration
| Error | HTTP Status | Recovery |
|-------|-------------|----------|
| Image not found | 404 | Verify image reference and credentials |
| Registry auth failed | 401 | Re-configure registry credentials |
| Analyzer timeout | 504 | Retry with increased timeout |
| Policy set not found | 400 | Verify policy set exists |
| Attestation signing failed | 500 | Check Signer service health |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `scan_submitted_total` | Counter | `tenant` |
| `scan_completed_total` | Counter | `tenant`, `verdict` |
| `scan_duration_seconds` | Histogram | `tenant`, `image_size` |
| `scan_analyzer_duration_seconds` | Histogram | `analyzer` |
| `scan_findings_total` | Counter | `severity` |
### Trace Context
```
scan-submission
├── gateway-auth
├── scheduler-enqueue
└── scanner-execute
├── image-pull
├── layer-extract
├── analyzer-dotnet
├── analyzer-java
├── analyzer-node
├── ...
├── concelier-match
├── vexlens-apply
├── policy-evaluate
└── attestor-sign
```
## Related Flows
- [SBOM Generation Flow](03-sbom-generation-flow.md) - Detailed SBOM creation
- [Policy Evaluation Flow](04-policy-evaluation-flow.md) - K4 lattice details
- [CI/CD Gate Flow](10-cicd-gate-flow.md) - Pipeline integration

View File

@@ -0,0 +1,429 @@
# SBOM Generation Flow
## Overview
The SBOM Generation Flow describes how StellaOps creates Software Bills of Materials for container images using its 11 language-specific analyzers. The flow covers layer extraction, dependency detection, transitive resolution, and final SBOM assembly in SPDX 3.0.1 or CycloneDX 1.6 format.
**Business Value**: Complete, accurate SBOMs enable precise vulnerability matching and regulatory compliance (Executive Order 14028, EU CRA).
## Actors
| Actor | Type | Role |
|-------|------|------|
| Scanner | Service | Orchestrates SBOM generation |
| Layer Extractor | Component | Unpacks OCI layers |
| OS Detector | Component | Identifies base operating system |
| Language Analyzers (11) | Components | Detect ecosystem-specific dependencies |
| SBOM Assembler | Component | Merges results into final SBOM |
| Attestor | Service | Signs SBOM with DSSE envelope |
## Prerequisites
- Container image accessible (registry or local)
- Registry credentials if private registry
- Analyzer plugins enabled for target ecosystems
## Language Analyzers
StellaOps includes 11 specialized analyzers:
| Analyzer | Ecosystems | Lock Files | Manifests |
|----------|------------|------------|-----------|
| **DotNet** | NuGet, .NET | `packages.lock.json`, `*.deps.json` | `*.csproj`, `*.fsproj`, `Directory.Packages.props` |
| **Java** | Maven, Gradle | `pom.xml`, `build.gradle.kts` | `gradle.lockfile`, `maven-dependency-tree.txt` |
| **Node** | npm, yarn, pnpm | `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml` | `package.json` |
| **Python** | pip, Poetry, Pipenv | `requirements.txt`, `poetry.lock`, `Pipfile.lock` | `pyproject.toml`, `setup.py` |
| **Go** | Go Modules | `go.sum` | `go.mod` |
| **Rust** | Cargo | `Cargo.lock` | `Cargo.toml` |
| **PHP** | Composer | `composer.lock` | `composer.json` |
| **Ruby** | Bundler | `Gemfile.lock` | `Gemfile` |
| **Deno** | Deno, JSR | `deno.lock` | `deno.json`, `import_map.json` |
| **Bun** | Bun | `bun.lockb` | `package.json` |
| **Binary** | Native ELF/PE/Mach-O | N/A | Symbol tables, build IDs |
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ SBOM Generation Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────┐ ┌───────────┐ ┌────────────┐ ┌───────────────────────────────┐
│ Scanner │ │ Layer │ │ OS │ │ Language Analyzers │
│ │ │ Extractor │ │ Detector │ │ (DotNet, Java, Node, etc.) │
└────┬────┘ └─────┬─────┘ └──────┬─────┘ └───────────────┬───────────────┘
│ │ │ │
│ Fetch image │ │ │
│ manifest │ │ │
│──────┐ │ │ │
│ │ │ │ │
│<─────┘ │ │ │
│ │ │ │
│ Extract │ │ │
│ layers │ │ │
│─────────────>│ │ │
│ │ │ │
│ │ Unpack to │ │
│ │ work dir │ │
│ │───────┐ │ │
│ │ │ │ │
│ │<──────┘ │ │
│ │ │ │
│ │ Layers ready │ │
│<─────────────│ │ │
│ │ │ │
│ Detect OS │ │ │
│──────────────────────────────>│ │
│ │ │ │
│ │ │ Parse /etc/os-release │
│ │ │ Check package manager │
│ │ │──────────┐ │
│ │ │ │ │
│ │ │<─────────┘ │
│ │ │ │
│ OS: Alpine 3.19 │ │
│<──────────────────────────────│ │
│ │ │ │
│ Fan-out to │ │ │
│ all analyzers│ │ │
│────────────────────────────────────────────────────────>│
│ │ │ │
│ │ │ │ ┌─────────┐
│ │ │ │ │ DotNet │
│ │ │ │ └────┬────┘
│ │ │ │ │
│ │ │ Scan *.csproj
│ │ │ Parse deps.json
│ │ │ │<─────┘
│ │ │ │
│ │ │ │ ┌─────────┐
│ │ │ │ │ Java │
│ │ │ │ └────┬────┘
│ │ │ │ │
│ │ │ Parse pom.xml
│ │ │ Run gradle deps
│ │ │ │<─────┘
│ │ │ │
│ │ │ │ ┌─────────┐
│ │ │ │ │ Node │
│ │ │ │ └────┬────┘
│ │ │ │ │
│ │ │ Parse lockfiles
│ │ │ Build dep tree
│ │ │ │<─────┘
│ │ │ │
│ │ │ ... (8 more analyzers)
│ │ │ │
│ Analyzer │ │ │
│ results │ │ │
│<────────────────────────────────────────────────────────│
│ │ │ │
│ ┌────────────────┐ │ │
│ │ SBOM Assembler │ │ │
│ └───────┬────────┘ │ │
│ │ │ │
│ Merge & │ │ │
│ dedupe │ │ │
│──────────> │ │
│ │ │ │
│ SBOM │ │ │
│ document │ │ │
│<─────────│ │ │
│ │ │ │
│ ┌─────────┐ │ │
│ │Attestor │ │ │
│ └────┬────┘ │ │
│ │ │ │
│ Sign │ │ │
│───────> │ │
│ │ │ │
│ DSSE │ │ │
│<──────│ │ │
│ │ │ │
```
## Step-by-Step
### 1. Image Manifest Fetch
Scanner retrieves OCI image manifest:
```http
GET /v2/library/nginx/manifests/1.25 HTTP/1.1
Host: registry-1.docker.io
Accept: application/vnd.oci.image.manifest.v1+json
```
Response contains layer digests:
```json
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"digest": "sha256:config123..."
},
"layers": [
{"digest": "sha256:layer1...", "size": 31456789},
{"digest": "sha256:layer2...", "size": 1234567}
]
}
```
### 2. Layer Extraction
Layer Extractor downloads and unpacks each layer:
```
work/
├── layer-0/ # Base OS layer (Alpine, Debian, etc.)
│ ├── etc/
│ ├── lib/
│ └── usr/
├── layer-1/ # Application layer
│ ├── app/
│ │ ├── node_modules/
│ │ ├── package.json
│ │ └── package-lock.json
│ └── ...
└── merged/ # Union filesystem view
```
### 3. OS Detection
OS Detector identifies the base operating system:
| Detection Method | Files Checked |
|-----------------|---------------|
| `/etc/os-release` | `ID`, `VERSION_ID` |
| `/etc/alpine-release` | Alpine version |
| `/etc/debian_version` | Debian version |
| Package manager | `apk`, `dpkg`, `rpm` |
Result:
```json
{
"os": "alpine",
"version": "3.19",
"package_manager": "apk",
"architecture": "amd64"
}
```
### 4. Parallel Analyzer Execution
All 11 analyzers run in parallel on the merged filesystem:
#### DotNet Analyzer
```
Scanning for:
- *.csproj, *.fsproj, *.vbproj
- *.deps.json (runtime dependencies)
- packages.lock.json (NuGet lock)
- Directory.Packages.props (central management)
Output:
{
"packages": [
{"purl": "pkg:nuget/Newtonsoft.Json@13.0.3", "scope": "runtime"},
{"purl": "pkg:nuget/Microsoft.Extensions.Logging@8.0.0", "scope": "runtime"}
]
}
```
#### Java Analyzer
```
Scanning for:
- pom.xml (Maven)
- build.gradle, build.gradle.kts (Gradle)
- *.jar in lib/ directories
- MANIFEST.MF inside JARs
Output:
{
"packages": [
{"purl": "pkg:maven/com.google.guava/guava@32.1.2-jre", "scope": "compile"},
{"purl": "pkg:maven/org.slf4j/slf4j-api@2.0.9", "scope": "runtime"}
]
}
```
#### Node Analyzer
```
Scanning for:
- package.json + package-lock.json (npm)
- yarn.lock (Yarn)
- pnpm-lock.yaml (pnpm)
- node_modules/.package-lock.json
Output:
{
"packages": [
{"purl": "pkg:npm/express@4.18.2", "scope": "runtime"},
{"purl": "pkg:npm/lodash@4.17.21", "scope": "runtime"}
]
}
```
### 5. Transitive Resolution
Each analyzer resolves transitive dependencies:
```
express@4.18.2
├── accepts@1.3.8
│ ├── mime-types@2.1.35
│ │ └── mime-db@1.52.0
│ └── negotiator@0.6.3
├── body-parser@1.20.1
│ ├── bytes@3.1.2
│ └── ...
└── ...
```
### 6. SBOM Assembly
SBOM Assembler merges all analyzer results:
1. **Deduplication**: Remove duplicate PURLs across analyzers
2. **Relationship mapping**: Build component dependency graph
3. **Metadata enrichment**: Add licenses, hashes, supplier info
4. **Format conversion**: Output as SPDX 3.0.1 or CycloneDX 1.6
```json
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"version": 1,
"metadata": {
"timestamp": "2024-12-29T10:30:00Z",
"tools": [{"name": "stellaops-scanner", "version": "2.1.0"}],
"component": {
"type": "container",
"name": "docker.io/library/nginx",
"version": "1.25"
}
},
"components": [
{
"type": "library",
"bom-ref": "pkg:npm/express@4.18.2",
"name": "express",
"version": "4.18.2",
"purl": "pkg:npm/express@4.18.2",
"hashes": [{"alg": "SHA-256", "content": "abc123..."}]
}
],
"dependencies": [
{
"ref": "pkg:npm/express@4.18.2",
"dependsOn": ["pkg:npm/accepts@1.3.8", "pkg:npm/body-parser@1.20.1"]
}
]
}
```
### 7. SBOM Attestation
Attestor creates DSSE envelope for the SBOM:
```json
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{
"name": "docker.io/library/nginx",
"digest": {"sha256": "abc123..."}
}
],
"predicateType": "https://spdx.dev/Document",
"predicate": {
"sbom": "base64-encoded-sbom...",
"generator": "stellaops-scanner@2.1.0",
"timestamp": "2024-12-29T10:30:00Z"
}
}
```
Signed with DSSE:
```json
{
"payloadType": "application/vnd.in-toto+json",
"payload": "base64-encoded-statement...",
"signatures": [
{
"keyid": "sha256:signer-key-fingerprint",
"sig": "base64-encoded-signature..."
}
]
}
```
## Data Contracts
### Analyzer Output Schema
```typescript
interface AnalyzerOutput {
analyzer: string;
ecosystem: string;
packages: Array<{
purl: string;
name: string;
version: string;
scope: 'runtime' | 'dev' | 'optional';
locations: string[];
hashes?: Record<string, string>;
licenses?: string[];
}>;
relationships: Array<{
parent: string;
child: string;
type: 'depends-on' | 'dev-depends-on';
}>;
}
```
### SBOM Output Formats
| Format | Schema Version | Use Case |
|--------|---------------|----------|
| CycloneDX | 1.6 | Default, rich dependency graph |
| SPDX | 3.0.1 | Regulatory compliance, legal |
| SPDX | 2.3 | Legacy compatibility |
## Error Handling
| Error | Recovery |
|-------|----------|
| Layer download failed | Retry with exponential backoff |
| Analyzer timeout | Mark analyzer as partial, continue |
| Lock file parse error | Fall back to manifest parsing |
| Invalid PURL | Log warning, skip component |
| Attestation failed | Return SBOM without attestation |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `sbom_generation_duration_seconds` | Histogram | `image_size`, `layer_count` |
| `sbom_analyzer_duration_seconds` | Histogram | `analyzer` |
| `sbom_components_total` | Counter | `analyzer`, `ecosystem` |
| `sbom_size_bytes` | Histogram | `format` |
### Key Log Events
| Event | Level | Fields |
|-------|-------|--------|
| `sbom.generation.start` | INFO | `image`, `digest` |
| `sbom.analyzer.complete` | DEBUG | `analyzer`, `package_count` |
| `sbom.assembly.complete` | INFO | `total_components`, `format` |
| `sbom.attestation.signed` | INFO | `digest`, `keyid` |
## Related Flows
- [Scan Submission Flow](02-scan-submission-flow.md) - Parent flow
- [Binary Delta Attestation Flow](15-binary-delta-attestation-flow.md) - Binary-level analysis
- [Evidence Bundle Export Flow](13-evidence-bundle-export-flow.md) - SBOM packaging

View File

@@ -0,0 +1,490 @@
# Policy Evaluation Flow
## Overview
The Policy Evaluation Flow describes how StellaOps applies K4 lattice logic to vulnerability findings, incorporating VEX statements, reachability analysis, and confidence scoring to produce deterministic pass/fail verdicts. This is the core decision-making flow that determines whether a container image meets security requirements.
**Business Value**: Consistent, explainable security verdicts with full audit trail for compliance and governance.
## Actors
| Actor | Type | Role |
|-------|------|------|
| Scanner | Service | Submits findings for evaluation |
| Policy Engine | Service | Applies policy rules |
| VexLens | Service | Provides VEX consensus |
| ReachGraph | Service | Provides reachability state |
| Policy Store | Component | Stores policy definitions |
## Prerequisites
- Policy set configured for the tenant
- Scan findings generated with SBOM
- VEX statements loaded (optional)
- Reachability analysis completed (optional)
## K4 Lattice Model
StellaOps uses a 7-state K4 lattice for vulnerability reachability:
```
┌─────────────────────┐
│ ConfirmedReachable │ (Highest certainty)
└──────────┬──────────┘
┌──────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌────────────────┐ ┌─────────────────┐
│RuntimeObserved│ │StaticallyReach.│ │ Contested │
└───────┬───────┘ └────────┬───────┘ └────────┬────────┘
│ │ │
└──────────────────────┼──────────────────────┘
┌─────────────────────┐
│ Unknown │ (Default state)
└──────────┬──────────┘
┌──────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌────────────────┐ ┌─────────────────┐
│RuntimeUnobserv│ │Statically Unr. │ │ │
└───────┬───────┘ └────────┬───────┘ └─────────────────┘
│ │
└──────────┬──────────┘
┌─────────────────────┐
│ConfirmedUnreachable │ (Lowest risk)
└─────────────────────┘
```
### State Definitions
| State | Code | Description |
|-------|------|-------------|
| Unknown | `U` | No reachability data available |
| StaticallyReachable | `SR` | Static analysis shows potential call path |
| StaticallyUnreachable | `SU` | Static analysis shows no call path |
| RuntimeObserved | `RO` | Runtime telemetry confirmed execution |
| RuntimeUnobserved | `RU` | Runtime telemetry shows no execution |
| ConfirmedReachable | `CR` | Both static and runtime confirm reachability |
| ConfirmedUnreachable | `CU` | Both static and runtime confirm unreachable |
| Contested | `X` | Conflicting evidence (requires review) |
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Policy Evaluation Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌───────────┐ ┌─────────────┐
│ Scanner │ │ Policy │ │ VexLens │ │ ReachGraph│ │Policy Store │
└────┬────┘ └────┬────┘ └────┬────┘ └─────┬─────┘ └──────┬──────┘
│ │ │ │ │
│ Evaluate │ │ │ │
│ request │ │ │ │
│────────────>│ │ │ │
│ │ │ │ │
│ │ Load policy │ │ │
│ │ set │ │ │
│ │─────────────────────────────────────────────>
│ │ │ │ │
│ │ Policy │ │ │
│ │ rules │ │ │
│ │<─────────────────────────────────────────────
│ │ │ │ │
│ │ Get VEX │ │ │
│ │ consensus │ │ │
│ │────────────>│ │ │
│ │ │ │ │
│ │ │ Query issuers│ │
│ │ │──────┐ │ │
│ │ │ │ │ │
│ │ │<─────┘ │ │
│ │ │ │ │
│ │ VEX status │ │ │
│ │ per CVE │ │ │
│ │<────────────│ │ │
│ │ │ │ │
│ │ Get reach │ │ │
│ │ states │ │ │
│ │─────────────────────────────> │
│ │ │ │ │
│ │ │ │ Query call │
│ │ │ │ graph │
│ │ │ │───────┐ │
│ │ │ │ │ │
│ │ │ │<──────┘ │
│ │ │ │ │
│ │ K4 states │ │ │
│ │<───────────────────────────── │
│ │ │ │ │
│ │ Apply rules │ │ │
│ │──────┐ │ │ │
│ │ │ │ │ │
│ │<─────┘ │ │ │
│ │ │ │ │
│ │ Compute │ │ │
│ │ confidence │ │ │
│ │──────┐ │ │ │
│ │ │ │ │ │
│ │<─────┘ │ │ │
│ │ │ │ │
│ Verdict + │ │ │ │
│ explain │ │ │ │
│<────────────│ │ │ │
│ │ │ │ │
```
## Step-by-Step
### 1. Evaluation Request
Scanner submits findings for policy evaluation:
```http
POST /internal/evaluate HTTP/1.1
Content-Type: application/json
```
### 2. Policy Loading
Policy Engine loads the policy set from storage:
```yaml
# Policy Set: production
version: "stella-dsl@1"
name: production
description: Production deployment policy
rules:
- name: no-critical-reachable
description: Block critical CVEs with reachable code
condition: |
severity == 'critical' AND
reachability IN ['SR', 'RO', 'CR'] AND
vex_status != 'not_affected'
action: FAIL
- name: no-critical-unfixed
description: Block critical CVEs without fixes
condition: |
severity == 'critical' AND
fixed_version == null
action: FAIL
- name: warn-high-reachable
description: Warn on high CVEs with reachable code
condition: |
severity == 'high' AND
reachability IN ['SR', 'RO', 'CR']
action: WARN
- name: allow-vex-not-affected
description: Allow CVEs marked not affected by trusted issuer
condition: |
vex_status == 'not_affected' AND
vex_issuer_trust >= 0.8
action: PASS
defaults:
action: PASS
confidence_threshold: 0.7
```
### 3. VEX Consensus Query
Policy Engine queries VexLens for VEX statements:
```http
POST /internal/vex/consensus HTTP/1.1
Content-Type: application/json
```
Response with issuer consensus:
```json
{
"statements": [
{
"vulnerability": "CVE-2024-1234",
"status": "affected",
"issuers": [
{"name": "vendor-psirt", "trust": 0.95, "status": "affected"},
{"name": "osv", "trust": 0.7, "status": "affected"}
],
"consensus": "affected",
"confidence": 0.92
},
{
"vulnerability": "CVE-2024-5678",
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path",
"issuers": [
{"name": "vendor-psirt", "trust": 0.95, "status": "not_affected"}
],
"consensus": "not_affected",
"confidence": 0.95
}
]
}
```
### 4. Reachability State Query
Policy Engine queries ReachGraph for K4 states:
```http
POST /internal/reachability/states HTTP/1.1
Content-Type: application/json
```
Response with K4 lattice states:
```json
{
"states": [
{
"package": "pkg:npm/lodash@4.17.20",
"state": "StaticallyReachable",
"evidence": {
"static": {"call_paths": 3, "entry_points": ["src/api/handler.js:45"]},
"runtime": null
}
},
{
"package": "pkg:npm/express@4.18.0",
"state": "RuntimeObserved",
"evidence": {
"static": {"call_paths": 12},
"runtime": {"invocations": 1547, "last_seen": "2024-12-29T09:00:00Z"}
}
}
]
}
```
### 5. Rule Evaluation
Policy Engine evaluates each finding against rules:
```
Finding: CVE-2024-1234 in pkg:npm/lodash@4.17.20
- severity: critical
- reachability: StaticallyReachable (SR)
- vex_status: affected
- fixed_version: 4.17.21
Rule: no-critical-reachable
- condition: severity == 'critical' AND reachability IN ['SR', 'RO', 'CR'] AND vex_status != 'not_affected'
- evaluation: critical == 'critical' ✓ AND SR IN ['SR', 'RO', 'CR'] ✓ AND 'affected' != 'not_affected' ✓
- result: MATCH → FAIL
```
### 6. Confidence Scoring
Policy Engine computes confidence score based on 5 factors:
| Factor | Weight | Description |
|--------|--------|-------------|
| Reachability | 0.30 | K4 state certainty |
| Runtime | 0.25 | Runtime observation freshness |
| VEX | 0.20 | VEX issuer trust level |
| Provenance | 0.15 | SBOM completeness |
| Policy | 0.10 | Rule specificity |
```
Confidence = Σ(factor_weight × factor_score)
For CVE-2024-1234:
- Reachability: 0.30 × 0.7 (SR state) = 0.21
- Runtime: 0.25 × 0.0 (no runtime data) = 0.00
- VEX: 0.20 × 0.92 (affected consensus) = 0.18
- Provenance: 0.15 × 1.0 (complete SBOM) = 0.15
- Policy: 0.10 × 1.0 (exact rule match) = 0.10
Total Confidence: 0.64
```
### 7. Verdict Assembly
Policy Engine assembles final verdict:
```json
{
"verdict": "FAIL",
"confidence": 0.64,
"summary": {
"total_findings": 2,
"blocked": 1,
"warned": 0,
"passed": 1
},
"violations": [
{
"finding": {
"cve": "CVE-2024-1234",
"package": "pkg:npm/lodash@4.17.20",
"severity": "critical"
},
"rule": "no-critical-reachable",
"action": "FAIL",
"explain": {
"reason": "Critical CVE with reachable code path",
"factors": {
"reachability": "StaticallyReachable - 3 call paths from entry points",
"vex": "Marked as 'affected' by vendor-psirt (trust: 0.95)",
"remediation": "Upgrade lodash to 4.17.21"
}
}
}
],
"passed": [
{
"finding": {
"cve": "CVE-2024-5678",
"package": "pkg:npm/express@4.18.0",
"severity": "high"
},
"rule": "allow-vex-not-affected",
"action": "PASS",
"explain": {
"reason": "VEX statement confirms not affected",
"factors": {
"vex": "Not affected - vulnerable_code_not_in_execute_path",
"issuer": "vendor-psirt (trust: 0.95)"
}
}
}
]
}
```
## Data Contracts
### Policy Rule Schema
```typescript
interface PolicyRule {
name: string;
description?: string;
condition: string; // stella-dsl@1 expression
action: 'PASS' | 'WARN' | 'FAIL';
priority?: number;
exceptions?: Array<{
id: string;
expires?: string;
justification: string;
}>;
}
```
### Verdict Schema
```typescript
interface PolicyVerdict {
verdict: 'PASS' | 'WARN' | 'FAIL';
confidence: number; // 0.0-1.0
summary: {
total_findings: number;
blocked: number;
warned: number;
passed: number;
};
violations: Array<ViolationDetail>;
warnings: Array<ViolationDetail>;
passed: Array<PassedDetail>;
metadata: {
policy_set: string;
policy_version: string;
evaluated_at: string;
evaluation_ms: number;
};
}
```
## Error Handling
| Error | Recovery |
|-------|----------|
| Policy set not found | Use default policy or return 400 |
| VexLens timeout | Continue without VEX data, reduce confidence |
| ReachGraph timeout | Use Unknown state, reduce confidence |
| Invalid rule syntax | Skip rule, log error, continue |
| Conflicting rules | Apply highest priority rule |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `policy_evaluation_total` | Counter | `policy_set`, `verdict` |
| `policy_evaluation_duration_ms` | Histogram | `policy_set` |
| `policy_rule_matches_total` | Counter | `rule`, `action` |
| `policy_confidence_score` | Histogram | `policy_set` |
### Trace Context
```
policy-evaluation
├── policy-load
├── vexlens-query
├── reachgraph-query
├── rule-evaluation
│ ├── rule-no-critical-reachable
│ ├── rule-no-critical-unfixed
│ └── rule-allow-vex-not-affected
├── confidence-scoring
└── verdict-assembly
```
## Related Flows
- [Scan Submission Flow](02-scan-submission-flow.md) - Parent flow
- [CI/CD Gate Flow](10-cicd-gate-flow.md) - Pipeline integration
- [Exception Approval Workflow](17-exception-approval-workflow.md) - Policy exceptions
- [Multi-Tenant Policy Rollout Flow](14-multi-tenant-policy-rollout-flow.md) - Policy distribution
| ReachGraph timeout | Use Unknown state, reduce confidence |
| Invalid rule syntax | Skip rule, log error, continue |
| Conflicting rules | Apply highest priority rule |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `policy_evaluation_total` | Counter | `policy_set`, `verdict` |
| `policy_evaluation_duration_ms` | Histogram | `policy_set` |
| `policy_rule_matches_total` | Counter | `rule`, `action` |
| `policy_confidence_score` | Histogram | `policy_set` |
### Trace Context
```
policy-evaluation
├── policy-load
├── vexlens-query
├── reachgraph-query
├── rule-evaluation
│ ├── rule-no-critical-reachable
│ ├── rule-no-critical-unfixed
│ └── rule-allow-vex-not-affected
├── confidence-scoring
└── verdict-assembly
```
## Related Flows
- [Scan Submission Flow](02-scan-submission-flow.md) - Parent flow
- [CI/CD Gate Flow](10-cicd-gate-flow.md) - Pipeline integration
- [Exception Approval Workflow](17-exception-approval-workflow.md) - Policy exceptions
- [Multi-Tenant Policy Rollout Flow](14-multi-tenant-policy-rollout-flow.md) - Policy distribution

View File

@@ -0,0 +1,382 @@
# Notification Flow
## Overview
The Notification Flow describes how StellaOps delivers alerts and notifications to users through multiple channels including email, Slack, Microsoft Teams, and webhooks. Notifications are triggered by system events such as scan completions, policy violations, new advisories, and scheduled reports.
**Business Value**: Timely notifications ensure security teams are immediately aware of critical issues without constantly monitoring dashboards.
## Actors
| Actor | Type | Role |
|-------|------|------|
| Event Source | Service | Emits triggering events |
| Scheduler | Service | Manages scheduled notifications |
| Notify | Service | Routes and delivers notifications |
| Channel Adapters | Components | Email, Slack, Teams, Webhook |
| User | Human | Receives notifications |
## Prerequisites
- Notification channels configured in tenant settings
- Channel credentials (SMTP, Slack webhook, Teams connector)
- User notification preferences set
## Supported Channels
| Channel | Protocol | Configuration |
|---------|----------|---------------|
| Email | SMTP/SMTPS | `smtp.host`, `smtp.port`, `smtp.user`, `smtp.password` |
| Slack | Webhook | `slack.webhook_url` |
| Microsoft Teams | Connector | `teams.webhook_url` |
| Webhook | HTTP POST | `webhook.url`, `webhook.headers` |
| PagerDuty | API | `pagerduty.routing_key` |
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Notification Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌──────────┐ ┌───────────┐ ┌────────┐ ┌─────────────────────────────────────────┐
│ Event │ │ Scheduler │ │ Notify │ │ Channel Adapters │
│ Source │ │ │ │ │ │ Email │ Slack │ Teams │ Webhook │ PD │
└────┬─────┘ └─────┬─────┘ └───┬────┘ └────┬───┴───┬───┴───┬───┴────┬────┴──┬──┘
│ │ │ │ │ │ │ │
│ Emit event │ │ │ │ │ │ │
│─────────────────────────>│ │ │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ Match │ │ │ │ │
│ │ │ rules │ │ │ │ │
│ │ │───┐ │ │ │ │ │
│ │ │ │ │ │ │ │ │
│ │ │<──┘ │ │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ Route to │ │ │ │ │
│ │ │ channels │ │ │ │ │
│ │ │───────────>│ │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ Send │ │ │ │
│ │ │ │ email │ │ │ │
│ │ │ │──┐ │ │ │ │
│ │ │ │ │ │ │ │ │
│ │ │ │<─┘ │ │ │ │
│ │ │ │ │ │ │ │
│ │ │───────────────────>│ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ │ Post │ │ │
│ │ │ │ │ msg │ │ │
│ │ │ │ │──┐ │ │ │
│ │ │ │ │ │ │ │ │
│ │ │ │ │<─┘ │ │ │
│ │ │ │ │ │ │ │
│ │ │───────────────────────────>│ │ │
│ │ │ │ │ │ │ │
│ │ │ │ │ │ Post │ │
│ │ │ │ │ │ card │ │
│ │ │ │ │ │──┐ │ │
│ │ │ │ │ │ │ │ │
│ │ │ │ │ │<─┘ │ │
│ │ │ │ │ │ │ │
│ │ │ Log │ │ │ │ │
│ │ │ delivery │ │ │ │ │
│ │ │───┐ │ │ │ │ │
│ │ │ │ │ │ │ │ │
│ │ │<──┘ │ │ │ │ │
│ │ │ │ │ │ │ │
```
## Step-by-Step
### 1. Event Emission
Various services emit notification-triggering events:
```json
{
"event_type": "scan.complete",
"event_id": "evt-123456",
"timestamp": "2024-12-29T10:30:00Z",
"tenant_id": "acme-corp",
"payload": {
"scan_id": "scan-7f3a9b2c-...",
"image": "docker.io/library/nginx:1.25",
"verdict": "FAIL",
"violations": 3,
"severity_breakdown": {
"critical": 1,
"high": 2
}
}
}
```
### 2. Notification Rule Matching
Notify service matches event against configured rules:
```yaml
# Notification Rules
rules:
- name: critical-scan-failure
description: Alert on critical vulnerabilities
event_type: scan.complete
conditions:
- field: payload.verdict
operator: eq
value: FAIL
- field: payload.severity_breakdown.critical
operator: gt
value: 0
channels:
- type: slack
config_ref: security-team-slack
urgency: high
- type: email
config_ref: security-leads
- type: pagerduty
config_ref: on-call-rotation
- name: daily-scan-summary
description: Daily summary of all scans
event_type: scheduled.daily_summary
channels:
- type: email
config_ref: security-distribution
```
### 3. Channel Routing
Notify determines which channels to activate:
| Rule Match | Channel | Priority |
|------------|---------|----------|
| critical-scan-failure | slack | HIGH |
| critical-scan-failure | email | HIGH |
| critical-scan-failure | pagerduty | HIGH |
### 4. Message Rendering
Each channel adapter renders the message in appropriate format:
#### Email Template
```html
<!DOCTYPE html>
<html>
<body>
<h1>🚨 Critical Vulnerability Detected</h1>
<p>A scan has failed with critical vulnerabilities.</p>
<table>
<tr><th>Image</th><td>docker.io/library/nginx:1.25</td></tr>
<tr><th>Verdict</th><td style="color:red">FAIL</td></tr>
<tr><th>Critical</th><td>1</td></tr>
<tr><th>High</th><td>2</td></tr>
</table>
<p><a href="https://console.stellaops.local/scans/scan-7f3a9b2c-...">View Details</a></p>
</body>
</html>
```
#### Slack Block Kit
```json
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "🚨 Critical Vulnerability Detected"
}
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": "*Image:*\n`nginx:1.25`"},
{"type": "mrkdwn", "text": "*Verdict:*\n:x: FAIL"},
{"type": "mrkdwn", "text": "*Critical:*\n1"},
{"type": "mrkdwn", "text": "*High:*\n2"}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "View Details"},
"url": "https://console.stellaops.local/scans/scan-7f3a9b2c-..."
}
]
}
]
}
```
#### Teams Adaptive Card
```json
{
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "🚨 Critical Vulnerability Detected",
"weight": "bolder",
"size": "large"
},
{
"type": "FactSet",
"facts": [
{"title": "Image", "value": "nginx:1.25"},
{"title": "Verdict", "value": "FAIL"},
{"title": "Critical", "value": "1"},
{"title": "High", "value": "2"}
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "View Details",
"url": "https://console.stellaops.local/scans/scan-7f3a9b2c-..."
}
]
}
```
### 5. Delivery
Each adapter delivers to its channel:
| Channel | Protocol | Retry Policy |
|---------|----------|--------------|
| Email | SMTP | 3 retries, exponential backoff |
| Slack | HTTPS POST | 3 retries, 1s/2s/4s |
| Teams | HTTPS POST | 3 retries, 1s/2s/4s |
| Webhook | HTTPS POST | 5 retries, configurable |
| PagerDuty | HTTPS POST | 5 retries, 2s/4s/8s/16s/32s |
### 6. Delivery Logging
Notify logs delivery status to `notify.delivery_log`:
```json
{
"delivery_id": "dlv-789abc",
"event_id": "evt-123456",
"channel": "slack",
"status": "delivered",
"attempts": 1,
"delivered_at": "2024-12-29T10:30:02Z",
"response": {"ok": true}
}
```
## Data Contracts
### Notification Event Schema
```typescript
interface NotificationEvent {
event_type: string;
event_id: string;
timestamp: string;
tenant_id: string;
payload: Record<string, unknown>;
metadata?: {
source_service: string;
correlation_id?: string;
};
}
```
### Channel Configuration Schema
```typescript
interface ChannelConfig {
type: 'email' | 'slack' | 'teams' | 'webhook' | 'pagerduty';
name: string;
enabled: boolean;
config: EmailConfig | SlackConfig | TeamsConfig | WebhookConfig | PagerDutyConfig;
}
interface EmailConfig {
smtp_host: string;
smtp_port: number;
smtp_user?: string;
smtp_password?: string;
smtp_tls: boolean;
from_address: string;
to_addresses: string[];
}
interface SlackConfig {
webhook_url: string;
channel?: string;
username?: string;
icon_emoji?: string;
}
interface WebhookConfig {
url: string;
method: 'POST' | 'PUT';
headers?: Record<string, string>;
auth?: {
type: 'basic' | 'bearer' | 'api_key';
credentials: string;
};
}
```
## Event Types
| Event Type | Source | Description |
|------------|--------|-------------|
| `scan.complete` | Scanner | Scan finished with results |
| `scan.failed` | Scanner | Scan execution failed |
| `policy.violation` | Policy | Policy rule triggered |
| `advisory.new` | Concelier | New advisory ingested |
| `advisory.update` | Concelier | Advisory modified |
| `vex.issued` | VexLens | New VEX statement |
| `exception.expiring` | Policy | Exception about to expire |
| `scheduled.daily_summary` | Scheduler | Daily digest |
| `scheduled.weekly_report` | Scheduler | Weekly report |
## Error Handling
| Error | Recovery |
|-------|----------|
| SMTP connection failed | Retry with backoff, queue for later |
| Slack webhook 429 | Respect Retry-After header |
| Teams connector 502 | Retry up to 3 times |
| Webhook timeout | Retry with increased timeout |
| Invalid recipient | Skip recipient, log error |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `notify_events_received_total` | Counter | `event_type` |
| `notify_deliveries_total` | Counter | `channel`, `status` |
| `notify_delivery_latency_ms` | Histogram | `channel` |
| `notify_retries_total` | Counter | `channel`, `reason` |
### Key Log Events
| Event | Level | Fields |
|-------|-------|--------|
| `notify.event.received` | INFO | `event_type`, `event_id` |
| `notify.rule.matched` | DEBUG | `rule_name`, `channels` |
| `notify.delivery.attempt` | DEBUG | `channel`, `attempt` |
| `notify.delivery.success` | INFO | `channel`, `delivery_id` |
| `notify.delivery.failed` | WARN | `channel`, `error` |
## Related Flows
- [Scan Submission Flow](02-scan-submission-flow.md) - Triggers scan notifications
- [Advisory Drift Re-scan Flow](11-advisory-drift-rescan-flow.md) - Advisory notifications
- [Exception Approval Workflow](17-exception-approval-workflow.md) - Exception notifications

View File

@@ -0,0 +1,383 @@
# Export Flow
## Overview
The Export Flow describes how StellaOps generates and delivers reports, evidence bundles, and compliance documentation. Exports can be triggered on-demand or scheduled, and support multiple formats including PDF, Excel, JSON, and SARIF.
**Business Value**: Automated, auditable exports reduce manual effort for compliance reporting and enable integration with external systems.
## Actors
| Actor | Type | Role |
|-------|------|------|
| User | Human | Requests or schedules exports |
| Console | System | UI for export configuration |
| Gateway | Service | Routes export requests |
| ExportCenter | Service | Orchestrates export generation |
| Scanner | Service | Provides scan data |
| Policy | Service | Provides policy verdicts |
| EvidenceLocker | Service | Stores sealed evidence |
| RustFS | Storage | Stores export artifacts |
## Prerequisites
- User has export permissions for the resource
- Data exists for the requested export scope
- Export template configured (for custom formats)
## Supported Export Formats
| Format | Extension | Use Case |
|--------|-----------|----------|
| PDF | `.pdf` | Human-readable reports |
| Excel | `.xlsx` | Data analysis, spreadsheet import |
| JSON | `.json` | API integration, automation |
| SARIF | `.sarif` | IDE integration, GitHub Code Scanning |
| CycloneDX | `.cdx.json` | SBOM exchange |
| SPDX | `.spdx.json` | SBOM compliance |
| CSV | `.csv` | Data export, legacy systems |
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Export Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌────────┐ ┌─────────┐ ┌─────────┐ ┌────────────┐ ┌──────────────┐ ┌────────┐
│ User │ │ Console │ │ Gateway │ │ExportCenter│ │EvidenceLocker│ │ RustFS │
└───┬────┘ └────┬────┘ └────┬────┘ └─────┬──────┘ └──────┬───────┘ └───┬────┘
│ │ │ │ │ │
│ Request │ │ │ │ │
│ export │ │ │ │ │
│───────────>│ │ │ │ │
│ │ │ │ │ │
│ │ POST │ │ │ │
│ │ /exports │ │ │ │
│ │───────────>│ │ │ │
│ │ │ │ │ │
│ │ │ Forward │ │ │
│ │ │────────────>│ │ │
│ │ │ │ │ │
│ │ 202 │ │ │ │
│ │ Accepted │ │ │ │
│ │<───────────│ │ │ │
│ │ │ │ │ │
│ Export │ │ │ │ │
│ queued │ │ │ │ │
│<───────────│ │ │ │ │
│ │ │ │ │ │
│ │ │ │ Query scan │ │
│ │ │ │ data │ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ │ │ Query policy │ │
│ │ │ │ verdicts │ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ │ │ Render │ │
│ │ │ │ template │ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ │ │ Store artifact │ │
│ │ │ │────────────────────────────────>
│ │ │ │ │ │
│ │ │ │ {path} │ │
│ │ │ │<────────────────────────────────
│ │ │ │ │ │
│ │ │ │ Seal evidence │ │
│ │ │ │───────────────>│ │
│ │ │ │ │ │
│ │ │ │ Sealed bundle │ │
│ │ │ │<───────────────│ │
│ │ │ │ │ │
│ │ │ │ Store sealed │ │
│ │ │ │────────────────────────────────>
│ │ │ │ │ │
│ WebSocket: │ │ │ │ │
│ export │ │ │ │ │
│ ready │ │ │ │ │
│<───────────│ │ │ │ │
│ │ │ │ │ │
```
## Step-by-Step
### 1. Export Request
User requests export via Console or API:
```http
POST /api/v1/exports HTTP/1.1
Authorization: Bearer {jwt}
X-Tenant-Id: acme-corp
Content-Type: application/json
```
### 2. Export Job Creation
ExportCenter creates export job:
```json
{
"export_id": "exp-456def",
"status": "queued",
"type": "scan_report",
"format": "pdf",
"created_at": "2024-12-29T10:30:00Z",
"estimated_completion": "PT2M"
}
```
### 3. Data Gathering
ExportCenter queries multiple data sources:
| Source | Query | Data |
|--------|-------|------|
| Scanner | `GET /internal/scans/{id}` | Scan results, findings |
| Policy | `GET /internal/verdicts/{scan_id}` | Policy verdicts |
| VexLens | `GET /internal/vex/applied/{scan_id}` | VEX statements |
| SbomService | `GET /internal/sboms/{digest}` | SBOM document |
### 4. Template Rendering
ExportCenter applies report template:
```
Templates Available:
├── compliance-executive # High-level summary for executives
├── compliance-detailed # Full findings with remediation
├── audit-evidence # Audit trail with attestations
├── developer-sarif # IDE-compatible SARIF output
└── custom-{tenant} # Tenant-specific templates
```
PDF generation uses Chromium for high-fidelity rendering:
```typescript
const pdf = await chromium.pdf({
content: renderedHtml,
format: 'A4',
margin: { top: '1cm', bottom: '1cm' },
displayHeaderFooter: true,
headerTemplate: '<div>StellaOps Security Report</div>',
footerTemplate: '<div>Page <span class="pageNumber"></span></div>'
});
```
### 5. Artifact Storage
Export artifact stored in RustFS:
```
blobs/
└── exports/
└── acme-corp/
└── 2024/
└── 12/
└── exp-456def/
├── report.pdf
├── sbom.cdx.json
└── manifest.json
```
Manifest tracks export contents:
```json
{
"export_id": "exp-456def",
"created_at": "2024-12-29T10:32:00Z",
"artifacts": [
{"name": "report.pdf", "size": 245678, "sha256": "abc..."},
{"name": "sbom.cdx.json", "size": 89012, "sha256": "def..."}
],
"expires_at": "2025-01-28T10:32:00Z"
}
```
### 6. Evidence Sealing (Optional)
If evidence sealing requested, EvidenceLocker creates sealed bundle:
```json
{
"bundle_id": "bnd-789ghi",
"sealed_at": "2024-12-29T10:32:00Z",
"contents": [
{"type": "scan_result", "id": "scan-7f3a9b2c-..."},
{"type": "sbom", "digest": "sha256:..."},
{"type": "policy_verdict", "id": "verdict-..."},
{"type": "attestation", "digest": "sha256:..."}
],
"merkle_root": "sha256:merkle-root...",
"signature": "base64:signature..."
}
```
### 7. Delivery
Export delivered via:
- **Download**: Signed URL with expiration
- **Email**: Attachment or link (based on size)
- **Webhook**: POST to configured endpoint
- **S3**: Direct upload to external bucket
Download URL generation:
```http
GET /api/v1/exports/exp-456def/download HTTP/1.1
Authorization: Bearer {jwt}
Response:
{
"download_url": "https://storage.stellaops.local/exports/exp-456def/report.pdf?sig=...",
"expires_at": "2024-12-29T11:32:00Z"
}
```
## Export Types
| Type | Description | Formats |
|------|-------------|---------|
| `scan_report` | Single scan results | PDF, JSON, SARIF |
| `scan_summary` | Multiple scans summary | PDF, Excel, CSV |
| `sbom` | Software Bill of Materials | CycloneDX, SPDX |
| `vulnerability_report` | CVE-focused report | PDF, Excel, CSV |
| `policy_compliance` | Policy compliance status | PDF, JSON |
| `evidence_bundle` | Sealed evidence package | ZIP, TAR.GZ |
| `audit_log` | Activity audit trail | JSON, CSV |
## Data Contracts
### Export Request Schema
```typescript
interface ExportRequest {
type: ExportType;
format: ExportFormat;
scope: {
scan_ids?: string[];
image_refs?: string[];
date_range?: {
start: string;
end: string;
};
policy_sets?: string[];
};
options?: {
include_sbom?: boolean;
include_evidence?: boolean;
include_remediation?: boolean;
template?: string;
custom_fields?: Record<string, string>;
};
delivery?: {
method: 'download' | 'email' | 'webhook' | 's3';
config?: DeliveryConfig;
};
}
```
### Export Response Schema
```typescript
interface ExportResponse {
export_id: string;
status: 'queued' | 'processing' | 'completed' | 'failed';
type: ExportType;
format: ExportFormat;
created_at: string;
completed_at?: string;
artifacts?: Array<{
name: string;
size: number;
sha256: string;
download_url?: string;
}>;
error?: string;
}
```
## Scheduled Exports
Configure recurring exports via cron:
```yaml
schedules:
- name: weekly-executive-summary
cron: "0 8 * * MON" # Every Monday at 8 AM
export:
type: scan_summary
format: pdf
scope:
date_range:
start: "-7d"
end: "now"
options:
template: compliance-executive
delivery:
method: email
config:
to: ["security-leads@acme.com"]
subject: "Weekly Security Summary - {{week}}"
```
## Error Handling
| Error | Recovery |
|-------|----------|
| Data not found | Return 404 with scope details |
| Template error | Fall back to default template |
| Storage failure | Retry with exponential backoff |
| PDF generation timeout | Simplify report, retry |
| Delivery failure | Queue for retry, notify user |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `export_requests_total` | Counter | `type`, `format` |
| `export_duration_seconds` | Histogram | `type`, `format` |
| `export_size_bytes` | Histogram | `type`, `format` |
| `export_failures_total` | Counter | `type`, `reason` |
### Trace Context
```
export-request
├── data-gather
│ ├── scanner-query
│ ├── policy-query
│ └── vexlens-query
├── template-render
├── artifact-storage
├── evidence-seal (optional)
└── delivery
```
## Related Flows
- [Scan Submission Flow](02-scan-submission-flow.md) - Source of scan data
- [Evidence Bundle Export Flow](13-evidence-bundle-export-flow.md) - Detailed evidence packaging
- [Dashboard Data Flow](01-dashboard-data-flow.md) - Data aggregation patterns
│ ├── policy-query
│ └── vexlens-query
├── template-render
├── artifact-storage
├── evidence-seal (optional)
└── delivery
```
## Related Flows
- [Scan Submission Flow](02-scan-submission-flow.md) - Source of scan data
- [Evidence Bundle Export Flow](13-evidence-bundle-export-flow.md) - Detailed evidence packaging
- [Dashboard Data Flow](01-dashboard-data-flow.md) - Data aggregation patterns

View File

@@ -0,0 +1,465 @@
# CI/CD Gate Flow
## Overview
The CI/CD Gate Flow describes how StellaOps integrates into continuous integration and deployment pipelines to provide automated security gates. The flow covers CLI-based scanning, policy evaluation, and pass/fail decisions that control pipeline progression.
**Business Value**: Shift-left security by catching vulnerabilities before deployment, with deterministic, reproducible verdicts that integrate into existing DevOps workflows.
## Actors
| Actor | Type | Role |
|-------|------|------|
| CI Pipeline | System | GitHub Actions, GitLab CI, Jenkins, etc. |
| StellaOps CLI | Tool | Executes scans from pipeline |
| Gateway | Service | API entry point |
| Scanner | Service | Performs image analysis |
| Policy Engine | Service | Evaluates security policies |
| Attestor | Service | Signs scan results |
## Prerequisites
- StellaOps CLI installed in CI environment
- API credentials configured (token or OIDC)
- Policy set defined for the pipeline
- Container image built and available
## Supported CI/CD Platforms
| Platform | Integration Method | Credentials |
|----------|-------------------|-------------|
| GitHub Actions | Action + CLI | OIDC or PAT |
| GitLab CI | Job template + CLI | CI_JOB_TOKEN or PAT |
| Azure DevOps | Task + CLI | Service connection |
| Jenkins | Plugin + CLI | Credentials binding |
| CircleCI | Orb + CLI | Context variables |
| Tekton | Task + CLI | Kubernetes secrets |
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ CI/CD Gate Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌────────────┐ ┌───────────┐ ┌─────────┐ ┌─────────┐ ┌────────┐ ┌─────────┐
│ CI Pipeline│ │StellaOps │ │ Gateway │ │ Scanner │ │ Policy │ │ Attestor│
│ │ │ CLI │ │ │ │ │ │ │ │ │
└─────┬──────┘ └─────┬─────┘ └────┬────┘ └────┬────┘ └───┬────┘ └────┬────┘
│ │ │ │ │ │
│ docker build │ │ │ │ │
│───────┐ │ │ │ │ │
│ │ │ │ │ │ │
│<──────┘ │ │ │ │ │
│ │ │ │ │ │
│ stellaops │ │ │ │ │
│ scan │ │ │ │ │
│ --policy=prod │ │ │ │ │
│──────────────>│ │ │ │ │
│ │ │ │ │ │
│ │ POST /scans │ │ │ │
│ │────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ Dispatch │ │ │
│ │ │───────────>│ │ │
│ │ │ │ │ │
│ │ │ │ Analyze │ │
│ │ │ │ image │ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ │ │ Evaluate │ │
│ │ │ │──────────>│ │
│ │ │ │ │ │
│ │ │ │ │ Apply │
│ │ │ │ │ rules │
│ │ │ │ │───┐ │
│ │ │ │ │ │ │
│ │ │ │ │<──┘ │
│ │ │ │ │ │
│ │ │ │ Verdict │ │
│ │ │ │<──────────│ │
│ │ │ │ │ │
│ │ │ │ Sign │ │
│ │ │ │──────────────────────>│
│ │ │ │ │ │
│ │ │ │ DSSE │ │
│ │ │ │<──────────────────────│
│ │ │ │ │ │
│ │ │ Result │ │ │
│ │ │<───────────│ │ │
│ │ │ │ │ │
│ │ Verdict │ │ │ │
│ │<────────────│ │ │ │
│ │ │ │ │ │
│ Exit code │ │ │ │ │
│ (0=pass, │ │ │ │ │
│ 1=fail) │ │ │ │ │
│<──────────────│ │ │ │ │
│ │ │ │ │ │
│ [if pass] │ │ │ │ │
│ docker push │ │ │ │ │
│───────┐ │ │ │ │ │
│ │ │ │ │ │ │
│<──────┘ │ │ │ │ │
│ │ │ │ │ │
```
## Step-by-Step
### 1. Pipeline Configuration
#### GitHub Actions Example
```yaml
name: Build and Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-scan:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # For OIDC
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: |
docker build -t myapp:${{ github.sha }} .
- name: Install StellaOps CLI
run: |
curl -sSL https://get.stellaops.io/cli | sh
echo "$HOME/.stellaops/bin" >> $GITHUB_PATH
- name: Authenticate with OIDC
run: |
stellaops auth login --oidc \
--issuer ${{ secrets.STELLAOPS_OIDC_ISSUER }} \
--client-id ${{ secrets.STELLAOPS_CLIENT_ID }}
- name: Scan image
id: scan
run: |
stellaops scan myapp:${{ github.sha }} \
--policy production \
--format sarif \
--output results.sarif \
--attestation \
--fail-on violation
- name: Upload SARIF to GitHub Security
if: always()
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: results.sarif
- name: Push to registry
if: steps.scan.outcome == 'success'
run: |
docker tag myapp:${{ github.sha }} ghcr.io/org/myapp:${{ github.sha }}
docker push ghcr.io/org/myapp:${{ github.sha }}
```
#### GitLab CI Example
```yaml
stages:
- build
- scan
- deploy
variables:
STELLAOPS_API_URL: https://api.stellaops.local
build:
stage: build
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
scan:
stage: scan
image: stellaops/cli:latest
script:
- stellaops auth login --token $STELLAOPS_TOKEN
- stellaops scan $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
--policy production
--fail-on violation
artifacts:
reports:
sast: gl-sast-report.json
deploy:
stage: deploy
needs: [scan]
script:
- kubectl set image deployment/myapp app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
```
### 2. CLI Authentication
The CLI supports multiple authentication methods:
| Method | Command | Use Case |
|--------|---------|----------|
| Token | `stellaops auth login --token $TOKEN` | CI/CD with secrets |
| OIDC | `stellaops auth login --oidc` | GitHub/GitLab OIDC |
| Interactive | `stellaops auth login` | Local development |
| Keyless | `stellaops auth login --keyless` | Sigstore OIDC |
### 3. Scan Execution
CLI submits scan request and waits for completion:
```bash
stellaops scan docker.io/myorg/myapp:v1.2.3 \
--policy production \
--format json \
--output scan-results.json \
--attestation \
--timeout 5m \
--fail-on violation
```
#### CLI Options
| Option | Description | Default |
|--------|-------------|---------|
| `--policy` | Policy set to evaluate against | `default` |
| `--format` | Output format (json, sarif, table) | `table` |
| `--output` | Write results to file | stdout |
| `--attestation` | Generate DSSE attestation | false |
| `--timeout` | Maximum wait time | 10m |
| `--fail-on` | Exit 1 on: `violation`, `warning`, `any` | `violation` |
| `--quiet` | Suppress progress output | false |
### 4. Policy Evaluation
Policy engine evaluates findings against CI-specific rules:
```yaml
# Policy Set: production
version: "stella-dsl@1"
name: production
rules:
- name: block-critical
condition: severity == 'critical' AND vex_status != 'not_affected'
action: FAIL
- name: block-high-unfixed
condition: severity == 'high' AND fixed_version == null
action: FAIL
- name: block-known-exploited
condition: kev == true
action: FAIL
- name: require-sbom
condition: sbom_complete == false
action: FAIL
message: "SBOM must cover all detected packages"
gates:
ci:
max_critical: 0
max_high_unfixed: 0
require_attestation: true
```
### 5. Verdict and Exit Code
CLI translates verdict to exit code:
| Verdict | Exit Code | Pipeline Result |
|---------|-----------|-----------------|
| PASS | 0 | Continue |
| WARN | 0 (or 1 if `--fail-on warning`) | Continue with warning |
| FAIL | 1 | Block deployment |
| ERROR | 2 | Pipeline failure |
### 6. SARIF Integration
CLI outputs SARIF for IDE and GitHub integration:
```json
{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "StellaOps",
"version": "2.1.0",
"informationUri": "https://stellaops.io"
}
},
"results": [
{
"ruleId": "CVE-2024-1234",
"level": "error",
"message": {
"text": "Critical vulnerability in lodash@4.17.20"
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "package-lock.json"
}
}
}
]
}
]
}
]
}
```
### 7. Attestation Storage
If `--attestation` is specified, CLI stores attestation:
```bash
# View attestation
stellaops attestation show --scan $SCAN_ID
# Verify attestation
stellaops attestation verify --image myapp:v1.2.3 --policy production
```
Attestation is stored as DSSE envelope:
```json
{
"payloadType": "application/vnd.in-toto+json",
"payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEi...",
"signatures": [
{
"keyid": "sha256:abc123...",
"sig": "MEQCI..."
}
]
}
```
## Gate Behaviors
### Soft Gate (Warning Only)
```yaml
# .stellaops.yaml
gates:
ci:
mode: soft # Report but don't fail
notify:
- slack://security-channel
```
### Hard Gate (Blocking)
```yaml
gates:
ci:
mode: hard # Fail pipeline on violations
exceptions:
- CVE-2024-9999 # Known false positive
```
### Progressive Gate
```yaml
gates:
ci:
mode: progressive
thresholds:
- branch: main
max_critical: 0
max_high: 5
- branch: develop
max_critical: 2
max_high: 20
- branch: feature/*
mode: soft # Warn only on feature branches
```
## Data Contracts
### CLI Scan Output Schema
```typescript
interface CliScanOutput {
scan_id: string;
image: string;
digest: string;
verdict: 'PASS' | 'WARN' | 'FAIL';
confidence: number;
summary: {
critical: number;
high: number;
medium: number;
low: number;
};
violations: Array<{
cve: string;
severity: string;
package: string;
rule: string;
message: string;
}>;
attestation?: {
digest: string;
rekor_log_index?: number;
};
timing: {
queued_ms: number;
scan_ms: number;
policy_ms: number;
total_ms: number;
};
}
```
## Error Handling
| Error | Exit Code | Recovery |
|-------|-----------|----------|
| Auth failure | 2 | Check credentials |
| Image not found | 2 | Verify image reference |
| API timeout | 2 | Retry with --timeout |
| Policy not found | 2 | Check policy name |
| Network error | 2 | Check connectivity |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `cli_scan_total` | Counter | `verdict`, `ci_platform` |
| `cli_scan_duration_seconds` | Histogram | `ci_platform` |
| `cli_gate_blocked_total` | Counter | `policy`, `reason` |
### CI/CD Annotations
GitHub Actions annotations:
```
::error file=package-lock.json::CVE-2024-1234: Critical vulnerability in lodash@4.17.20
::warning file=Dockerfile::Using outdated base image
```
## Related Flows
- [Scan Submission Flow](02-scan-submission-flow.md) - Underlying scan mechanics
- [Policy Evaluation Flow](04-policy-evaluation-flow.md) - Policy rule details
- [Binary Delta Attestation Flow](15-binary-delta-attestation-flow.md) - Attestation details

View File

@@ -0,0 +1,425 @@
# Advisory Drift Re-scan Flow
## Overview
The Advisory Drift Re-scan Flow describes how StellaOps automatically re-evaluates previously scanned images when new vulnerability advisories are published or existing advisories are updated. This ensures that security verdicts remain current without requiring manual re-scans.
**Business Value**: Continuous security posture updates as new vulnerabilities are disclosed, catching newly-vulnerable images before they're exploited.
## Actors
| Actor | Type | Role |
|-------|------|------|
| Concelier | Service | Ingests new advisories |
| Scheduler | Service | Triggers re-evaluation jobs |
| Scanner | Service | Re-matches against new data |
| Policy Engine | Service | Re-evaluates verdicts |
| Notify | Service | Alerts on status changes |
| SbomService | Service | Provides stored SBOMs |
## Prerequisites
- Advisory connectors configured (NVD, GHSA, OSV, etc.)
- Images previously scanned with stored SBOMs
- Re-scan policies configured
## Advisory Sources
StellaOps ingests advisories from 32+ sources:
| Category | Sources |
|----------|---------|
| **National DBs** | NVD, GHSA, OSV, CISA KEV |
| **Vendor PSIRTs** | Microsoft, Red Hat, Oracle, Cisco, VMware |
| **Distros** | Ubuntu, Debian, Alpine, RHEL, SUSE |
| **Ecosystems** | npm, PyPI, Go, RubyGems, Packagist |
| **CERTs** | CERT/CC, JPCERT, BSI |
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Advisory Drift Re-scan Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌─────────┐ ┌────────┐ ┌────────┐
│ Advisory │ │ Concelier │ │ Scheduler │ │ Scanner │ │ Policy │ │ Notify │
│ Source │ │ │ │ │ │ │ │ │ │ │
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘ └────┬────┘ └───┬────┘ └───┬────┘
│ │ │ │ │ │
│ New CVE │ │ │ │ │
│ published │ │ │ │ │
│─────────────>│ │ │ │ │
│ │ │ │ │ │
│ │ Ingest & │ │ │ │
│ │ normalize │ │ │ │
│ │──────┐ │ │ │ │
│ │ │ │ │ │ │
│ │<─────┘ │ │ │ │
│ │ │ │ │ │
│ │ Emit event: │ │ │ │
│ │ advisory.new │ │ │ │
│ │─────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ Query │ │ │
│ │ │ affected │ │ │
│ │ │ SBOMs │ │ │
│ │ │───┐ │ │ │
│ │ │ │ │ │ │
│ │ │<──┘ │ │ │
│ │ │ │ │ │
│ │ │ For each │ │ │
│ │ │ affected: │ │ │
│ │ │ │ │ │
│ │ │ Re-match │ │ │
│ │ │────────────>│ │ │
│ │ │ │ │ │
│ │ │ │ Load SBOM │ │
│ │ │ │ from store│ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ │ │ Match new │ │
│ │ │ │ advisory │ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ │ │ Re-eval │ │
│ │ │ │──────────>│ │ │
│ │ │ │ │ │
│ │ │ │ │ Compare │
│ │ │ │ │ old vs │
│ │ │ │ │ new │
│ │ │ │ │───┐ │
│ │ │ │ │ │ │
│ │ │ │ │<──┘ │
│ │ │ │ │ │
│ │ │ │ Verdict │ │
│ │ │ │ changed │ │
│ │ │ │<──────────│ │
│ │ │ │ │ │
│ │ │ Complete │ │ │
│ │ │<────────────│ │ │
│ │ │ │ │ │
│ │ │ [if changed]│ │ │
│ │ │ Alert │ │ │
│ │ │─────────────────────────────────────>│
│ │ │ │ │ │
│ │ │ │ │ │ Send
│ │ │ │ │ │ notif
│ │ │ │ │ │──┐
│ │ │ │ │ │ │
│ │ │ │ │ │<─┘
│ │ │ │ │ │
```
## Step-by-Step
### 1. Advisory Ingestion
Concelier connector fetches new advisory:
```json
{
"source": "nvd",
"advisory_id": "CVE-2024-1234",
"published": "2024-12-29T08:00:00Z",
"severity": "critical",
"cvss": {
"v3": {"score": 9.8, "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"}
},
"affected": [
{
"ecosystem": "npm",
"package": "lodash",
"versions": {
"vulnerable": ["<4.17.21"],
"fixed": ["4.17.21"]
}
}
],
"references": [
{"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-1234"}
]
}
```
### 2. Event Emission
Concelier emits `advisory.new` event to Valkey stream:
```json
{
"event_type": "advisory.new",
"event_id": "evt-adv-123",
"timestamp": "2024-12-29T08:01:00Z",
"payload": {
"advisory_id": "CVE-2024-1234",
"source": "nvd",
"severity": "critical",
"affected_purls": [
"pkg:npm/lodash"
]
}
}
```
### 3. Affected SBOM Query
Scheduler queries for SBOMs containing affected packages:
```sql
SELECT DISTINCT s.scan_id, s.image_ref, s.digest, s.tenant_id
FROM scanner.scans s
JOIN scanner.sbom_components c ON s.sbom_id = c.sbom_id
WHERE c.purl LIKE 'pkg:npm/lodash@%'
AND s.status = 'completed'
AND s.created_at > NOW() - INTERVAL '90 days'
ORDER BY s.created_at DESC;
```
Result:
```json
{
"affected_images": [
{
"scan_id": "scan-abc123",
"image_ref": "docker.io/myorg/app:v1.2.3",
"digest": "sha256:...",
"tenant_id": "acme-corp",
"matched_component": "pkg:npm/lodash@4.17.20"
},
{
"scan_id": "scan-def456",
"image_ref": "docker.io/myorg/api:v2.0.0",
"digest": "sha256:...",
"tenant_id": "acme-corp",
"matched_component": "pkg:npm/lodash@4.17.19"
}
],
"total_affected": 2
}
```
### 4. Re-evaluation Jobs
Scheduler creates re-evaluation jobs (not full re-scans):
```json
{
"job_type": "advisory_drift_reevaluate",
"job_id": "job-reeval-789",
"priority": "high",
"batch": [
{
"scan_id": "scan-abc123",
"new_advisories": ["CVE-2024-1234"],
"affected_packages": ["pkg:npm/lodash@4.17.20"]
},
{
"scan_id": "scan-def456",
"new_advisories": ["CVE-2024-1234"],
"affected_packages": ["pkg:npm/lodash@4.17.19"]
}
]
}
```
### 5. SBOM-Based Re-matching
Scanner loads stored SBOM and matches against new advisory:
```json
{
"scan_id": "scan-abc123",
"new_findings": [
{
"cve": "CVE-2024-1234",
"package": "pkg:npm/lodash@4.17.20",
"severity": "critical",
"fixed_version": "4.17.21",
"source": "nvd"
}
],
"reused_data": {
"sbom": true,
"reachability": true,
"vex": false // Re-query VEX for new CVE
}
}
```
### 6. Policy Re-evaluation
Policy engine re-evaluates with new findings:
```json
{
"reevaluation": {
"scan_id": "scan-abc123",
"previous_verdict": "PASS",
"new_verdict": "FAIL",
"verdict_changed": true,
"reason": "New critical CVE-2024-1234 matched",
"delta": {
"added_findings": [
{"cve": "CVE-2024-1234", "severity": "critical"}
],
"removed_findings": [],
"changed_findings": []
}
}
}
```
### 7. Status Change Notification
If verdict changed, Notify sends alerts:
```json
{
"event_type": "scan.verdict_changed",
"payload": {
"scan_id": "scan-abc123",
"image": "docker.io/myorg/app:v1.2.3",
"previous_verdict": "PASS",
"new_verdict": "FAIL",
"trigger": "advisory_drift",
"new_cve": "CVE-2024-1234",
"severity": "critical"
}
}
```
## Re-scan Policies
### Immediate Re-evaluation
```yaml
advisory_drift:
trigger: immediate
severity_threshold: critical
batch_size: 100
parallelism: 10
```
### Scheduled Re-evaluation
```yaml
advisory_drift:
trigger: scheduled
schedule: "0 */4 * * *" # Every 4 hours
severity_threshold: high
include_new_vex: true
```
### Smart Batching
```yaml
advisory_drift:
trigger: smart
rules:
- severity: critical
delay: 0s
- severity: high
delay: 15m
batch_with_same_package: true
- severity: medium
delay: 1h
- severity: low
delay: 24h
```
## Material Risk Detection
The flow uses Smart-Diff rules to identify material changes:
| Rule | ID | Trigger |
|------|-----|---------|
| Verdict flip PASS→FAIL | R1 | Immediately actionable |
| New critical/high finding | R2 | Review required |
| KEV addition | R3 | Urgent remediation |
| VEX invalidation | R4 | Re-review VEX statement |
## Data Contracts
### Advisory Drift Event Schema
```typescript
interface AdvisoryDriftEvent {
event_type: 'advisory.new' | 'advisory.update' | 'advisory.withdrawn';
advisory_id: string;
source: string;
severity?: 'critical' | 'high' | 'medium' | 'low';
affected_purls: string[];
timestamp: string;
}
```
### Re-evaluation Result Schema
```typescript
interface ReevaluationResult {
scan_id: string;
image: string;
previous_verdict: Verdict;
new_verdict: Verdict;
verdict_changed: boolean;
delta: {
added_findings: Finding[];
removed_findings: Finding[];
changed_findings: Finding[];
};
trigger: 'advisory_drift' | 'vex_update' | 'policy_change';
triggered_by: string; // Advisory ID or VEX ID
evaluated_at: string;
}
```
## Performance Optimizations
| Optimization | Description |
|--------------|-------------|
| SBOM reuse | Load stored SBOM instead of re-analyzing |
| Incremental matching | Only match new advisories |
| Batch processing | Group by package for efficient queries |
| Priority queue | Critical advisories processed first |
| Parallel evaluation | Evaluate multiple images concurrently |
## Error Handling
| Error | Recovery |
|-------|----------|
| SBOM not found | Mark scan as stale, suggest re-scan |
| Advisory parse error | Skip advisory, log for review |
| Evaluation timeout | Retry with lower parallelism |
| Notification failure | Queue for retry |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `advisory_drift_events_total` | Counter | `source`, `severity` |
| `advisory_drift_affected_images` | Gauge | `advisory_id` |
| `advisory_drift_reevaluations_total` | Counter | `verdict_changed` |
| `advisory_drift_latency_seconds` | Histogram | `batch_size` |
### Key Log Events
| Event | Level | Fields |
|-------|-------|--------|
| `advisory.ingested` | INFO | `advisory_id`, `source`, `severity` |
| `advisory.drift_scan_started` | INFO | `advisory_id`, `affected_count` |
| `advisory.verdict_changed` | WARN | `scan_id`, `previous`, `new` |
| `advisory.drift_scan_complete` | INFO | `advisory_id`, `duration_ms` |
## Related Flows
- [Scan Submission Flow](02-scan-submission-flow.md) - Original scan process
- [Policy Evaluation Flow](04-policy-evaluation-flow.md) - Policy details
- [Notification Flow](05-notification-flow.md) - Alert delivery
- [Reachability Drift Alert Flow](19-reachability-drift-alert-flow.md) - Runtime drift

View File

@@ -0,0 +1,434 @@
# VEX Auto-Generation Flow
## Overview
The VEX (Vulnerability Exploitability eXchange) Auto-Generation Flow describes how StellaOps assists in creating VEX statements by analyzing reachability data, runtime observations, and historical patterns. This flow combines automated analysis with human review to produce accurate exploitability assessments.
**Business Value**: Reduce false positive burden by automatically identifying vulnerabilities that are not exploitable in the specific deployment context.
## Actors
| Actor | Type | Role |
|-------|------|------|
| Security Analyst | Human | Reviews and approves VEX statements |
| AdvisoryAI | Service | ML-assisted analysis |
| ReachGraph | Service | Provides reachability analysis |
| Signals | Service | Provides runtime observations |
| VexLens | Service | Stores and distributes VEX |
| Scanner | Service | Provides SBOM context |
## Prerequisites
- Image scanned with SBOM generated
- Reachability analysis completed (optional but recommended)
- Runtime signals available (optional)
- VEX issuer identity configured
## VEX Statuses
| Status | Description | Automation Confidence |
|--------|-------------|----------------------|
| `not_affected` | Vulnerability not exploitable | High (with evidence) |
| `affected` | Vulnerability is exploitable | Medium |
| `fixed` | Vulnerability has been remediated | High |
| `under_investigation` | Status being determined | N/A |
## Justification Types (OpenVEX)
| Justification | Description |
|---------------|-------------|
| `component_not_present` | Vulnerable component not in product |
| `vulnerable_code_not_present` | Specific vulnerable code not included |
| `vulnerable_code_not_in_execute_path` | Code present but unreachable |
| `vulnerable_code_cannot_be_controlled_by_adversary` | Attack vector blocked |
| `inline_mitigations_already_exist` | Compensating controls in place |
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ VEX Auto-Generation Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────┐ ┌───────────┐ ┌───────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Analyst │ │AdvisoryAI │ │ ReachGraph│ │ Signals │ │ VexLens │ │ Scanner │
└────┬────┘ └─────┬─────┘ └─────┬─────┘ └────┬────┘ └────┬────┘ └────┬────┘
│ │ │ │ │ │
│ Review │ │ │ │ │
│ finding │ │ │ │ │
│────────────>│ │ │ │ │
│ │ │ │ │ │
│ │ Get SBOM │ │ │ │
│ │ context │ │ │ │
│ │────────────────────────────────────────────────────>│
│ │ │ │ │ │
│ │ SBOM + │ │ │ │
│ │ call graph │ │ │ │
│ │<────────────────────────────────────────────────────│
│ │ │ │ │ │
│ │ Query reach │ │ │ │
│ │─────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ Analyze │ │ │
│ │ │ call paths │ │ │
│ │ │───┐ │ │ │
│ │ │ │ │ │ │
│ │ │<──┘ │ │ │
│ │ │ │ │ │
│ │ K4 state + │ │ │ │
│ │ evidence │ │ │ │
│ │<─────────────│ │ │ │
│ │ │ │ │ │
│ │ Query runtime│ │ │ │
│ │─────────────────────────────> │ │
│ │ │ │ │ │
│ │ │ │ Check │ │
│ │ │ │ invocations│ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ Runtime │ │ │ │
│ │ evidence │ │ │ │
│ │<───────────────────────────── │ │
│ │ │ │ │ │
│ │ Analyze │ │ │ │
│ │ with LLM │ │ │ │
│ │───┐ │ │ │ │
│ │ │ │ │ │ │
│ │<──┘ │ │ │ │
│ │ │ │ │ │
│ VEX draft │ │ │ │ │
│ + confidence│ │ │ │ │
│<────────────│ │ │ │ │
│ │ │ │ │ │
│ [Review] │ │ │ │ │
│ Approve/ │ │ │ │ │
│ Modify │ │ │ │ │
│───┐ │ │ │ │ │
│ │ │ │ │ │ │
│<──┘ │ │ │ │ │
│ │ │ │ │ │
│ Submit VEX │ │ │ │ │
│────────────────────────────────────────────────────────> │
│ │ │ │ │ │
│ │ │ │ │ Store │
│ │ │ │ │ + sign │
│ │ │ │ │───┐ │
│ │ │ │ │ │ │
│ │ │ │ │<──┘ │
│ │ │ │ │ │
│ VEX ID │ │ │ │ │
│<──────────────────────────────────────────────────────── │
│ │ │ │ │ │
```
## Step-by-Step
### 1. Finding Review Initiation
Analyst selects finding for VEX assessment:
```json
{
"scan_id": "scan-abc123",
"cve": "CVE-2024-1234",
"package": "pkg:npm/lodash@4.17.20",
"severity": "critical",
"current_status": "affected",
"request": "assess_exploitability"
}
```
### 2. Context Gathering
AdvisoryAI gathers context from multiple sources:
#### SBOM Context
```json
{
"component": {
"purl": "pkg:npm/lodash@4.17.20",
"locations": ["/app/node_modules/lodash"],
"dependents": ["express", "webpack"],
"scope": "runtime"
},
"call_graph": {
"entry_points": ["src/api/handler.js", "src/worker/processor.js"],
"functions_imported": ["_.get", "_.merge", "_.template"]
}
}
```
#### Reachability Analysis
```json
{
"package": "pkg:npm/lodash@4.17.20",
"k4_state": "StaticallyReachable",
"vulnerable_function": "_.template",
"analysis": {
"function_imported": true,
"call_sites": 3,
"call_paths": [
{
"path": ["src/api/handler.js:45", "lib/renderer.js:12", "_.template"],
"reachable": true
}
]
}
}
```
#### Runtime Signals
```json
{
"package": "pkg:npm/lodash@4.17.20",
"observation_period": "30d",
"signals": {
"function_invocations": {
"_.get": 15234,
"_.merge": 892,
"_.template": 0
},
"vulnerable_function_called": false,
"last_check": "2024-12-29T10:00:00Z"
}
}
```
### 3. AI-Assisted Analysis
AdvisoryAI analyzes gathered evidence:
```json
{
"analysis": {
"cve": "CVE-2024-1234",
"vulnerable_function": "_.template",
"evidence_summary": {
"static_reachability": "reachable",
"runtime_observation": "never_invoked",
"import_analysis": "function_imported_but_not_called",
"call_site_analysis": "call site exists but appears to be dead code"
},
"recommendation": {
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path",
"confidence": 0.85,
"reasoning": [
"Vulnerable function _.template is imported but analysis shows:",
"1. Static analysis found 3 potential call sites",
"2. Runtime signals over 30 days show 0 invocations",
"3. Call sites appear to be in deprecated code path",
"4. No user-controlled input reaches the function"
]
}
}
}
```
### 4. VEX Draft Generation
AdvisoryAI generates draft VEX statement:
```json
{
"draft_vex": {
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "https://stellaops.local/vex/draft/vex-draft-123",
"author": "StellaOps AdvisoryAI",
"timestamp": "2024-12-29T10:30:00Z",
"version": 1,
"statements": [
{
"vulnerability": {
"@id": "https://nvd.nist.gov/vuln/detail/CVE-2024-1234"
},
"products": [
{
"@id": "pkg:oci/myorg/app@sha256:abc123",
"subcomponents": [
{"@id": "pkg:npm/lodash@4.17.20"}
]
}
],
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path",
"impact_statement": "The vulnerable _.template function is imported but never invoked. Runtime monitoring over 30 days confirms zero executions."
}
]
},
"confidence": 0.85,
"evidence_refs": [
"reachability:reach-analysis-456",
"signals:runtime-obs-789"
],
"requires_human_review": true
}
```
### 5. Human Review
Analyst reviews draft in Console UI:
```
┌─────────────────────────────────────────────────────────────────┐
│ VEX Draft Review │
├─────────────────────────────────────────────────────────────────┤
│ │
│ CVE: CVE-2024-1234 (Critical) │
│ Package: lodash@4.17.20 │
│ Image: docker.io/myorg/app:v1.2.3 │
│ │
│ ┌─ AI Recommendation ──────────────────────────────────────────┐│
│ │ Status: not_affected ││
│ │ Justification: vulnerable_code_not_in_execute_path ││
│ │ Confidence: 85% ││
│ └──────────────────────────────────────────────────────────────┘│
│ │
│ ┌─ Evidence ───────────────────────────────────────────────────┐│
│ │ ✓ Static analysis: 3 potential call sites found ││
│ │ ✓ Runtime (30d): 0 invocations of _.template ││
│ │ ✓ Call graph: paths exist but appear unused ││
│ │ ⚠ Note: Function is imported in production code ││
│ └──────────────────────────────────────────────────────────────┘│
│ │
│ [Approve] [Modify] [Reject] [Request More Analysis] │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 6. VEX Submission
After approval, VEX is signed and stored:
```json
{
"vex_id": "vex-789ghi",
"status": "published",
"signed_by": "analyst@acme.com",
"signature": {
"keyid": "sha256:analyst-key-fingerprint",
"sig": "base64:signature..."
},
"transparency_log": {
"rekor_log_index": 12345678,
"log_id": "sha256:rekor-log..."
}
}
```
## Automation Levels
### Fully Automated (High Confidence)
```yaml
vex_automation:
auto_approve:
- condition: component_not_present
confidence_threshold: 0.99
- condition: fixed_version_deployed
confidence_threshold: 0.95
```
### Semi-Automated (Human Review)
```yaml
vex_automation:
require_review:
- condition: runtime_not_observed
confidence_threshold: 0.70
review_timeout: 24h
```
### Manual Only
```yaml
vex_automation:
manual_only:
- condition: affected
- condition: inline_mitigations
```
## Data Contracts
### VEX Draft Request Schema
```typescript
interface VexDraftRequest {
scan_id: string;
cve: string;
package_purl: string;
context?: {
include_reachability: boolean;
include_runtime: boolean;
observation_period?: string; // ISO-8601 duration
};
}
```
### VEX Draft Response Schema
```typescript
interface VexDraftResponse {
draft_id: string;
cve: string;
product: string;
recommended_status: VexStatus;
recommended_justification?: VexJustification;
confidence: number;
evidence: Array<{
type: 'reachability' | 'runtime' | 'code_analysis';
summary: string;
ref: string;
}>;
impact_statement: string;
requires_human_review: boolean;
expires_at?: string;
}
```
## Confidence Scoring
| Evidence Type | Base Confidence | Modifiers |
|--------------|-----------------|-----------|
| Component not in SBOM | 0.99 | - |
| Fixed version confirmed | 0.95 | - |
| Runtime never invoked (30d+) | 0.85 | +0.05 per additional 30d |
| Static unreachable | 0.70 | +0.10 with runtime confirm |
| AI code analysis | 0.60 | Requires human review |
| Historical pattern match | 0.50 | Requires human review |
## Error Handling
| Error | Recovery |
|-------|----------|
| Reachability unavailable | Lower confidence, require review |
| Runtime signals missing | Use static analysis only |
| AI analysis timeout | Fall back to template-based |
| Signing failure | Queue for retry |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `vex_drafts_generated_total` | Counter | `status`, `justification` |
| `vex_drafts_approved_total` | Counter | `auto_approved` |
| `vex_confidence_score` | Histogram | `status` |
| `vex_review_duration_seconds` | Histogram | `outcome` |
### Key Log Events
| Event | Level | Fields |
|-------|-------|--------|
| `vex.draft.generated` | INFO | `cve`, `status`, `confidence` |
| `vex.draft.reviewed` | INFO | `draft_id`, `outcome`, `reviewer` |
| `vex.published` | INFO | `vex_id`, `cve`, `status` |
## Related Flows
- [Policy Evaluation Flow](04-policy-evaluation-flow.md) - VEX consumption
- [Advisory Drift Re-scan Flow](11-advisory-drift-rescan-flow.md) - VEX updates
- [Exception Approval Workflow](17-exception-approval-workflow.md) - Related approval pattern

View File

@@ -0,0 +1,545 @@
# Evidence Bundle Export Flow
## Overview
The Evidence Bundle Export Flow describes how StellaOps creates comprehensive, cryptographically sealed evidence packages for audits, compliance, and legal proceedings. Bundles include SBOMs, scan results, policy verdicts, attestations, and complete provenance chains with tamper-evident sealing.
**Business Value**: Auditable, legally defensible evidence packages that demonstrate due diligence in vulnerability management and support regulatory compliance.
## Actors
| Actor | Type | Role |
|-------|------|------|
| Auditor | Human | Requests and verifies evidence bundles |
| Compliance Officer | Human | Configures bundle requirements |
| EvidenceLocker | Service | Creates and seals bundles |
| Scanner | Service | Provides scan artifacts |
| Policy | Service | Provides verdict artifacts |
| Attestor | Service | Provides attestations |
| Signer | Service | Signs bundle manifest |
## Prerequisites
- Evidence exists for requested scope
- Bundle schema configured
- Signing keys available
- Storage quota available
## Bundle Contents
| Artifact Type | Format | Description |
|---------------|--------|-------------|
| SBOM | CycloneDX/SPDX | Software bill of materials |
| Scan Result | JSON | Vulnerability findings |
| Policy Verdict | JSON | Policy evaluation result |
| Attestation | DSSE | in-toto attestation envelope |
| VEX Statements | OpenVEX | Applied VEX statements |
| Reachability | JSON | K4 lattice state evidence |
| Audit Log | NDJSON | Activity audit trail |
| Manifest | JSON | Bundle index with hashes |
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Evidence Bundle Export Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────┐ ┌───────────────┐ ┌─────────┐ ┌─────────┐ ┌────────┐ ┌────────┐
│ Auditor │ │EvidenceLocker │ │ Scanner │ │ Policy │ │Attestor│ │ Signer │
└────┬────┘ └───────┬───────┘ └────┬────┘ └────┬────┘ └───┬────┘ └───┬────┘
│ │ │ │ │ │
│ Request │ │ │ │ │
│ bundle │ │ │ │ │
│──────────────>│ │ │ │ │
│ │ │ │ │ │
│ │ Validate │ │ │ │
│ │ request │ │ │ │
│ │───┐ │ │ │ │
│ │ │ │ │ │ │
│ │<──┘ │ │ │ │
│ │ │ │ │ │
│ │ Collect scan │ │ │ │
│ │ artifacts │ │ │ │
│ │──────────────>│ │ │ │
│ │ │ │ │ │
│ │ SBOM + result │ │ │ │
│ │<──────────────│ │ │ │
│ │ │ │ │ │
│ │ Collect │ │ │ │
│ │ verdicts │ │ │ │
│ │───────────────────────────>│ │ │
│ │ │ │ │ │
│ │ Policy eval │ │ │ │
│ │<───────────────────────────│ │ │
│ │ │ │ │ │
│ │ Collect │ │ │ │
│ │ attestations │ │ │ │
│ │───────────────────────────────────────>│ │
│ │ │ │ │ │
│ │ DSSE │ │ │ │
│ │ envelopes │ │ │ │
│ │<───────────────────────────────────────│ │
│ │ │ │ │ │
│ │ Build │ │ │ │
│ │ manifest │ │ │ │
│ │───┐ │ │ │ │
│ │ │ │ │ │ │
│ │<──┘ │ │ │ │
│ │ │ │ │ │
│ │ Compute │ │ │ │
│ │ Merkle root │ │ │ │
│ │───┐ │ │ │ │
│ │ │ │ │ │ │
│ │<──┘ │ │ │ │
│ │ │ │ │ │
│ │ Sign bundle │ │ │ │
│ │───────────────────────────────────────────────────>│
│ │ │ │ │ │
│ │ Signature │ │ │ │
│ │<───────────────────────────────────────────────────│
│ │ │ │ │ │
│ │ Seal bundle │ │ │ │
│ │───┐ │ │ │ │
│ │ │ │ │ │ │
│ │<──┘ │ │ │ │
│ │ │ │ │ │
│ Bundle + │ │ │ │ │
│ download URL │ │ │ │ │
│<──────────────│ │ │ │ │
│ │ │ │ │ │
```
## Step-by-Step
### 1. Bundle Request
Auditor requests evidence bundle:
```http
POST /api/v1/evidence/bundles HTTP/1.1
Authorization: Bearer {jwt}
X-Tenant-Id: acme-corp
Content-Type: application/json
```
### 2. Scope Validation
EvidenceLocker validates the request scope:
```json
{
"validation": {
"scope_valid": true,
"artifacts_found": {
"scans": 47,
"images": 23,
"sboms": 47,
"attestations": 47,
"vex_statements": 156
},
"estimated_size": "245 MB",
"estimated_generation_time": "PT3M"
}
}
```
### 3. Artifact Collection
EvidenceLocker collects artifacts from each service:
#### SBOM Collection
```json
{
"sboms": [
{
"scan_id": "scan-abc123",
"format": "cyclonedx-1.6",
"path": "sboms/scan-abc123.cdx.json",
"sha256": "abc123...",
"size": 89012
}
]
}
```
#### Scan Result Collection
```json
{
"scan_results": [
{
"scan_id": "scan-abc123",
"path": "scans/scan-abc123.json",
"sha256": "def456...",
"finding_count": 12,
"verdict": "PASS"
}
]
}
```
#### Policy Verdict Collection
```json
{
"policy_verdicts": [
{
"scan_id": "scan-abc123",
"policy_set": "production",
"path": "verdicts/scan-abc123-production.json",
"sha256": "ghi789...",
"verdict": "PASS",
"confidence": 0.92
}
]
}
```
#### Attestation Collection
```json
{
"attestations": [
{
"scan_id": "scan-abc123",
"type": "sbom",
"path": "attestations/scan-abc123-sbom.dsse.json",
"sha256": "jkl012...",
"signer": "scanner@stellaops.local"
},
{
"scan_id": "scan-abc123",
"type": "verdict",
"path": "attestations/scan-abc123-verdict.dsse.json",
"sha256": "mno345...",
"signer": "policy@stellaops.local"
}
]
}
```
### 4. Manifest Generation
EvidenceLocker creates bundle manifest:
```json
{
"manifest_version": "1.0.0",
"bundle_id": "bundle-789ghi",
"created_at": "2024-12-29T10:30:00Z",
"created_by": "auditor@acme.com",
"tenant_id": "acme-corp",
"scope": {
"scan_count": 47,
"image_count": 23,
"date_range": {
"start": "2024-10-01T00:00:00Z",
"end": "2024-12-31T23:59:59Z"
}
},
"contents": {
"sboms": {
"count": 47,
"path": "sboms/",
"index": "sboms/index.json"
},
"scans": {
"count": 47,
"path": "scans/",
"index": "scans/index.json"
},
"verdicts": {
"count": 94,
"path": "verdicts/",
"index": "verdicts/index.json"
},
"attestations": {
"count": 188,
"path": "attestations/",
"index": "attestations/index.json"
},
"vex": {
"count": 156,
"path": "vex/",
"index": "vex/index.json"
},
"audit_log": {
"path": "audit/activity.ndjson",
"entries": 1247
}
},
"integrity": {
"algorithm": "sha256",
"merkle_root": "sha256:merkle-root-hash...",
"file_hashes": "hashes.json"
}
}
```
### 5. Merkle Tree Construction
EvidenceLocker computes Merkle root for tamper detection:
```
merkle_root
/ \
hash_01 hash_23
/ \ / \
hash_0 hash_1 hash_2 hash_3
| | | |
sbom_0 sbom_1 scan_0 scan_1
```
```json
{
"merkle_tree": {
"algorithm": "sha256",
"root": "sha256:abc123...",
"leaves": [
{"path": "sboms/scan-abc123.cdx.json", "hash": "sha256:..."},
{"path": "scans/scan-abc123.json", "hash": "sha256:..."},
{"path": "verdicts/scan-abc123-production.json", "hash": "sha256:..."}
],
"proof_format": "rfc6962"
}
}
```
### 6. Bundle Signing
Signer creates signature over manifest:
```json
{
"signed_manifest": {
"payload": "base64:manifest-json...",
"signatures": [
{
"keyid": "sha256:evidence-signing-key",
"sig": "base64:signature...",
"certificate": "base64:x509-cert...",
"timestamp": "2024-12-29T10:30:00Z"
}
]
},
"transparency_log": {
"enabled": true,
"rekor_log_index": 12345678,
"inclusion_proof": "base64:proof..."
}
}
```
### 7. Bundle Sealing
EvidenceLocker creates final sealed archive:
```
bundle-789ghi.tar.gz
├── manifest.json # Bundle manifest
├── manifest.sig # Manifest signature
├── hashes.json # All file hashes
├── merkle.json # Merkle tree structure
├── sboms/
│ ├── index.json
│ ├── scan-abc123.cdx.json
│ └── scan-def456.cdx.json
├── scans/
│ ├── index.json
│ ├── scan-abc123.json
│ └── scan-def456.json
├── verdicts/
│ ├── index.json
│ ├── scan-abc123-production.json
│ └── scan-abc123-pci-dss.json
├── attestations/
│ ├── index.json
│ ├── scan-abc123-sbom.dsse.json
│ └── scan-abc123-verdict.dsse.json
├── vex/
│ ├── index.json
│ └── vex-statements.json
├── audit/
│ └── activity.ndjson
└── README.md # Bundle documentation
```
### 8. Verification
Auditor can verify bundle integrity:
```bash
# Verify bundle signature
stellaops evidence verify bundle-789ghi.tar.gz
# Output:
✓ Manifest signature valid
✓ Merkle root verified
47 SBOMs verified
47 scan results verified
94 verdicts verified
188 attestations verified
✓ Transparency log inclusion verified (Rekor #12345678)
Bundle verified successfully.
```
## Bundle Schemas
### Compliance-Focused Bundle
```yaml
compliance_bundle:
include:
- sbom
- scan_results
- policy_verdicts
- attestations
- audit_log
format:
archive: tar.gz
encryption: aes-256-gcm
signing: sigstore
retention: 7y
```
### Incident Response Bundle
```yaml
incident_bundle:
scope:
images: ["affected-image:*"]
date_range: auto # From first detection
include:
- sbom
- scan_results
- reachability
- runtime_signals
- vex_statements
format:
archive: zip
signing: internal
priority: urgent
```
## Data Contracts
### Bundle Request Schema
```typescript
interface BundleRequest {
name: string;
description?: string;
scope: {
scan_ids?: string[];
date_range?: DateRange;
images?: string[]; // Glob patterns
policy_sets?: string[];
tenants?: string[]; // Multi-tenant
};
include: {
sbom?: boolean;
scan_results?: boolean;
policy_verdicts?: boolean;
attestations?: boolean;
vex_statements?: boolean;
reachability?: boolean;
audit_log?: boolean;
provenance_chain?: boolean;
};
format: {
archive: 'tar.gz' | 'zip';
signing: 'sigstore' | 'internal' | 'none';
encryption?: {
algorithm: 'aes-256-gcm';
recipient_keys: string[];
};
transparency_log?: boolean;
};
}
```
### Bundle Response Schema
```typescript
interface BundleResponse {
bundle_id: string;
status: 'queued' | 'generating' | 'completed' | 'failed';
created_at: string;
completed_at?: string;
manifest: BundleManifest;
download: {
url: string;
expires_at: string;
size_bytes: number;
sha256: string;
};
verification: {
merkle_root: string;
signature: string;
rekor_log_index?: number;
};
}
```
## Error Handling
| Error | Recovery |
|-------|----------|
| Scope too large | Suggest date range reduction |
| Missing artifacts | Generate partial bundle with warning |
| Signing failure | Retry or fall back to internal signing |
| Storage quota exceeded | Alert admin, queue for later |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `evidence_bundles_created_total` | Counter | `status` |
| `evidence_bundle_size_bytes` | Histogram | - |
| `evidence_bundle_generation_seconds` | Histogram | - |
| `evidence_bundle_artifact_count` | Histogram | `type` |
### Key Log Events
| Event | Level | Fields |
|-------|-------|--------|
| `evidence.bundle.requested` | INFO | `bundle_id`, `scope` |
| `evidence.bundle.generating` | INFO | `bundle_id`, `artifact_count` |
| `evidence.bundle.sealed` | INFO | `bundle_id`, `size_bytes`, `merkle_root` |
| `evidence.bundle.verified` | INFO | `bundle_id`, `verifier` |
## Related Flows
- [Export Flow](06-export-flow.md) - General export mechanics
- [Scan Submission Flow](02-scan-submission-flow.md) - Source of evidence
- [Offline Sync Flow](16-offline-sync-flow.md) - Air-gapped bundle transfer
| Signing failure | Retry or fall back to internal signing |
| Storage quota exceeded | Alert admin, queue for later |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `evidence_bundles_created_total` | Counter | `status` |
| `evidence_bundle_size_bytes` | Histogram | - |
| `evidence_bundle_generation_seconds` | Histogram | - |
| `evidence_bundle_artifact_count` | Histogram | `type` |
### Key Log Events
| Event | Level | Fields |
|-------|-------|--------|
| `evidence.bundle.requested` | INFO | `bundle_id`, `scope` |
| `evidence.bundle.generating` | INFO | `bundle_id`, `artifact_count` |
| `evidence.bundle.sealed` | INFO | `bundle_id`, `size_bytes`, `merkle_root` |
| `evidence.bundle.verified` | INFO | `bundle_id`, `verifier` |
## Related Flows
- [Export Flow](06-export-flow.md) - General export mechanics
- [Scan Submission Flow](02-scan-submission-flow.md) - Source of evidence
- [Offline Sync Flow](16-offline-sync-flow.md) - Air-gapped bundle transfer

View File

@@ -0,0 +1,476 @@
# Multi-Tenant Policy Rollout Flow
## Overview
The Multi-Tenant Policy Rollout Flow describes how StellaOps propagates policy changes across multiple tenants in a controlled, auditable manner. This flow supports staged rollouts, canary deployments, and rollback capabilities for enterprise policy governance.
**Business Value**: Centralized policy management with controlled rollout reduces risk of policy changes breaking production workflows while ensuring consistent security standards across the organization.
## Actors
| Actor | Type | Role |
|-------|------|------|
| Policy Admin | Human | Creates and approves policy changes |
| Platform Admin | Human | Manages cross-tenant rollouts |
| Policy Engine | Service | Evaluates and applies policies |
| Authority | Service | Manages tenant hierarchy |
| Notify | Service | Alerts on rollout status |
| Scheduler | Service | Orchestrates staged rollout |
## Prerequisites
- Multi-tenant environment configured
- Tenant hierarchy defined (org → teams → projects)
- Policy inheritance rules established
- Rollout approval workflow configured
## Tenant Hierarchy
```
Organization (acme-corp)
├── Team: Platform Engineering
│ ├── Project: core-services
│ └── Project: infrastructure
├── Team: Product Development
│ ├── Project: web-app
│ ├── Project: mobile-api
│ └── Project: data-pipeline
└── Team: Security
└── Project: security-tools
```
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Multi-Tenant Policy Rollout Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌──────────┐ ┌─────────┐ ┌───────────┐ ┌──────────┐ ┌────────┐ ┌────────┐
│ Policy │ │ Policy │ │ Scheduler │ │ Authority│ │ Policy │ │ Notify │
│ Admin │ │ Store │ │ │ │ │ │ Engine │ │ │
└────┬─────┘ └────┬────┘ └─────┬─────┘ └────┬─────┘ └───┬────┘ └───┬────┘
│ │ │ │ │ │
│ Create │ │ │ │ │
│ policy v2 │ │ │ │ │
│────────────>│ │ │ │ │
│ │ │ │ │ │
│ │ Store as │ │ │ │
│ │ draft │ │ │ │
│ │───┐ │ │ │ │
│ │ │ │ │ │ │
│ │<──┘ │ │ │ │
│ │ │ │ │ │
│ Define │ │ │ │ │
│ rollout │ │ │ │ │
│────────────────────────────> │ │ │
│ │ │ │ │ │
│ │ │ Get tenant │ │ │
│ │ │ hierarchy │ │ │
│ │ │────────────>│ │ │
│ │ │ │ │ │
│ │ │ Tenant tree │ │ │
│ │ │<────────────│ │ │
│ │ │ │ │ │
│ Rollout │ │ │ │ │
│ plan │ │ │ │ │
│<──────────────────────────── │ │ │
│ │ │ │ │ │
│ Approve │ │ │ │ │
│────────────────────────────> │ │ │
│ │ │ │ │ │
│ │ │ Stage 1: │ │ │
│ │ │ Canary │ │ │
│ │ │─────────────────────────>│ │
│ │ │ │ │ │
│ │ │ │ │ Apply to │
│ │ │ │ │ canary │
│ │ │ │ │ tenant │
│ │ │ │ │───┐ │
│ │ │ │ │ │ │
│ │ │ │ │<──┘ │
│ │ │ │ │ │
│ │ │ Monitor │ │ │
│ │ │ (24h) │ │ │
│ │ │───┐ │ │ │
│ │ │ │ │ │ │
│ │ │<──┘ │ │ │
│ │ │ │ │ │
│ │ │ Stage 2: │ │ │
│ │ │ 25% tenants │ │ │
│ │ │─────────────────────────>│ │
│ │ │ │ │ │
│ │ │ ... │ │ │
│ │ │ │ │ │
│ │ │ Stage N: │ │ │
│ │ │ 100% │ │ │
│ │ │─────────────────────────>│ │
│ │ │ │ │ │
│ │ │ Complete │ │ │
│ │ │───────────────────────────────────────>
│ │ │ │ │ │
│ Rollout │ │ │ │ │ Notify
│ complete │ │ │ │ │ admins
│<────────────────────────────────────────────────────────────────────
│ │ │ │ │ │
```
## Step-by-Step
### 1. Policy Creation
Policy Admin creates new policy version:
```yaml
# Policy Set: production-v2
version: "stella-dsl@1"
name: production
version_tag: "v2.0.0"
description: "Updated production policy with KEV blocking"
changes_from_v1:
- added: block-kev-vulnerabilities
- modified: critical-threshold (9.0 → 8.5)
- removed: legacy-exception-rule
rules:
- name: block-kev-vulnerabilities
description: Block any KEV-listed vulnerability
condition: kev == true
action: FAIL
severity: critical
- name: no-critical-reachable
condition: |
severity == 'critical' AND
cvss >= 8.5 AND
reachability IN ['SR', 'RO', 'CR']
action: FAIL
```
### 2. Rollout Plan Definition
Platform Admin defines rollout strategy:
```json
{
"rollout_id": "rollout-789",
"policy_set": "production",
"from_version": "v1.0.0",
"to_version": "v2.0.0",
"strategy": "staged",
"stages": [
{
"name": "canary",
"description": "Single low-risk tenant",
"tenants": ["platform-eng-core-services"],
"duration": "24h",
"success_criteria": {
"max_new_failures": 5,
"max_failure_rate_increase": 0.05
},
"auto_proceed": false
},
{
"name": "early-adopters",
"description": "25% of tenants (by scan volume)",
"selection": {
"method": "percentage",
"value": 25,
"weight_by": "scan_volume"
},
"duration": "48h",
"success_criteria": {
"max_new_failures": 20,
"max_failure_rate_increase": 0.10
},
"auto_proceed": true
},
{
"name": "majority",
"description": "75% of tenants",
"selection": {
"method": "percentage",
"value": 75
},
"duration": "24h",
"auto_proceed": true
},
{
"name": "full",
"description": "100% of tenants",
"selection": {
"method": "all"
}
}
],
"rollback": {
"automatic": true,
"triggers": [
{"metric": "failure_rate_increase", "threshold": 0.20},
{"metric": "new_critical_blocks", "threshold": 50}
]
}
}
```
### 3. Impact Analysis
Before approval, system analyzes potential impact:
```json
{
"impact_analysis": {
"rollout_id": "rollout-789",
"analysis_date": "2024-12-29T10:00:00Z",
"historical_data_range": "30d",
"results": {
"total_scans_analyzed": 15234,
"predicted_new_failures": 127,
"predicted_failure_rate_change": "+0.83%",
"affected_images": 89,
"by_team": [
{"team": "Product Development", "new_failures": 78},
{"team": "Platform Engineering", "new_failures": 31},
{"team": "Security", "new_failures": 18}
],
"top_triggered_rules": [
{"rule": "block-kev-vulnerabilities", "count": 45},
{"rule": "no-critical-reachable", "count": 82}
],
"recommendation": "PROCEED_WITH_CAUTION"
}
}
}
```
### 4. Approval and Initiation
Policy Admin approves rollout after review:
```json
{
"approval": {
"rollout_id": "rollout-789",
"approved_by": "policy-admin@acme.com",
"approved_at": "2024-12-29T11:00:00Z",
"approval_notes": "Impact acceptable. Notified affected teams.",
"notifications_sent": [
{"channel": "slack", "target": "#platform-engineering"},
{"channel": "email", "target": "team-leads@acme.com"}
]
}
}
```
### 5. Staged Execution
Scheduler executes each stage:
#### Stage 1: Canary
```json
{
"stage_execution": {
"rollout_id": "rollout-789",
"stage": "canary",
"started_at": "2024-12-29T11:00:00Z",
"tenants_activated": ["platform-eng-core-services"],
"status": "monitoring"
}
}
```
#### Stage Monitoring
```json
{
"stage_metrics": {
"rollout_id": "rollout-789",
"stage": "canary",
"monitored_period": "24h",
"metrics": {
"scans_evaluated": 234,
"new_failures": 3,
"failure_rate_before": 0.12,
"failure_rate_after": 0.13,
"success_criteria_met": true
}
}
}
```
### 6. Progressive Rollout
After canary success, proceed to next stages:
```json
{
"stage_progression": {
"rollout_id": "rollout-789",
"completed_stages": ["canary", "early-adopters", "majority"],
"current_stage": "full",
"tenants_on_v2": 47,
"tenants_on_v1": 0,
"total_rollout_duration": "96h",
"status": "completed"
}
}
```
### 7. Rollback (If Needed)
If success criteria not met, automatic rollback:
```json
{
"rollback": {
"rollout_id": "rollout-789",
"triggered_at": "2024-12-30T15:30:00Z",
"trigger_reason": "failure_rate_increase exceeded 0.20 threshold",
"rollback_stage": "early-adopters",
"tenants_rolled_back": 12,
"action": "reverted to v1.0.0",
"notifications_sent": true
}
}
```
## Rollout Strategies
### Blue-Green
```yaml
strategy: blue_green
config:
parallel_evaluation: true # Both versions evaluated
comparison_period: 24h
switch_threshold:
verdict_match_rate: 0.95
```
### Canary with Traffic Split
```yaml
strategy: canary_traffic
config:
initial_percentage: 5
increment: 10
increment_interval: 4h
max_error_rate: 0.01
```
### Feature Flag
```yaml
strategy: feature_flag
config:
flag_name: "policy-v2-enabled"
default: false
overrides:
- tenant: "security-team"
value: true
```
## Data Contracts
### Rollout Plan Schema
```typescript
interface RolloutPlan {
rollout_id: string;
policy_set: string;
from_version: string;
to_version: string;
strategy: 'staged' | 'blue_green' | 'canary_traffic' | 'feature_flag';
stages: Stage[];
rollback: {
automatic: boolean;
triggers: RollbackTrigger[];
};
notifications: NotificationConfig[];
}
interface Stage {
name: string;
description?: string;
tenants?: string[];
selection?: TenantSelection;
duration?: string; // ISO-8601 duration
success_criteria?: SuccessCriteria;
auto_proceed?: boolean;
}
```
### Rollout Status Schema
```typescript
interface RolloutStatus {
rollout_id: string;
status: 'pending' | 'in_progress' | 'paused' | 'completed' | 'rolled_back' | 'failed';
current_stage?: string;
stages: Array<{
name: string;
status: 'pending' | 'active' | 'monitoring' | 'completed' | 'failed';
started_at?: string;
completed_at?: string;
metrics?: StageMetrics;
}>;
tenant_status: Array<{
tenant_id: string;
policy_version: string;
activated_at?: string;
}>;
}
```
## Policy Inheritance
```
Organization Policy (base)
└── inherits_from: stellaops-default
Team Policy (override)
└── inherits_from: organization
└── overrides: [severity-thresholds]
Project Policy (final)
└── inherits_from: team
└── overrides: [specific-exceptions]
```
Resolution order: Project → Team → Organization → Platform Default
## Error Handling
| Error | Recovery |
|-------|----------|
| Stage timeout | Pause rollout, alert admin |
| Metrics unavailable | Use last known good, extend monitoring |
| Tenant unreachable | Skip tenant, continue with others |
| Rollback failure | Manual intervention required |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `policy_rollout_status` | Gauge | `rollout_id`, `stage` |
| `policy_rollout_tenant_count` | Gauge | `rollout_id`, `version` |
| `policy_rollout_failures_total` | Counter | `rollout_id`, `stage` |
| `policy_version_active` | Gauge | `tenant`, `policy_set`, `version` |
### Key Log Events
| Event | Level | Fields |
|-------|-------|--------|
| `rollout.created` | INFO | `rollout_id`, `policy_set`, `stages` |
| `rollout.stage.started` | INFO | `rollout_id`, `stage`, `tenants` |
| `rollout.stage.completed` | INFO | `rollout_id`, `stage`, `metrics` |
| `rollout.rollback` | WARN | `rollout_id`, `reason`, `tenants` |
| `rollout.completed` | INFO | `rollout_id`, `duration` |
## Related Flows
- [Policy Evaluation Flow](04-policy-evaluation-flow.md) - Policy application
- [Exception Approval Workflow](17-exception-approval-workflow.md) - Exception handling
- [Notification Flow](05-notification-flow.md) - Rollout alerts

View File

@@ -0,0 +1,508 @@
# Binary Delta Attestation Flow
## Overview
The Binary Delta Attestation Flow describes how StellaOps tracks and attests changes at the binary level between image versions. This flow detects modified, added, or removed binaries and generates attestations about what changed, enabling precise supply chain verification.
**Business Value**: Detect unauthorized modifications, track binary provenance, and ensure only expected changes are deployed between versions.
## Actors
| Actor | Type | Role |
|-------|------|------|
| CI Pipeline | System | Triggers delta analysis |
| BinaryIndex | Service | Extracts binary fingerprints |
| Scanner | Service | Coordinates analysis |
| Attestor | Service | Generates delta attestations |
| Signer | Service | Signs attestations |
| Symbols | Service | Resolves debug symbols |
## Prerequisites
- Previous image version scanned and indexed
- Binary analysis enabled for image
- Build provenance available (optional)
## Binary Fingerprinting
StellaOps extracts multiple identifiers from binaries:
| Identifier | Source | Stability |
|------------|--------|-----------|
| SHA-256 | File content | Exact match |
| Build ID | ELF `.note.gnu.build-id` | Build-specific |
| PE Checksum | PE header | Load-time |
| Code Hash | `.text` section only | Ignores data |
| Symbol Hash | Exported symbols | API stability |
| Debug ID | DWARF/PDB reference | Debug matching |
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Binary Delta Attestation Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌───────────┐ ┌─────────┐ ┌─────────────┐ ┌─────────┐ ┌─────────┐ ┌────────┐
│ CI │ │ Scanner │ │ BinaryIndex │ │ Symbols │ │ Attestor│ │ Signer │
└─────┬─────┘ └────┬────┘ └──────┬──────┘ └────┬────┘ └────┬────┘ └───┬────┘
│ │ │ │ │ │
│ Scan v1.2.4 │ │ │ │ │
│ --baseline │ │ │ │ │
│ v1.2.3 │ │ │ │ │
│────────────>│ │ │ │ │
│ │ │ │ │ │
│ │ Load v1.2.3 │ │ │ │
│ │ index │ │ │ │
│ │─────────────>│ │ │ │
│ │ │ │ │ │
│ │ Baseline │ │ │ │
│ │ binaries │ │ │ │
│ │<─────────────│ │ │ │
│ │ │ │ │ │
│ │ Index v1.2.4 │ │ │ │
│ │─────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ Extract │ │ │
│ │ │ binaries │ │ │
│ │ │───┐ │ │ │
│ │ │ │ │ │ │
│ │ │<──┘ │ │ │
│ │ │ │ │ │
│ │ │ Compute │ │ │
│ │ │ fingerprints │ │ │
│ │ │───┐ │ │ │
│ │ │ │ │ │ │
│ │ │<──┘ │ │ │
│ │ │ │ │ │
│ │ Current │ │ │ │
│ │ binaries │ │ │ │
│ │<─────────────│ │ │ │
│ │ │ │ │ │
│ │ Compute │ │ │ │
│ │ delta │ │ │ │
│ │───┐ │ │ │ │
│ │ │ │ │ │ │
│ │<──┘ │ │ │ │
│ │ │ │ │ │
│ │ Resolve │ │ │ │
│ │ symbols │ │ │ │
│ │──────────────────────────────> │ │
│ │ │ │ │ │
│ │ │ │ Map to │ │
│ │ │ │ source │ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ Symbol │ │ │ │
│ │ mappings │ │ │ │
│ │<────────────────────────────── │ │
│ │ │ │ │ │
│ │ Generate │ │ │ │
│ │ attestation │ │ │ │
│ │─────────────────────────────────────────>│ │
│ │ │ │ │ │
│ │ │ │ │ Create │
│ │ │ │ │ statement │
│ │ │ │ │───┐ │
│ │ │ │ │ │ │
│ │ │ │ │<──┘ │
│ │ │ │ │ │
│ │ │ │ │ Sign │
│ │ │ │ │──────────>│
│ │ │ │ │ │
│ │ │ │ │ DSSE │
│ │ │ │ │<──────────│
│ │ │ │ │ │
│ │ Attestation │ │ │ │
│ │<─────────────────────────────────────────│ │
│ │ │ │ │ │
│ Delta │ │ │ │ │
│ report │ │ │ │ │
│<────────────│ │ │ │ │
│ │ │ │ │ │
```
## Step-by-Step
### 1. Delta Scan Request
CI pipeline requests delta analysis:
```bash
stellaops scan myapp:v1.2.4 \
--baseline myapp:v1.2.3 \
--binary-delta \
--attestation
```
API equivalent:
```http
POST /api/v1/scans HTTP/1.1
Content-Type: application/json
```
### 2. Baseline Index Retrieval
Scanner retrieves existing binary index for baseline:
```json
{
"image": "docker.io/myorg/myapp:v1.2.3",
"digest": "sha256:baseline123...",
"indexed_at": "2024-12-28T10:00:00Z",
"binaries": [
{
"path": "/app/myapp",
"type": "elf-x86_64",
"sha256": "abc123...",
"build_id": "7f3a9b2c1234567890abcdef",
"code_hash": "def456...",
"symbols": {
"exported": 234,
"hash": "ghi789..."
},
"size": 15234567
},
{
"path": "/app/lib/libcore.so",
"type": "elf-x86_64-shared",
"sha256": "jkl012...",
"build_id": "abcdef1234567890",
"size": 2345678
}
]
}
```
### 3. Current Image Indexing
BinaryIndex extracts and fingerprints current image binaries:
```json
{
"image": "docker.io/myorg/myapp:v1.2.4",
"digest": "sha256:current456...",
"indexed_at": "2024-12-29T10:00:00Z",
"binaries": [
{
"path": "/app/myapp",
"type": "elf-x86_64",
"sha256": "xyz789...", // Changed
"build_id": "9f5b7d4e2345678901bcdefg", // New build
"code_hash": "uvw012...", // Code changed
"symbols": {
"exported": 238, // 4 new symbols
"hash": "rst345..." // Symbol hash changed
},
"size": 15456789 // Size increased
},
{
"path": "/app/lib/libcore.so",
"type": "elf-x86_64-shared",
"sha256": "jkl012...", // Unchanged
"build_id": "abcdef1234567890",
"size": 2345678
},
{
"path": "/app/lib/libnew.so", // New binary
"type": "elf-x86_64-shared",
"sha256": "new123...",
"build_id": "newbuild123456",
"size": 567890
}
]
}
```
### 4. Delta Computation
Scanner computes binary delta:
```json
{
"delta": {
"baseline": {
"image": "docker.io/myorg/myapp:v1.2.3",
"digest": "sha256:baseline123..."
},
"current": {
"image": "docker.io/myorg/myapp:v1.2.4",
"digest": "sha256:current456..."
},
"summary": {
"total_binaries_baseline": 2,
"total_binaries_current": 3,
"modified": 1,
"added": 1,
"removed": 0,
"unchanged": 1
},
"changes": [
{
"type": "modified",
"path": "/app/myapp",
"baseline": {
"sha256": "abc123...",
"build_id": "7f3a9b2c1234567890abcdef",
"size": 15234567
},
"current": {
"sha256": "xyz789...",
"build_id": "9f5b7d4e2345678901bcdefg",
"size": 15456789
},
"analysis": {
"code_changed": true,
"symbols_added": ["new_feature_init", "new_feature_process", "new_feature_cleanup", "handle_error_v2"],
"symbols_removed": [],
"size_delta": "+222222 bytes"
}
},
{
"type": "added",
"path": "/app/lib/libnew.so",
"current": {
"sha256": "new123...",
"build_id": "newbuild123456",
"size": 567890
}
}
],
"unchanged": [
{
"path": "/app/lib/libcore.so",
"sha256": "jkl012..."
}
]
}
}
```
### 5. Symbol Resolution
Symbols service maps build IDs to source:
```json
{
"symbol_resolution": {
"/app/myapp": {
"build_id": "9f5b7d4e2345678901bcdefg",
"debug_info_available": true,
"source_mapping": {
"repository": "github.com/myorg/myapp",
"commit": "abc123def456",
"build_time": "2024-12-29T08:00:00Z",
"compiler": "gcc 13.2.0",
"flags": "-O2 -fstack-protector-strong"
},
"new_symbols": [
{
"name": "new_feature_init",
"source": "src/features/new_feature.c:45",
"size": 256
},
{
"name": "new_feature_process",
"source": "src/features/new_feature.c:89",
"size": 1024
}
]
}
}
}
```
### 6. Delta Attestation Generation
Attestor creates in-toto statement for the delta:
```json
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{
"name": "docker.io/myorg/myapp",
"digest": {"sha256": "current456..."}
}
],
"predicateType": "https://stellaops.io/attestation/binary-delta/v1",
"predicate": {
"baseline": {
"name": "docker.io/myorg/myapp",
"digest": {"sha256": "baseline123..."}
},
"delta_summary": {
"modified": 1,
"added": 1,
"removed": 0,
"unchanged": 1
},
"binary_changes": [
{
"path": "/app/myapp",
"change_type": "modified",
"baseline_hash": "sha256:abc123...",
"current_hash": "sha256:xyz789...",
"provenance": {
"commit": "abc123def456",
"build_id": "9f5b7d4e2345678901bcdefg"
}
},
{
"path": "/app/lib/libnew.so",
"change_type": "added",
"current_hash": "sha256:new123...",
"provenance": {
"commit": "abc123def456",
"build_id": "newbuild123456"
}
}
],
"verification": {
"all_changes_have_provenance": true,
"all_binaries_signed": true,
"no_unexpected_modifications": true
},
"timestamp": "2024-12-29T10:30:00Z"
}
}
```
### 7. DSSE Signing
Signer wraps statement in DSSE envelope:
```json
{
"payloadType": "application/vnd.in-toto+json",
"payload": "base64:statement-json...",
"signatures": [
{
"keyid": "sha256:binary-attestation-key",
"sig": "base64:signature...",
"cert": "base64:x509-certificate..."
}
]
}
```
## Verification Policies
### Strict Mode
```yaml
binary_delta_policy:
mode: strict
rules:
- all_changes_must_have_provenance: true
- allow_added_binaries: true
- allow_removed_binaries: false
- require_signed_builds: true
- max_modified_binaries: 10
```
### Permissive Mode
```yaml
binary_delta_policy:
mode: permissive
rules:
- allow_unsigned_binaries: true
- warn_on_missing_provenance: true
- block_known_malware_hashes: true
```
## Data Contracts
### Binary Delta Request Schema
```typescript
interface BinaryDeltaRequest {
image: string;
options: {
binary_delta: {
enabled: boolean;
baseline: string; // Image reference or digest
include_symbols?: boolean;
include_provenance?: boolean;
};
attestation?: boolean;
};
}
```
### Binary Delta Response Schema
```typescript
interface BinaryDeltaResponse {
scan_id: string;
baseline: ImageReference;
current: ImageReference;
summary: {
total_binaries_baseline: number;
total_binaries_current: number;
modified: number;
added: number;
removed: number;
unchanged: number;
};
changes: BinaryChange[];
unchanged: BinaryReference[];
attestation?: {
digest: string;
rekor_log_index?: number;
};
policy_verdict?: {
verdict: 'PASS' | 'WARN' | 'FAIL';
violations?: string[];
};
}
```
## Error Handling
| Error | Recovery |
|-------|----------|
| Baseline not found | Return error, suggest scanning baseline first |
| Build ID not found | Continue without provenance, reduce confidence |
| Symbol resolution failed | Continue with hash-only comparison |
| Signing failure | Return delta without attestation |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `binary_delta_scans_total` | Counter | `result` |
| `binary_delta_changes_total` | Counter | `type` (modified/added/removed) |
| `binary_delta_duration_seconds` | Histogram | - |
| `binary_index_size_bytes` | Histogram | - |
### Key Log Events
| Event | Level | Fields |
|-------|-------|--------|
| `binary.delta.started` | INFO | `baseline`, `current` |
| `binary.delta.computed` | INFO | `modified`, `added`, `removed` |
| `binary.provenance.missing` | WARN | `path`, `build_id` |
| `binary.delta.attested` | INFO | `digest`, `rekor_index` |
## Related Flows
- [SBOM Generation Flow](03-sbom-generation-flow.md) - Component-level tracking
- [CI/CD Gate Flow](10-cicd-gate-flow.md) - Pipeline integration
- [Evidence Bundle Export Flow](13-evidence-bundle-export-flow.md) - Attestation packaging
| `binary.delta.started` | INFO | `baseline`, `current` |
| `binary.delta.computed` | INFO | `modified`, `added`, `removed` |
| `binary.provenance.missing` | WARN | `path`, `build_id` |
| `binary.delta.attested` | INFO | `digest`, `rekor_index` |
## Related Flows
- [SBOM Generation Flow](03-sbom-generation-flow.md) - Component-level tracking
- [CI/CD Gate Flow](10-cicd-gate-flow.md) - Pipeline integration
- [Evidence Bundle Export Flow](13-evidence-bundle-export-flow.md) - Attestation packaging

View File

@@ -0,0 +1,457 @@
# Offline Sync Flow
## Overview
The Offline Sync Flow describes how StellaOps supports air-gapped and disconnected environments through the Offline Kit. This flow covers advisory bundle generation, secure transfer, verification, and import in environments with no external network connectivity.
**Business Value**: Enable full vulnerability scanning and policy evaluation capabilities in highly secure, air-gapped environments while maintaining audit trails and cryptographic verification.
## Actors
| Actor | Type | Role |
|-------|------|------|
| Online Admin | Human | Generates and exports offline bundles |
| Offline Admin | Human | Imports and verifies bundles |
| Mirror | Service | Creates advisory snapshots |
| EvidenceLocker | Service | Seals bundles for transfer |
| AirGap Importer | Service | Validates and imports bundles |
| Signer | Service | Signs bundle manifests |
## Prerequisites
### Online Environment
- Access to vulnerability feeds (NVD, GHSA, etc.)
- Signing keys configured
- Bundle generation scheduled
### Offline Environment
- AirGap services deployed
- Trust anchors configured (public keys)
- Secure transfer mechanism available
## Bundle Types
| Bundle Type | Contents | Frequency |
|-------------|----------|-----------|
| Advisory Bundle | CVE data, CVSS scores, affected versions | Daily/Weekly |
| VEX Bundle | VEX statements from trusted issuers | Daily |
| Policy Bundle | Policy sets, rules, exceptions | On-demand |
| Trust Bundle | Signing keys, certificates, CRLs | Monthly |
| Time Anchor | Roughtime proofs, NTP alternatives | Daily |
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Offline Sync Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
ONLINE ENVIRONMENT OFFLINE ENVIRONMENT
┌────────────────────────────────┐ ┌────────────────────────────────┐
│ │ │ │
│ ┌────────┐ ┌────────┐ ┌────┐│ │┌────┐ ┌─────────┐ ┌────────┐│
│ │ Mirror │ │Evidence│ │Sign││ ││Verif│ │ AirGap │ │Concelier││
│ │ │ │ Locker │ │ ││ ││ │ │Importer │ │ ││
│ └───┬────┘ └───┬────┘ └──┬─┘│ │└──┬──┘ └────┬────┘ └───┬────┘│
│ │ │ │ │ │ │ │ │ │
│ │ Snapshot │ │ │ │ │ │ │ │
│ │ advisories│ │ │ │ │ │ │ │
│ │───┐ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │<──┘ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ Create │ │ │ │ │ │ │ │
│ │ bundle │ │ │ │ │ │ │ │
│ │──────────>│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ Seal │ │ │ │ │ │ │
│ │ │──────────> │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ Signed │ │ │ │ │ │ │
│ │ │ bundle │ │ │ │ │ │ │
│ │ │<────────── │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ [Export to ] │ │ │ │ │ │
│ │ [removable media ] │ =========> [Import from│ │ │
│ │ │ │ │ │ │removable]│ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ Verify │ │ │
│ │ │ │ │ │ │ signature│ │ │
│ │ │ │ │ │ │───┐ │ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │<──┘ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ Verify │ │ │
│ │ │ │ │ │ │ Merkle │ │ │
│ │ │ │ │ │ │───┐ │ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │<──┘ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ Import │ │ │
│ │ │ │ │ │ │──────────> │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ Unpack │ │
│ │ │ │ │ │ │ │ advisories│ │
│ │ │ │ │ │ │ │───┐ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │<──┘ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ Merge to │ │
│ │ │ │ │ │ │ │ Concelier │ │
│ │ │ │ │ │ │ │──────────>│ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
└────────────────────────────────┘ └────────────────────────────────┘
```
## Step-by-Step
### 1. Advisory Snapshot (Online)
Mirror service creates point-in-time snapshot:
```json
{
"snapshot_id": "snap-20241229",
"created_at": "2024-12-29T00:00:00Z",
"sources": [
{"name": "nvd", "last_sync": "2024-12-29T00:00:00Z", "count": 245678},
{"name": "ghsa", "last_sync": "2024-12-28T23:45:00Z", "count": 45678},
{"name": "osv", "last_sync": "2024-12-28T23:30:00Z", "count": 89012}
],
"delta_from": "snap-20241228",
"advisories": {
"new": 127,
"updated": 456,
"unchanged": 245095
}
}
```
### 2. Bundle Generation (Online)
EvidenceLocker creates sealed bundle:
```json
{
"bundle_id": "offline-adv-20241229",
"bundle_type": "advisory",
"created_at": "2024-12-29T01:00:00Z",
"contents": {
"advisories": {
"full_count": 245678,
"delta_count": 583,
"format": "ndjson.gz"
},
"metadata": {
"sources": ["nvd", "ghsa", "osv"],
"schema_version": "1.0.0"
}
},
"integrity": {
"merkle_root": "sha256:abc123...",
"file_count": 15,
"total_size": "125 MB"
}
}
```
### 3. Bundle Signing (Online)
Signer creates detached signature:
```json
{
"bundle_id": "offline-adv-20241229",
"signature": {
"algorithm": "ecdsa-p256",
"keyid": "sha256:offline-signing-key",
"sig": "base64:signature...",
"timestamp": "2024-12-29T01:00:00Z"
},
"certificate_chain": [
"base64:signing-cert...",
"base64:intermediate-ca...",
"base64:root-ca..."
],
"timestamping": {
"tsa_url": "https://timestamp.stellaops.io",
"timestamp_token": "base64:tst..."
}
}
```
### 4. Export Package
Final export package structure:
```
offline-kit-20241229/
├── manifest.json # Bundle manifest
├── manifest.sig # Detached signature
├── advisories/
│ ├── nvd-full.ndjson.gz
│ ├── nvd-delta.ndjson.gz
│ ├── ghsa-full.ndjson.gz
│ └── osv-full.ndjson.gz
├── vex/
│ └── vex-statements.ndjson.gz
├── policies/
│ └── policy-sets.json
├── trust/
│ ├── root-ca.pem
│ └── signing-keys.json
├── merkle/
│ └── tree.json
├── verify.sh # Verification script
└── README.md
```
### 5. Secure Transfer
Transfer via approved mechanism:
- USB drive (encrypted)
- Optical media (write-once)
- Data diode (one-way network)
- Secure courier
### 6. Import Verification (Offline)
AirGap Importer verifies bundle:
```bash
# Run verification
stellaops-airgap verify /media/usb/offline-kit-20241229/
# Verification steps:
✓ Manifest signature valid
✓ Certificate chain verified (trust anchor: sha256:root-ca)
✓ Timestamp verified (within 7 day window)
✓ Merkle root matches: sha256:abc123...
✓ All 15 files verified against Merkle tree
✓ No tamper detected
Bundle verified successfully. Ready for import.
```
### 7. Time Anchor Verification (Offline)
Verify bundle freshness without network time:
```json
{
"time_verification": {
"bundle_timestamp": "2024-12-29T01:00:00Z",
"tsa_timestamp": "2024-12-29T01:00:05Z",
"local_time_anchor": "2024-12-29T10:00:00Z",
"max_age_policy": "7d",
"age": "9h",
"status": "FRESH"
}
}
```
### 8. Advisory Import (Offline)
AirGap Importer loads advisories into Concelier:
```json
{
"import_id": "import-20241229-001",
"bundle_id": "offline-adv-20241229",
"started_at": "2024-12-29T10:30:00Z",
"completed_at": "2024-12-29T10:35:00Z",
"results": {
"advisories_imported": 583,
"advisories_updated": 456,
"advisories_new": 127,
"conflicts_resolved": 0,
"errors": 0
},
"state": {
"previous_snapshot": "snap-20241228",
"current_snapshot": "snap-20241229"
}
}
```
## Bundle Freshness Policies
### Strict (High Security)
```yaml
freshness_policy:
mode: strict
max_age:
advisory: 24h
vex: 24h
policy: 7d
trust: 30d
require_tsa: true
reject_stale: true
```
### Standard
```yaml
freshness_policy:
mode: standard
max_age:
advisory: 7d
vex: 7d
policy: 30d
trust: 90d
require_tsa: false
warn_stale: true
```
### Permissive (Disconnected Operations)
```yaml
freshness_policy:
mode: permissive
max_age:
advisory: 30d
vex: 30d
policy: 90d
trust: 365d
require_tsa: false
warn_stale: true
allow_manual_override: true
```
## Data Contracts
### Bundle Manifest Schema
```typescript
interface OfflineBundle {
bundle_id: string;
bundle_type: 'advisory' | 'vex' | 'policy' | 'trust' | 'time_anchor';
version: string;
created_at: string;
created_by: string;
contents: {
files: Array<{
path: string;
size: number;
sha256: string;
}>;
metadata: Record<string, unknown>;
};
integrity: {
merkle_root: string;
algorithm: 'sha256';
tree_path: string;
};
signature: {
keyid: string;
algorithm: string;
sig: string;
};
freshness: {
timestamp: string;
tsa_timestamp?: string;
valid_until?: string;
};
}
```
### Import Result Schema
```typescript
interface ImportResult {
import_id: string;
bundle_id: string;
status: 'success' | 'partial' | 'failed';
started_at: string;
completed_at: string;
results: {
records_imported: number;
records_updated: number;
records_new: number;
conflicts: number;
errors: number;
};
verification: {
signature_valid: boolean;
merkle_verified: boolean;
freshness_check: 'FRESH' | 'STALE' | 'EXPIRED';
};
audit_log_entry: string;
}
```
## Scheduling Strategies
### Daily Sync
```yaml
offline_sync:
advisory_bundle:
schedule: "0 1 * * *" # Daily at 1 AM
type: delta
retention: 7
vex_bundle:
schedule: "0 2 * * *" # Daily at 2 AM
type: delta
retention: 7
```
### Weekly Full + Daily Delta
```yaml
offline_sync:
advisory_bundle:
full:
schedule: "0 0 * * SUN" # Weekly full on Sunday
retention: 4
delta:
schedule: "0 1 * * MON-SAT" # Daily delta Mon-Sat
retention: 7
```
## Error Handling
| Error | Recovery |
|-------|----------|
| Signature invalid | Reject bundle, alert admin |
| Merkle verification failed | Reject bundle, request retransfer |
| Bundle too old | Warn user, require override |
| Import conflict | Log conflict, apply latest |
| Disk space insufficient | Cleanup old imports, retry |
## Observability
### Metrics (Online)
| Metric | Type | Labels |
|--------|------|--------|
| `offline_bundle_created_total` | Counter | `type` |
| `offline_bundle_size_bytes` | Histogram | `type` |
| `offline_bundle_advisory_count` | Gauge | `bundle_id` |
### Metrics (Offline)
| Metric | Type | Labels |
|--------|------|--------|
| `offline_import_total` | Counter | `status`, `type` |
| `offline_bundle_age_hours` | Gauge | `bundle_id` |
| `offline_advisory_freshness_hours` | Gauge | - |
### Key Log Events
| Event | Level | Fields |
|-------|-------|--------|
| `offline.bundle.created` | INFO | `bundle_id`, `type`, `size` |
| `offline.bundle.verified` | INFO | `bundle_id`, `verifier` |
| `offline.import.started` | INFO | `import_id`, `bundle_id` |
| `offline.import.complete` | INFO | `import_id`, `records` |
| `offline.freshness.warning` | WARN | `bundle_id`, `age` |
## Related Flows
- [Evidence Bundle Export Flow](13-evidence-bundle-export-flow.md) - Similar sealing mechanics
- [Advisory Drift Re-scan Flow](11-advisory-drift-rescan-flow.md) - Advisory consumption
- [Scan Submission Flow](02-scan-submission-flow.md) - Uses imported advisories

View File

@@ -0,0 +1,469 @@
# Exception Approval Workflow
## Overview
The Exception Approval Workflow describes how StellaOps handles policy exception requests, from initial submission through multi-level approval, time-limited grants, and expiration tracking. This flow ensures exceptions are documented, justified, and automatically expire.
**Business Value**: Maintain security governance while allowing controlled exceptions for legitimate business needs, with full audit trail and automatic expiration.
## Actors
| Actor | Type | Role |
|-------|------|------|
| Developer | Human | Requests exception |
| Team Lead | Human | First-level approval |
| Security Team | Human | Security review and approval |
| CISO | Human | Final approval (high severity) |
| Policy Engine | Service | Enforces exception |
| Scheduler | Service | Tracks expiration |
| Notify | Service | Sends alerts |
## Prerequisites
- Policy violation detected
- Exception workflow configured
- Approval chain defined
- Notification channels configured
## Exception Types
| Type | Description | Max Duration | Approval Level |
|------|-------------|--------------|----------------|
| Temporary | Time-limited deferral | 30 days | Team Lead |
| Extended | Business justification | 90 days | Security Team |
| Permanent | Architectural constraint | Indefinite | CISO |
| Emergency | Incident response | 7 days | Security Team |
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Exception Approval Workflow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌───────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ ┌───────────┐
│ Developer │ │ Team Lead│ │ Security │ │ Policy │ │Scheduler│ │ Notify │
└─────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └───┬────┘ └─────┬─────┘
│ │ │ │ │ │
│ Request │ │ │ │ │
│ exception │ │ │ │ │
│───────────────────────────────────────> │ │
│ │ │ │ │ │
│ │ │ │ Validate │ │
│ │ │ │ request │ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ │ │ Notify │ │
│ │ │ │ approvers │ │
│ │ │ │──────────────────────────>
│ │ │ │ │ │
│ │ Approval │ │ │ │ Send
│ │ request │ │ │ │ email
│ │<──────────────────────────────────────────────────
│ │ │ │ │ │
│ │ Review & │ │ │ │
│ │ approve │ │ │ │
│ │───────────────────────────> │ │
│ │ │ │ │ │
│ │ │ │ [If high │ │
│ │ │ │ severity] │ │
│ │ │ │───────────────────────────>
│ │ │ │ │ │
│ │ │ Approval │ │ │
│ │ │ request │ │ │
│ │ │<──────────────────────────────────────
│ │ │ │ │ │
│ │ │ Review & │ │ │
│ │ │ approve │ │ │
│ │ │───────────>│ │ │
│ │ │ │ │ │
│ │ │ │ Grant │ │
│ │ │ │ exception │ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ │ │ Schedule │ │
│ │ │ │ expiration│ │
│ │ │ │──────────>│ │
│ │ │ │ │ │
│ │ │ │ │ Schedule │
│ │ │ │ │ reminders │
│ │ │ │ │───┐ │
│ │ │ │ │ │ │
│ │ │ │ │<──┘ │
│ │ │ │ │ │
│ Exception │ │ │ │ │
│ granted │ │ │ │ │
│<───────────────────────────────────────────────────────────────
│ │ │ │ │ │
│ │ │ │ │[7d before] │
│ │ │ │ │ expiration │
│ │ │ │ │────────────>
│ │ │ │ │ │
│ Expiration │ │ │ │ │ Notify
│ warning │ │ │ │ │ owner
│<──────────────────────────────────────────────────────────────
│ │ │ │ │ │
```
## Step-by-Step
### 1. Exception Request
Developer submits exception request:
```json
{
"request_id": "exc-req-123",
"requester": "developer@acme.com",
"created_at": "2024-12-29T10:00:00Z",
"finding": {
"scan_id": "scan-abc123",
"cve": "CVE-2024-1234",
"package": "pkg:npm/lodash@4.17.20",
"severity": "high",
"policy_violation": "no-high-production"
},
"exception_type": "temporary",
"requested_duration": "30d",
"justification": {
"business_reason": "Upgrading lodash breaks critical authentication flow. Need time to refactor.",
"risk_acceptance": "Vulnerability requires prototype pollution which is not exploitable in our usage pattern.",
"mitigation_plan": "WAF rule deployed to block exploitation vectors. Ticket JIRA-1234 tracks fix.",
"remediation_deadline": "2025-01-28"
},
"scope": {
"images": ["docker.io/myorg/auth-service:*"],
"environments": ["production"]
}
}
```
### 2. Request Validation
Policy Engine validates the request:
```json
{
"validation": {
"request_id": "exc-req-123",
"valid": true,
"checks": [
{"check": "cve_exists", "passed": true},
{"check": "policy_violation_active", "passed": true},
{"check": "requester_authorized", "passed": true},
{"check": "scope_valid", "passed": true},
{"check": "duration_within_limits", "passed": true}
],
"required_approvers": [
{"role": "team_lead", "required": true},
{"role": "security_team", "required": true, "reason": "high_severity"}
],
"approval_deadline": "2024-12-31T10:00:00Z"
}
}
```
### 3. Approval Chain Notification
Notify sends approval requests:
```json
{
"notification": {
"type": "exception_approval_request",
"recipients": [
{"email": "team-lead@acme.com", "role": "team_lead"},
{"channel": "slack", "target": "#security-approvals"}
],
"content": {
"request_id": "exc-req-123",
"requester": "developer@acme.com",
"cve": "CVE-2024-1234",
"severity": "high",
"justification_summary": "Upgrading breaks auth flow. WAF mitigation in place.",
"approval_url": "https://console.stellaops.local/exceptions/exc-req-123/approve"
}
}
}
```
### 4. First-Level Approval (Team Lead)
Team Lead reviews and approves:
```json
{
"approval": {
"request_id": "exc-req-123",
"approver": "team-lead@acme.com",
"role": "team_lead",
"decision": "approved",
"approved_at": "2024-12-29T11:00:00Z",
"conditions": [
"Must have JIRA ticket for remediation",
"Weekly status update required"
],
"notes": "Verified WAF rule is active. Approve with conditions."
}
}
```
### 5. Security Review (Second Level)
Security team reviews high-severity exceptions:
```json
{
"security_review": {
"request_id": "exc-req-123",
"reviewer": "security-analyst@acme.com",
"role": "security_team",
"review_date": "2024-12-29T14:00:00Z",
"assessment": {
"risk_rating": "medium",
"exploitation_likelihood": "low",
"mitigation_effectiveness": "high",
"recommendation": "approve_with_monitoring"
},
"decision": "approved",
"conditions": [
"Enable runtime monitoring for auth-service",
"Review exception at 15-day mark"
],
"approved_duration": "30d" // May reduce requested duration
}
}
```
### 6. Exception Grant
Policy Engine activates the exception:
```json
{
"exception": {
"exception_id": "exc-456",
"request_id": "exc-req-123",
"status": "active",
"granted_at": "2024-12-29T14:30:00Z",
"granted_by": ["team-lead@acme.com", "security-analyst@acme.com"],
"finding": {
"cve": "CVE-2024-1234",
"package": "pkg:npm/lodash@4.17.20"
},
"scope": {
"images": ["docker.io/myorg/auth-service:*"],
"policy_rules": ["no-high-production"]
},
"validity": {
"starts_at": "2024-12-29T14:30:00Z",
"expires_at": "2025-01-28T14:30:00Z",
"duration": "30d"
},
"conditions": {
"require_jira_ticket": true,
"weekly_status_update": true,
"runtime_monitoring": true,
"mid_point_review": "2025-01-13T14:30:00Z"
}
}
}
```
### 7. Exception Enforcement
During policy evaluation, exception is applied:
```yaml
# Policy evaluation with exception
finding:
cve: CVE-2024-1234
package: pkg:npm/lodash@4.17.20
severity: high
exception_check:
exception_id: exc-456
scope_match: true
still_valid: true
conditions_met: true
verdict:
original: FAIL
with_exception: PASS
reason: "Exception exc-456 active until 2025-01-28"
```
### 8. Expiration Tracking
Scheduler manages exception lifecycle:
```json
{
"exception_schedule": {
"exception_id": "exc-456",
"events": [
{
"type": "mid_point_review",
"scheduled_at": "2025-01-13T14:30:00Z",
"notify": ["requester", "approvers"]
},
{
"type": "expiration_warning",
"scheduled_at": "2025-01-21T14:30:00Z",
"days_before": 7,
"notify": ["requester", "team_lead"]
},
{
"type": "expiration_final_warning",
"scheduled_at": "2025-01-27T14:30:00Z",
"days_before": 1,
"notify": ["requester", "team_lead", "security_team"]
},
{
"type": "expiration",
"scheduled_at": "2025-01-28T14:30:00Z",
"action": "deactivate_exception"
}
]
}
}
```
### 9. Renewal Request (If Needed)
Before expiration, requester can request renewal:
```json
{
"renewal_request": {
"exception_id": "exc-456",
"requested_extension": "30d",
"reason": "Refactoring taking longer than expected. PR in review.",
"progress_update": {
"jira_ticket": "JIRA-1234",
"status": "In Review",
"completion_percentage": 75
},
"new_deadline": "2025-02-27"
}
}
```
## Approval Matrix
| Severity | Exception Type | Required Approvers |
|----------|---------------|-------------------|
| Low | Temporary | Team Lead |
| Medium | Temporary | Team Lead |
| High | Temporary | Team Lead + Security |
| Critical | Any | Team Lead + Security + CISO |
| Any | Extended | Team Lead + Security |
| Any | Permanent | Security + CISO |
| Any | Emergency | Security (expedited) |
## Data Contracts
### Exception Request Schema
```typescript
interface ExceptionRequest {
request_id: string;
requester: string;
created_at: string;
finding: {
scan_id: string;
cve: string;
package: string;
severity: 'critical' | 'high' | 'medium' | 'low';
policy_violation: string;
};
exception_type: 'temporary' | 'extended' | 'permanent' | 'emergency';
requested_duration?: string;
justification: {
business_reason: string;
risk_acceptance: string;
mitigation_plan: string;
remediation_deadline?: string;
};
scope: {
images?: string[];
environments?: string[];
tenants?: string[];
};
}
```
### Exception Status Schema
```typescript
interface Exception {
exception_id: string;
request_id: string;
status: 'pending' | 'approved' | 'rejected' | 'active' | 'expired' | 'revoked';
finding: {
cve: string;
package: string;
};
scope: {
images: string[];
policy_rules: string[];
};
validity: {
starts_at: string;
expires_at: string;
duration: string;
};
approvals: Array<{
approver: string;
role: string;
decision: 'approved' | 'rejected';
timestamp: string;
conditions?: string[];
notes?: string;
}>;
audit_trail: AuditEntry[];
}
```
## Error Handling
| Error | Recovery |
|-------|----------|
| Approval timeout | Escalate to next level or reject |
| Approver unavailable | Route to backup approver |
| Scope too broad | Request scope refinement |
| Missing justification | Request additional details |
| Exception conflict | Flag for manual review |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `exception_requests_total` | Counter | `type`, `severity` |
| `exception_approvals_total` | Counter | `decision`, `approver_role` |
| `exception_active_count` | Gauge | `severity` |
| `exception_approval_duration_hours` | Histogram | `severity` |
| `exception_expiring_7d` | Gauge | - |
### Key Log Events
| Event | Level | Fields |
|-------|-------|--------|
| `exception.requested` | INFO | `request_id`, `cve`, `requester` |
| `exception.approved` | INFO | `exception_id`, `approver`, `duration` |
| `exception.rejected` | INFO | `request_id`, `approver`, `reason` |
| `exception.activated` | INFO | `exception_id`, `scope` |
| `exception.expiring` | WARN | `exception_id`, `days_remaining` |
| `exception.expired` | INFO | `exception_id` |
## Related Flows
- [Policy Evaluation Flow](04-policy-evaluation-flow.md) - Exception enforcement
- [Notification Flow](05-notification-flow.md) - Approval notifications
- [Multi-Tenant Policy Rollout Flow](14-multi-tenant-policy-rollout-flow.md) - Policy context

View File

@@ -0,0 +1,469 @@
# Risk Score Dashboard Flow
## Overview
The Risk Score Dashboard Flow describes how StellaOps computes, aggregates, and displays risk scores across multiple dimensions including images, teams, applications, and the entire organization. This flow enables data-driven security prioritization and trend analysis.
**Business Value**: Quantified risk visibility enables resource prioritization, executive reporting, and measurable security improvement over time.
## Actors
| Actor | Type | Role |
|-------|------|------|
| Security Leader | Human | Reviews risk posture |
| RiskEngine | Service | Computes risk scores |
| Platform | Service | Aggregates across tenants |
| Scanner | Service | Provides vulnerability data |
| Policy | Service | Provides compliance data |
| Console | System | Displays dashboards |
## Prerequisites
- Images scanned with findings
- Risk scoring model configured
- Historical data available for trends
- Aggregation permissions configured
## Risk Dimensions
| Dimension | Scope | Aggregation |
|-----------|-------|-------------|
| Image | Single container | Direct score |
| Application | Group of images | Weighted average |
| Team | All team assets | Sum/average |
| Environment | prod/staging/dev | Environment-weighted |
| Organization | All tenants | Executive rollup |
## Risk Factors
| Factor | Weight | Source |
|--------|--------|--------|
| CVSS Score | 25% | Advisory data |
| Exploitability | 20% | KEV, EPSS |
| Reachability | 20% | K4 lattice state |
| Exposure | 15% | Network exposure |
| Asset Criticality | 10% | Business metadata |
| Time to Remediate | 10% | Age of finding |
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Risk Score Dashboard Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌──────────┐ ┌─────────┐ ┌────────────┐ ┌─────────┐ ┌────────┐ ┌─────────┐
│ Security │ │ Console │ │ Platform │ │ Risk │ │ Scanner│ │ Policy │
│ Leader │ │ │ │ Service │ │ Engine │ │ │ │ │
└────┬─────┘ └────┬────┘ └─────┬──────┘ └────┬────┘ └───┬────┘ └────┬────┘
│ │ │ │ │ │
│ View risk │ │ │ │ │
│ dashboard │ │ │ │ │
│────────────>│ │ │ │ │
│ │ │ │ │ │
│ │ GET /risk/ │ │ │ │
│ │ summary │ │ │ │
│ │────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ Get findings │ │ │
│ │ │──────────────────────────> │
│ │ │ │ │ │
│ │ │ Vuln data │ │ │
│ │ │<────────────────────────── │
│ │ │ │ │ │
│ │ │ Get verdicts │ │ │
│ │ │───────────────────────────────────────>
│ │ │ │ │ │
│ │ │ Policy data │ │ │
│ │ │<───────────────────────────────────────
│ │ │ │ │ │
│ │ │ Compute │ │ │
│ │ │ scores │ │ │
│ │ │─────────────>│ │ │
│ │ │ │ │ │
│ │ │ │ Calculate │ │
│ │ │ │ per-image │ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ │ │ Aggregate │ │
│ │ │ │ by team │ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ │ Risk scores │ │ │
│ │ │<─────────────│ │ │
│ │ │ │ │ │
│ │ Dashboard │ │ │ │
│ │ data │ │ │ │
│ │<────────────│ │ │ │
│ │ │ │ │ │
│ Render │ │ │ │ │
│ dashboard │ │ │ │ │
│<────────────│ │ │ │ │
│ │ │ │ │ │
```
## Step-by-Step
### 1. Dashboard Request
Security Leader accesses risk dashboard:
```http
GET /api/v1/risk/summary HTTP/1.1
Authorization: Bearer {jwt}
X-Tenant-Id: acme-corp
Query params:
?scope=organization
&period=30d
&breakdown=team,severity,trend
```
### 2. Data Collection
Platform Service collects data from multiple sources:
#### Vulnerability Data (Scanner)
```json
{
"vulnerability_summary": {
"total_findings": 1847,
"by_severity": {
"critical": 23,
"high": 189,
"medium": 567,
"low": 1068
},
"by_status": {
"new": 145,
"existing": 1502,
"fixed": 200
},
"unique_cves": 423,
"affected_images": 89,
"affected_packages": 234
}
}
```
#### Policy Data (Policy Engine)
```json
{
"policy_summary": {
"total_evaluations": 892,
"by_verdict": {
"pass": 743,
"warn": 98,
"fail": 51
},
"compliance_rate": 0.83,
"by_policy_set": {
"production": {"pass": 234, "fail": 12},
"pci-dss": {"pass": 198, "fail": 8},
"default": {"pass": 311, "fail": 31}
}
}
}
```
#### Reachability Data (ReachGraph)
```json
{
"reachability_summary": {
"total_analyzed": 1847,
"by_state": {
"ConfirmedReachable": 45,
"StaticallyReachable": 234,
"Unknown": 890,
"StaticallyUnreachable": 456,
"ConfirmedUnreachable": 222
}
}
}
```
### 3. Risk Calculation
RiskEngine computes risk scores using the model:
#### Per-Finding Risk Score
```
Finding Risk = Σ(factor_weight × factor_score)
For CVE-2024-1234:
- CVSS Score: 0.25 × (9.8/10) = 0.245
- Exploitability: 0.20 × 0.95 (KEV)= 0.190
- Reachability: 0.20 × 0.80 (SR) = 0.160
- Exposure: 0.15 × 0.70 (ext)= 0.105
- Criticality: 0.10 × 0.90 = 0.090
- Time to Remediate:0.10 × 0.50 (30d)= 0.050
Finding Risk Score: 0.84 (High)
```
#### Per-Image Risk Score
```
Image Risk = max(finding_risks) + 0.1 × avg(other_findings)
Image: docker.io/myorg/api:v1.2.3
- Highest finding risk: 0.84
- Average other risks: 0.45
- Image Risk Score: 0.84 + 0.1 × 0.45 = 0.885
```
#### Aggregated Risk Score
```
Team Risk = Σ(image_risk × image_weight) / Σ(image_weight)
Team: Platform Engineering
- 12 images, weighted by deployment frequency
- Team Risk Score: 0.67
```
### 4. Dashboard Response
Platform returns aggregated risk data:
```json
{
"risk_summary": {
"organization": {
"score": 0.58,
"grade": "C",
"trend": "-0.05",
"trend_direction": "improving"
},
"breakdown": {
"by_team": [
{
"team": "Platform Engineering",
"score": 0.45,
"grade": "B",
"image_count": 12,
"critical_findings": 2
},
{
"team": "Product Development",
"score": 0.72,
"grade": "D",
"image_count": 34,
"critical_findings": 18
}
],
"by_severity": {
"critical": {"count": 23, "risk_contribution": 0.35},
"high": {"count": 189, "risk_contribution": 0.40},
"medium": {"count": 567, "risk_contribution": 0.20},
"low": {"count": 1068, "risk_contribution": 0.05}
},
"by_environment": {
"production": {"score": 0.62, "image_count": 45},
"staging": {"score": 0.55, "image_count": 23},
"development": {"score": 0.48, "image_count": 67}
}
},
"trends": {
"period": "30d",
"scores": [
{"date": "2024-11-29", "score": 0.63},
{"date": "2024-12-06", "score": 0.61},
{"date": "2024-12-13", "score": 0.59},
{"date": "2024-12-20", "score": 0.57},
{"date": "2024-12-29", "score": 0.58}
],
"change_30d": -0.05,
"change_7d": +0.01
},
"top_risks": [
{
"cve": "CVE-2024-1234",
"risk_score": 0.92,
"affected_images": 8,
"teams": ["Product Development"],
"remediation": "Upgrade lodash to 4.17.21"
}
],
"recommendations": [
{
"priority": 1,
"action": "Remediate CVE-2024-1234 in Product Development",
"impact": "Reduces org risk by 0.08 points"
}
]
}
}
```
### 5. Dashboard Rendering
Console displays risk visualization:
```
┌─────────────────────────────────────────────────────────────────┐
│ Organization Risk Dashboard │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Overall Risk Score: 58/100 (Grade: C) ↓ 5% from last month │
│ ████████████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│ │
│ ┌─ Risk by Team ──────────────────────────────────────────────┐│
│ │ Platform Engineering ████████░░░░░░░ 45 (B) ↓3% ││
│ │ Product Development ████████████████░░ 72 (D) ↑2% ││
│ │ Security ████░░░░░░░░░░░░ 28 (A) ↓1% ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─ 30-Day Trend ──────────────────────────────────────────────┐│
│ │ ╭───────────────────────────────────────────╮ ││
│ │ 63 │ ╲ │ ││
│ │ 61 │ ╲ │ ││
│ │ 59 │ ╲ │ ││
│ │ 57 │ ╲___________/ │ ││
│ │ ╰─────────────────────────────────────────────╯ ││
│ │ Nov 29 Dec 13 Dec 22 Dec 29 ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─ Top Actions ───────────────────────────────────────────────┐│
│ │ 1. Remediate CVE-2024-1234 (-0.08 risk) ││
│ │ 2. Update base images in Product team (-0.05 risk) ││
│ │ 3. Enable runtime monitoring for api-service (-0.03 risk) ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
└─────────────────────────────────────────────────────────────────┘
```
## Risk Grading Scale
| Score Range | Grade | Description |
|-------------|-------|-------------|
| 0-20 | A | Excellent - minimal risk |
| 21-40 | B | Good - well managed |
| 41-60 | C | Fair - needs attention |
| 61-80 | D | Poor - significant risk |
| 81-100 | F | Critical - immediate action |
## Data Contracts
### Risk Summary Request Schema
```typescript
interface RiskSummaryRequest {
scope: 'image' | 'application' | 'team' | 'environment' | 'organization';
scope_id?: string; // Required for image/application/team
period?: string; // ISO-8601 duration, default 30d
breakdown?: Array<'team' | 'severity' | 'environment' | 'trend'>;
compare_to?: string; // Previous period for comparison
}
```
### Risk Summary Response Schema
```typescript
interface RiskSummaryResponse {
risk_summary: {
scope: string;
score: number; // 0-100
grade: 'A' | 'B' | 'C' | 'D' | 'F';
trend: number;
trend_direction: 'improving' | 'stable' | 'degrading';
breakdown?: {
by_team?: TeamRisk[];
by_severity?: SeverityBreakdown;
by_environment?: EnvironmentRisk[];
};
trends?: {
period: string;
scores: Array<{date: string; score: number}>;
change_30d: number;
change_7d: number;
};
top_risks?: TopRisk[];
recommendations?: Recommendation[];
};
metadata: {
calculated_at: string;
data_freshness: string;
model_version: string;
};
}
```
## Risk Model Configuration
```yaml
risk_model:
version: "1.0.0"
factors:
cvss_score:
weight: 0.25
normalization: linear # score/10
exploitability:
weight: 0.20
components:
kev: 0.50 # In KEV = 1.0
epss: 0.30 # EPSS percentile
poc: 0.20 # Public PoC exists
reachability:
weight: 0.20
mapping:
ConfirmedReachable: 1.0
RuntimeObserved: 0.9
StaticallyReachable: 0.7
Unknown: 0.5
StaticallyUnreachable: 0.2
ConfirmedUnreachable: 0.1
exposure:
weight: 0.15
mapping:
internet_facing: 1.0
internal_network: 0.6
isolated: 0.2
asset_criticality:
weight: 0.10
source: business_metadata
remediation_age:
weight: 0.10
decay_days: 90 # Linear decay over 90 days
aggregation:
image: max_plus_average
team: weighted_average
organization: weighted_sum
```
## Error Handling
| Error | Recovery |
|-------|----------|
| Data stale | Show warning, use cached data |
| Partial data | Calculate with available, note gaps |
| Model error | Fall back to simplified model |
| Aggregation timeout | Return partial results |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `risk_score_current` | Gauge | `scope`, `scope_id` |
| `risk_calculation_duration_ms` | Histogram | `scope` |
| `risk_grade_distribution` | Gauge | `grade` |
| `risk_trend_change_30d` | Gauge | `scope`, `scope_id` |
### Key Log Events
| Event | Level | Fields |
|-------|-------|--------|
| `risk.calculated` | INFO | `scope`, `score`, `grade` |
| `risk.trend_alert` | WARN | `scope`, `change`, `direction` |
| `risk.threshold_exceeded` | WARN | `scope`, `threshold`, `score` |
## Related Flows
- [Dashboard Data Flow](01-dashboard-data-flow.md) - Dashboard patterns
- [Policy Evaluation Flow](04-policy-evaluation-flow.md) - Compliance data
- [Advisory Drift Re-scan Flow](11-advisory-drift-rescan-flow.md) - Risk updates

View File

@@ -0,0 +1,513 @@
# Reachability Drift Alert Flow
## Overview
The Reachability Drift Alert Flow describes how StellaOps detects and alerts on changes in code reachability that affect vulnerability risk assessments. When runtime observations or static analysis reveal that previously unreachable vulnerable code has become reachable (or vice versa), this flow triggers re-evaluation and notifications.
**Business Value**: Catch newly reachable vulnerabilities before they're exploited, and reduce alert fatigue by downgrading unreachable vulnerabilities automatically.
## Actors
| Actor | Type | Role |
|-------|------|------|
| Signals | Service | Collects runtime telemetry |
| ReachGraph | Service | Analyzes reachability state |
| Scanner | Service | Re-evaluates findings |
| Policy Engine | Service | Re-evaluates verdicts |
| Notify | Service | Sends drift alerts |
| Scheduler | Service | Orchestrates periodic checks |
## Prerequisites
- Runtime instrumentation deployed (eBPF agent or OpenTelemetry)
- Baseline reachability analysis completed
- Drift detection policies configured
- Alert channels configured
## Reachability State Transitions
| From State | To State | Risk Impact | Alert Priority |
|------------|----------|-------------|----------------|
| Unknown → StaticallyReachable | Increased | Medium |
| Unknown → RuntimeObserved | Increased | High |
| StaticallyUnreachable → StaticallyReachable | Increased | Medium |
| StaticallyReachable → RuntimeObserved | Confirmed | High |
| RuntimeObserved → ConfirmedReachable | Confirmed | High |
| StaticallyReachable → StaticallyUnreachable | Decreased | Low |
| RuntimeObserved → RuntimeUnobserved | Decreased | Medium |
| Any → ConfirmedUnreachable | Decreased | Low |
| Any → Contested | Review needed | High |
## Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Reachability Drift Alert Flow │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────┐ ┌───────────┐ ┌───────────┐ ┌─────────┐ ┌────────┐ ┌────────┐
│ Signals │ │ ReachGraph│ │ Scheduler │ │ Scanner │ │ Policy │ │ Notify │
└────┬────┘ └─────┬─────┘ └─────┬─────┘ └────┬────┘ └───┬────┘ └───┬────┘
│ │ │ │ │ │
│ Runtime │ │ │ │ │
│ event │ │ │ │ │
│────────────>│ │ │ │ │
│ │ │ │ │ │
│ │ Update │ │ │ │
│ │ call graph │ │ │ │
│ │───┐ │ │ │ │
│ │ │ │ │ │ │
│ │<──┘ │ │ │ │
│ │ │ │ │ │
│ │ Detect │ │ │ │
│ │ state change │ │ │ │
│ │───┐ │ │ │ │
│ │ │ │ │ │ │
│ │<──┘ │ │ │ │
│ │ │ │ │ │
│ │ [If state │ │ │ │
│ │ changed] │ │ │ │
│ │ │ │ │ │
│ │ Emit drift │ │ │ │
│ │ event │ │ │ │
│ │─────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ Queue │ │ │
│ │ │ re-eval │ │ │
│ │ │────────────>│ │ │
│ │ │ │ │ │
│ │ │ │ Load scan │ │
│ │ │ │ + findings│ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ │ │ Update │ │
│ │ │ │ reach │ │
│ │ │ │ states │ │
│ │ │ │───┐ │ │
│ │ │ │ │ │ │
│ │ │ │<──┘ │ │
│ │ │ │ │ │
│ │ │ │ Re-eval │ │
│ │ │ │──────────>│ │
│ │ │ │ │ │
│ │ │ │ │ Compare │
│ │ │ │ │ verdicts │
│ │ │ │ │───┐ │
│ │ │ │ │ │ │
│ │ │ │ │<──┘ │
│ │ │ │ │ │
│ │ │ │ New │ │
│ │ │ │ verdict │ │
│ │ │ │<──────────│ │
│ │ │ │ │ │
│ │ │ [If verdict │ │ │
│ │ │ changed] │ │ │
│ │ │ │ │ │
│ │ │ Alert │ │ │
│ │ │─────────────────────────────────────>│
│ │ │ │ │ │
│ │ │ │ │ │ Send
│ │ │ │ │ │ alert
│ │ │ │ │ │───┐
│ │ │ │ │ │ │
│ │ │ │ │ │<──┘
│ │ │ │ │ │
```
## Step-by-Step
### 1. Runtime Event Collection
Signals service collects function invocation data:
```json
{
"event_type": "function_invocation",
"timestamp": "2024-12-29T10:30:00Z",
"source": "ebpf-agent",
"payload": {
"container_id": "abc123...",
"image_digest": "sha256:...",
"function": "lodash.template",
"package": "pkg:npm/lodash@4.17.20",
"call_stack": [
"app/routes/render.js:45",
"lib/template-engine.js:123",
"node_modules/lodash/template.js:89"
],
"invocation_count": 1
}
}
```
### 2. Reachability State Update
ReachGraph updates K4 lattice state:
```json
{
"state_transition": {
"package": "pkg:npm/lodash@4.17.20",
"function": "lodash.template",
"image_digest": "sha256:...",
"previous_state": "StaticallyReachable",
"new_state": "RuntimeObserved",
"transition_reason": "first_runtime_invocation",
"evidence": {
"static": {
"call_paths": 3,
"entry_points": ["app/routes/render.js:45"]
},
"runtime": {
"first_observed": "2024-12-29T10:30:00Z",
"invocation_count": 1,
"call_stack_hash": "sha256:stackhash..."
}
}
}
}
```
### 3. Drift Detection
ReachGraph compares against baseline and detects drift:
```json
{
"drift_event": {
"drift_id": "drift-123",
"detected_at": "2024-12-29T10:30:01Z",
"image_digest": "sha256:...",
"package": "pkg:npm/lodash@4.17.20",
"transition": {
"from": "StaticallyReachable",
"to": "RuntimeObserved"
},
"affected_vulnerabilities": [
{
"cve": "CVE-2024-1234",
"severity": "critical",
"previous_risk": "medium",
"new_risk": "high"
}
],
"risk_impact": "increased",
"alert_priority": "high"
}
}
```
### 4. Re-evaluation Trigger
Scheduler queues affected scans for re-evaluation:
```json
{
"reevaluation_job": {
"job_id": "reeval-456",
"trigger": "reachability_drift",
"drift_id": "drift-123",
"scans": [
{
"scan_id": "scan-abc123",
"image": "docker.io/myorg/app:v1.2.3",
"affected_findings": ["CVE-2024-1234"]
}
],
"priority": "high"
}
}
```
### 5. Finding Re-evaluation
Scanner updates findings with new reachability:
```json
{
"finding_update": {
"scan_id": "scan-abc123",
"cve": "CVE-2024-1234",
"package": "pkg:npm/lodash@4.17.20",
"previous": {
"reachability": "StaticallyReachable",
"confidence": 0.70
},
"updated": {
"reachability": "RuntimeObserved",
"confidence": 0.95,
"evidence": {
"runtime_observed_at": "2024-12-29T10:30:00Z",
"call_stack": ["..."]
}
}
}
}
```
### 6. Policy Re-evaluation
Policy engine re-evaluates with updated reachability:
```json
{
"verdict_comparison": {
"scan_id": "scan-abc123",
"previous_verdict": "WARN",
"new_verdict": "FAIL",
"verdict_changed": true,
"changes": [
{
"finding": "CVE-2024-1234",
"rule": "no-critical-reachable",
"previous_result": "WARN (StaticallyReachable)",
"new_result": "FAIL (RuntimeObserved)",
"reason": "Runtime execution confirmed - elevated from warning to block"
}
]
}
}
```
### 7. Alert Generation
Notify sends drift alert:
```json
{
"alert": {
"alert_id": "alert-789",
"type": "reachability_drift",
"priority": "high",
"title": "Vulnerability Reachability Confirmed by Runtime",
"body": {
"summary": "CVE-2024-1234 in lodash@4.17.20 was observed at runtime",
"image": "docker.io/myorg/app:v1.2.3",
"cve": "CVE-2024-1234",
"severity": "critical",
"transition": "StaticallyReachable → RuntimeObserved",
"impact": "Verdict changed from WARN to FAIL",
"action_required": "Immediate remediation recommended",
"remediation": "Upgrade lodash to 4.17.21"
},
"channels": ["slack", "pagerduty", "email"]
}
}
```
### Slack Alert Format
```
🚨 Vulnerability Reachability Confirmed
CVE: CVE-2024-1234 (Critical)
Package: lodash@4.17.20
Image: myorg/app:v1.2.3
State Change: StaticallyReachable → RuntimeObserved
Impact: Verdict changed WARN → FAIL
The vulnerable function `lodash.template` was invoked at runtime,
confirming the vulnerability is exploitable.
Action: Immediate remediation required
Fix: Upgrade lodash to 4.17.21
[View Details] [Create Ticket] [Add Exception]
```
## Drift Detection Modes
### Real-time Detection
```yaml
drift_detection:
mode: realtime
config:
latency_target: 5s
buffer_window: 0
immediate_alert_severity: [critical, high]
```
### Batch Detection
```yaml
drift_detection:
mode: batch
config:
check_interval: 15m
aggregate_similar: true
min_invocations_for_transition: 3
```
### Hybrid Detection
```yaml
drift_detection:
mode: hybrid
config:
realtime_for: [critical]
batch_for: [high, medium, low]
batch_interval: 1h
```
## Downgrade Handling
When reachability decreases (e.g., code removed):
```json
{
"downgrade_event": {
"drift_id": "drift-456",
"type": "risk_decrease",
"package": "pkg:npm/lodash@4.17.20",
"cve": "CVE-2024-1234",
"transition": {
"from": "RuntimeObserved",
"to": "RuntimeUnobserved",
"observation_gap": "30d"
},
"risk_impact": "decreased",
"action": "auto_downgrade",
"verdict_change": "FAIL → WARN",
"notification": "info" // Lower priority for improvements
}
}
```
## Data Contracts
### Drift Event Schema
```typescript
interface ReachabilityDriftEvent {
drift_id: string;
detected_at: string;
image_digest: string;
package: string;
function?: string;
transition: {
from: K4State;
to: K4State;
reason: string;
};
affected_vulnerabilities: Array<{
cve: string;
severity: string;
previous_risk: string;
new_risk: string;
}>;
risk_impact: 'increased' | 'decreased' | 'unchanged';
alert_priority: 'critical' | 'high' | 'medium' | 'low' | 'info';
evidence: {
static?: StaticEvidence;
runtime?: RuntimeEvidence;
};
}
```
### Drift Alert Schema
```typescript
interface DriftAlert {
alert_id: string;
drift_id: string;
type: 'reachability_drift';
priority: 'critical' | 'high' | 'medium' | 'low';
title: string;
body: {
summary: string;
image: string;
cve: string;
severity: string;
transition: string;
impact: string;
action_required?: string;
remediation?: string;
};
channels: string[];
sent_at: string;
acknowledged_at?: string;
resolved_at?: string;
}
```
## Drift Policies
### Aggressive (High Security)
```yaml
drift_policy:
mode: aggressive
rules:
- any_reachability_increase: alert_immediately
- runtime_first_observation: alert_critical
- contested_state: require_investigation
- auto_downgrade: disabled
```
### Balanced
```yaml
drift_policy:
mode: balanced
rules:
- critical_cve_reachability_increase: alert_high
- high_cve_runtime_observation: alert_medium
- contested_state: alert_medium
- auto_downgrade:
enabled: true
observation_gap: 30d
confidence_threshold: 0.9
```
### Permissive
```yaml
drift_policy:
mode: permissive
rules:
- runtime_observation_critical: alert_high
- other_increases: log_only
- auto_downgrade:
enabled: true
observation_gap: 14d
```
## Error Handling
| Error | Recovery |
|-------|----------|
| Signal collection gap | Use last known state, note uncertainty |
| State conflict | Mark as Contested, require review |
| Alert delivery failure | Queue for retry |
| Scan not found | Skip re-evaluation, log warning |
## Observability
### Metrics
| Metric | Type | Labels |
|--------|------|--------|
| `reachability_drift_events_total` | Counter | `transition_type`, `risk_impact` |
| `reachability_state_transitions_total` | Counter | `from_state`, `to_state` |
| `drift_alert_sent_total` | Counter | `priority`, `channel` |
| `drift_detection_latency_ms` | Histogram | - |
### Key Log Events
| Event | Level | Fields |
|-------|-------|--------|
| `reach.state_transition` | INFO | `package`, `from`, `to` |
| `reach.drift_detected` | WARN | `drift_id`, `impact` |
| `reach.verdict_changed` | WARN | `scan_id`, `previous`, `new` |
| `reach.alert_sent` | INFO | `alert_id`, `priority` |
## Related Flows
- [Policy Evaluation Flow](04-policy-evaluation-flow.md) - K4 lattice details
- [Advisory Drift Re-scan Flow](11-advisory-drift-rescan-flow.md) - Similar re-evaluation
- [Risk Score Dashboard Flow](18-risk-score-dashboard-flow.md) - Risk impact
- [Notification Flow](05-notification-flow.md) - Alert delivery

63
docs/flows/README.md Normal file
View File

@@ -0,0 +1,63 @@
# StellaOps Flow Documentation
This directory contains detailed end-to-end flow documentation for all major StellaOps workflows.
## Flow Categories
### Core Platform Flows (Existing)
| Flow | File | Description |
|------|------|-------------|
| Dashboard Data Flow | [01-dashboard-data-flow.md](01-dashboard-data-flow.md) | How dashboard aggregates and displays security posture |
| Scan Submission Flow | [02-scan-submission-flow.md](02-scan-submission-flow.md) | End-to-end container image scan lifecycle |
| SBOM Generation Flow | [03-sbom-generation-flow.md](03-sbom-generation-flow.md) | Multi-analyzer SBOM generation and attestation |
| Policy Evaluation Flow | [04-policy-evaluation-flow.md](04-policy-evaluation-flow.md) | K4 lattice policy evaluation with confidence scoring |
| Notification Flow | [05-notification-flow.md](05-notification-flow.md) | Multi-channel notification delivery |
| Export Flow | [06-export-flow.md](06-export-flow.md) | Report and evidence bundle generation |
### Advanced Flows (New)
| Flow | File | Description |
|------|------|-------------|
| CI/CD Gate Flow | [10-cicd-gate-flow.md](10-cicd-gate-flow.md) | Pipeline integration with pass/fail gates |
| Advisory Drift Re-scan Flow | [11-advisory-drift-rescan-flow.md](11-advisory-drift-rescan-flow.md) | Automatic re-evaluation on new advisories |
| VEX Auto-Generation Flow | [12-vex-auto-generation-flow.md](12-vex-auto-generation-flow.md) | ML-assisted VEX statement generation |
| Evidence Bundle Export Flow | [13-evidence-bundle-export-flow.md](13-evidence-bundle-export-flow.md) | Auditable evidence package creation |
| Multi-Tenant Policy Rollout Flow | [14-multi-tenant-policy-rollout-flow.md](14-multi-tenant-policy-rollout-flow.md) | Cross-tenant policy propagation |
| Binary Delta Attestation Flow | [15-binary-delta-attestation-flow.md](15-binary-delta-attestation-flow.md) | Binary-level change attestation |
| Offline Sync Flow | [16-offline-sync-flow.md](16-offline-sync-flow.md) | Air-gapped environment synchronization |
| Exception Approval Workflow | [17-exception-approval-workflow.md](17-exception-approval-workflow.md) | Policy exception request and approval |
| Risk Score Dashboard Flow | [18-risk-score-dashboard-flow.md](18-risk-score-dashboard-flow.md) | Real-time risk aggregation and display |
| Reachability Drift Alert Flow | [19-reachability-drift-alert-flow.md](19-reachability-drift-alert-flow.md) | Runtime reachability change detection |
## Flow Documentation Format
Each flow document follows a standard structure:
1. **Overview** - Brief description and business value
2. **Actors** - Users, systems, and services involved
3. **Prerequisites** - Required configuration and dependencies
4. **Flow Diagram** - UML sequence/activity diagram
5. **Step-by-Step** - Detailed step descriptions
6. **Data Contracts** - Input/output schemas
7. **Error Handling** - Failure modes and recovery
8. **Observability** - Metrics, logs, and traces
9. **Related Flows** - Cross-references to related workflows
## Module Ownership
| Flow Category | Primary Module | Supporting Modules |
|---------------|----------------|-------------------|
| Scanning | Scanner | Gateway, Scheduler, Attestor |
| Policy | Policy | VexLens, Concelier, Scanner |
| Advisory | Concelier | Excititor, Mirror, VexLens |
| Export | ExportCenter | EvidenceLocker, Attestor, Signer |
| Notification | Notify | Scheduler, Orchestrator |
| CI/CD | CLI | Gateway, Scanner, Policy |
## Related Documentation
- [User Flows (UML)](../technical/architecture/user-flows.md)
- [Data Flows](../technical/architecture/data-flows.md)
- [Module Matrix](../technical/architecture/module-matrix.md)
- [Schema Mapping](../technical/architecture/schema-mapping.md)