
Data Breach Response
- 58 installs
- 451 repo stars
- Updated July 21, 2026
- borghei/claude-skills
Data Breach Response is a Claude skill that scores personal-data-breach severity with the ENISA methodology and tracks GDPR, NIS2, and other statutory notification deadlines.
About
Data Breach Response is an incident-response toolkit for personal data breaches. It calculates ENISA severity scores, determines who must be notified, and tracks statutory notification deadlines like the GDPR 72-hour clock from the moment of awareness. A team uses it during a breach to assess severity, confirm notification obligations, and manage the response timeline. The skill is marked experimental and explicitly not legal advice.
- Calculates ENISA breach severity (SE = DPC x EI + CB) and returns LOW/MEDIUM/HIGH/VERY HIGH verdict
- Tracks GDPR 72h, NIS2 24h, and processor 24h/48h notification deadlines from time of awareness
- Maps notification obligations across GDPR Art. 33/34, CCPA, HIPAA, PCI DSS, and NIS2
Data Breach Response by the numbers
- 58 all-time installs (skills.sh)
- Ranked #1,245 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
data-breach-response capabilities & compatibility
- Capabilities
- dpia assessment · eu ai act specialist
- Use cases
- security audit
- Pricing
- Free
What data-breach-response says it does
Incident response and legal compliance for personal data breaches under GDPR Art. 33/34, CCPA, HIPAA, NIS2, PCI DSS, and other regulations.
Formula: SE = (DPC x EI) + CB
GDPR 72-hour SA notification deadline
npx skills add https://github.com/borghei/claude-skills --skill data-breach-responseAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 58 |
|---|---|
| repo stars | ★ 451 |
| Last updated | July 21, 2026 |
| Repository | borghei/claude-skills ↗ |
What it does
Assess personal-data breach severity and track GDPR/NIS2 notification deadlines during incident response.
Who is it for?
Security, privacy, or legal teams triaging a personal-data breach and computing notification obligations and deadlines.
Skip if: Preventing or detecting intrusions; it handles post-incident assessment and reporting, not live threat detection, and is not legal advice.
When should I use this skill?
A data breach is suspected or confirmed and you need to score severity, confirm notification duties, or track statutory deadlines.
What you get
An ENISA severity verdict, a notification-obligation determination, and a tracked timeline against GDPR/NIS2 deadlines.
- ENISA severity score and verdict
- notification-obligation determination
- breach response timeline with deadline countdowns
By the numbers
- GDPR 72-hour SA notification deadline
- NIS2 24-hour early warning and 72-hour notification
- 18 EDPB reference cases for case matching
Files
⚠️ EXPERIMENTAL — This skill is provided for educational and informational purposes only. It does NOT constitute legal advice. All responsibility for usage rests with the user. Consult qualified legal professionals before acting on any output.
Data Breach Response
Incident response and legal compliance for personal data breaches under GDPR Art. 33/34, CCPA, HIPAA, NIS2, PCI DSS, and other regulations. Calculates breach severity, tracks notification deadlines, and manages response timelines.
---
Table of Contents
- Tools
- Breach Severity Calculator
- Breach Timeline Tracker
- Reference Guides
- Workflows
- ENISA Severity Formula
- Notification Decision Matrix
- Troubleshooting
- Success Criteria
- Scope & Limitations
- Anti-Patterns
- Tool Reference
---
Tools
Breach Severity Calculator
Calculates ENISA breach severity score from breach parameters. Determines notification obligations based on severity verdict.
# Calculate severity from parameters
python scripts/breach_severity_calculator.py \
--dpc 3 --ei 0.75 \
--confidentiality 0.5 --integrity 0.25 --availability 0 \
--malicious
# JSON output
python scripts/breach_severity_calculator.py \
--dpc 2 --ei 0.5 --confidentiality 0.5 --json
# With T0 timestamp for countdown
python scripts/breach_severity_calculator.py \
--dpc 3 --ei 1.0 --confidentiality 0.5 \
--t0 "2026-04-10T08:00:00" --json
# Generate input template
python scripts/breach_severity_calculator.py --templateOutput includes:
- ENISA severity score (SE)
- Severity verdict: LOW / MEDIUM / HIGH / VERY HIGH
- Notification obligations (SA, data subjects, public)
- Time remaining for GDPR 72h notification from T0
---
Breach Timeline Tracker
Tracks breach response timeline from T0 (moment of awareness). Records events, monitors deadlines, and generates status dashboards.
# Initialize a new breach timeline
python scripts/breach_timeline_tracker.py init \
--breach-id "BR-2026-001" --t0 "2026-04-10T08:00:00" \
--description "Unauthorized database access" \
--output breach_timeline.json
# Record an event
python scripts/breach_timeline_tracker.py event \
--timeline breach_timeline.json \
--action "Containment team activated" --category containment
# View status dashboard
python scripts/breach_timeline_tracker.py status --timeline breach_timeline.json
# Check deadlines
python scripts/breach_timeline_tracker.py deadlines --timeline breach_timeline.json
# JSON status output
python scripts/breach_timeline_tracker.py status --timeline breach_timeline.json --jsonTracks:
- GDPR 72-hour SA notification deadline
- DPA contractual deadlines (24h / 48h processor notification)
- NIS2 24-hour early warning and 72-hour notification
- Completed vs. pending response actions
- Time elapsed and time remaining per deadline
---
Reference Guides
ENISA Methodology
references/enisa_methodology.md
Complete ENISA breach severity methodology:
- DPC (Data Processing Context) scoring 1-4
- EI (Ease of Identification) scoring 0.25-1.00
- CB (Circumstances of Breach) additive scoring
- Formula: SE = (DPC x EI) + CB
- Adjustments for encryption, pseudonymization, volume
- EDPB case matching (18 reference cases)
Notification Obligations
references/notification_obligations.md
Multi-regulation notification requirements:
- GDPR Art. 33 (SA within 72h) and Art. 34 (data subjects)
- CCPA, HIPAA, PCI DSS, NIS2, state breach notification
- Controller vs. Processor obligation matrix
- Cross-border notification rules
- AI Act Art. 62 serious incident reporting
---
Workflows
Workflow 1: Standard Breach Response
Step 1: Emergency check — is there <12h remaining on any deadline?
→ If yes, skip to Step 4 (emergency notification)
Step 2: Initialize breach timeline
→ python scripts/breach_timeline_tracker.py init --breach-id "BR-2026-001" \
--t0 "2026-04-10T08:00:00" --description "Description"
Step 3: Calculate severity
→ python scripts/breach_severity_calculator.py --dpc N --ei N \
--confidentiality N --integrity N --availability N [--malicious]
Step 4: Based on severity verdict, determine notifications
→ LOW (<2): Internal log only, no external notification
→ MEDIUM (2 to <3): Notify supervisory authority within 72h
→ HIGH (3 to <4): Notify SA + individual data subjects
→ VERY HIGH (>=4): Notify SA + data subjects + consider public notice
Step 5: Execute containment and record events
→ python scripts/breach_timeline_tracker.py event --timeline breach.json \
--action "Action taken" --category containment
Step 6: Monitor deadlines continuously
→ python scripts/breach_timeline_tracker.py deadlines --timeline breach.json
Step 7: Complete notification obligations and documentWorkflow 2: Emergency Mode (<12h Remaining)
Step 1: Calculate severity immediately
→ python scripts/breach_severity_calculator.py --dpc N --ei N \
--confidentiality N --t0 "original-t0" --json
Step 2: If MEDIUM or higher, prepare phased notification
→ Art. 33(4) allows phased notification when full information unavailable
→ Initial notification: what is known + promise of update
→ Supplementary notification: full details when available
Step 3: File initial SA notification before deadline expires
Step 4: Initialize timeline for ongoing tracking
→ Continue gathering information for supplementary notification
Step 5: Document emergency timeline and decisionsWorkflow 3: Processor Breach Notification
Step 1: Processor becomes aware of breach
→ T0 for processor = moment of awareness
Step 2: Processor must notify controller "without undue delay"
→ Check DPA for specific contractual deadline (24h/48h common)
Step 3: Controller's T0 starts when controller becomes aware
→ Controller's 72h clock starts at this point
Step 4: Controller assesses severity independently
→ python scripts/breach_severity_calculator.py (controller's assessment)
Step 5: Controller makes notification decisions
→ Processor provides information; controller decides on SA/subject notification---
ENISA Severity Formula
SE = (DPC x EI) + CB| Component | Range | Description |
|---|---|---|
| DPC | 1-4 | Data Processing Context — nature and sensitivity of data |
| EI | 0.25-1.0 | Ease of Identification — how easily individuals can be identified |
| CB | -0.5 to +1.0 | Circumstances of Breach — additive factors (malicious intent, volume, loss type) |
Severity Verdicts
| Score Range | Verdict | Notification Obligations |
|---|---|---|
| <2 | LOW | Internal log only. No SA or subject notification required |
| 2 to <3 | MEDIUM | Notify supervisory authority within 72h (Art. 33) |
| 3 to <4 | HIGH | Notify SA within 72h + notify individual data subjects (Art. 34) |
| >=4 | VERY HIGH | Notify SA + data subjects + consider public notice; crisis management |
---
Notification Decision Matrix
Quick reference for notification obligations per regulation and severity.
| Regulation | Authority Notification | Individual Notification | Trigger |
|---|---|---|---|
| GDPR Art. 33 | SA within 72h | N/A | Unless unlikely to result in risk to rights/freedoms |
| GDPR Art. 34 | N/A | Without undue delay | When likely to result in high risk |
| CCPA | State AG | Affected consumers | Unencrypted personal information compromised |
| HIPAA | HHS within 60 days | Affected individuals | Unsecured PHI; >500: notify media |
| PCI DSS | Card brands within 24h | Cardholders (via issuer) | Cardholder data compromised |
| NIS2 Art. 23 | CSIRT within 24h (early warning), 72h (notification) | N/A | Significant incident |
| AI Act Art. 62 | Market surveillance within 15 days | N/A | Serious incident involving AI system |
Controller vs. Processor Obligations
| Obligation | Controller | Processor |
|---|---|---|
| Notify supervisory authority | Yes (Art. 33) | No (notify controller only) |
| Notify data subjects | Yes (Art. 34) | No |
| Document all breaches | Yes (Art. 33(5)) | Yes (assist controller) |
| Notify controller | N/A | Yes, without undue delay (Art. 33(2)) |
| Conduct severity assessment | Yes | Assist (provide information) |
| Timeline starts (T0) | When controller becomes aware | When processor becomes aware |
---
Troubleshooting
| Problem | Possible Cause | Resolution |
|---|---|---|
| Severity score is borderline between MEDIUM and HIGH | Parameters are at threshold boundaries | Score conservatively — if near 3.0, treat as HIGH and notify data subjects; document the borderline analysis |
| 72-hour deadline approaching with incomplete information | Complex breach requiring ongoing investigation | Use Art. 33(4) phased notification — notify SA with available information and supplement later |
| Processor discovered breach but delayed notifying controller | DPA contractual deadline may have been missed | Document the delay; assess whether processor's delay affected controller's ability to comply; review DPA terms |
| Cross-border breach — unclear which SA to notify | Multi-jurisdictional processing with unclear lead SA | Notify the SA of your main establishment (one-stop-shop); if unclear, notify the SA where most affected subjects reside |
| Breach involves encrypted data — unclear if notification needed | Encryption may lower severity or eliminate notification | If encryption was effective (strong algorithm, key not compromised), this may make notification unnecessary per Art. 34(3)(a); document the analysis |
| AI system involved in breach — unclear additional obligations | AI Act Art. 62 may apply alongside GDPR | Assess whether AI system is high-risk under AI Act; if serious incident, notify market surveillance authority within 15 days in addition to GDPR obligations |
---
Success Criteria
- Breach severity calculated within 2 hours of awareness -- ENISA methodology applied with documented parameters and scoring rationale
- SA notification filed within 72 hours of T0 -- for MEDIUM or higher severity breaches, phased notification used when full information unavailable
- Data subject notification completed without undue delay -- for HIGH or higher severity breaches, clear communication of impact and protective measures
- All response actions tracked with timestamps -- breach timeline maintained from T0 through closure with all events recorded
- Cross-regulation obligations identified and met -- GDPR, CCPA, HIPAA, PCI DSS, NIS2, and AI Act obligations assessed and fulfilled per applicable law
- Post-breach documentation complete -- internal breach log maintained per Art. 33(5) regardless of notification decision
---
Scope & Limitations
In Scope:
- ENISA breach severity calculation with full parameter support
- GDPR Art. 33/34 notification timeline tracking
- Multi-regulation notification obligation assessment (GDPR, CCPA, HIPAA, PCI DSS, NIS2, AI Act)
- Controller vs. processor obligation guidance
- Cross-border breach notification routing
- Phased notification guidance per Art. 33(4)
- Breach response event tracking and deadline monitoring
Out of Scope:
- Technical incident containment (network isolation, forensics, malware removal)
- Filing notifications with supervisory authorities (document preparation only)
- Insurance claim processing or coverage analysis
- Law enforcement coordination
- Public relations or crisis communications strategy
- Forensic investigation methodology
---
Anti-Patterns
- Delaying T0 determination to buy more time -- T0 is the moment the controller becomes "aware" of the breach, not when full details are known; deliberately delaying awareness to extend the 72-hour window is a compliance violation and will be treated as such by regulators
- Defaulting to no notification without documented analysis -- every breach must be documented and assessed, even if the conclusion is that notification is not required; "we decided not to notify" without documented severity analysis is indefensible
- Treating processor notification as controller notification -- processor notifying its own SA does not satisfy the controller's Art. 33 obligation; the controller must make its own independent notification decision and filing
- Using encryption as an automatic notification exemption -- Art. 34(3)(a) exemption requires that the encrypted data was rendered unintelligible AND the encryption key was not compromised; weak encryption or compromised keys do not qualify
- Ignoring AI Act obligations for AI-involved breaches -- if the breach involves a high-risk AI system, Art. 62 serious incident reporting (15 days to market surveillance authority) applies in addition to GDPR; these are separate obligations with different timelines
---
Tool Reference
breach_severity_calculator.py
Calculates ENISA breach severity score and determines notification obligations.
| Flag | Required | Description |
|---|---|---|
--dpc <1-4> | Yes | Data Processing Context: 1=Simple demographic, 2=Behavioral/financial, 3=Sensitive personal, 4=Special category/highly sensitive |
--ei <0.25-1.0> | Yes | Ease of Identification: 0.25=Negligible, 0.5=Limited, 0.75=Significant, 1.0=Maximum |
--confidentiality <0/0.25/0.5> | No | Confidentiality loss score (default 0) |
--integrity <0/0.25/0.5> | No | Integrity loss score (default 0) |
--availability <0/0.25/0.5> | No | Availability loss score (default 0) |
--malicious | No | Flag for malicious intent (adds +0.5 to CB) |
--t0 <ISO datetime> | No | T0 timestamp for deadline calculation |
--template | No | Generate input template |
--json | No | Output in JSON format |
breach_timeline_tracker.py
Tracks breach response timeline, events, and regulatory deadlines.
| Subcommand | Description |
|---|---|
init | Initialize breach timeline (--breach-id, --t0, --description required, --output optional) |
event | Record event (--timeline, --action, --category required) |
status | View status dashboard (--timeline required, --json optional) |
deadlines | Check deadline status (--timeline required, --json optional) |
ENISA Breach Severity Methodology
Complete reference for the European Union Agency for Cybersecurity (ENISA) personal data breach severity assessment methodology.
---
Table of Contents
- Severity Formula
- Data Processing Context (DPC)
- Ease of Identification (EI)
- Circumstances of Breach (CB)
- Severity Thresholds
- Adjustments
- Borderline Guidance
- EDPB Case Matching
- Quick Decision Tree
---
Severity Formula
SE = (DPC x EI) + CB| Component | Name | Range | Description |
|---|---|---|---|
| DPC | Data Processing Context | 1-4 | Nature and sensitivity of the personal data involved |
| EI | Ease of Identification | 0.25-1.00 | How easily the data can be used to identify specific individuals |
| CB | Circumstances of Breach | Additive | Aggregated score of loss type and malicious intent |
| SE | Severity | Calculated | Final severity score determining notification obligations |
Interpretation: The formula weights the sensitivity of data (DPC) by how identifiable the data subjects are (EI), then adds circumstantial factors (CB) for the type of loss and attacker intent.
---
Data Processing Context (DPC)
DPC measures the nature and sensitivity of the personal data compromised.
| Score | Label | Description | Examples |
|---|---|---|---|
| 1 | Basic | Simple demographic or contact data that is widely available | Name, email address, phone number, mailing address, job title, employer |
| 2 | Behavioral / Financial | Data revealing behavioral patterns, preferences, or financial information | Purchase history, browsing behavior, location data (non-continuous), bank account number, salary information, tax records |
| 3 | Sensitive Personal | Data that could cause significant harm if disclosed | Social security/national ID number, passport number, driver's license, login credentials, detailed financial records, communication content |
| 4 | Special Category / Highly Sensitive | Art. 9 special category data or data with extreme sensitivity | Health/medical records, genetic data, biometric data, sexual orientation, political opinions, religious beliefs, trade union membership, criminal records, children's data combined with other sensitive data |
DPC Assessment Guidance
| Factor | Increases DPC | Decreases DPC |
|---|---|---|
| Data type | Special category (Art. 9), criminal (Art. 10) | Publicly available information |
| Combination | Multiple data categories combined | Single data element |
| Context | Employment, healthcare, financial services | General consumer context |
| Volume per subject | Comprehensive profile | Minimal data elements |
---
Ease of Identification (EI)
EI measures how easily the compromised data can be used to identify specific individuals.
| Score | Label | Description | Examples |
|---|---|---|---|
| 0.25 | Negligible | Data alone cannot identify individuals; requires significant additional information not available to the recipient | Aggregated statistics, anonymized survey data, encrypted data with key not compromised |
| 0.50 | Limited | Identification requires additional data that may be obtainable but requires effort | Pseudonymized data, partial records, coded identifiers without lookup table |
| 0.75 | Significant | Identification is reasonably achievable using available resources | Email addresses combined with behavioral data, IP addresses with timestamps, device identifiers |
| 1.00 | Maximum | Direct identification possible from the data itself | Full name + SSN, photo ID, biometric data, unambiguous unique identifiers |
EI Assessment Guidance
| Factor | Increases EI | Decreases EI |
|---|---|---|
| Direct identifiers | Name, ID number, photo, biometric | Absent or removed |
| Pseudonymization | Not applied or mapping compromised | Properly applied, mapping secure |
| Data combination | Multiple identifying elements together | Single non-identifying element |
| Public availability | Matching data available publicly | No matching data available |
| Encryption | Not encrypted or key compromised | Properly encrypted, key secure |
---
Circumstances of Breach (CB)
CB is an additive score combining the type of data loss and circumstances.
Loss Types
| Component | Score Options | Description |
|---|---|---|
| Confidentiality loss | 0 / 0.25 / 0.5 | Was data disclosed to unauthorized parties? |
| Integrity loss | 0 / 0.25 / 0.5 | Was data altered or corrupted? |
| Availability loss | 0 / 0.25 / 0.5 | Was access to data lost or disrupted? |
Scoring guidance for each loss type:
| Score | Confidentiality | Integrity | Availability |
|---|---|---|---|
| 0 | No unauthorized disclosure | No data alteration | No access disruption |
| 0.25 | Limited disclosure (small number of unauthorized recipients, contained) | Minor alteration (detectable, reversible) | Temporary disruption (<24h, workaround available) |
| 0.5 | Broad disclosure (public exposure, unknown recipients, dark web) | Significant alteration (hard to detect, affects decisions) | Extended disruption (>24h, no workaround, data loss) |
Malicious Intent
| Condition | Score Addition | Description |
|---|---|---|
| Not malicious | +0.0 | Accidental breach, human error, system failure |
| Malicious | +0.5 | Deliberate attack, insider threat, ransomware, social engineering |
CB Calculation
CB = Confidentiality_loss + Integrity_loss + Availability_loss + Malicious_intentPractical CB range: 0 to 2.0
| CB Score | Interpretation |
|---|---|
| 0 | No significant breach circumstances (e.g., encrypted backup loss) |
| 0.25-0.5 | Minor circumstances (single loss type, no malicious intent) |
| 0.5-1.0 | Moderate circumstances (multiple loss types or malicious intent) |
| 1.0-1.5 | Serious circumstances (multiple loss types with malicious intent) |
| 1.5-2.0 | Severe circumstances (full CIA triad loss with malicious intent) |
---
Severity Thresholds
| Score Range | Verdict | Notification Requirement | Response Level |
|---|---|---|---|
| SE < 2 | LOW | No notification to SA or data subjects | Internal documentation in breach register (Art. 33(5)). Monitor for escalation. |
| 2 <= SE < 3 | MEDIUM | Notify supervisory authority within 72h (Art. 33) | SA notification. Internal investigation. Containment. |
| 3 <= SE < 4 | HIGH | Notify SA within 72h + notify data subjects without undue delay (Art. 34) | SA and subject notification. Response team activation. Remediation plan. |
| SE >= 4 | VERY HIGH | SA + data subjects + consider public notice | Crisis management. Executive briefing. Outside counsel. Board notification. Consider public statement. |
Notification Decision Logic
IF SE < 2:
→ Log internally only
→ Retain documentation for accountability
ELIF SE < 3:
→ Notify SA within 72h of T0
→ Document breach and response
→ No data subject notification required
ELIF SE < 4:
→ Notify SA within 72h of T0
→ Notify data subjects without undue delay
→ Describe nature of breach, consequences, measures taken
ELSE:
→ All of the above
→ Consider public communication (Art. 34(3)(c))
→ Activate crisis management protocol
→ Brief executive leadership and board---
Adjustments
Encryption Adjustment
| Situation | Effect on SE |
|---|---|
| Data properly encrypted with AES-256 or equivalent, key NOT compromised | DPC effectively reduced by 1-2 levels (data unintelligible per Art. 34(3)(a)) |
| Data encrypted but key also compromised | No reduction — encryption ineffective |
| Data encrypted with weak algorithm (DES, RC4, short key) | Minimal reduction — regulator may not consider protection adequate |
| Partial encryption (some fields encrypted, others clear) | Assess clear-text fields separately; encrypted fields may reduce EI |
Pseudonymization Adjustment
| Situation | Effect on SE |
|---|---|
| Pseudonymized with mapping NOT compromised | EI reduced to 0.25 or 0.50 (depending on reversibility) |
| Pseudonymized but mapping also compromised | No EI reduction |
| Pseudonymized with deterministic method (reversible by recipient) | Minimal EI reduction |
Volume Adjustment
ENISA methodology does not include volume as a direct formula component, but volume affects notification obligations:
| Volume | Practical Impact |
|---|---|
| <100 individuals | Individual notification straightforward |
| 100-10,000 | Individual notification required but operationally significant |
| >10,000 | Consider whether individual notification is disproportionate effort; if so, public communication per Art. 34(3)(c) |
| >100,000 | Strong case for public communication in addition to individual notification |
---
Borderline Guidance
When the severity score falls near a threshold boundary, apply these principles:
| Score Range | Borderline Guidance |
|---|---|
| 1.8-2.0 | Conservative: treat as MEDIUM if any uncertainty about DPC or EI scoring. Document the borderline analysis. |
| 2.8-3.0 | Conservative: treat as HIGH. The cost of notifying data subjects when not strictly required is far lower than the risk of not notifying when required. |
| 3.8-4.0 | Conservative: treat as VERY HIGH. Activate crisis management as a precaution. |
General principle: When in doubt, notify. Under-notification carries regulatory risk (fines, enforcement action). Over-notification carries minimal risk (slight reputational concern from appearing to have frequent breaches, but regulators view proactive notification favorably).
---
EDPB Case Matching
Reference cases from EDPB Guidelines 01/2021 on examples regarding personal data breach notification. Use these to validate your severity assessment against similar scenarios.
Ransomware Cases
| Case | Scenario | Typical DPC | Typical EI | Key Factors | Expected Verdict |
|---|---|---|---|---|---|
| 01 | Ransomware with proper backup, no exfiltration | 2-3 | 0.50-1.00 | Availability loss only; backup restored quickly | LOW-MEDIUM |
| 02 | Ransomware without proper backup | 2-3 | 0.50-1.00 | Availability loss, potential permanent data loss | MEDIUM-HIGH |
| 03 | Ransomware on hospital system with exfiltration | 4 | 1.00 | Health data, malicious, confidentiality + availability | VERY HIGH |
| 04 | Ransomware with exfiltration, no backup | 3-4 | 0.75-1.00 | Full CIA triad loss, malicious intent | HIGH-VERY HIGH |
Data Exfiltration Cases
| Case | Scenario | Typical DPC | Typical EI | Key Factors | Expected Verdict |
|---|---|---|---|---|---|
| 05 | Exfiltration of hashed passwords | 2 | 0.50 | Hashing reduces EI; depends on hash strength | MEDIUM |
| 06 | Exfiltration of employee HR records | 3 | 1.00 | Sensitive personal, directly identifying | HIGH |
| 07 | Exfiltration of customer financial data | 3-4 | 0.75-1.00 | Financial harm potential, identity theft risk | HIGH-VERY HIGH |
Internal Human Risk Cases
| Case | Scenario | Typical DPC | Typical EI | Key Factors | Expected Verdict |
|---|---|---|---|---|---|
| 08 | Accidental email to wrong recipient (small dataset) | 1-2 | 1.00 | Limited scope, single recipient, likely recoverable | LOW-MEDIUM |
| 09 | Employee deliberately exfiltrating customer data | 3 | 1.00 | Malicious intent, insider knowledge | HIGH |
Lost/Stolen Device Cases
| Case | Scenario | Typical DPC | Typical EI | Key Factors | Expected Verdict |
|---|---|---|---|---|---|
| 10 | Encrypted laptop stolen | 2-3 | 0.25 | Encryption effective — EI reduced significantly | LOW |
| 11 | Unencrypted USB with personal data lost | 2-3 | 1.00 | No encryption, portable, unknown finder | MEDIUM-HIGH |
| 12 | Encrypted phone lost, remote wipe successful | 2 | 0.25 | Encryption + remote wipe = minimal risk | LOW |
Mispostal Cases
| Case | Scenario | Typical DPC | Typical EI | Key Factors | Expected Verdict |
|---|---|---|---|---|---|
| 13 | Wrong person receives utility bill | 1 | 0.75 | Basic data, limited sensitivity | LOW |
| 14 | Medical records sent to wrong patient | 4 | 1.00 | Special category, directly identifying | HIGH |
| 15 | Payslips mixed up between employees | 3 | 1.00 | Financial data, workplace context | MEDIUM-HIGH |
| 16 | Marketing email CC instead of BCC (large list) | 1 | 1.00 | Basic data, large volume, email exposed | LOW-MEDIUM |
Social Engineering Cases
| Case | Scenario | Typical DPC | Typical EI | Key Factors | Expected Verdict |
|---|---|---|---|---|---|
| 17 | Phishing attack stealing credentials | 2-3 | 0.75-1.00 | Malicious, credential access, potential further compromise | MEDIUM-HIGH |
| 18 | Business email compromise with data exfiltration | 3 | 1.00 | Malicious, targeted, potential financial harm | HIGH |
---
Quick Decision Tree
For rapid initial assessment when detailed scoring is not yet possible:
Was the data encrypted with strong encryption AND the key is NOT compromised?
→ YES → Likely LOW. Confirm with full ENISA scoring.
→ NO → Continue
Is special category data (Art. 9) or criminal data (Art. 10) involved?
→ YES → Likely HIGH or VERY HIGH. Prepare for subject notification.
→ NO → Continue
Are individuals directly identifiable from the compromised data?
→ YES, and data is sensitive (financial, ID numbers, credentials)
→ Likely HIGH. Score formally to confirm.
→ YES, but data is basic (name, email, phone only)
→ Likely MEDIUM. Score formally to confirm.
→ NO (pseudonymized, aggregated, or coded)
→ Likely LOW or MEDIUM. Score formally to confirm.
Was the breach malicious (attack, insider theft, ransomware)?
→ YES → Add 0.5 to CB. Likely increases by one severity level.
→ NO → Score without malicious adjustment.
Was data actually accessed/exfiltrated, or just potentially exposed?
→ Actually accessed → Score confidentiality at 0.25-0.5
→ Potentially exposed but no evidence of access → Score confidentiality at 0-0.25
→ Evidence of exfiltration to dark web → Score confidentiality at 0.5, EI at maximumIn all cases: Complete the full ENISA severity calculation within 2 hours of T0 to support notification decision-making. The quick decision tree provides initial triage, not a substitute for formal scoring.
Data Breach Notification Obligations
Multi-regulation notification requirements for personal data breaches including GDPR, CCPA, HIPAA, PCI DSS, NIS2, state breach notification laws, and AI Act.
---
Table of Contents
- GDPR Notification
- CCPA Notification
- HIPAA Notification
- PCI DSS Notification
- NIS2 Notification
- AI Act Serious Incident Reporting
- US State Breach Notification Overview
- Cross-Border Notification Rules
- Controller vs Processor Obligations
- Phased Notification Guidance
- Notification Content Requirements
---
GDPR Notification
Art. 33 — Notification to Supervisory Authority
| Aspect | Requirement |
|---|---|
| Who notifies | Controller |
| Notify whom | Competent supervisory authority (SA) under Art. 55 |
| Timeline | Without undue delay, not later than 72 hours after becoming aware |
| Trigger | Personal data breach, unless unlikely to result in risk to rights and freedoms |
| Exemption | Only if breach is "unlikely to result in a risk to the rights and freedoms of natural persons" — must be documented |
Art. 33(3) Required content:
| Element | Description |
|---|---|
| Nature of breach | Categories and approximate number of data subjects and records |
| DPO contact | Name and contact details of DPO or other point of contact |
| Consequences | Likely consequences of the breach |
| Measures | Measures taken or proposed to address the breach, including mitigation |
Art. 33(4) Phased notification: Where it is not possible to provide all information at the same time, the information may be provided in phases without undue further delay.
Art. 33(5) Documentation: Controller shall document any personal data breaches, comprising the facts, effects, and remedial action taken. This documentation must enable the SA to verify compliance. Applies to ALL breaches, even those not notified.
Art. 34 — Communication to Data Subjects
| Aspect | Requirement |
|---|---|
| Who notifies | Controller |
| Notify whom | Affected data subjects |
| Timeline | Without undue delay |
| Trigger | Breach "likely to result in a high risk to the rights and freedoms of natural persons" |
| Language | Clear and plain language |
Art. 34(3) Exemptions from individual notification:
| Exemption | Condition |
|---|---|
| (a) Encryption/unintelligibility | Controller implemented measures rendering data unintelligible (e.g., strong encryption with key not compromised) |
| (b) Subsequent measures | Controller has taken subsequent measures ensuring high risk is no longer likely to materialize |
| (c) Disproportionate effort | Individual notification would involve disproportionate effort — in which case public communication or similar measure must be used instead |
---
CCPA Notification
California Consumer Privacy Act (as amended by CPRA)
| Aspect | Requirement |
|---|---|
| Who notifies | Business (any entity meeting CCPA thresholds) |
| Notify whom | Affected California residents |
| Timeline | "Most expedient time possible and without unreasonable delay" |
| Trigger | Unauthorized access to unencrypted and unredacted personal information |
Personal information under CCPA breach provisions (Civ. Code 1798.81.5):
| Data Type | Examples |
|---|---|
| SSN | Social Security number |
| Driver's license / state ID | License number, identification card number |
| Financial account | Account number + access code/password |
| Medical / health insurance | Medical information, health insurance information |
| Biometric | Fingerprint, retina, iris, or other unique biometric data |
| Username + password/security question | Credentials enabling access to online account |
Notice requirements:
| Element | Requirement |
|---|---|
| Method | Written notice or electronic notice (per consent) |
| Content | Name and contact of notifying entity; types of PI subject to breach; date, estimated date, or date range of breach; description of incident; toll-free telephone number for inquiries |
| Substitute notice | If >500,000 affected or cost >$250,000: email + conspicuous website posting + major statewide media |
| AG notification | If >500 California residents affected |
---
HIPAA Notification
Health Insurance Portability and Accountability Act
| Aspect | Requirement |
|---|---|
| Who notifies | Covered entity (healthcare providers, health plans, healthcare clearinghouses) |
| Notify whom | Affected individuals, HHS, and potentially media |
| Timeline | Without unreasonable delay, no later than 60 calendar days from discovery |
| Trigger | Breach of unsecured Protected Health Information (PHI) |
Notification tiers:
| Affected Individuals | Requirements |
|---|---|
| <500 | Notify individuals within 60 days; log with HHS annually |
| >=500 in single state/jurisdiction | Notify individuals + prominent local media within 60 days; notify HHS within 60 days |
| >=500 total | Notify individuals; notify HHS within 60 days (immediate posting on HHS breach portal) |
Breach presumption: Any unauthorized acquisition, access, use, or disclosure of PHI is presumed to be a breach unless the covered entity demonstrates a low probability that PHI was compromised based on a 4-factor risk assessment:
| Factor | Assessment |
|---|---|
| 1. Nature and extent of PHI | What data elements were involved |
| 2. Unauthorized person | Who gained access or to whom was it disclosed |
| 3. PHI actually acquired/viewed | Was PHI actually accessed or only potentially exposed |
| 4. Extent of risk mitigation | What steps were taken to reduce harm |
Safe harbor: PHI rendered unusable, unreadable, or indecipherable through encryption per NIST guidelines (or destruction) is NOT "unsecured PHI" — breach notification rules do not apply.
---
PCI DSS Notification
Payment Card Industry Data Security Standard
| Aspect | Requirement |
|---|---|
| Who notifies | Merchant, service provider, or acquiring bank |
| Notify whom | Card brands (Visa, Mastercard, etc.) via acquiring bank |
| Timeline | Immediately / within 24 hours (varies by card brand) |
| Trigger | Known or suspected compromise of cardholder data |
Card brand specific requirements:
| Brand | Notification Timeline | Additional Requirements |
|---|---|---|
| Visa | Within 24 hours to acquirer | Forensic investigation by PFI within 72 hours |
| Mastercard | Immediately upon detection | Account Data Compromise event report |
| American Express | Within 24 hours | Engage PFI within 5 business days |
| Discover | Within 48 hours | Cooperate with Discover investigation |
Cardholder data elements:
| Data Element | Storage Permitted | Notification Trigger |
|---|---|---|
| Primary Account Number (PAN) | Yes (encrypted) | Yes if compromised |
| Cardholder name | Yes | Yes if with PAN |
| Expiration date | Yes | Yes if with PAN |
| Service code | Yes | Yes if with PAN |
| Full magnetic stripe / CVV2 / PIN | No (never store) | Always if compromised |
---
NIS2 Notification
Network and Information Security Directive 2 (EU 2022/2555)
| Aspect | Requirement |
|---|---|
| Who notifies | Essential and important entities |
| Notify whom | Competent CSIRT and, where applicable, competent authority |
| Trigger | Significant incident (as defined by member state transposition) |
Notification timeline (Art. 23):
| Phase | Timeline | Content |
|---|---|---|
| Early warning | Within 24 hours of becoming aware | Whether incident is suspected of being caused by unlawful or malicious acts; whether it could have cross-border impact |
| Incident notification | Within 72 hours of becoming aware | Update to early warning; initial assessment of severity and impact; indicators of compromise where applicable |
| Intermediate report | Upon request of CSIRT | Status update with relevant information |
| Final report | Within 1 month of incident notification | Detailed description; root cause; mitigation measures; cross-border impact if applicable |
Significant incident criteria (Art. 23(3)):
| Criterion | Description |
|---|---|
| (a) | Has caused or is capable of causing severe operational disruption of services or financial loss |
| (b) | Has affected or is capable of affecting other natural or legal persons by causing considerable material or non-material damage |
---
AI Act Serious Incident Reporting
EU AI Act (2024/1689) Art. 62
| Aspect | Requirement |
|---|---|
| Who reports | Providers of high-risk AI systems placed on EU market |
| Report to | Market surveillance authority of member state where incident occurred |
| Timeline | Within 15 days of becoming aware (or immediately if death/serious health damage) |
| Trigger | Serious incident involving high-risk AI system |
Serious incident definition (Art. 3(49)):
| Type | Description |
|---|---|
| Death | AI system caused or contributed to death of a person |
| Serious damage to health | Physical or psychological harm |
| Serious damage to property | Property damage or significant environmental damage |
| Fundamental rights violation | Serious and irreversible breach of fundamental rights |
Relationship to GDPR:
- AI Act Art. 62 reporting is SEPARATE from GDPR Art. 33/34 breach notification
- Both may apply to the same incident (e.g., AI system breach involving personal data)
- Different timelines: GDPR 72h vs. AI Act 15 days (or immediate for death/health)
- Different authorities: GDPR supervisory authority vs. AI Act market surveillance authority
---
US State Breach Notification Overview
All 50 US states have breach notification laws. Key variations:
| State | Timeline | AG Notification Threshold | Notable Provisions |
|---|---|---|---|
| California | Most expedient time | >500 residents | Broadest PI definition; private right of action for certain breaches |
| New York | Most expedient time | Any number | SHIELD Act expanded PI definition; broad security requirements |
| Texas | Within 60 days | >250 residents | AG notification within 60 days |
| Florida | Within 30 days | >500 residents | AG within 30 days; 30-day individual notification |
| Illinois | Most expedient time | Any number | BIPA for biometric data (private right of action) |
| Massachusetts | As soon as practicable | Any number | AG and Office of Consumer Affairs notification |
| Virginia | Without unreasonable delay | >1,000 residents | AG notification; consumer reporting agency notification |
| Colorado | Within 30 days | >500 residents | AG notification within 30 days |
| Washington | Within 30 days | >500 residents | AG notification within 30 days |
| Connecticut | Within 60 days | Any number | AG notification without unreasonable delay |
Common elements across state laws:
| Element | Typical Requirement |
|---|---|
| Covered data | SSN, driver's license, financial account + access, medical, biometric (varies) |
| Encryption safe harbor | Most states exempt encrypted data (if key not compromised) |
| Good faith exception | Most states exempt good-faith acquisition by employee/agent |
| AG notification | Threshold varies (any number to 500+) |
| Consumer reporting agencies | Typically required if >1,000 affected in that state |
---
Cross-Border Notification Rules
GDPR One-Stop-Shop (Art. 56)
| Scenario | Lead SA | Notification Target |
|---|---|---|
| Single establishment in one member state | SA of that member state | That SA |
| Main establishment in one member state, processing across EU | SA of main establishment | Lead SA (who coordinates with concerned SAs) |
| No main establishment but processing EU data | SA of member state most affected | That SA (may need to notify multiple) |
| Processor breach | N/A for processor — controller determines | Controller's lead SA |
Multi-Regulation Cross-Border
| If processing involves... | Notify under... | Timeline |
|---|---|---|
| EU data subjects' personal data | GDPR (Art. 33/34) | 72h to SA; without undue delay to subjects |
| California residents' personal info | CCPA | Most expedient time |
| US patients' health info | HIPAA | 60 days |
| Payment card data | PCI DSS | 24h to card brands |
| Essential/important entity in EU | NIS2 (Art. 23) | 24h early warning + 72h notification |
| High-risk AI system | AI Act (Art. 62) | 15 days (immediate if death/health) |
Best practice: Notify under the most stringent applicable timeline. If GDPR (72h), NIS2 (24h), and PCI (24h) all apply, prepare for 24h notification and align content for all recipients.
---
Controller vs Processor Obligations
| Obligation | Controller | Processor |
|---|---|---|
| Detect and investigate breach | Primary responsibility | Assist; notify controller of own detection |
| Notify supervisory authority (Art. 33) | Yes — sole obligation | No — notify controller only |
| Notify data subjects (Art. 34) | Yes — sole obligation | No |
| Notify controller of breach | N/A | Yes — without undue delay (Art. 33(2)) |
| Determine severity | Yes | Provide information to support assessment |
| Decide on notification | Yes | No decision authority |
| Document breach (Art. 33(5)) | Yes — all breaches | Yes — assist controller; maintain own records |
| Contractual notification deadline | N/A | Per DPA terms (commonly 24h or 48h) |
Processor-Specific Obligations
| Obligation | Detail |
|---|---|
| Without undue delay | Art. 33(2) requires processor to notify controller after becoming aware — no specific hour limit, but "without undue delay" is strict |
| DPA contractual terms | Most DPAs specify 24h or 48h — contractual obligation may be stricter than statutory |
| Assistance | Processor must assist controller in ensuring compliance with Art. 33-34 obligations (Art. 28(3)(f)) |
| Sub-processor | If sub-processor discovers breach, sub-processor notifies processor, processor notifies controller — chain notification |
---
Phased Notification Guidance
GDPR Art. 33(4) — Phased Notification
When full information is not available within the 72-hour window, GDPR explicitly permits phased notification.
Phase 1: Initial notification (within 72h)
| Content | Status |
|---|---|
| Nature of breach | Provide what is known |
| Categories of data subjects | Estimate if exact numbers unavailable |
| DPO contact | Required |
| Likely consequences | Preliminary assessment |
| Measures taken | Containment measures underway |
| Reason for delay | Explain why full information unavailable |
Phase 2: Supplementary notification (without undue further delay)
| Content | Status |
|---|---|
| Updated scope | Revised numbers and categories |
| Updated consequences | Based on investigation findings |
| Root cause | If determined |
| Additional measures | Remediation actions taken since initial notification |
| Data subject notification status | Whether subjects were notified and how |
Best Practices for Phased Notification
| Practice | Rationale |
|---|---|
| File initial notification early | Better to notify within 72h with partial information than to miss the deadline |
| Clearly mark as "phased" / "initial" | SA expects follow-up; prevents confusion about completeness |
| Set internal deadline for supplementary | Aim for 14 days after initial; no later than 30 days |
| Track notification status | Maintain record of all notifications (initial + supplementary) |
| Align supplementary with investigation milestones | Update SA as investigation completes phases (containment → analysis → remediation) |
---
Notification Content Requirements
Comparison Across Regulations
| Element | GDPR Art. 33 (SA) | GDPR Art. 34 (Subject) | CCPA | HIPAA | NIS2 |
|---|---|---|---|---|---|
| Nature of breach | Yes | Yes (clear, plain) | Yes | Yes | Yes |
| Data categories | Yes | Yes | Yes (types of PI) | Yes (types of PHI) | N/A |
| Number affected | Approximate | N/A | N/A | Number affected | Severity/impact |
| DPO / contact | Yes | Yes | Contact info | Contact info | Contact info |
| Consequences | Yes | Yes | N/A | Description of what entity is doing | Impact assessment |
| Measures taken | Yes | Yes | N/A | Steps to protect from harm | Mitigation measures |
| Date of breach | N/A | N/A | Date or date range | Date | Date of detection |
| Recommendations to subjects | N/A | Yes | N/A | Steps individuals can take | N/A |
| Indicators of compromise | N/A | N/A | N/A | N/A | Yes (NIS2 Art. 23) |
| Cross-border impact | N/A | N/A | N/A | N/A | Yes |
| Root cause | N/A (supplementary) | N/A | N/A | N/A | Yes (final report) |
Data Subject Communication Template Elements
Per GDPR Art. 34, communication to data subjects must include at minimum:
| Element | Example Content |
|---|---|
| What happened | "We detected unauthorized access to our customer database on [date]" |
| What data was affected | "Your name, email address, and encrypted password were accessed" |
| What we are doing | "We have contained the incident, reset all passwords, and engaged forensic investigators" |
| What you can do | "We recommend you change your password on any service where you used the same password" |
| Who to contact | "Contact our DPO at [email] or call [phone] for questions" |
| Clear and plain language | No legal jargon; accessible to general public; translated if multilingual user base |
#!/usr/bin/env python3
"""
Breach Severity Calculator
Calculates ENISA breach severity score using the formula SE = (DPC x EI) + CB.
Determines severity verdict (LOW/MEDIUM/HIGH/VERY HIGH) and notification
obligations under GDPR, CCPA, HIPAA, PCI DSS, and NIS2.
Usage:
python breach_severity_calculator.py --dpc 3 --ei 0.75 --confidentiality 0.5 --malicious
python breach_severity_calculator.py --dpc 2 --ei 0.5 --confidentiality 0.5 --json
python breach_severity_calculator.py --dpc 3 --ei 1.0 --confidentiality 0.5 --t0 "2026-04-10T08:00:00" --json
python breach_severity_calculator.py --template
"""
import argparse
import json
import sys
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional, Tuple
DPC_LABELS: Dict[int, str] = {1: "Simple demographic data", 2: "Behavioral or financial data", 3: "Sensitive personal data", 4: "Special category or highly sensitive data"}
EI_LABELS: Dict[float, str] = {0.25: "Negligible", 0.50: "Limited", 0.75: "Significant", 1.00: "Maximum (direct identifiers)"}
GDPR_HOURS, NIS2_EW_HOURS, NIS2_HOURS, PCI_HOURS = 72, 24, 72, 24
def calculate_cb(
confidentiality: float,
integrity: float,
availability: float,
malicious: bool,
) -> Tuple[float, Dict[str, float]]:
"""Calculate Circumstances of Breach (CB) component."""
components: Dict[str, float] = {
"confidentiality_loss": confidentiality,
"integrity_loss": integrity,
"availability_loss": availability,
"malicious_intent": 0.5 if malicious else 0.0,
}
# CB is the sum of loss types minus overlap (max 1.0 practical cap)
# but ENISA allows it to go negative with encryption offset
cb = sum(components.values())
return cb, components
def calculate_severity(
dpc: int, ei: float, cb: float
) -> Tuple[float, str, str]:
"""Calculate ENISA severity score and return (score, verdict, description)."""
se = (dpc * ei) + cb
se = round(se, 2)
if se < 2:
verdict = "LOW"
description = "Internal documentation only. No SA or data subject notification required."
elif se < 3:
verdict = "MEDIUM"
description = "Notify supervisory authority within 72 hours (GDPR Art. 33)."
elif se < 4:
verdict = "HIGH"
description = "Notify SA within 72h + notify individual data subjects without undue delay (GDPR Art. 34)."
else:
verdict = "VERY HIGH"
description = "Notify SA + data subjects + consider public notice. Activate crisis management."
return se, verdict, description
def get_notification_obligations(verdict: str) -> List[Dict[str, str]]:
"""Return notification obligations based on severity verdict."""
obligations: List[Dict[str, str]] = []
# Internal documentation always required
obligations.append({
"regulation": "GDPR Art. 33(5)",
"action": "Document breach in internal breach register",
"deadline": "Immediately",
"required": "Always",
})
if verdict in ("MEDIUM", "HIGH", "VERY HIGH"):
obligations.append({
"regulation": "GDPR Art. 33",
"action": "Notify supervisory authority",
"deadline": "72 hours from T0",
"required": "Yes",
})
if verdict in ("HIGH", "VERY HIGH"):
obligations.append({
"regulation": "GDPR Art. 34",
"action": "Notify affected data subjects",
"deadline": "Without undue delay",
"required": "Yes — high risk to rights and freedoms",
})
if verdict == "VERY HIGH":
obligations.append({
"regulation": "GDPR Art. 34 / national law",
"action": "Consider public communication",
"deadline": "Without undue delay",
"required": "When individual notification is disproportionate effort",
})
# Cross-regulation obligations (always assess)
obligations.append({
"regulation": "CCPA",
"action": "Notify affected California residents",
"deadline": "Most expedient time possible",
"required": "If unencrypted PI of CA residents compromised",
})
obligations.append({
"regulation": "NIS2 Art. 23",
"action": "Early warning to CSIRT + full notification",
"deadline": "24h early warning + 72h notification",
"required": "If essential/important entity with significant incident",
})
obligations.append({
"regulation": "HIPAA",
"action": "Notify HHS and affected individuals",
"deadline": "60 days; if >500: media notification",
"required": "If unsecured PHI compromised",
})
obligations.append({
"regulation": "PCI DSS",
"action": "Notify card brands",
"deadline": "24 hours",
"required": "If cardholder data compromised",
})
return obligations
def calculate_countdown(t0_str: str) -> Dict[str, Any]:
"""Calculate time remaining for various deadlines from T0."""
try:
t0 = datetime.fromisoformat(t0_str)
except ValueError:
return {"error": f"Invalid T0 format: {t0_str}. Use ISO: YYYY-MM-DDTHH:MM:SS"}
now = datetime.now()
elapsed_hours = (now - t0).total_seconds() / 3600
deadlines: Dict[str, Any] = {}
dl_defs = [("gdpr_72h", GDPR_HOURS, 12), ("nis2_24h_ew", NIS2_EW_HOURS, 6),
("nis2_72h", NIS2_HOURS, 12), ("pci_24h", PCI_HOURS, 6)]
for name, hours, urgent_thresh in dl_defs:
deadline_time = t0 + timedelta(hours=hours)
remaining = (deadline_time - now).total_seconds() / 3600
status = "EXPIRED" if remaining <= 0 else "URGENT" if remaining <= urgent_thresh else "OK"
deadlines[name] = {"deadline": deadline_time.isoformat(), "remaining_hours": round(remaining, 1),
"expired": remaining <= 0, "status": status}
return {"t0": t0.isoformat(), "current_time": now.isoformat(),
"elapsed_hours": round(elapsed_hours, 1), "deadlines": deadlines}
def generate_template() -> Dict[str, Any]:
"""Generate input template."""
return {"dpc": {"value": 0, "options": "1=Simple demographic, 2=Behavioral/financial, 3=Sensitive, 4=Special category"},
"ei": {"value": 0, "options": "0.25=Negligible, 0.5=Limited, 0.75=Significant, 1.0=Maximum"},
"confidentiality_loss": 0, "integrity_loss": 0, "availability_loss": 0,
"malicious_intent": False, "t0": "YYYY-MM-DDTHH:MM:SS"}
def format_human(
dpc: int, ei: float, cb: float, cb_components: Dict[str, float],
se: float, verdict: str, description: str,
obligations: List[Dict[str, str]], countdown: Optional[Dict[str, Any]],
) -> str:
"""Format results for human-readable output."""
lines: List[str] = ["=" * 65, "DATA BREACH SEVERITY ASSESSMENT", "=" * 65, "",
f" DPC: {dpc} ({DPC_LABELS.get(dpc, '?')}) | EI: {ei} ({EI_LABELS.get(ei, '?')})", ""]
lines.append(" CB components: " + ", ".join(f"{k.replace('_',' ').title()}: {v}" for k, v in cb_components.items()))
lines.append(f"\n SE = ({dpc} x {ei}) + {round(cb, 2)} = {se}")
lines.append(f"\n VERDICT: {verdict}\n {description}\n")
if countdown and "error" not in countdown:
lines.append(f"DEADLINES (T0: {countdown['t0']}, elapsed: {countdown['elapsed_hours']}h):")
for name, dl in countdown["deadlines"].items():
icon = "!!!" if dl["status"] == "EXPIRED" else ">>>" if dl["status"] == "URGENT" else " "
lines.append(f" {icon} {name}: {dl['remaining_hours']}h [{dl['status']}]")
lines.append("")
lines.append("NOTIFICATION OBLIGATIONS:")
for ob in obligations:
lines.append(f" [{ob['regulation']}] {ob['action']} | Deadline: {ob['deadline']} | {ob['required']}")
lines.append("=" * 65)
return "\n".join(lines)
def format_json_output(
dpc: int, ei: float, cb: float, cb_components: Dict[str, float],
se: float, verdict: str, description: str,
obligations: List[Dict[str, str]],
countdown: Optional[Dict[str, Any]],
) -> Dict[str, Any]:
"""Format results as JSON."""
result: Dict[str, Any] = {
"calculated": datetime.now().isoformat(),
"parameters": {
"dpc": {"value": dpc, "label": DPC_LABELS.get(dpc, "Unknown")},
"ei": {"value": ei, "label": EI_LABELS.get(ei, "Unknown")},
"cb": {"value": round(cb, 2), "components": cb_components},
},
"formula": f"SE = ({dpc} x {ei}) + {round(cb, 2)} = {se}",
"severity_score": se,
"verdict": verdict,
"description": description,
"notification_obligations": obligations,
}
if countdown:
result["countdown"] = countdown
return result
def main() -> None:
parser = argparse.ArgumentParser(
description="Breach Severity Calculator — ENISA methodology"
)
parser.add_argument("--dpc", type=int, choices=[1, 2, 3, 4],
help="Data Processing Context (1-4)")
parser.add_argument("--ei", type=float,
help="Ease of Identification (0.25, 0.5, 0.75, 1.0)")
parser.add_argument("--confidentiality", type=float, default=0.0,
help="Confidentiality loss (0, 0.25, 0.5)")
parser.add_argument("--integrity", type=float, default=0.0,
help="Integrity loss (0, 0.25, 0.5)")
parser.add_argument("--availability", type=float, default=0.0,
help="Availability loss (0, 0.25, 0.5)")
parser.add_argument("--malicious", action="store_true",
help="Breach involved malicious intent")
parser.add_argument("--t0", type=str,
help="T0 timestamp (ISO format) for deadline calculation")
parser.add_argument("--template", action="store_true",
help="Generate input template")
parser.add_argument("--json", action="store_true",
help="Output in JSON format")
args = parser.parse_args()
if args.template:
print(json.dumps(generate_template(), indent=2))
return
if args.dpc is None or args.ei is None:
parser.error("--dpc and --ei are required (or use --template)")
# Validate EI
valid_ei = [0.25, 0.5, 0.75, 1.0]
if args.ei not in valid_ei:
print(f"Error: --ei must be one of {valid_ei}", file=sys.stderr)
sys.exit(1)
# Validate loss values
valid_loss = [0.0, 0.25, 0.5]
for name, val in [("confidentiality", args.confidentiality),
("integrity", args.integrity),
("availability", args.availability)]:
if val not in valid_loss:
print(f"Error: --{name} must be one of {valid_loss}", file=sys.stderr)
sys.exit(1)
# Calculate
cb, cb_components = calculate_cb(
args.confidentiality, args.integrity, args.availability, args.malicious
)
se, verdict, description = calculate_severity(args.dpc, args.ei, cb)
obligations = get_notification_obligations(verdict)
countdown = None
if args.t0:
countdown = calculate_countdown(args.t0)
if args.json:
result = format_json_output(
args.dpc, args.ei, cb, cb_components,
se, verdict, description, obligations, countdown
)
print(json.dumps(result, indent=2))
else:
output = format_human(
args.dpc, args.ei, cb, cb_components,
se, verdict, description, obligations, countdown
)
print(output)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Breach Timeline Tracker
Tracks data breach response timeline from T0 (moment of awareness).
Records events, calculates time remaining for regulatory deadlines,
and generates status dashboards.
Usage:
python breach_timeline_tracker.py init --breach-id "BR-2026-001" --t0 "2026-04-10T08:00:00" --description "Unauthorized access"
python breach_timeline_tracker.py event --timeline breach.json --action "Containment team activated" --category containment
python breach_timeline_tracker.py status --timeline breach.json
python breach_timeline_tracker.py deadlines --timeline breach.json --json
"""
import argparse
import json
import sys
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any, Dict, List, Optional
DEADLINE_DEFINITIONS: Dict[str, Dict[str, Any]] = {
"gdpr_72h_sa": {"label": "GDPR Art. 33 — SA Notification", "hours": 72, "description": "Notify SA of personal data breach"},
"nis2_24h_ew": {"label": "NIS2 Art. 23 — Early Warning", "hours": 24, "description": "Submit early warning to CSIRT"},
"nis2_72h": {"label": "NIS2 Art. 23 — Full Notification", "hours": 72, "description": "Full incident notification to CSIRT"},
"pci_24h": {"label": "PCI DSS — Card Brand Notification", "hours": 24, "description": "Notify card brands"},
"dpa_24h": {"label": "DPA Contractual — 24h Processor", "hours": 24, "description": "Processor notifies controller (24h)"},
"dpa_48h": {"label": "DPA Contractual — 48h Processor", "hours": 48, "description": "Processor notifies controller (48h)"},
}
VALID_CATEGORIES: List[str] = ["detection", "containment", "assessment", "notification", "remediation", "communication", "documentation", "other"]
RESPONSE_CHECKLIST: List[Dict[str, str]] = [
{"id": "C1", "action": "Breach detected and confirmed", "cat": "detection"},
{"id": "C2", "action": "Incident response team activated", "cat": "containment"},
{"id": "C3", "action": "Containment measures applied", "cat": "containment"},
{"id": "C4", "action": "Scope and impact assessed", "cat": "assessment"},
{"id": "C5", "action": "Severity score calculated", "cat": "assessment"},
{"id": "C6", "action": "Notification obligations determined", "cat": "assessment"},
{"id": "C7", "action": "Legal counsel engaged", "cat": "assessment"},
{"id": "C8", "action": "SA notified (if required)", "cat": "notification"},
{"id": "C9", "action": "Data subjects notified (if required)", "cat": "notification"},
{"id": "C10", "action": "Root cause analysis completed", "cat": "remediation"},
{"id": "C11", "action": "Remediation implemented", "cat": "remediation"},
{"id": "C12", "action": "Breach register updated", "cat": "documentation"},
{"id": "C13", "action": "Post-incident review conducted", "cat": "documentation"},
{"id": "C14", "action": "Lessons learned documented", "cat": "documentation"},
]
def load_timeline(filepath: str) -> Dict[str, Any]:
"""Load timeline from file."""
path = Path(filepath)
if not path.exists():
print(f"Error: Timeline file not found: {filepath}", file=sys.stderr)
sys.exit(1)
try:
with open(path, "r") as f:
return json.load(f)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON: {e}", file=sys.stderr)
sys.exit(1)
def save_timeline(filepath: str, timeline: Dict[str, Any]) -> None:
"""Save timeline to file."""
timeline["last_updated"] = datetime.now().isoformat()
with open(filepath, "w") as f:
json.dump(timeline, f, indent=2)
def calculate_deadlines(t0_str: str) -> Dict[str, Dict[str, Any]]:
"""Calculate all deadline statuses from T0."""
try:
t0 = datetime.fromisoformat(t0_str)
except ValueError:
return {}
now = datetime.now()
result: Dict[str, Dict[str, Any]] = {}
for dl_id, dl_def in DEADLINE_DEFINITIONS.items():
deadline_time = t0 + timedelta(hours=dl_def["hours"])
remaining = deadline_time - now
remaining_hours = remaining.total_seconds() / 3600
if remaining_hours <= 0:
status = "EXPIRED"
elif remaining_hours <= 6:
status = "CRITICAL"
elif remaining_hours <= 12:
status = "URGENT"
elif remaining_hours <= 24:
status = "WARNING"
else:
status = "OK"
result[dl_id] = {
"label": dl_def["label"],
"description": dl_def["description"],
"deadline": deadline_time.isoformat(),
"remaining_hours": round(remaining_hours, 1),
"status": status,
"expired": remaining_hours <= 0,
}
return result
def cmd_init(args: argparse.Namespace) -> None:
"""Initialize a new breach timeline."""
try:
t0 = datetime.fromisoformat(args.t0)
except ValueError:
print(f"Error: Invalid T0 format '{args.t0}'. Use ISO: YYYY-MM-DDTHH:MM:SS", file=sys.stderr)
sys.exit(1)
timeline: Dict[str, Any] = {
"breach_id": args.breach_id,
"description": args.description,
"t0": args.t0,
"status": "active",
"severity_score": None,
"severity_verdict": None,
"created": datetime.now().isoformat(),
"last_updated": datetime.now().isoformat(),
"events": [
{
"timestamp": datetime.now().isoformat(),
"action": "Breach timeline initialized",
"category": "detection",
"hours_from_t0": round((datetime.now() - t0).total_seconds() / 3600, 1),
}
],
"completed_actions": [],
"notifications_sent": [],
}
output = args.output or f"breach_{args.breach_id.lower().replace('-', '_')}.json"
save_timeline(output, timeline)
print(f"Breach timeline initialized: {output}")
print(f" Breach ID: {args.breach_id}")
print(f" T0: {args.t0}")
print(f" Description: {args.description}")
# Show immediate deadlines
deadlines = calculate_deadlines(args.t0)
urgent = [dl for dl in deadlines.values() if dl["status"] in ("CRITICAL", "URGENT", "EXPIRED")]
if urgent:
print("\n URGENT DEADLINES:")
for dl in urgent:
print(f" [{dl['status']}] {dl['label']}: {dl['remaining_hours']}h remaining")
def cmd_event(args: argparse.Namespace) -> None:
"""Record an event in the timeline."""
if args.category not in VALID_CATEGORIES:
print(f"Error: Invalid category '{args.category}'", file=sys.stderr)
print(f"Valid: {', '.join(VALID_CATEGORIES)}", file=sys.stderr)
sys.exit(1)
timeline = load_timeline(args.timeline)
try:
t0 = datetime.fromisoformat(timeline["t0"])
except (ValueError, KeyError):
t0 = datetime.now()
now = datetime.now()
hours_from_t0 = round((now - t0).total_seconds() / 3600, 1)
event: Dict[str, Any] = {
"timestamp": now.isoformat(),
"action": args.action,
"category": args.category,
"hours_from_t0": hours_from_t0,
}
timeline["events"].append(event)
# Auto-complete matching checklist items
for item in RESPONSE_CHECKLIST:
if item["cat"] == args.category and item["id"] not in timeline.get("completed_actions", []):
action_lower = args.action.lower()
item_lower = item["action"].lower()
# Simple keyword match
keywords = item_lower.split()
matches = sum(1 for kw in keywords if kw in action_lower)
if matches >= len(keywords) * 0.4:
timeline.setdefault("completed_actions", []).append(item["id"])
save_timeline(args.timeline, timeline)
if args.json:
print(json.dumps(event, indent=2))
else:
print(f"Event recorded at T0+{hours_from_t0}h: {args.action}")
def cmd_status(args: argparse.Namespace) -> None:
"""Display status dashboard."""
timeline = load_timeline(args.timeline)
events = timeline.get("events", [])
t0_str = timeline.get("t0", "")
try:
t0 = datetime.fromisoformat(t0_str)
elapsed_hours = round((datetime.now() - t0).total_seconds() / 3600, 1)
except ValueError:
elapsed_hours = 0
deadlines = calculate_deadlines(t0_str)
completed = set(timeline.get("completed_actions", []))
if args.json:
result = {
"breach_id": timeline.get("breach_id", "Unknown"),
"status": timeline.get("status", "active"),
"t0": t0_str,
"elapsed_hours": elapsed_hours,
"severity_score": timeline.get("severity_score"),
"severity_verdict": timeline.get("severity_verdict"),
"events_count": len(events),
"completed_actions": list(completed),
"pending_actions": [c["id"] for c in RESPONSE_CHECKLIST if c["id"] not in completed],
"deadlines": deadlines,
}
print(json.dumps(result, indent=2))
return
lines: List[str] = ["=" * 60, "BREACH RESPONSE STATUS DASHBOARD", "=" * 60, ""]
lines.append(f" Breach: {timeline.get('breach_id', 'Unknown')} | Status: {timeline.get('status', 'active').upper()} | Elapsed: {elapsed_hours}h")
lines.append(f" T0: {t0_str} | Severity: {timeline.get('severity_verdict', 'N/A')}\n")
lines.append("DEADLINES:")
for dl in deadlines.values():
icon = {"EXPIRED": "!!!", "CRITICAL": "!!!", "URGENT": ">>>", "WARNING": " > "}.get(dl["status"], " ")
lines.append(f" {icon} {dl['label']}: {dl['remaining_hours']}h [{dl['status']}]")
lines.append("\nCHECKLIST:")
for item in RESPONSE_CHECKLIST:
done = "[X]" if item["id"] in completed else "[ ]"
lines.append(f" {done} {item['id']}: {item['action']}")
lines.append(f" Progress: {len(completed)}/{len(RESPONSE_CHECKLIST)}\n")
lines.append("RECENT EVENTS:")
for evt in list(reversed(events))[:10]:
lines.append(f" T0+{evt.get('hours_from_t0', 0)}h [{evt.get('category', 'other')}] {evt.get('action', '')}")
lines.append("=" * 60)
print("\n".join(lines))
def cmd_deadlines(args: argparse.Namespace) -> None:
"""Check and display deadline status."""
timeline = load_timeline(args.timeline)
t0_str = timeline.get("t0", "")
deadlines = calculate_deadlines(t0_str)
if args.json:
print(json.dumps({
"breach_id": timeline.get("breach_id", "Unknown"),
"t0": t0_str,
"deadlines": deadlines,
}, indent=2))
return
print(f"Breach: {timeline.get('breach_id', 'Unknown')}")
print(f"T0: {t0_str}")
print()
for dl in sorted(deadlines.values(), key=lambda x: x["remaining_hours"]):
icon = "!!!" if dl["expired"] else ">>>" if dl["status"] in ("CRITICAL", "URGENT") else " "
print(f" {icon} {dl['label']}: {dl['remaining_hours']}h [{dl['status']}]")
def main() -> None:
parser = argparse.ArgumentParser(description="Breach Timeline Tracker")
sub = parser.add_subparsers(dest="command")
p = sub.add_parser("init")
p.add_argument("--breach-id", required=True); p.add_argument("--t0", required=True)
p.add_argument("--description", required=True); p.add_argument("--output", type=str)
p.add_argument("--json", action="store_true")
p = sub.add_parser("event")
p.add_argument("--timeline", required=True); p.add_argument("--action", required=True)
p.add_argument("--category", required=True); p.add_argument("--json", action="store_true")
for name in ["status", "deadlines"]:
p = sub.add_parser(name)
p.add_argument("--timeline", required=True); p.add_argument("--json", action="store_true")
args = parser.parse_args()
if not args.command:
parser.print_help(); sys.exit(1)
cmds = {"init": cmd_init, "event": cmd_event, "status": cmd_status, "deadlines": cmd_deadlines}
try:
cmds[args.command](args)
except KeyError:
parser.print_help(); sys.exit(1)
except Exception as e:
print(f"Error: {e}", file=sys.stderr); sys.exit(1)
if __name__ == "__main__":
main()
Related skills
FAQ
How does it score breach severity?
It uses the ENISA formula SE = (DPC x EI) + CB and returns a LOW/MEDIUM/HIGH/VERY HIGH verdict with notification obligations.
Which regulations does it cover?
GDPR Art. 33/34, CCPA, HIPAA, NIS2, PCI DSS, and state breach-notification rules, plus AI Act Art. 62 serious-incident reporting.