Files
breakpilot-compliance/ai-compliance-sdk/migrations/010_incidents_schema.sql
Benjamin Boenisch 504dd3591b feat: Add Academy, Whistleblower, Incidents, Vendor, DSB, SSO, Reporting, Multi-Tenant and Industry backends
Go handlers, models, stores and migrations for all SDK modules.
Updates developer portal navigation and BYOEH page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:11:27 +01:00

112 lines
5.7 KiB
SQL

-- ============================================================================
-- Migration 010: Incident/Breach Management Schema
-- DSGVO Art. 33 (Authority Notification) & Art. 34 (Data Subject Notification)
--
-- Art. 33 requires notification of the supervisory authority within 72 hours
-- of becoming aware of a personal data breach, unless the breach is unlikely
-- to result in a risk to the rights and freedoms of natural persons.
--
-- Art. 34 requires notification of affected data subjects without undue delay
-- when the breach is likely to result in a high risk to their rights and freedoms.
-- ============================================================================
-- Incident incidents table
CREATE TABLE IF NOT EXISTS incident_incidents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
-- Incident info
title VARCHAR(255) NOT NULL,
description TEXT,
category VARCHAR(50) NOT NULL, -- data_breach, unauthorized_access, data_loss, system_compromise, phishing, ransomware, insider_threat, physical_breach, other
status VARCHAR(50) DEFAULT 'detected', -- detected, assessment, containment, notification_required, notification_sent, remediation, closed
severity VARCHAR(50) NOT NULL, -- critical, high, medium, low
-- Detection & reporting
detected_at TIMESTAMPTZ NOT NULL,
reported_by UUID NOT NULL,
-- Affected scope
affected_data_categories JSONB DEFAULT '[]', -- e.g. ["personal_data", "health_data", "financial_data"]
affected_data_subject_count INT DEFAULT 0,
affected_systems JSONB DEFAULT '[]', -- e.g. ["crm", "email_server", "database"]
-- Assessments & notifications (JSONB embedded objects)
risk_assessment JSONB, -- {likelihood, impact, risk_level, assessed_at, assessed_by, notes}
authority_notification JSONB, -- {status, deadline, submitted_at, authority_name, reference_number, contact_person, notes}
data_subject_notification JSONB, -- {required, status, sent_at, affected_count, notification_text, channel}
-- Resolution
root_cause TEXT,
lessons_learned TEXT,
-- Timeline (JSONB array of events)
timeline JSONB DEFAULT '[]', -- [{timestamp, action, user_id, details}, ...]
-- Audit
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
closed_at TIMESTAMPTZ
);
-- Incident measures table (corrective and preventive measures)
CREATE TABLE IF NOT EXISTS incident_measures (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
incident_id UUID NOT NULL REFERENCES incident_incidents(id) ON DELETE CASCADE,
-- Measure info
title VARCHAR(255) NOT NULL,
description TEXT,
measure_type VARCHAR(50) NOT NULL, -- immediate, long_term
status VARCHAR(50) DEFAULT 'planned', -- planned, in_progress, completed
responsible VARCHAR(255),
due_date TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
-- Audit
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ============================================================================
-- Indexes
-- ============================================================================
-- Incident indexes
CREATE INDEX IF NOT EXISTS idx_incident_incidents_tenant ON incident_incidents(tenant_id);
CREATE INDEX IF NOT EXISTS idx_incident_incidents_status ON incident_incidents(tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_incident_incidents_severity ON incident_incidents(tenant_id, severity);
CREATE INDEX IF NOT EXISTS idx_incident_incidents_detected_at ON incident_incidents(detected_at DESC);
CREATE INDEX IF NOT EXISTS idx_incident_incidents_category ON incident_incidents(tenant_id, category);
-- Measure indexes
CREATE INDEX IF NOT EXISTS idx_incident_measures_incident ON incident_measures(incident_id);
CREATE INDEX IF NOT EXISTS idx_incident_measures_status ON incident_measures(incident_id, status);
-- ============================================================================
-- Triggers
-- ============================================================================
-- Reuse existing update_updated_at_column function
-- Incidents trigger
DROP TRIGGER IF EXISTS update_incident_incidents_updated_at ON incident_incidents;
CREATE TRIGGER update_incident_incidents_updated_at
BEFORE UPDATE ON incident_incidents
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================================
-- Comments
-- ============================================================================
COMMENT ON TABLE incident_incidents IS 'Security and data breach incidents per DSGVO Art. 33/34';
COMMENT ON TABLE incident_measures IS 'Corrective and preventive measures for incidents';
COMMENT ON COLUMN incident_incidents.detected_at IS 'When the incident was first detected - starts the 72h Art. 33 notification clock';
COMMENT ON COLUMN incident_incidents.authority_notification IS 'JSONB: Supervisory authority notification tracking per DSGVO Art. 33 (72h deadline)';
COMMENT ON COLUMN incident_incidents.data_subject_notification IS 'JSONB: Data subject notification tracking per DSGVO Art. 34';
COMMENT ON COLUMN incident_incidents.risk_assessment IS 'JSONB: Risk assessment with likelihood, impact, and auto-calculated risk level';
COMMENT ON COLUMN incident_incidents.timeline IS 'JSONB array: Chronological record of all actions taken during incident response';
COMMENT ON COLUMN incident_incidents.affected_data_categories IS 'JSONB array: Categories of personal data affected (e.g. health, financial)';
COMMENT ON COLUMN incident_incidents.affected_systems IS 'JSONB array: Systems affected by the incident';
COMMENT ON COLUMN incident_measures.measure_type IS 'immediate = short-term containment, long_term = preventive/structural fix';