save progress
This commit is contained in:
250
docs/flows/01-dashboard-data-flow.md
Normal file
250
docs/flows/01-dashboard-data-flow.md
Normal 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
|
||||
411
docs/flows/02-scan-submission-flow.md
Normal file
411
docs/flows/02-scan-submission-flow.md
Normal 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
|
||||
429
docs/flows/03-sbom-generation-flow.md
Normal file
429
docs/flows/03-sbom-generation-flow.md
Normal 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
|
||||
490
docs/flows/04-policy-evaluation-flow.md
Normal file
490
docs/flows/04-policy-evaluation-flow.md
Normal 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
|
||||
382
docs/flows/05-notification-flow.md
Normal file
382
docs/flows/05-notification-flow.md
Normal 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
|
||||
383
docs/flows/06-export-flow.md
Normal file
383
docs/flows/06-export-flow.md
Normal 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
|
||||
465
docs/flows/10-cicd-gate-flow.md
Normal file
465
docs/flows/10-cicd-gate-flow.md
Normal 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
|
||||
425
docs/flows/11-advisory-drift-rescan-flow.md
Normal file
425
docs/flows/11-advisory-drift-rescan-flow.md
Normal 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
|
||||
434
docs/flows/12-vex-auto-generation-flow.md
Normal file
434
docs/flows/12-vex-auto-generation-flow.md
Normal 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
|
||||
545
docs/flows/13-evidence-bundle-export-flow.md
Normal file
545
docs/flows/13-evidence-bundle-export-flow.md
Normal 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
|
||||
476
docs/flows/14-multi-tenant-policy-rollout-flow.md
Normal file
476
docs/flows/14-multi-tenant-policy-rollout-flow.md
Normal 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
|
||||
508
docs/flows/15-binary-delta-attestation-flow.md
Normal file
508
docs/flows/15-binary-delta-attestation-flow.md
Normal 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
|
||||
457
docs/flows/16-offline-sync-flow.md
Normal file
457
docs/flows/16-offline-sync-flow.md
Normal 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
|
||||
469
docs/flows/17-exception-approval-workflow.md
Normal file
469
docs/flows/17-exception-approval-workflow.md
Normal 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
|
||||
469
docs/flows/18-risk-score-dashboard-flow.md
Normal file
469
docs/flows/18-risk-score-dashboard-flow.md
Normal 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
|
||||
513
docs/flows/19-reachability-drift-alert-flow.md
Normal file
513
docs/flows/19-reachability-drift-alert-flow.md
Normal 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
63
docs/flows/README.md
Normal 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)
|
||||
Reference in New Issue
Block a user