Signet Forge 0.1.0
C++20 Parquet library with AI-native extensions
DEMO
Loading...
Searching...
No Matches
incident_response.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright 2026 Johnson Ogundeji
3// See LICENSE_COMMERCIAL for full terms.
4#pragma once
5
6#if !defined(SIGNET_ENABLE_COMMERCIAL) || !SIGNET_ENABLE_COMMERCIAL
7#error "signet/ai/incident_response.hpp requires SIGNET_ENABLE_COMMERCIAL=ON (AGPL-3.0 commercial tier). See LICENSE_COMMERCIAL."
8#endif
9
10// ---------------------------------------------------------------------------
11// incident_response.hpp -- Incident Response Playbook Framework
12//
13// Gap R-19: Structured incident response playbook for regulatory compliance.
14//
15// Provides a machine-readable incident response framework conforming to:
16// - DORA Art.10/15/19: ICT incident management lifecycle
17// - NIST SP 800-61 Rev. 2: Computer Security Incident Handling Guide
18// - ISO 27035: Information security incident management
19// - EU AI Act Art.62: Serious incident reporting
20//
21// Components:
22// - IncidentPhase / IncidentSeverity / EscalationLevel enums
23// - PlaybookStep: individual response action with SLA
24// - IncidentPlaybook: ordered sequence of response steps
25// - PlaybookRegistry: lookup playbooks by incident type
26// - IncidentResponseTracker: tracks execution of playbook steps
27//
28// Header-only. Part of the signet::forge AI module.
29// ---------------------------------------------------------------------------
30
31#include "signet/error.hpp"
32
33#include <chrono>
34#include <cstdint>
35#include <string>
36#include <unordered_map>
37#include <vector>
38
39namespace signet::forge {
40
41// ---------------------------------------------------------------------------
42// Enumerations
43// ---------------------------------------------------------------------------
44
46enum class IncidentPhase : int32_t {
47 PREPARATION = 0,
48 DETECTION = 1,
49 CONTAINMENT = 2,
50 ERADICATION = 3,
51 RECOVERY = 4,
53};
54
56enum class IncidentSeverity : int32_t {
57 P4_LOW = 0,
58 P3_MEDIUM = 1,
59 P2_HIGH = 2,
60 P1_CRITICAL = 3
61};
62
64enum class EscalationLevel : int32_t {
65 L1_OPERATIONS = 0,
66 L2_ENGINEERING = 1,
67 L3_MANAGEMENT = 2,
68 L4_REGULATORY = 3
69};
70
72enum class NotificationChannel : int32_t {
73 INTERNAL_LOG = 0,
74 EMAIL = 1,
75 PAGER = 2,
76 REGULATORY = 3
77};
78
79// ---------------------------------------------------------------------------
80// PlaybookStep
81// ---------------------------------------------------------------------------
82
96
97// ---------------------------------------------------------------------------
98// IncidentPlaybook
99// ---------------------------------------------------------------------------
100
103 std::string playbook_id;
104 std::string incident_type;
105 std::string version;
107 std::vector<PlaybookStep> steps;
108 std::vector<std::string> regulatory_references;
109
111 [[nodiscard]] size_t step_count() const { return steps.size(); }
112
114 [[nodiscard]] std::vector<PlaybookStep> steps_for_phase(IncidentPhase phase) const {
115 std::vector<PlaybookStep> result;
116 for (const auto& s : steps) {
117 if (s.phase == phase) result.push_back(s);
118 }
119 return result;
120 }
121};
122
123// ---------------------------------------------------------------------------
124// PlaybookRegistry
125// ---------------------------------------------------------------------------
126
129public:
131 (void)commercial::require_feature("PlaybookRegistry");
132 }
133
136 playbooks_[pb.incident_type] = pb;
137 }
138
140 [[nodiscard]] expected<IncidentPlaybook> lookup(const std::string& incident_type) const {
141 auto it = playbooks_.find(incident_type);
142 if (it == playbooks_.end()) {
144 "No playbook registered for incident type: " + incident_type};
145 }
146 return it->second;
147 }
148
150 [[nodiscard]] std::vector<std::string> incident_types() const {
151 std::vector<std::string> types;
152 types.reserve(playbooks_.size());
153 for (const auto& [k, _] : playbooks_) types.push_back(k);
154 return types;
155 }
156
158 [[nodiscard]] size_t size() const { return playbooks_.size(); }
159
161 [[nodiscard]] static PlaybookRegistry financial_defaults() {
163 reg.register_playbook(key_compromise_playbook());
164 reg.register_playbook(data_breach_playbook());
165 reg.register_playbook(service_outage_playbook());
166 return reg;
167 }
168
169private:
170 // --- Default playbooks ---
171
172 static IncidentPlaybook key_compromise_playbook() {
174 pb.playbook_id = "PB-CRYPTO-001";
175 pb.incident_type = "key_compromise";
176 pb.version = "1.0.0";
179 "NIST SP 800-57 Part 2 Rev. 1 §6.1",
180 "DORA Art.10(1)", "PCI-DSS Req. 3.6.8"
181 };
182
183 pb.steps = {
184 {"KC-01", IncidentPhase::DETECTION,
185 "Detect key compromise via anomaly detection or external report",
186 "Security Operations", EscalationLevel::L2_ENGINEERING,
188 {"Verify alert authenticity", "Identify affected key IDs"}, false},
189
191 "Revoke compromised keys in KMS and block further use",
192 "Crypto Engineering", EscalationLevel::L2_ENGINEERING,
194 {"Revoke KEK in KMS", "Invalidate cached DEKs",
195 "Enable emergency key rotation"}, true},
196
198 "Notify CISO and legal team; assess regulatory notification obligation",
201 {"Assess GDPR Art.33 72h window", "Assess DORA Art.19(1) notification"}, true},
202
204 "Rotate all affected DEKs and re-encrypt impacted data",
205 "Crypto Engineering", EscalationLevel::L2_ENGINEERING,
207 {"Generate new DEKs via KMS", "Re-encrypt affected Parquet files",
208 "Verify re-encryption with test reads"}, true},
209
210 {"KC-05", IncidentPhase::RECOVERY,
211 "Verify data integrity and restore normal key rotation schedule",
212 "Engineering", EscalationLevel::L1_OPERATIONS,
214 {"Verify hash chain integrity", "Resume automated key rotation",
215 "Update key inventory"}, false},
216
218 "Conduct post-incident review and update threat model (DORA Art.13)",
219 "Security Team", EscalationLevel::L3_MANAGEMENT,
221 {"Root cause analysis", "Update threat model D-12",
222 "Document timeline for regulatory record"}, true},
223 };
224 return pb;
225 }
226
227 static IncidentPlaybook data_breach_playbook() {
228 IncidentPlaybook pb;
229 pb.playbook_id = "PB-DATA-001";
230 pb.incident_type = "data_breach";
231 pb.version = "1.0.0";
232 pb.min_severity = IncidentSeverity::P1_CRITICAL;
233 pb.regulatory_references = {
234 "GDPR Art.33/34", "DORA Art.19", "EU AI Act Art.62"
235 };
236
237 pb.steps = {
238 {"DB-01", IncidentPhase::DETECTION,
239 "Identify scope of data breach — affected records, data types, time window",
240 "Security Operations", EscalationLevel::L2_ENGINEERING,
242 {"Identify affected data classification levels (G-9)",
243 "Count affected data subjects", "Determine attack vector"}, false},
244
246 "Isolate affected systems and preserve forensic evidence",
247 "Engineering", EscalationLevel::L2_ENGINEERING,
249 {"Block attacker access", "Snapshot affected systems",
250 "Preserve audit chain logs"}, true},
251
253 "Notify DPO within 24h; prepare GDPR Art.33 notification (72h deadline)",
254 "DPO / Legal", EscalationLevel::L3_MANAGEMENT,
256 {"Draft supervisory authority notification",
257 "Assess need for Art.34 data subject notification"}, true},
258
260 "Patch vulnerability, invoke crypto-shredding (G-1) if applicable",
261 "Engineering", EscalationLevel::L2_ENGINEERING,
263 {"Apply security patch", "Crypto-shred affected key material",
264 "Reset affected credentials"}, true},
265
266 {"DB-05", IncidentPhase::RECOVERY,
267 "Verify remediation, restore services, monitor for reoccurrence",
268 "Operations", EscalationLevel::L1_OPERATIONS,
270 {"Verify breach vector is closed", "Resume normal monitoring",
271 "Update anomaly detection rules"}, false},
272
274 "Post-incident review, update ROPA (G-3), notify regulator of completion",
275 "Security Team", EscalationLevel::L4_REGULATORY,
277 {"Root cause analysis", "Update ROPA records",
278 "File final regulatory report"}, true},
279 };
280 return pb;
281 }
282
283 static IncidentPlaybook service_outage_playbook() {
284 IncidentPlaybook pb;
285 pb.playbook_id = "PB-SVC-001";
286 pb.incident_type = "service_outage";
287 pb.version = "1.0.0";
288 pb.min_severity = IncidentSeverity::P2_HIGH;
289 pb.regulatory_references = {"DORA Art.11", "DORA Art.10(1)"};
290
291 pb.steps = {
292 {"SO-01", IncidentPhase::DETECTION,
293 "Detect service degradation via monitoring and alerting",
294 "Operations", EscalationLevel::L1_OPERATIONS,
296 {"Verify monitoring alerts", "Check WAL ingestion pipeline"}, false},
297
299 "Activate recovery procedures (DORA Art.11)",
300 "Engineering", EscalationLevel::L2_ENGINEERING,
302 {"Failover to backup systems", "Enable WAL recovery mode"}, false},
303
304 {"SO-03", IncidentPhase::RECOVERY,
305 "Restore service and verify data integrity",
306 "Engineering", EscalationLevel::L2_ENGINEERING,
308 {"Verify hash chain integrity", "Confirm no data loss",
309 "Resume normal operations"}, true},
310
312 "Update resilience testing scenarios (D-2)",
313 "Engineering", EscalationLevel::L1_OPERATIONS,
315 {"Document root cause", "Update resilience test cases"}, false},
316 };
317 return pb;
318 }
319
320 std::unordered_map<std::string, IncidentPlaybook> playbooks_;
321};
322
323// ---------------------------------------------------------------------------
324// IncidentResponseTracker
325// ---------------------------------------------------------------------------
326
332public:
334 struct StepRecord {
335 std::string step_id;
336 std::string completed_by;
337 int64_t started_at_ns = 0;
338 int64_t completed_at_ns = 0;
339 bool sla_met = true;
340 std::string notes;
341 };
342
346 : incident_id_(incident_id), playbook_(playbook) {
347 (void)commercial::require_feature("IncidentResponseTracker");
348 }
349
352 const std::string& step_id,
353 const std::string& completed_by,
354 int64_t started_at_ns,
355 int64_t completed_at_ns,
356 const std::string& notes = "")
357 {
358 // Verify step exists in playbook
359 bool found = false;
360 int64_t sla_ns = 0;
361 for (const auto& s : playbook_.steps) {
362 if (s.step_id == step_id) {
363 found = true;
364 sla_ns = s.sla_seconds * INT64_C(1000000000);
365 break;
366 }
367 }
368 if (!found) {
370 "Step '" + step_id + "' not found in playbook " +
371 playbook_.playbook_id};
372 }
373
374 if (completed_at_ns < started_at_ns) {
376 "completed_at must be >= started_at"};
377 }
378
379 StepRecord rec;
380 rec.step_id = step_id;
381 rec.completed_by = completed_by;
382 rec.started_at_ns = started_at_ns;
383 rec.completed_at_ns = completed_at_ns;
384 rec.sla_met = (sla_ns == 0) ||
385 ((completed_at_ns - started_at_ns) <= sla_ns);
386 rec.notes = notes;
387 completed_.push_back(std::move(rec));
388 return {};
389 }
390
392 [[nodiscard]] const std::vector<StepRecord>& completed_steps() const {
393 return completed_;
394 }
395
397 [[nodiscard]] bool all_steps_complete() const {
398 return completed_.size() == playbook_.steps.size();
399 }
400
402 [[nodiscard]] std::vector<std::string> remaining_steps() const {
403 std::vector<std::string> remaining;
404 for (const auto& s : playbook_.steps) {
405 bool done = false;
406 for (const auto& c : completed_) {
407 if (c.step_id == s.step_id) { done = true; break; }
408 }
409 if (!done) remaining.push_back(s.step_id);
410 }
411 return remaining;
412 }
413
415 [[nodiscard]] int32_t sla_breach_count() const {
416 int32_t count = 0;
417 for (const auto& c : completed_) {
418 if (!c.sla_met) ++count;
419 }
420 return count;
421 }
422
424 [[nodiscard]] const std::string& incident_id() const { return incident_id_; }
425
427 [[nodiscard]] const IncidentPlaybook& playbook() const { return playbook_; }
428
429private:
430 std::string incident_id_;
431 IncidentPlaybook playbook_;
432 std::vector<StepRecord> completed_;
433};
434
435} // namespace signet::forge
Tracks execution progress of a playbook during an active incident.
IncidentResponseTracker(const std::string &incident_id, const IncidentPlaybook &playbook)
Initialize tracker for a specific incident and playbook.
expected< void > complete_step(const std::string &step_id, const std::string &completed_by, int64_t started_at_ns, int64_t completed_at_ns, const std::string &notes="")
Record completion of a playbook step.
bool all_steps_complete() const
Check if all playbook steps have been completed.
std::vector< std::string > remaining_steps() const
Get remaining (uncompleted) step IDs.
const IncidentPlaybook & playbook() const
Associated playbook.
int32_t sla_breach_count() const
Count of SLA breaches across completed steps.
const std::string & incident_id() const
Incident identifier.
const std::vector< StepRecord > & completed_steps() const
Get all completed step records.
Registry of incident response playbooks indexed by incident type.
size_t size() const
Number of registered playbooks.
static PlaybookRegistry financial_defaults()
Build a registry with default playbooks for financial/compliance scenarios.
std::vector< std::string > incident_types() const
Get all registered incident types.
expected< IncidentPlaybook > lookup(const std::string &incident_type) const
Look up a playbook by incident type.
void register_playbook(const IncidentPlaybook &pb)
Register a playbook for a specific incident type.
A lightweight result type that holds either a success value of type T or an Error.
Definition error.hpp:145
@ REGULATORY
Regulatory or compliance-driven halt.
EscalationLevel
Escalation hierarchy for incident routing.
@ L1_OPERATIONS
First-line operations team.
@ L2_ENGINEERING
Engineering / DevOps on-call.
@ L4_REGULATORY
Regulatory authority notification (DORA Art.19)
@ L3_MANAGEMENT
Management / CISO notification.
@ INVALID_ARGUMENT
A caller-supplied argument is outside the valid range or violates a precondition.
IncidentPhase
NIST SP 800-61 incident response lifecycle phases.
@ ERADICATION
Remove root cause.
@ LESSONS_LEARNED
Post-incident review (DORA Art.13)
@ RECOVERY
Restore normal operations.
@ DETECTION
Anomaly detection / alert triage.
@ PREPARATION
Pre-incident readiness.
@ CONTAINMENT
Limit blast radius.
NotificationChannel
Notification channel for incident communications.
@ EMAIL
Email to responsible parties.
@ REGULATORY
Formal regulatory notification (DORA Art.19(1))
@ PAGER
PagerDuty / on-call alert.
IncidentSeverity
Incident severity per DORA Art.10(1) classification.
@ P4_LOW
Minor, no customer impact.
@ P1_CRITICAL
Major outage, data loss, regulatory notification required.
@ P2_HIGH
Significant impact, SLA breach risk.
@ P3_MEDIUM
Limited impact, workaround available.
Lightweight error value carrying an ErrorCode and a human-readable message.
Definition error.hpp:101
An ordered sequence of response steps for a specific incident type.
std::vector< std::string > regulatory_references
std::vector< PlaybookStep > steps_for_phase(IncidentPhase phase) const
Get all steps for a specific phase.
std::string version
Playbook version.
std::string incident_type
Category (e.g., "data_breach", "key_compromise")
std::vector< PlaybookStep > steps
std::string playbook_id
Unique playbook identifier.
size_t step_count() const
Total number of steps in this playbook.
Step completion record for audit trail.
A single step in an incident response playbook.
std::vector< std::string > checklist
Sub-items to verify.
int64_t sla_seconds
Maximum time to complete (0 = no SLA)
std::string responsible_role
Who performs this step.
std::string step_id
Unique step identifier.
bool requires_sign_off
Needs explicit sign-off before proceeding.
std::string action
What to do.