mirror of
https://github.com/mblanke/ThreatHunt.git
synced 2026-03-01 14:00:20 -05:00
version 0.4.0
This commit is contained in:
@@ -326,3 +326,221 @@ class Keyword(Base):
|
||||
Index("ix_keywords_theme", "theme_id"),
|
||||
Index("ix_keywords_value", "value"),
|
||||
)
|
||||
|
||||
|
||||
# ── Cases ─────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class Case(Base):
|
||||
"""Incident / investigation case, inspired by TheHive."""
|
||||
__tablename__ = "cases"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(32), primary_key=True, default=_new_id)
|
||||
title: Mapped[str] = mapped_column(String(512), nullable=False)
|
||||
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
severity: Mapped[str] = mapped_column(String(16), default="medium") # info|low|medium|high|critical
|
||||
tlp: Mapped[str] = mapped_column(String(16), default="amber") # white|green|amber|red
|
||||
pap: Mapped[str] = mapped_column(String(16), default="amber") # white|green|amber|red
|
||||
status: Mapped[str] = mapped_column(String(24), default="open") # open|in-progress|resolved|closed
|
||||
priority: Mapped[int] = mapped_column(Integer, default=2) # 1(urgent)..4(low)
|
||||
assignee: Mapped[Optional[str]] = mapped_column(String(128), nullable=True)
|
||||
tags: Mapped[Optional[list]] = mapped_column(JSON, nullable=True)
|
||||
hunt_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(32), ForeignKey("hunts.id"), nullable=True
|
||||
)
|
||||
owner_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(32), ForeignKey("users.id"), nullable=True
|
||||
)
|
||||
mitre_techniques: Mapped[Optional[list]] = mapped_column(JSON, nullable=True)
|
||||
iocs: Mapped[Optional[list]] = mapped_column(JSON, nullable=True) # [{type, value, description}]
|
||||
started_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
resolved_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
|
||||
)
|
||||
|
||||
# relationships
|
||||
tasks: Mapped[list["CaseTask"]] = relationship(
|
||||
back_populates="case", lazy="selectin", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_cases_hunt", "hunt_id"),
|
||||
Index("ix_cases_status", "status"),
|
||||
)
|
||||
|
||||
|
||||
class CaseTask(Base):
|
||||
"""Task within a case (Kanban board item)."""
|
||||
__tablename__ = "case_tasks"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(32), primary_key=True, default=_new_id)
|
||||
case_id: Mapped[str] = mapped_column(
|
||||
String(32), ForeignKey("cases.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
title: Mapped[str] = mapped_column(String(512), nullable=False)
|
||||
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
status: Mapped[str] = mapped_column(String(24), default="todo") # todo|in-progress|done
|
||||
assignee: Mapped[Optional[str]] = mapped_column(String(128), nullable=True)
|
||||
order: Mapped[int] = mapped_column(Integer, default=0)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
|
||||
)
|
||||
|
||||
# relationships
|
||||
case: Mapped["Case"] = relationship(back_populates="tasks")
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_case_tasks_case", "case_id"),
|
||||
)
|
||||
|
||||
|
||||
# ── Activity Log ──────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class ActivityLog(Base):
|
||||
"""Audit trail / activity log for cases and hunts."""
|
||||
__tablename__ = "activity_logs"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
entity_type: Mapped[str] = mapped_column(String(32), nullable=False) # case|hunt|annotation
|
||||
entity_id: Mapped[str] = mapped_column(String(32), nullable=False)
|
||||
action: Mapped[str] = mapped_column(String(64), nullable=False) # created|updated|status_changed|etc
|
||||
details: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
|
||||
user_id: Mapped[Optional[str]] = mapped_column(String(32), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_activity_entity", "entity_type", "entity_id"),
|
||||
)
|
||||
|
||||
|
||||
# ── Alerts ────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class Alert(Base):
|
||||
"""Security alert generated by analyzers or rules."""
|
||||
__tablename__ = "alerts"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(32), primary_key=True, default=_new_id)
|
||||
title: Mapped[str] = mapped_column(String(512), nullable=False)
|
||||
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
severity: Mapped[str] = mapped_column(String(16), default="medium") # critical|high|medium|low|info
|
||||
status: Mapped[str] = mapped_column(String(24), default="new") # new|acknowledged|in-progress|resolved|false-positive
|
||||
analyzer: Mapped[str] = mapped_column(String(64), nullable=False) # which analyzer produced it
|
||||
score: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
evidence: Mapped[Optional[list]] = mapped_column(JSON, nullable=True) # [{row_index, field, value, ...}]
|
||||
mitre_technique: Mapped[Optional[str]] = mapped_column(String(32), nullable=True)
|
||||
tags: Mapped[Optional[list]] = mapped_column(JSON, nullable=True)
|
||||
hunt_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(32), ForeignKey("hunts.id"), nullable=True
|
||||
)
|
||||
dataset_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(32), ForeignKey("datasets.id"), nullable=True
|
||||
)
|
||||
case_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(32), ForeignKey("cases.id"), nullable=True
|
||||
)
|
||||
assignee: Mapped[Optional[str]] = mapped_column(String(128), nullable=True)
|
||||
acknowledged_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
resolved_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_alerts_severity", "severity"),
|
||||
Index("ix_alerts_status", "status"),
|
||||
Index("ix_alerts_hunt", "hunt_id"),
|
||||
Index("ix_alerts_dataset", "dataset_id"),
|
||||
)
|
||||
|
||||
|
||||
class AlertRule(Base):
|
||||
"""User-defined alert rule (triggers analyzers automatically on upload)."""
|
||||
__tablename__ = "alert_rules"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(32), primary_key=True, default=_new_id)
|
||||
name: Mapped[str] = mapped_column(String(256), nullable=False)
|
||||
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
analyzer: Mapped[str] = mapped_column(String(64), nullable=False) # analyzer name
|
||||
config: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True) # analyzer config overrides
|
||||
severity_override: Mapped[Optional[str]] = mapped_column(String(16), nullable=True)
|
||||
enabled: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
hunt_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(32), ForeignKey("hunts.id"), nullable=True
|
||||
) # None = global
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_alert_rules_analyzer", "analyzer"),
|
||||
)
|
||||
|
||||
|
||||
# ── Notebooks ────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class Notebook(Base):
|
||||
"""Investigation notebook — cell-based document for analyst notes and queries."""
|
||||
__tablename__ = "notebooks"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(32), primary_key=True, default=_new_id)
|
||||
title: Mapped[str] = mapped_column(String(512), nullable=False)
|
||||
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
cells: Mapped[Optional[list]] = mapped_column(JSON, nullable=True) # [{id, cell_type, source, output, metadata}]
|
||||
hunt_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(32), ForeignKey("hunts.id"), nullable=True
|
||||
)
|
||||
case_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(32), ForeignKey("cases.id"), nullable=True
|
||||
)
|
||||
owner_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(32), ForeignKey("users.id"), nullable=True
|
||||
)
|
||||
tags: Mapped[Optional[list]] = mapped_column(JSON, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_notebooks_hunt", "hunt_id"),
|
||||
)
|
||||
|
||||
|
||||
# ── Playbook Runs ────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class PlaybookRun(Base):
|
||||
"""Record of a playbook execution (links a template to a hunt/case)."""
|
||||
__tablename__ = "playbook_runs"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(32), primary_key=True, default=_new_id)
|
||||
playbook_name: Mapped[str] = mapped_column(String(256), nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(24), default="in-progress") # in-progress | completed | aborted
|
||||
current_step: Mapped[int] = mapped_column(Integer, default=1)
|
||||
total_steps: Mapped[int] = mapped_column(Integer, default=0)
|
||||
step_results: Mapped[Optional[list]] = mapped_column(JSON, nullable=True) # [{step, status, notes, completed_at}]
|
||||
hunt_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(32), ForeignKey("hunts.id"), nullable=True
|
||||
)
|
||||
case_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(32), ForeignKey("cases.id"), nullable=True
|
||||
)
|
||||
started_by: Mapped[Optional[str]] = mapped_column(String(128), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
|
||||
)
|
||||
completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_playbook_runs_hunt", "hunt_id"),
|
||||
Index("ix_playbook_runs_status", "status"),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user