Incidents Module
Overview
The Incidents module manages the full lifecycle of security incidents in the ThreatOps SOCaaS platform. When alerts are escalated -- either by analysts or by the autonomous SOC engine -- they become incidents that require coordinated investigation, containment, and resolution.
This module tracks MITRE ATT&CK tactics and techniques, affected assets, indicators of compromise (IOCs), assigned analysts, and resolution timelines. It supports the NIST incident response lifecycle: detection, investigation, containment, eradication, resolution, and closure.
What Was Proposed
- Full incident lifecycle management (new, investigating, containment, eradication, resolved, closed)
- MITRE ATT&CK tactic and technique tagging
- Multi-SIEM source tracking
- Analyst assignment and workload distribution
- IOC and affected asset recording
- Incident statistics (total, open, critical counts)
- List view with filtering + detail view per incident
- Response action buttons (contain, escalate, resolve)
What's Built
| Incident CRUD (create, read, update) | ✓ Complete |
| 6-stage lifecycle (new through closed) | ✓ Complete |
| MITRE ATT&CK tactic/technique mapping | ✓ Complete |
| Multi-SIEM source tracking | ✓ Complete |
| Analyst assignment | ✓ Complete |
| IOC and affected asset fields | ✓ Complete |
| Statistics endpoint | ✓ Complete |
| List page with severity/status/tactic filters | ✓ Complete |
| Detail page with response actions | ✓ Complete |
| Auto-set resolved_at/closed_at timestamps | ✓ Complete |
| Demo data fallback when DB unavailable | ✓ Complete |
Architecture
API Router
File: app/routers/incidents.py — Prefix: /api/v1/incidents
GET /api/v1/incidents/ # List incidents (filters: severity, status, mitre_tactic, source_siem)
POST /api/v1/incidents/ # Create a new incident
GET /api/v1/incidents/stats # Statistics (total, open, critical counts)
GET /api/v1/incidents/{incident_id} # Get incident detail
PATCH /api/v1/incidents/{incident_id} # Update incident (auto-sets resolved_at/closed_at on status change)
How It Connects
The incidents router is a standard FastAPI CRUD router scoped by request.state.tenant_id. Every query filters by tenant. When an alert is escalated (via the alerts bulk action), it can be linked to an incident via the Alert.incident_id foreign key.
The PATCH endpoint intelligently handles lifecycle transitions: setting resolved_at when status becomes "resolved" and closed_at when status becomes "closed", but only if those timestamps are not already set.
When the database is unavailable, the router returns 8 realistic demo incidents covering ransomware, data exfiltration, BEC, supply chain, insider threat, C2, credential theft, and zero-day scenarios.
Frontend Pages
List View (src/app/incidents/page.tsx): Fetches from GET /api/v1/incidents/. Shows a filterable table with severity, status, and MITRE tactic dropdowns. Each incident title links to the detail page.
Detail View (src/app/incidents/[id]/page.tsx): Fetches from GET /api/v1/incidents/{id}. Shows a 2/3 + 1/3 column layout with incident details, response actions, MITRE ATT&CK info, and IOCs.
Routing
| Layer | Path |
|---|---|
| /incidents | Frontend list page (Next.js App Router) |
| /incidents/[id] | Frontend detail page (dynamic route) |
| /api/v1/incidents | API prefix (FastAPI router) |
Prerequisites
- Database — PostgreSQL with async SQLAlchemy,
incidentstable - Tenant Middleware — Multi-tenant scoping via
request.state.tenant_id - Users Table — For
assigned_toforeign key reference - Alerts Module — Alerts link to incidents via
incident_idFK
Data Model
Model: app/models/incident.py — Table: incidents (extends TenantScopedBase)
| Field | Type | Description |
|---|---|---|
id | String(36) PK | UUID primary key |
tenant_id | String(36) FK | Tenant scope |
title | String(500) | Incident title (required) |
description | Text | Detailed description |
severity | Enum(IncidentSeverity) | critical / high / medium / low / informational |
status | Enum(IncidentStatus) | new / investigating / containment / eradication / resolved / closed |
mitre_tactic | String(100) | MITRE ATT&CK tactic name |
mitre_technique | String(20) | MITRE technique ID (e.g., T1059.001) |
source_siem | String(50) | Source SIEM platform |
assigned_to | String(36) FK | Assigned analyst (FK to users.id) |
resolution | Text | Resolution description |
affected_assets | JSON | List of affected systems/assets |
iocs | JSON | Indicators of compromise |
acknowledged_at | DateTime | When an analyst acknowledged |
resolved_at | DateTime | Resolution timestamp (auto-set) |
closed_at | DateTime | Closure timestamp (auto-set) |
created_at | DateTime | Creation timestamp |
updated_at | DateTime | Last update timestamp |
UI Layout
List Page (/incidents)
- Header — Shield icon + "Incidents" title + count badge in orange
- Filter Bar — Three dropdown selects: severity (all/critical/high/medium/low), status (all/new/investigating/resolved/closed), and MITRE tactic (dynamically populated from data)
- Table — White rounded card with columns: Severity (color badge), Title (orange link to detail), MITRE Tactic, Source SIEM, Status (colored badge), Assigned To (or "Unassigned" italic), Created timestamp
Detail Page (/incidents/[id])
- Back Link — Arrow icon linking back to
/incidents - Header — Incident title + severity badge + status badge + ID
- Left Column (2/3):
- Details Card — 2x2 grid: Assigned To, Source SIEM, Created, Status
- Response Actions Card — Three action buttons: Contain (orange), Escalate (amber), Resolve (emerald). Each sends a PATCH request to update status.
- Right Column (1/3):
- MITRE ATT&CK Card — Shows tactic name and technique ID in orange monospace
- IOC Card — Displays indicators of compromise (or "No IOCs recorded")