Files
Benjamin Admin cc80e59e5e feat(cra): Phase 4 — Vulnerability Disclosure + Post-Market Monitoring
Migration 121: compliance_cra_vulnerabilities table with full lifecycle tracking
- Status state machine: reported → triaged → patched → disclosed (+ withdrawn)
- CRA Art. 14(2) deadlines tracked: reported_to_enisa_at (24h), detailed_report_at (72h)
- CVE-ID, severity, CVSS, affected_components (JSONB), embargo_until

Backend endpoints in cra_routes.py:
- POST /vulnerabilities — create with validation (severity, CVSS range)
- GET /vulnerabilities — list with deadline-breach summary (24h/72h counters)
- PATCH /vulnerabilities/{id} — update fields + auto-set lifecycle timestamps
- DELETE /vulnerabilities/{id} — soft-delete (withdrawn)
- GET /monitoring — combined view: CRA deadlines + vuln summary + post-market checklist

Frontend:
- /vuln page: intake form, vuln cards with 24h/72h-countdown buttons,
  status-transition flow with auto-timestamps
- /monitoring page: CRA deadlines (11.06.26 / 11.09.26 / 11.12.27), breach banner
  if 24h/72h obligations missed, post-market checklist with deep-links
- Dashboard: +2 buttons (Vulns, Monitoring)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 22:08:49 +02:00

56 lines
2.3 KiB
SQL

-- Migration 121: CRA Vulnerability Disclosure + Lifecycle
-- Tracks vulnerabilities reported against a CRA project + CRA-mandated deadlines:
-- - 24h: Early warning to ENISA / national CSIRT (CRA Art. 14(2)(a))
-- - 72h: Detailed report (CRA Art. 14(2)(b))
-- - Patch -> Disclosure (typically with embargo)
--
-- Status state machine (whitelist):
-- reported -> triaged -> patched -> disclosed
-- (or withdrawn at any time)
CREATE TABLE IF NOT EXISTS compliance_cra_vulnerabilities (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cra_project_id UUID NOT NULL,
tenant_id VARCHAR(255) NOT NULL,
-- Identification
cve_id VARCHAR(50), -- CVE-YYYY-NNNN (optional)
title VARCHAR(500) NOT NULL,
description TEXT DEFAULT '',
severity VARCHAR(20), -- LOW | MEDIUM | HIGH | CRITICAL
cvss_score NUMERIC(3,1), -- 0.0 - 10.0
-- Affected components (e.g. ["lodash@4.17.20", "axios@0.21.0"])
affected_components JSONB NOT NULL DEFAULT '[]'::jsonb,
-- Reporter
reporter_source VARCHAR(50) DEFAULT 'internal', -- internal | external | researcher | scanner
reporter_contact VARCHAR(500),
-- Lifecycle timestamps
discovered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
triaged_at TIMESTAMPTZ,
patched_at TIMESTAMPTZ,
disclosed_at TIMESTAMPTZ,
embargo_until TIMESTAMPTZ,
-- CRA-Mandated reports (Art. 14(2))
reported_to_enisa_at TIMESTAMPTZ, -- 24h deadline
detailed_report_at TIMESTAMPTZ, -- 72h deadline
-- Status (whitelist)
status VARCHAR(30) NOT NULL DEFAULT 'reported',
-- Free-text notes (triage rationale, decision log)
notes TEXT DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_cra_vuln_project ON compliance_cra_vulnerabilities(cra_project_id);
CREATE INDEX IF NOT EXISTS idx_cra_vuln_tenant ON compliance_cra_vulnerabilities(tenant_id);
CREATE INDEX IF NOT EXISTS idx_cra_vuln_status ON compliance_cra_vulnerabilities(cra_project_id, status);
CREATE INDEX IF NOT EXISTS idx_cra_vuln_cve ON compliance_cra_vulnerabilities(cve_id) WHERE cve_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_cra_vuln_discovered ON compliance_cra_vulnerabilities(cra_project_id, discovered_at DESC);