426 lines
16 KiB
Markdown
426 lines
16 KiB
Markdown
# 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
|