Signet Forge 0.1.1
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 <stdexcept>
35#include <cstdint>
36#include <string>
37#include <unordered_map>
38#include <vector>
39
40namespace signet::forge {
41
42// ---------------------------------------------------------------------------
43// Enumerations
44// ---------------------------------------------------------------------------
45
47enum class IncidentPhase : int32_t {
48 PREPARATION = 0,
49 DETECTION = 1,
50 CONTAINMENT = 2,
51 ERADICATION = 3,
52 RECOVERY = 4,
54};
55
57enum class IncidentSeverity : int32_t {
58 P4_LOW = 0,
59 P3_MEDIUM = 1,
60 P2_HIGH = 2,
61 P1_CRITICAL = 3
62};
63
65enum class EscalationLevel : int32_t {
66 L1_OPERATIONS = 0,
67 L2_ENGINEERING = 1,
68 L3_MANAGEMENT = 2,
69 L4_REGULATORY = 3
70};
71
73enum class NotificationChannel : int32_t {
74 INTERNAL_LOG = 0,
75 EMAIL = 1,
76 PAGER = 2,
77 REGULATORY = 3
78};
79
80// ---------------------------------------------------------------------------
81// PlaybookStep
82// ---------------------------------------------------------------------------
83
97
98// ---------------------------------------------------------------------------
99// IncidentPlaybook
100// ---------------------------------------------------------------------------
101
104 std::string playbook_id;
105 std::string incident_type;
106 std::string version;
108 std::vector<PlaybookStep> steps;
109 std::vector<std::string> regulatory_references;
110
112 [[nodiscard]] size_t step_count() const { return steps.size(); }
113
115 [[nodiscard]] std::vector<PlaybookStep> steps_for_phase(IncidentPhase phase) const {
116 std::vector<PlaybookStep> result;
117 for (const auto& s : steps) {
118 if (s.phase == phase) result.push_back(s);
119 }
120 return result;
121 }
122};
123
124// ---------------------------------------------------------------------------
125// PlaybookRegistry
126// ---------------------------------------------------------------------------
127
130public:
132 auto gate = commercial::require_feature("PlaybookRegistry");
133 if (!gate) throw std::runtime_error(gate.error().message);
134 }
135
138 playbooks_[pb.incident_type] = pb;
139 }
140
142 [[nodiscard]] expected<IncidentPlaybook> lookup(const std::string& incident_type) const {
143 auto it = playbooks_.find(incident_type);
144 if (it == playbooks_.end()) {
146 "No playbook registered for incident type: " + incident_type};
147 }
148 return it->second;
149 }
150
152 [[nodiscard]] std::vector<std::string> incident_types() const {
153 std::vector<std::string> types;
154 types.reserve(playbooks_.size());
155 for (const auto& [k, _] : playbooks_) types.push_back(k);
156 return types;
157 }
158
160 [[nodiscard]] size_t size() const { return playbooks_.size(); }
161
163 [[nodiscard]] static PlaybookRegistry financial_defaults() {
165 reg.register_playbook(key_compromise_playbook());
166 reg.register_playbook(data_breach_playbook());
167 reg.register_playbook(service_outage_playbook());
168 return reg;
169 }
170
171private:
172 // --- Default playbooks ---
173
174 static IncidentPlaybook key_compromise_playbook() {
176 pb.playbook_id = "PB-CRYPTO-001";
177 pb.incident_type = "key_compromise";
178 pb.version = "1.0.0";
181 "NIST SP 800-57 Part 2 Rev. 1 §6.1",
182 "DORA Art.10(1)", "PCI-DSS Req. 3.6.8"
183 };
184
185 pb.steps = {
186 {"KC-01", IncidentPhase::DETECTION,
187 "Detect key compromise via anomaly detection or external report",
188 "Security Operations", EscalationLevel::L2_ENGINEERING,
190 {"Verify alert authenticity", "Identify affected key IDs"}, false},
191
193 "Revoke compromised keys in KMS and block further use",
194 "Crypto Engineering", EscalationLevel::L2_ENGINEERING,
196 {"Revoke KEK in KMS", "Invalidate cached DEKs",
197 "Enable emergency key rotation"}, true},
198
200 "Notify CISO and legal team; assess regulatory notification obligation",
203 {"Assess GDPR Art.33 72h window", "Assess DORA Art.19(1) notification"}, true},
204
206 "Rotate all affected DEKs and re-encrypt impacted data",
207 "Crypto Engineering", EscalationLevel::L2_ENGINEERING,
209 {"Generate new DEKs via KMS", "Re-encrypt affected Parquet files",
210 "Verify re-encryption with test reads"}, true},
211
212 {"KC-05", IncidentPhase::RECOVERY,
213 "Verify data integrity and restore normal key rotation schedule",
214 "Engineering", EscalationLevel::L1_OPERATIONS,
216 {"Verify hash chain integrity", "Resume automated key rotation",
217 "Update key inventory"}, false},
218
220 "Conduct post-incident review and update threat model (DORA Art.13)",
221 "Security Team", EscalationLevel::L3_MANAGEMENT,
223 {"Root cause analysis", "Update threat model D-12",
224 "Document timeline for regulatory record"}, true},
225 };
226 return pb;
227 }
228
229 static IncidentPlaybook data_breach_playbook() {
230 IncidentPlaybook pb;
231 pb.playbook_id = "PB-DATA-001";
232 pb.incident_type = "data_breach";
233 pb.version = "1.0.0";
234 pb.min_severity = IncidentSeverity::P1_CRITICAL;
235 pb.regulatory_references = {
236 "GDPR Art.33/34", "DORA Art.19", "EU AI Act Art.62"
237 };
238
239 pb.steps = {
240 {"DB-01", IncidentPhase::DETECTION,
241 "Identify scope of data breach — affected records, data types, time window",
242 "Security Operations", EscalationLevel::L2_ENGINEERING,
244 {"Identify affected data classification levels (G-9)",
245 "Count affected data subjects", "Determine attack vector"}, false},
246
248 "Isolate affected systems and preserve forensic evidence",
249 "Engineering", EscalationLevel::L2_ENGINEERING,
251 {"Block attacker access", "Snapshot affected systems",
252 "Preserve audit chain logs"}, true},
253
255 "Notify DPO within 24h; prepare GDPR Art.33 notification (72h deadline)",
256 "DPO / Legal", EscalationLevel::L3_MANAGEMENT,
258 {"Draft supervisory authority notification",
259 "Assess need for Art.34 data subject notification"}, true},
260
262 "Patch vulnerability, invoke crypto-shredding (G-1) if applicable",
263 "Engineering", EscalationLevel::L2_ENGINEERING,
265 {"Apply security patch", "Crypto-shred affected key material",
266 "Reset affected credentials"}, true},
267
268 {"DB-05", IncidentPhase::RECOVERY,
269 "Verify remediation, restore services, monitor for reoccurrence",
270 "Operations", EscalationLevel::L1_OPERATIONS,
272 {"Verify breach vector is closed", "Resume normal monitoring",
273 "Update anomaly detection rules"}, false},
274
276 "Post-incident review, update ROPA (G-3), notify regulator of completion",
277 "Security Team", EscalationLevel::L4_REGULATORY,
279 {"Root cause analysis", "Update ROPA records",
280 "File final regulatory report"}, true},
281 };
282 return pb;
283 }
284
285 static IncidentPlaybook service_outage_playbook() {
286 IncidentPlaybook pb;
287 pb.playbook_id = "PB-SVC-001";
288 pb.incident_type = "service_outage";
289 pb.version = "1.0.0";
290 pb.min_severity = IncidentSeverity::P2_HIGH;
291 pb.regulatory_references = {"DORA Art.11", "DORA Art.10(1)"};
292
293 pb.steps = {
294 {"SO-01", IncidentPhase::DETECTION,
295 "Detect service degradation via monitoring and alerting",
296 "Operations", EscalationLevel::L1_OPERATIONS,
298 {"Verify monitoring alerts", "Check WAL ingestion pipeline"}, false},
299
301 "Activate recovery procedures (DORA Art.11)",
302 "Engineering", EscalationLevel::L2_ENGINEERING,
304 {"Failover to backup systems", "Enable WAL recovery mode"}, false},
305
306 {"SO-03", IncidentPhase::RECOVERY,
307 "Restore service and verify data integrity",
308 "Engineering", EscalationLevel::L2_ENGINEERING,
310 {"Verify hash chain integrity", "Confirm no data loss",
311 "Resume normal operations"}, true},
312
314 "Update resilience testing scenarios (D-2)",
315 "Engineering", EscalationLevel::L1_OPERATIONS,
317 {"Document root cause", "Update resilience test cases"}, false},
318 };
319 return pb;
320 }
321
322 std::unordered_map<std::string, IncidentPlaybook> playbooks_;
323};
324
325// ---------------------------------------------------------------------------
326// IncidentResponseTracker
327// ---------------------------------------------------------------------------
328
334public:
336 struct StepRecord {
337 std::string step_id;
338 std::string completed_by;
339 int64_t started_at_ns = 0;
340 int64_t completed_at_ns = 0;
341 bool sla_met = true;
342 std::string notes;
343 };
344
348 : incident_id_(incident_id), playbook_(playbook) {
349 auto gate = commercial::require_feature("IncidentResponseTracker");
350 if (!gate) throw std::runtime_error(gate.error().message);
351 }
352
355 const std::string& step_id,
356 const std::string& completed_by,
357 int64_t started_at_ns,
358 int64_t completed_at_ns,
359 const std::string& notes = "")
360 {
361 // Verify step exists in playbook
362 bool found = false;
363 int64_t sla_ns = 0;
364 for (const auto& s : playbook_.steps) {
365 if (s.step_id == step_id) {
366 found = true;
367 sla_ns = s.sla_seconds * INT64_C(1000000000);
368 break;
369 }
370 }
371 if (!found) {
373 "Step '" + step_id + "' not found in playbook " +
374 playbook_.playbook_id};
375 }
376
377 if (completed_at_ns < started_at_ns) {
379 "completed_at must be >= started_at"};
380 }
381
382 StepRecord rec;
383 rec.step_id = step_id;
384 rec.completed_by = completed_by;
385 rec.started_at_ns = started_at_ns;
386 rec.completed_at_ns = completed_at_ns;
387 rec.sla_met = (sla_ns == 0) ||
388 ((completed_at_ns - started_at_ns) <= sla_ns);
389 rec.notes = notes;
390 completed_.push_back(std::move(rec));
391 return {};
392 }
393
395 [[nodiscard]] const std::vector<StepRecord>& completed_steps() const {
396 return completed_;
397 }
398
400 [[nodiscard]] bool all_steps_complete() const {
401 return completed_.size() == playbook_.steps.size();
402 }
403
405 [[nodiscard]] std::vector<std::string> remaining_steps() const {
406 std::vector<std::string> remaining;
407 for (const auto& s : playbook_.steps) {
408 bool done = false;
409 for (const auto& c : completed_) {
410 if (c.step_id == s.step_id) { done = true; break; }
411 }
412 if (!done) remaining.push_back(s.step_id);
413 }
414 return remaining;
415 }
416
418 [[nodiscard]] int32_t sla_breach_count() const {
419 int32_t count = 0;
420 for (const auto& c : completed_) {
421 if (!c.sla_met) ++count;
422 }
423 return count;
424 }
425
427 [[nodiscard]] const std::string& incident_id() const { return incident_id_; }
428
430 [[nodiscard]] const IncidentPlaybook& playbook() const { return playbook_; }
431
432private:
433 std::string incident_id_;
434 IncidentPlaybook playbook_;
435 std::vector<StepRecord> completed_;
436};
437
438} // 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:143
@ 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:99
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.