The 72-Hour Clock
When a breach is discovered, the clock starts. You have 72 hours to notify the DPA. This is not a lot of time when you're also trying to contain the incident.
Having a structured process in place before a breach occurs is critical.
Documenting a Breach
SELECT pgcomply.report_breach(
'Staging database credentials exposed in public Git repository',
'Developer committed .env file containing staging DB credentials to public GitHub repo. Staging DB contains copy of production user data from November snapshot.',
'high',
data_types := ARRAY['email', 'name', 'address', 'phone'],
affected_tables := ARRAY['users', 'profiles'],
cause := 'Human error — .env not in .gitignore',
subjects_count := 200
);
-- Output:
-- NOTICE: Breach BR-2026-0003 logged. DPA notification deadline: 2026-02-23 09:15:00
Tracking Progress
SELECT * FROM pgcomply.breach_status();
Shows all breaches with: ID, severity, status (open/investigating/contained/resolved), DPA deadline, hours remaining, reporting status, and subject notification status.
Updating a Breach
As investigation progresses:
SELECT pgcomply.update_breach(
'BR-2026-0003',
status := 'contained',
remediation := 'Credentials rotated, repo made private, .gitignore updated. Staging DB wiped and recreated with anonymized data.'
);
-- After DPA notification:
SELECT pgcomply.update_breach(
'BR-2026-0003',
reported_to_dpa := true,
subjects_notified := true,
status := 'resolved'
);
Preventing Future Breaches
The best breach management is prevention:
-- Detect staging environments with real PII
SELECT * FROM pgcomply.schema_drift();
-- Ensure masking is active on all PII tables
SELECT * FROM pgcomply.masking_status();
-- Verify health check catches configuration issues
SELECT * FROM pgcomply.health_check();
Real-World Breach Scenarios
Scenario 1: Credential Leak (Most Common)
A developer commits database credentials to a public repository:
SELECT pgcomply.report_breach(
'Database credentials exposed in public GitHub repository',
'Developer pushed .env file with production DATABASE_URL to public repo. Repo had 3 forks before discovery.',
'major',
data_types := ARRAY['email', 'name', 'phone', 'address'],
affected_tables := ARRAY['users', 'profiles', 'orders'],
cause := 'Human error: .env not in .gitignore, no pre-commit hook',
subjects_count := 4200
);
Immediate actions:
-- 1. Rotate credentials
ALTER ROLE app_writer PASSWORD 'new_strong_password_here';
-- 2. Check if anyone accessed the database
SELECT * FROM pgcomply.session_tracking();
SELECT * FROM pgcomply.dml_history('users', NOW() - INTERVAL '48 hours');
-- 3. Verify data integrity
SELECT pgcomply.verify_audit();
Scenario 2: SQL Injection (Severe)
Application vulnerability allows unauthorized data access:
SELECT pgcomply.report_breach(
'SQL injection via search endpoint',
'WAF alert: UNION-based SQL injection on /api/search. Attacker extracted users table. RLS was not enabled.',
'critical',
data_types := ARRAY['email', 'name', 'password_hash'],
affected_tables := ARRAY['users'],
cause := 'Unsanitized user input in raw SQL query',
subjects_count := 15000
);
Scenario 3: Excessive Access (Subtle)
An analytics role had more access than needed:
SELECT pgcomply.report_breach(
'Analytics role accessed unmasked PII for 3 months',
'Access review found analyst role had SELECT on raw users table instead of masked view.',
'minor',
data_types := ARRAY['email', 'phone'],
affected_tables := ARRAY['users'],
cause := 'Overly permissive GRANT during onboarding, no periodic access review',
subjects_count := 12000
);
This one is subtle: no malicious intent, no external actor, but still a reportable incident if the analyst accessed data they shouldn't have seen.
DPA Contact Information
Keep your supervisory authority contact information ready:
-- Store DPA contacts in pgcomply tags
SELECT pgcomply.tag('_compliance', 'dpa_authority', 'Landesbeauftragter für Datenschutz Schleswig-Holstein');
SELECT pgcomply.tag('_compliance', 'dpa_contact', 'mail@datenschutzzentrum.de');
SELECT pgcomply.tag('_compliance', 'dpa_phone', '+49 431 988-1200');
SELECT pgcomply.tag('_compliance', 'dpo_name', 'Robert Langner');
Summary
Breach notification is a race against the clock. pgcomply.report_breach() gives you a structured process: document immediately, track the deadline, update as you investigate, and maintain the evidence trail. The best time to set this up is before you need it.