From da315965ffee87979a85e018f13278f95bc63a9f Mon Sep 17 00:00:00 2001 From: StellaOps Bot Date: Sat, 20 Dec 2025 22:22:09 +0200 Subject: [PATCH] feat: Add operations runbooks and UI API models for Sprint 3500.0004.x Operations documentation: - docs/operations/reachability-runbook.md - Reachability troubleshooting guide - docs/operations/unknowns-queue-runbook.md - Unknowns queue management guide UI TypeScript models: - src/Web/StellaOps.Web/src/app/core/api/proof.models.ts - Proof ledger types - src/Web/StellaOps.Web/src/app/core/api/reachability.models.ts - Reachability types - src/Web/StellaOps.Web/src/app/core/api/unknowns.models.ts - Unknowns queue types Sprint: SPRINT_3500_0004_0002 (UI), SPRINT_3500_0004_0004 (Docs) --- docs/operations/reachability-runbook.md | 594 ++++++++++++++++++ docs/operations/unknowns-queue-runbook.md | 590 +++++++++++++++++ .../src/app/core/api/proof.models.ts | 180 ++++++ .../src/app/core/api/reachability.models.ts | 192 ++++++ .../src/app/core/api/unknowns.models.ts | 163 +++++ 5 files changed, 1719 insertions(+) create mode 100644 docs/operations/reachability-runbook.md create mode 100644 docs/operations/unknowns-queue-runbook.md create mode 100644 src/Web/StellaOps.Web/src/app/core/api/proof.models.ts create mode 100644 src/Web/StellaOps.Web/src/app/core/api/reachability.models.ts create mode 100644 src/Web/StellaOps.Web/src/app/core/api/unknowns.models.ts diff --git a/docs/operations/reachability-runbook.md b/docs/operations/reachability-runbook.md new file mode 100644 index 000000000..0e122276c --- /dev/null +++ b/docs/operations/reachability-runbook.md @@ -0,0 +1,594 @@ +# Reachability Analysis Operations Runbook + +> **Version**: 1.0.0 +> **Sprint**: 3500.0004.0004 +> **Last Updated**: 2025-12-20 + +This runbook covers operational procedures for Reachability Analysis, including call graph management, analysis troubleshooting, and explain queries. + +--- + +## Table of Contents + +1. [Overview](#1-overview) +2. [Call Graph Operations](#2-call-graph-operations) +3. [Reachability Computation](#3-reachability-computation) +4. [Explain Queries](#4-explain-queries) +5. [Troubleshooting](#5-troubleshooting) +6. [Monitoring & Alerting](#6-monitoring--alerting) +7. [Escalation Procedures](#7-escalation-procedures) + +--- + +## 1. Overview + +### What is Reachability Analysis? + +Reachability Analysis determines whether vulnerable code is actually reachable from application entrypoints. This reduces false positives by filtering out vulnerabilities in code that cannot be executed. + +### Reachability Statuses + +| Status | Confidence | Description | +|--------|------------|-------------| +| `UNREACHABLE` | High | No path from entrypoints to vulnerable code | +| `POSSIBLY_REACHABLE` | Medium | Path exists but contains heuristic edges | +| `REACHABLE_STATIC` | High | Static analysis proves path exists | +| `REACHABLE_PROVEN` | Very High | Runtime evidence confirms execution | +| `UNKNOWN` | Low | Insufficient data to determine | + +### Key Components + +| Component | Purpose | Location | +|-----------|---------|----------| +| Call Graph Extractor | Language-specific CG extraction | Scanner Worker plugins | +| Call Graph Store | Persistent graph storage | `scanner.cg_node`, `scanner.cg_edge` | +| Reachability Analyzer | BFS pathfinding algorithm | Scanner Core library | +| Entrypoint Detector | Identifies application entrypoints | Language-specific plugins | + +### Prerequisites + +- Access to Scanner WebService API +- `scanner.reachability` OAuth scope +- CLI access with `stella` configured +- Language-specific workers deployed (dotnet, java, etc.) + +--- + +## 2. Call Graph Operations + +### 2.1 Call Graph Upload + +```bash +# Upload via API +curl -X POST "https://scanner.example.com/api/v1/scanner/scans/$SCAN_ID/callgraphs" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -H "Content-Digest: sha256=$(sha256sum callgraph.json | cut -d' ' -f1)" \ + -d @callgraph.json + +# Upload via CLI +stella scan graph upload --scan-id $SCAN_ID --file callgraph.json + +# Upload streaming NDJSON (for large graphs) +stella scan graph upload --scan-id $SCAN_ID \ + --file callgraph.ndjson \ + --format ndjson \ + --streaming +``` + +### 2.2 Call Graph Inspection + +```bash +# Get call graph summary +stella scan graph summary --scan-id $SCAN_ID + +# Output: +# Nodes: 12,345 +# Edges: 56,789 +# Entrypoints: 42 +# Languages: [dotnet, java] +# Size: 15.2 MB + +# List entrypoints +stella scan graph entrypoints --scan-id $SCAN_ID + +# Export full graph (for debugging) +stella scan graph export --scan-id $SCAN_ID --output graph.json + +# Visualize subgraph (requires GraphViz) +stella scan graph visualize --scan-id $SCAN_ID \ + --node sha256:node123... \ + --depth 3 \ + --output subgraph.svg +``` + +### 2.3 Call Graph Validation + +```bash +# Validate graph structure +stella scan graph validate --scan-id $SCAN_ID + +# Checks performed: +# - All edge targets exist as nodes +# - Entrypoints reference valid nodes +# - No orphan nodes +# - No cycles in entrypoint definitions +# - Schema compliance + +# Validate before upload +stella scan graph validate --file callgraph.json --strict +``` + +### 2.4 Call Graph Merging + +When multiple language workers produce graphs: + +```bash +# View merge status +stella scan graph merges --scan-id $SCAN_ID + +# Output: +# Language | Nodes | Edges | Status +# dotnet | 8,234 | 34,567 | merged +# java | 4,111 | 22,222 | merged +# Total | 12,345 | 56,789 | complete + +# Force re-merge (after fix) +stella scan graph merge --scan-id $SCAN_ID --force +``` + +--- + +## 3. Reachability Computation + +### 3.1 Triggering Computation + +```bash +# Trigger via API +curl -X POST "https://scanner.example.com/api/v1/scanner/scans/$SCAN_ID/reachability/compute" \ + -H "Authorization: Bearer $TOKEN" + +# Trigger via CLI +stella reachability compute --scan-id $SCAN_ID + +# Trigger with options +stella reachability compute --scan-id $SCAN_ID \ + --max-depth 20 \ + --indirect-resolution conservative \ + --timeout 300s +``` + +### 3.2 Computation Options + +| Option | Default | Description | +|--------|---------|-------------| +| `max-depth` | 10 | Maximum path length to explore | +| `indirect-resolution` | `conservative` | How to handle indirect calls: `conservative`, `aggressive`, `skip` | +| `timeout` | 300s | Maximum computation time | +| `parallel` | true | Parallel BFS from multiple entrypoints | +| `include-runtime` | true | Merge runtime evidence if available | + +### 3.3 Job Monitoring + +```bash +# Check job status +stella reachability job-status --job-id reachability-job-001 + +# Output: +# Status: running +# Progress: 67% (8,234 / 12,345 nodes visited) +# Started: 2025-12-20T10:00:00Z +# Estimated completion: 2025-12-20T10:02:30Z + +# Stream job logs +stella reachability job-logs --job-id reachability-job-001 --follow + +# Cancel running job +stella reachability job-cancel --job-id reachability-job-001 +``` + +### 3.4 Computation Results + +```bash +# Get summary +stella reachability summary --scan-id $SCAN_ID + +# Output: +# Total vulnerabilities: 45 +# Unreachable: 38 (84%) +# Possibly reachable: 4 (9%) +# Reachable (static): 2 (4%) +# Reachable (proven): 1 (2%) +# Unknown: 0 (0%) + +# Get detailed findings +stella reachability findings --scan-id $SCAN_ID --format json + +# Filter by status +stella reachability findings --scan-id $SCAN_ID --status REACHABLE_STATIC + +# Export for CI gate +stella reachability findings --scan-id $SCAN_ID \ + --status REACHABLE_STATIC,REACHABLE_PROVEN \ + --format sarif \ + --output findings.sarif +``` + +--- + +## 4. Explain Queries + +### 4.1 Explain Single Finding + +```bash +# Via API +curl "https://scanner.example.com/api/v1/scanner/scans/$SCAN_ID/reachability/explain?cve=CVE-2024-1234&purl=pkg:npm/lodash@4.17.20" \ + -H "Authorization: Bearer $TOKEN" + +# Via CLI +stella reachability explain --scan-id $SCAN_ID \ + --cve CVE-2024-1234 \ + --purl "pkg:npm/lodash@4.17.20" + +# Output: +# Status: REACHABLE_STATIC +# Confidence: 0.70 +# +# Shortest Path (depth=3): +# [0] MyApp.Controllers.OrdersController::Get(Guid) +# Entrypoint: HTTP GET /api/orders/{id} +# [1] MyApp.Services.OrderService::Process(Order) +# Edge: static (direct_call) +# [2] Lodash.merge(Object, Object) [VULNERABLE] +# Edge: static (direct_call) +# +# Why Reachable: +# - Static call path exists from HTTP entrypoint +# - All edges are statically proven +# - Vulnerable function is directly invoked +``` + +### 4.2 Explain with Alternatives + +```bash +# Show all paths (not just shortest) +stella reachability explain --scan-id $SCAN_ID \ + --cve CVE-2024-1234 \ + --purl "pkg:npm/lodash@4.17.20" \ + --all-paths + +# Output includes: +# Alternative paths found: 3 +# Path 1 (depth=3): ... [shown above] +# Path 2 (depth=5): Controllers.UserController -> ... -> Lodash.merge +# Path 3 (depth=7): Background.JobProcessor -> ... -> Lodash.merge +``` + +### 4.3 Why Unreachable + +```bash +# Explain why vulnerability is unreachable +stella reachability explain --scan-id $SCAN_ID \ + --cve CVE-2024-5678 \ + --purl "pkg:npm/unused-lib@1.0.0" + +# Output: +# Status: UNREACHABLE +# Confidence: 0.95 +# +# Why Unreachable: +# - No path found from any entrypoint +# - Vulnerable function: UnusedLib.dangerousMethod() +# - Function visibility: private +# - Callers found: 0 +# - Dead code analysis: likely dead code +``` + +### 4.4 Batch Explain + +```bash +# Export all reachability explanations +stella reachability explain-all --scan-id $SCAN_ID \ + --output explanations.json + +# Explain only reachable findings +stella reachability explain-all --scan-id $SCAN_ID \ + --status REACHABLE_STATIC,REACHABLE_PROVEN \ + --output reachable-explanations.json +``` + +--- + +## 5. Troubleshooting + +### 5.1 Call Graph Too Large + +**Symptom**: Upload fails with "413 Payload Too Large". + +**Diagnosis**: + +```bash +# Check graph size +du -h callgraph.json +wc -l callgraph.json + +# Count nodes/edges +jq '.nodes | length' callgraph.json +jq '.edges | length' callgraph.json +``` + +**Resolution**: + +```bash +# Option 1: Use streaming upload +stella scan graph upload --scan-id $SCAN_ID \ + --file callgraph.json \ + --streaming + +# Option 2: Convert to NDJSON +stella scan graph convert --input callgraph.json \ + --output callgraph.ndjson \ + --format ndjson + +# Option 3: Partition by artifact +stella scan graph partition --input callgraph.json \ + --output-dir ./partitions/ \ + --by artifact +``` + +### 5.2 Missing Entrypoints + +**Symptom**: "No entrypoints found" warning. + +**Diagnosis**: + +```bash +# Check entrypoint detection +stella scan graph entrypoints --scan-id $SCAN_ID --verbose + +# Check for framework detection +stella scan graph detect-framework --scan-id $SCAN_ID +``` + +**Common causes**: + +1. **Framework not detected**: Add framework hints +2. **Custom entrypoints**: Manually specify +3. **Wrong language worker**: Check artifact analysis + +**Resolution**: + +```bash +# Specify framework explicitly +stella scan graph upload --scan-id $SCAN_ID \ + --file callgraph.json \ + --framework aspnetcore + +# Add custom entrypoints +stella scan graph entrypoint add --scan-id $SCAN_ID \ + --node sha256:node123... \ + --kind http \ + --route "/api/custom" +``` + +### 5.3 Reachability Computation Timeout + +**Symptom**: Job fails with "computation timeout". + +**Diagnosis**: + +```bash +# Check computation stats +stella reachability job-stats --job-id reachability-job-001 + +# Output: +# Nodes visited: 500,000 +# Edges traversed: 2,500,000 +# Time elapsed: 300s +# Memory used: 4.2 GB +``` + +**Resolution**: + +```bash +# Option 1: Increase timeout +stella reachability compute --scan-id $SCAN_ID --timeout 600s + +# Option 2: Reduce depth +stella reachability compute --scan-id $SCAN_ID --max-depth 5 + +# Option 3: Skip indirect calls +stella reachability compute --scan-id $SCAN_ID --indirect-resolution skip + +# Option 4: Partition analysis +stella reachability compute --scan-id $SCAN_ID --partition-by artifact +``` + +### 5.4 Inconsistent Results + +**Symptom**: Different results between runs. + +**Diagnosis**: + +```bash +# Check determinism settings +stella scan manifest --scan-id $SCAN_ID | jq '.deterministic, .seed' + +# Compare graph hashes +stella scan graph hash --scan-id $SCAN_ID +``` + +**Resolution**: + +```bash +# Ensure deterministic mode +stella reachability compute --scan-id $SCAN_ID \ + --deterministic \ + --seed "AQIDBA==" # Fixed seed + +# Use same graph version +stella reachability compute --scan-id $SCAN_ID \ + --graph-digest sha256:cg123... +``` + +### 5.5 False Positives/Negatives + +**Symptom**: Reachability verdict seems incorrect. + +**Diagnosis**: + +```bash +# Get detailed explanation +stella reachability explain --scan-id $SCAN_ID \ + --cve CVE-2024-1234 \ + --purl "pkg:npm/lodash@4.17.20" \ + --verbose + +# Check edge confidence +stella scan graph edge --scan-id $SCAN_ID \ + --from sha256:nodeA... \ + --to sha256:nodeB... +``` + +**Common causes for false positives**: + +1. **Heuristic edges**: Indirect call resolution too aggressive +2. **Reflection/dynamic calls**: May create false paths +3. **Dead code not detected**: Code exists but never executes + +**Common causes for false negatives**: + +1. **Missing edges**: Call graph incomplete +2. **Indirect calls skipped**: Resolution too conservative +3. **Cross-language calls**: Language boundary not bridged + +**Resolution**: + +```bash +# Adjust indirect call resolution +stella reachability compute --scan-id $SCAN_ID \ + --indirect-resolution conservative + +# Add runtime evidence +stella scan evidence upload --scan-id $SCAN_ID \ + --file runtime-trace.json + +# Report false positive/negative for ML training +stella reachability feedback --scan-id $SCAN_ID \ + --cve CVE-2024-1234 \ + --verdict false-positive \ + --reason "Dead code - feature flag disabled" +``` + +--- + +## 6. Monitoring & Alerting + +### 6.1 Key Metrics + +| Metric | Description | Alert Threshold | +|--------|-------------|-----------------| +| `callgraph_upload_duration_seconds` | Time to upload call graph | > 60s | +| `callgraph_size_bytes` | Size of uploaded graphs | > 200MB | +| `reachability_computation_duration_seconds` | Time to compute reachability | > 300s | +| `reachability_nodes_visited` | Nodes visited during BFS | > 1M | +| `reachability_job_failures_total` | Failed computation jobs | > 0/hour | +| `entrypoint_detection_rate` | % of scans with entrypoints | < 90% | + +### 6.2 Grafana Dashboard + +``` +Dashboard: Reachability Operations +Panels: +- Call graph upload throughput +- Graph size distribution +- Computation duration (p50, p95, p99) +- Reachability verdict distribution +- Job queue depth +- Entrypoint detection rate +``` + +### 6.3 Alerting Rules + +```yaml +groups: + - name: reachability + rules: + - alert: ReachabilityComputationSlow + expr: histogram_quantile(0.95, reachability_computation_duration_seconds) > 300 + for: 10m + labels: + severity: warning + annotations: + summary: "Reachability computation is slow" + + - alert: ReachabilityJobFailures + expr: increase(reachability_job_failures_total[1h]) > 5 + for: 5m + labels: + severity: critical + annotations: + summary: "Multiple reachability job failures" + + - alert: LowEntrypointDetectionRate + expr: entrypoint_detection_rate < 0.8 + for: 1h + labels: + severity: warning + annotations: + summary: "Entrypoint detection rate is low" +``` + +--- + +## 7. Escalation Procedures + +### 7.1 Escalation Matrix + +| Severity | Condition | Response Time | Escalation Path | +|----------|-----------|---------------|-----------------| +| P1 | Reachability failing for all scans | 15 min | On-call → Team Lead | +| P2 | Computation failures > 20% | 1 hour | On-call → Team Lead | +| P3 | Computation latency > 600s p95 | 4 hours | On-call | +| P4 | Entrypoint detection < 70% | 24 hours | Ticket | + +### 7.2 P1 Response Procedure + +1. **Acknowledge** alert +2. **Triage**: + ```bash + # Check worker health + stella scanner workers status + + # Check graph store connectivity + stella health check --service graph-store + + # Check recent failures + stella reachability jobs --status failed --last 10 + ``` +3. **Mitigate**: + ```bash + # Scale up workers if queue backlog + kubectl scale deployment scanner-worker --replicas=10 + + # Clear stuck jobs + stella reachability jobs cancel --status stuck + ``` +4. **Communicate**: Update status page +5. **Resolve**: Fix root cause +6. **Postmortem**: Document within 48 hours + +--- + +## Related Documentation + +- [Reachability API Reference](../api/score-proofs-reachability-api-reference.md) +- [Scanner Architecture](../modules/scanner/architecture.md) +- [Call Graph Schema](../schemas/callgraph-v1.md) +- [Entrypoint Detection](../modules/scanner/operations/entrypoint-problem.md) + +--- + +**Last Updated**: 2025-12-20 +**Version**: 1.0.0 +**Sprint**: 3500.0004.0004 diff --git a/docs/operations/unknowns-queue-runbook.md b/docs/operations/unknowns-queue-runbook.md new file mode 100644 index 000000000..e54b3a389 --- /dev/null +++ b/docs/operations/unknowns-queue-runbook.md @@ -0,0 +1,590 @@ +# Unknowns Queue Management Runbook + +> **Version**: 1.0.0 +> **Sprint**: 3500.0004.0004 +> **Last Updated**: 2025-12-20 + +This runbook covers operational procedures for managing the Unknowns queue, including triage, escalation, resolution, and queue health maintenance. + +--- + +## Table of Contents + +1. [Overview](#1-overview) +2. [Queue Operations](#2-queue-operations) +3. [Triage Procedures](#3-triage-procedures) +4. [Escalation Workflows](#4-escalation-workflows) +5. [Resolution Procedures](#5-resolution-procedures) +6. [Troubleshooting](#6-troubleshooting) +7. [Monitoring & Alerting](#7-monitoring--alerting) + +--- + +## 1. Overview + +### What are Unknowns? + +Unknowns are items that could not be fully classified during scanning due to: + +- Missing VEX statements +- Ambiguous indirect calls in call graphs +- Incomplete SBOM data +- Missing advisory information +- Conflicting evidence from multiple sources + +### Unknown Ranking + +Unknowns are ranked using a 2-factor scoring model: + +``` +score = 0.60 × blast + 0.30 × scarcity + 0.30 × pressure + containment_deduction +``` + +| Factor | Weight | Description | +|--------|--------|-------------| +| Blast Radius | 0.60 | Impact scope (dependents, network exposure) | +| Evidence Scarcity | 0.30 | How much data is missing | +| Exploit Pressure | 0.30 | EPSS score, KEV status | +| Containment | -0.20 | Mitigation factors (seccomp, read-only FS) | + +### Band Assignment + +| Band | Score Range | Priority | SLA | +|------|-------------|----------|-----| +| HOT | ≥ 0.70 | Critical | 24 hours | +| WARM | 0.40 - 0.69 | Normal | 7 days | +| COLD | < 0.40 | Low | 30 days | + +--- + +## 2. Queue Operations + +### 2.1 View Queue Status + +```bash +# Get queue summary +stella unknowns summary + +# Output: +# Total: 142 unknowns +# HOT: 12 (8%) - Requires immediate attention +# WARM: 85 (60%) - Normal priority +# COLD: 45 (32%) - Low priority +# +# KEV items: 3 +# Average score: 0.52 + +# Get queue summary via API +curl "https://scanner.example.com/api/v1/unknowns/summary" \ + -H "Authorization: Bearer $TOKEN" +``` + +### 2.2 List Unknowns + +```bash +# List all HOT unknowns +stella unknowns list --band HOT + +# List by score (highest first) +stella unknowns list --sort score --order desc --limit 20 + +# Filter by reason +stella unknowns list --reason missing_vex + +# Filter by artifact +stella unknowns list --artifact sha256:abc123... + +# Filter by KEV status +stella unknowns list --kev true +``` + +### 2.3 View Unknown Details + +```bash +# Get detailed view +stella unknowns show unk-12345678-abcd-1234-5678-abcdef123456 + +# Output: +# ID: unk-12345678-... +# Artifact: pkg:oci/myapp@sha256:abc123 +# Reasons: [missing_vex, ambiguous_indirect_call] +# +# Blast Radius: +# Dependents: 15 services +# Network: internet-facing +# Privilege: user +# +# Evidence Scarcity: 0.7 (high) +# +# Exploit Pressure: +# EPSS: 0.45 +# KEV: false +# +# Containment: +# Seccomp: enforced (-0.10) +# Filesystem: read-only (-0.10) +# +# Score: 0.62 (WARM band) +# Score Breakdown: +# Blast component: +0.35 +# Scarcity component: +0.21 +# Pressure component: +0.26 +# Containment deduction: -0.20 + +# Show proof tree +stella unknowns proof unk-12345678-... +``` + +### 2.4 Export Queue Data + +```bash +# Export for analysis +stella unknowns export --format json --output unknowns.json + +# Export HOT items for daily review +stella unknowns export --band HOT --format csv --output hot-unknowns.csv + +# Export with full details +stella unknowns export --verbose --include-proofs --output full-export.json +``` + +--- + +## 3. Triage Procedures + +### 3.1 Daily Triage Workflow + +**Schedule**: Daily at 9:00 AM + +**Duration**: 30 minutes + +**Participants**: Security analyst, on-call engineer + +**Process**: + +```bash +# 1. Get today's queue snapshot +stella unknowns snapshot --output daily-$(date +%Y%m%d).json + +# 2. Review all HOT items +stella unknowns list --band HOT --since 24h + +# 3. For each HOT unknown, determine action: +# - Escalate: Trigger immediate rescan +# - Investigate: Needs manual analysis +# - Defer: Move to WARM (with justification) +# - Resolve: Evidence found, can close + +# 4. Process each item +stella unknowns triage unk-12345678-... --action escalate +stella unknowns triage unk-87654321-... --action investigate --notes "Need VEX from vendor" +stella unknowns triage unk-11111111-... --action defer --reason "False positive suspected" +``` + +### 3.2 Triage Decision Matrix + +| Reason Code | KEV | EPSS > 0.5 | Action | +|-------------|-----|------------|--------| +| `missing_vex` | Yes | Any | Escalate + Vendor outreach | +| `missing_vex` | No | Yes | Escalate | +| `missing_vex` | No | No | Request VEX | +| `ambiguous_indirect_call` | Any | Any | Manual code review | +| `incomplete_sbom` | Any | Any | Rescan with updated extractor | +| `conflicting_evidence` | Any | Any | Manual analysis | + +### 3.3 Triage Templates + +```bash +# Quick escalate (HOT + KEV) +stella unknowns triage unk-... --action escalate \ + --priority P1 \ + --notes "KEV item, requires immediate attention" + +# Request vendor VEX +stella unknowns triage unk-... --action investigate \ + --notes "Requested VEX from vendor via security@vendor.com" \ + --due-date 7d + +# Mark for code review +stella unknowns triage unk-... --action investigate \ + --notes "Requires manual code review to resolve indirect call" \ + --assign @code-review-team + +# Defer with justification +stella unknowns triage unk-... --action defer \ + --reason "Component not deployed to production" \ + --evidence "deployment-manifest.yaml shows staging-only" +``` + +--- + +## 4. Escalation Workflows + +### 4.1 Automatic Escalation + +Unknowns are automatically escalated when: + +- Score increases above HOT threshold (0.70) +- KEV status added to related CVE +- EPSS score increases significantly (> 0.2 delta) +- Blast radius increases (new dependents detected) + +**Configure auto-escalation**: + +```yaml +# policy.unknowns.escalation.yaml +autoEscalation: + enabled: true + triggers: + - condition: score >= 0.70 + action: escalate + notify: [security-team] + - condition: kev == true + action: escalate + priority: P1 + notify: [security-team, management] + - condition: epss_delta > 0.2 + action: escalate + notify: [security-team] +``` + +### 4.2 Manual Escalation + +```bash +# Escalate via CLI +stella unknowns escalate unk-12345678-... + +# Escalate with reason +stella unknowns escalate unk-12345678-... \ + --reason "Customer reported potential exploit" + +# Escalate to trigger rescan +stella unknowns escalate unk-12345678-... --rescan + +# Output: +# Escalated: unk-12345678-... +# Rescan job: rescan-job-001 +# Status: queued +# ETA: 5 minutes +``` + +### 4.3 Bulk Escalation + +```bash +# Escalate all KEV items +stella unknowns escalate --filter "kev=true" --reason "KEV bulk escalation" + +# Escalate high-score items +stella unknowns escalate --filter "score>=0.8" --rescan + +# Escalate by artifact +stella unknowns escalate --artifact sha256:abc123... --reason "Production incident" +``` + +### 4.4 Escalation SLA Tracking + +```bash +# Check SLA status +stella unknowns sla-status + +# Output: +# HOT unknowns SLA (24h): +# In SLA: 10 (83%) +# Breached: 2 (17%) +# +# Breached items: +# unk-111... (26h old) - missing_vex +# unk-222... (30h old) - conflicting_evidence + +# Get SLA breach notifications +stella unknowns list --sla-breached +``` + +--- + +## 5. Resolution Procedures + +### 5.1 Resolution Types + +| Resolution | Description | Evidence Required | +|------------|-------------|-------------------| +| `not_affected` | Vulnerability doesn't apply | VEX statement or manual analysis | +| `fixed` | Vulnerability patched | Version upgrade confirmation | +| `mitigated` | Controls in place | Mitigation documentation | +| `false_positive` | Incorrect classification | Analysis report | +| `wont_fix` | Accepted risk | Risk acceptance form | + +### 5.2 Resolve Unknown + +```bash +# Resolve as not affected +stella unknowns resolve unk-12345678-... \ + --resolution not_affected \ + --justification "vulnerable_code_not_present" \ + --notes "Manual code review confirmed function not used" + +# Resolve as fixed +stella unknowns resolve unk-12345678-... \ + --resolution fixed \ + --justification "version_upgraded" \ + --evidence "Upgraded lodash to 4.17.21, CVE patched" + +# Resolve as mitigated +stella unknowns resolve unk-12345678-... \ + --resolution mitigated \ + --justification "inline_mitigations_exist" \ + --evidence "WAF rule WAF-001 blocks exploit pattern" + +# Resolve as won't fix (risk accepted) +stella unknowns resolve unk-12345678-... \ + --resolution wont_fix \ + --justification "risk_accepted" \ + --evidence "Risk acceptance ticket RISK-123" \ + --expires 90d # Re-evaluate in 90 days +``` + +### 5.3 Bulk Resolution + +```bash +# Resolve all items for a fixed package version +stella unknowns resolve-batch \ + --filter "purl=pkg:npm/lodash@4.17.20" \ + --resolution fixed \ + --justification "Upgraded to 4.17.21 fleet-wide" \ + --evidence "Fleet upgrade ticket FLEET-456" + +# Resolve false positives from analysis +stella unknowns resolve-batch \ + --file false-positives.json \ + --resolution false_positive +``` + +### 5.4 Resolution Audit Trail + +```bash +# View resolution history +stella unknowns history unk-12345678-... + +# Output: +# 2025-12-15 10:00:00 - Created (score: 0.62) +# 2025-12-16 09:30:00 - Triaged by analyst@example.com +# 2025-12-17 14:00:00 - Escalated (KEV added) +# 2025-12-18 11:00:00 - Resolved by security@example.com +# Resolution: not_affected +# Justification: vulnerable_code_not_present +# Notes: Manual code review confirmed function not used + +# Export audit trail +stella unknowns audit-export --from 2025-01-01 --to 2025-12-31 --output audit.json +``` + +--- + +## 6. Troubleshooting + +### 6.1 Score Seems Wrong + +**Symptom**: Unknown scored too high or too low. + +**Diagnosis**: + +```bash +# View score breakdown +stella unknowns show unk-... --score-details + +# View proof tree +stella unknowns proof unk-... --verbose +``` + +**Common causes**: + +1. **Stale EPSS data**: EPSS feed not updated +2. **Incorrect blast radius**: Dependency data outdated +3. **Missing containment data**: Seccomp/filesystem status unknown + +**Resolution**: + +```bash +# Trigger score recalculation +stella unknowns recalculate unk-... + +# Force refresh of all input signals +stella unknowns refresh unk-... --force +``` + +### 6.2 Duplicate Unknowns + +**Symptom**: Same issue appears multiple times. + +**Diagnosis**: + +```bash +# Find potential duplicates +stella unknowns duplicates --scan + +# Output shows items with same CVE+PURL but different artifacts +``` + +**Resolution**: + +```bash +# Merge duplicates +stella unknowns merge \ + --primary unk-111... \ + --secondary unk-222... \ + --reason "Same CVE across artifact versions" +``` + +### 6.3 Escalation Not Working + +**Symptom**: Escalation doesn't trigger rescan. + +**Diagnosis**: + +```bash +# Check escalation status +stella unknowns escalation-status unk-... + +# Check Scheduler connectivity +stella health check --service scheduler + +# Check job queue +stella scheduler queue status rescan +``` + +**Resolution**: + +```bash +# Retry escalation +stella unknowns escalate unk-... --force + +# Manual rescan trigger +stella scan trigger --artifact sha256:abc123... --priority high +``` + +### 6.4 Resolution Rejected + +**Symptom**: Resolution attempt fails validation. + +**Diagnosis**: + +```bash +# Check resolution requirements +stella unknowns resolution-requirements unk-... + +# Output: +# Resolution requirements for unk-12345678-... +# - Justification: required +# - Evidence: required (reason: KEV item) +# - Approver: required (band: HOT) +``` + +**Resolution**: + +```bash +# Provide required evidence +stella unknowns resolve unk-... \ + --resolution not_affected \ + --justification "vulnerable_code_not_present" \ + --evidence "Code review: CRV-123" \ + --approver security-lead@example.com +``` + +--- + +## 7. Monitoring & Alerting + +### 7.1 Key Metrics + +| Metric | Description | Alert Threshold | +|--------|-------------|-----------------| +| `unknowns_total` | Total unknowns in queue | > 500 | +| `unknowns_hot_count` | HOT band count | > 20 | +| `unknowns_sla_breached` | SLA breaches | > 0 | +| `unknowns_resolution_rate` | Daily resolutions | < 5 | +| `unknowns_escalation_failures` | Failed escalations | > 0 | +| `unknowns_avg_age_hours` | Average unknown age | > 168 (1 week) | + +### 7.2 Grafana Dashboard + +``` +Dashboard: Unknowns Queue Health +Panels: +- Queue size by band (HOT/WARM/COLD) +- SLA compliance rate +- Unknowns by reason code +- Resolution velocity +- Escalation success rate +- Queue age distribution +- KEV item tracking +``` + +### 7.3 Alerting Rules + +```yaml +groups: + - name: unknowns-queue + rules: + - alert: UnknownsHotBandHigh + expr: unknowns_hot_count > 20 + for: 5m + labels: + severity: warning + annotations: + summary: "HOT unknowns queue is high ({{ $value }} items)" + + - alert: UnknownsSLABreach + expr: unknowns_sla_breached > 0 + for: 1m + labels: + severity: critical + annotations: + summary: "{{ $value }} unknowns have breached SLA" + + - alert: UnknownsQueueGrowing + expr: rate(unknowns_total[1h]) > 10 + for: 30m + labels: + severity: warning + annotations: + summary: "Unknowns queue is growing rapidly" + + - alert: UnknownsKEVPending + expr: unknowns_kev_count > 0 and unknowns_kev_unresolved_age_hours > 24 + for: 5m + labels: + severity: critical + annotations: + summary: "KEV unknown pending for over 24 hours" +``` + +### 7.4 Daily Report + +```bash +# Generate daily report +stella unknowns report --format email --send-to security-team@example.com + +# Report includes: +# - Queue summary (total, by band, by reason) +# - SLA status (in compliance, breaches) +# - Top 10 highest-scored items +# - Newly added items (last 24h) +# - Resolved items (last 24h) +# - KEV item status +# - Trends (7-day, 30-day) +``` + +--- + +## Related Documentation + +- [Unknowns API Reference](../api/score-proofs-reachability-api-reference.md#5-unknowns-api) +- [Triage Technical Reference](../product-advisories/14-Dec-2025%20-%20Triage%20and%20Unknowns%20Technical%20Reference.md) +- [Score Proofs Runbook](./score-proofs-runbook.md) +- [Policy Engine](../modules/policy/architecture.md) + +--- + +**Last Updated**: 2025-12-20 +**Version**: 1.0.0 +**Sprint**: 3500.0004.0004 diff --git a/src/Web/StellaOps.Web/src/app/core/api/proof.models.ts b/src/Web/StellaOps.Web/src/app/core/api/proof.models.ts new file mode 100644 index 000000000..a44300e59 --- /dev/null +++ b/src/Web/StellaOps.Web/src/app/core/api/proof.models.ts @@ -0,0 +1,180 @@ +/** + * Proof and manifest models for Sprint 3500.0004.0002 - T6. + * Supports proof ledger visualization and score replay operations. + */ + +// ============================================================================ +// Manifest Models +// ============================================================================ + +/** + * Hash entry in the scan manifest. + */ +export interface ManifestHashEntry { + readonly label: string; + readonly algorithm: 'sha256' | 'sha384' | 'sha512'; + readonly value: string; + readonly source: 'sbom' | 'layer' | 'config' | 'composition'; +} + +/** + * Scan manifest with all input hashes for proof computation. + */ +export interface ScanManifest { + readonly manifestId: string; + readonly scanId: string; + readonly imageDigest: string; + readonly createdAt: string; + readonly hashes: readonly ManifestHashEntry[]; + readonly merkleRoot: string; + readonly compositionManifestUri?: string; +} + +// ============================================================================ +// Merkle Tree Models +// ============================================================================ + +/** + * Node in a Merkle tree visualization. + */ +export interface MerkleTreeNode { + readonly nodeId: string; + readonly hash: string; + readonly label?: string; + readonly isLeaf: boolean; + readonly isRoot: boolean; + readonly children?: readonly MerkleTreeNode[]; + readonly level: number; + readonly position: number; +} + +/** + * Complete Merkle tree structure for visualization. + */ +export interface MerkleTree { + readonly root: MerkleTreeNode; + readonly leafCount: number; + readonly depth: number; +} + +// ============================================================================ +// Proof Bundle Models +// ============================================================================ + +export type ProofVerificationStatus = 'verified' | 'failed' | 'pending' | 'unknown'; +export type SignatureStatus = 'valid' | 'invalid' | 'expired' | 'unknown'; + +/** + * DSSE signature information. + */ +export interface DsseSignature { + readonly keyId: string; + readonly algorithm: string; + readonly status: SignatureStatus; + readonly signedAt?: string; + readonly expiresAt?: string; + readonly issuer?: string; +} + +/** + * Rekor transparency log entry. + */ +export interface RekorLogEntry { + readonly logId: string; + readonly logIndex: number; + readonly integratedTime: string; + readonly logUrl: string; + readonly bodyHash: string; +} + +/** + * Proof bundle containing all verification artifacts. + */ +export interface ProofBundle { + readonly bundleId: string; + readonly scanId: string; + readonly createdAt: string; + readonly merkleRoot: string; + readonly dsseEnvelope: string; // Base64-encoded DSSE envelope + readonly signatures: readonly DsseSignature[]; + readonly rekorEntry?: RekorLogEntry; + readonly verificationStatus: ProofVerificationStatus; + readonly downloadUrl: string; +} + +/** + * Proof verification result from the backend. + */ +export interface ProofVerificationResult { + readonly bundleId: string; + readonly verified: boolean; + readonly merkleRootValid: boolean; + readonly signatureValid: boolean; + readonly rekorInclusionValid?: boolean; + readonly verifiedAt: string; + readonly errors?: readonly string[]; + readonly warnings?: readonly string[]; +} + +// ============================================================================ +// Score Replay Models +// ============================================================================ + +export type ScoreReplayStatus = 'pending' | 'running' | 'completed' | 'failed'; + +/** + * Score component with breakdown. + */ +export interface ScoreComponent { + readonly name: string; + readonly weight: number; + readonly rawScore: number; + readonly weightedScore: number; + readonly details?: string; +} + +/** + * Complete score breakdown. + */ +export interface ScoreBreakdown { + readonly totalScore: number; + readonly components: readonly ScoreComponent[]; + readonly computedAt: string; +} + +/** + * Score comparison result showing drift between original and replayed. + */ +export interface ScoreDrift { + readonly componentName: string; + readonly originalScore: number; + readonly replayedScore: number; + readonly delta: number; + readonly driftPercent: number; + readonly significant: boolean; +} + +/** + * Score replay request. + */ +export interface ScoreReplayRequest { + readonly scanId: string; + readonly useCurrentPolicy?: boolean; +} + +/** + * Score replay response with comparison. + */ +export interface ScoreReplayResult { + readonly replayId: string; + readonly scanId: string; + readonly status: ScoreReplayStatus; + readonly startedAt: string; + readonly completedAt?: string; + readonly originalScore: ScoreBreakdown; + readonly replayedScore?: ScoreBreakdown; + readonly drifts?: readonly ScoreDrift[]; + readonly hasDrift: boolean; + readonly proofBundle?: ProofBundle; + readonly error?: string; +} diff --git a/src/Web/StellaOps.Web/src/app/core/api/reachability.models.ts b/src/Web/StellaOps.Web/src/app/core/api/reachability.models.ts new file mode 100644 index 000000000..e51c5b57e --- /dev/null +++ b/src/Web/StellaOps.Web/src/app/core/api/reachability.models.ts @@ -0,0 +1,192 @@ +/** + * Reachability analysis models for Sprint 3500.0004.0002 - T6. + * Supports the reachability explain widget with call graph visualization. + */ + +// ============================================================================ +// Call Graph Models +// ============================================================================ + +/** + * Type of node in the call graph. + */ +export type CallGraphNodeType = + | 'entrypoint' + | 'method' + | 'function' + | 'class' + | 'module' + | 'vulnerable' + | 'external'; + +/** + * Node in the call graph. + */ +export interface CallGraphNode { + readonly nodeId: string; + readonly type: CallGraphNodeType; + readonly name: string; + readonly qualifiedName: string; + readonly filePath?: string; + readonly lineNumber?: number; + readonly package?: string; + readonly isVulnerable: boolean; + readonly isEntrypoint: boolean; + readonly metadata?: Record; +} + +/** + * Edge in the call graph representing a call relationship. + */ +export interface CallGraphEdge { + readonly edgeId: string; + readonly sourceId: string; + readonly targetId: string; + readonly callType: 'direct' | 'indirect' | 'dynamic' | 'virtual'; + readonly weight?: number; + readonly confidence?: number; +} + +/** + * Complete call graph structure. + */ +export interface CallGraph { + readonly graphId: string; + readonly language: string; + readonly nodes: readonly CallGraphNode[]; + readonly edges: readonly CallGraphEdge[]; + readonly nodeCount: number; + readonly edgeCount: number; + readonly createdAt: string; + readonly digest: string; +} + +// ============================================================================ +// Reachability Path Models +// ============================================================================ + +/** + * Step in a reachability path. + */ +export interface ReachabilityPathStep { + readonly stepIndex: number; + readonly node: CallGraphNode; + readonly callType?: 'direct' | 'indirect' | 'dynamic' | 'virtual'; + readonly confidence: number; +} + +/** + * A reachability path from entrypoint to vulnerable function. + */ +export interface ReachabilityPath { + readonly pathId: string; + readonly entrypoint: CallGraphNode; + readonly vulnerable: CallGraphNode; + readonly steps: readonly ReachabilityPathStep[]; + readonly pathLength: number; + readonly overallConfidence: number; + readonly isShortestPath: boolean; +} + +// ============================================================================ +// Confidence Scoring Models +// ============================================================================ + +/** + * Factor contributing to reachability confidence. + */ +export interface ConfidenceFactor { + readonly factorName: string; + readonly weight: number; + readonly score: number; + readonly contribution: number; + readonly description: string; +} + +/** + * Complete confidence breakdown. + */ +export interface ConfidenceBreakdown { + readonly overallScore: number; + readonly factors: readonly ConfidenceFactor[]; + readonly computedAt: string; +} + +// ============================================================================ +// Reachability Explanation Models +// ============================================================================ + +export type ReachabilityVerdict = 'reachable' | 'unreachable' | 'uncertain' | 'no_data'; + +/** + * Reachability explanation for a CVE. + */ +export interface ReachabilityExplanation { + readonly explanationId: string; + readonly cveId: string; + readonly vulnerablePackage: string; + readonly vulnerableFunction?: string; + readonly verdict: ReachabilityVerdict; + readonly confidence: ConfidenceBreakdown; + readonly paths: readonly ReachabilityPath[]; + readonly callGraph?: CallGraph; + readonly entrypointsAnalyzed: number; + readonly shortestPathLength?: number; + readonly analysisTime: string; + readonly createdAt: string; +} + +/** + * Request for reachability analysis. + */ +export interface ReachabilityAnalysisRequest { + readonly scanId: string; + readonly cveId: string; + readonly includeCallGraph?: boolean; + readonly maxPaths?: number; + readonly maxDepth?: number; +} + +/** + * Summary of reachability analysis for a scan. + */ +export interface ReachabilitySummary { + readonly scanId: string; + readonly totalCves: number; + readonly reachableCves: number; + readonly unreachableCves: number; + readonly uncertainCves: number; + readonly noDataCves: number; + readonly analysisCompletedAt?: string; + readonly callGraphAvailable: boolean; +} + +// ============================================================================ +// Visualization Export Models +// ============================================================================ + +/** + * Export format for call graph visualization. + */ +export type ExportFormat = 'png' | 'svg' | 'json' | 'dot'; + +/** + * Request to export call graph visualization. + */ +export interface ExportGraphRequest { + readonly explanationId: string; + readonly format: ExportFormat; + readonly highlightPath?: string; + readonly width?: number; + readonly height?: number; +} + +/** + * Export result. + */ +export interface ExportGraphResult { + readonly format: ExportFormat; + readonly dataUrl?: string; // For PNG/SVG + readonly data?: string; // For JSON/DOT + readonly filename: string; +} diff --git a/src/Web/StellaOps.Web/src/app/core/api/unknowns.models.ts b/src/Web/StellaOps.Web/src/app/core/api/unknowns.models.ts new file mode 100644 index 000000000..46f47266d --- /dev/null +++ b/src/Web/StellaOps.Web/src/app/core/api/unknowns.models.ts @@ -0,0 +1,163 @@ +/** + * Unknowns registry models for Sprint 3500.0004.0002 - T6. + * Supports the unknowns queue component with band-based prioritization. + */ + +// ============================================================================ +// Band Classification +// ============================================================================ + +/** + * Band classification for unknown packages. + * HOT: Recently discovered, high occurrence, needs immediate attention + * WARM: Moderate priority, stable occurrence + * COLD: Low priority, rare or old unknowns + */ +export type UnknownBand = 'HOT' | 'WARM' | 'COLD'; + +/** + * Status of an unknown package in the registry. + */ +export type UnknownStatus = 'pending' | 'escalated' | 'resolved' | 'ignored'; + +/** + * Resolution action taken for an unknown. + */ +export type ResolutionAction = + | 'mapped_to_cve' + | 'marked_not_vulnerable' + | 'added_to_allowlist' + | 'false_positive' + | 'vendor_confirmed' + | 'other'; + +// ============================================================================ +// Unknown Entry Models +// ============================================================================ + +/** + * Package information for an unknown. + */ +export interface UnknownPackage { + readonly name: string; + readonly version: string; + readonly ecosystem: string; // npm, pypi, maven, nuget, etc. + readonly purl?: string; +} + +/** + * Scan occurrence information. + */ +export interface UnknownOccurrence { + readonly scanId: string; + readonly imageDigest: string; + readonly imageName: string; + readonly detectedAt: string; +} + +/** + * An unknown entry in the registry. + */ +export interface UnknownEntry { + readonly unknownId: string; + readonly package: UnknownPackage; + readonly band: UnknownBand; + readonly status: UnknownStatus; + readonly rank: number; // Priority rank within band + readonly occurrenceCount: number; + readonly firstSeenAt: string; + readonly lastSeenAt: string; + readonly ageInDays: number; + readonly relatedCves?: readonly string[]; + readonly assignee?: string; + readonly notes?: string; + readonly recentOccurrences: readonly UnknownOccurrence[]; +} + +// ============================================================================ +// API Request/Response Models +// ============================================================================ + +/** + * Filter options for listing unknowns. + */ +export interface UnknownsFilter { + readonly band?: UnknownBand; + readonly status?: UnknownStatus; + readonly ecosystem?: string; + readonly scanId?: string; + readonly imageDigest?: string; + readonly assignee?: string; + readonly limit?: number; + readonly offset?: number; + readonly sortBy?: 'rank' | 'age' | 'occurrenceCount' | 'lastSeen'; + readonly sortOrder?: 'asc' | 'desc'; +} + +/** + * Paginated response for unknowns listing. + */ +export interface UnknownsListResponse { + readonly items: readonly UnknownEntry[]; + readonly total: number; + readonly limit: number; + readonly offset: number; + readonly hasMore: boolean; +} + +/** + * Summary statistics for unknowns. + */ +export interface UnknownsSummary { + readonly hotCount: number; + readonly warmCount: number; + readonly coldCount: number; + readonly totalCount: number; + readonly pendingCount: number; + readonly escalatedCount: number; + readonly resolvedToday: number; + readonly oldestUnresolvedDays: number; +} + +/** + * Request to escalate an unknown. + */ +export interface EscalateUnknownRequest { + readonly unknownId: string; + readonly reason: string; + readonly assignTo?: string; + readonly priority?: 'low' | 'medium' | 'high' | 'critical'; +} + +/** + * Request to resolve an unknown. + */ +export interface ResolveUnknownRequest { + readonly unknownId: string; + readonly action: ResolutionAction; + readonly mappedCve?: string; + readonly notes?: string; +} + +/** + * Bulk action request for multiple unknowns. + */ +export interface BulkUnknownsRequest { + readonly unknownIds: readonly string[]; + readonly action: 'escalate' | 'resolve' | 'ignore' | 'assign'; + readonly resolutionAction?: ResolutionAction; + readonly assignee?: string; + readonly notes?: string; +} + +/** + * Result of a bulk action. + */ +export interface BulkUnknownsResult { + readonly successCount: number; + readonly failureCount: number; + readonly failures?: readonly { + readonly unknownId: string; + readonly error: string; + }[]; +}