mirror of
https://github.com/FunnyWolf/agentic-soc-platform.git
synced 2026-08-22 13:12:56 +02:00
237 lines
10 KiB
Python
237 lines
10 KiB
Python
from django.db import models
|
|
from django.db.models.functions import Coalesce
|
|
|
|
from apps.common.models import BaseModel
|
|
from apps.common.readable_ids import save_with_readable_id
|
|
|
|
|
|
def value_labeled(choice_class):
|
|
for member in choice_class:
|
|
member._label_ = member.value
|
|
return choice_class
|
|
|
|
|
|
@value_labeled
|
|
class Severity(models.TextChoices):
|
|
UNKNOWN = "Unknown"
|
|
INFORMATIONAL = "Informational"
|
|
LOW = "Low"
|
|
MEDIUM = "Medium"
|
|
HIGH = "High"
|
|
CRITICAL = "Critical"
|
|
|
|
|
|
@value_labeled
|
|
class Confidence(models.TextChoices):
|
|
UNKNOWN = "Unknown"
|
|
LOW = "Low"
|
|
MEDIUM = "Medium"
|
|
HIGH = "High"
|
|
|
|
|
|
@value_labeled
|
|
class Impact(models.TextChoices):
|
|
UNKNOWN = "Unknown"
|
|
LOW = "Low"
|
|
MEDIUM = "Medium"
|
|
HIGH = "High"
|
|
CRITICAL = "Critical"
|
|
|
|
|
|
@value_labeled
|
|
class Disposition(models.TextChoices):
|
|
UNKNOWN = "Unknown"
|
|
ALLOWED = "Allowed"
|
|
BLOCKED = "Blocked"
|
|
QUARANTINED = "Quarantined"
|
|
ISOLATED = "Isolated"
|
|
DELETED = "Deleted"
|
|
DROPPED = "Dropped"
|
|
CUSTOM_ACTION = "Custom Action"
|
|
APPROVED = "Approved"
|
|
RESTORED = "Restored"
|
|
EXONERATED = "Exonerated"
|
|
CORRECTED = "Corrected"
|
|
PARTIALLY_CORRECTED = "Partially Corrected"
|
|
UNCORRECTED = "Uncorrected"
|
|
DELAYED = "Delayed"
|
|
DETECTED = "Detected"
|
|
NO_ACTION = "No Action"
|
|
LOGGED = "Logged"
|
|
TAGGED = "Tagged"
|
|
ALERT = "Alert"
|
|
COUNT = "Count"
|
|
RESET = "Reset"
|
|
CAPTCHA = "Captcha"
|
|
CHALLENGE = "Challenge"
|
|
ACCESS_REVOKED = "Access Revoked"
|
|
REJECTED = "Rejected"
|
|
UNAUTHORIZED = "Unauthorized"
|
|
ERROR = "Error"
|
|
OTHER = "Other"
|
|
|
|
|
|
@value_labeled
|
|
class AlertAction(models.TextChoices):
|
|
UNKNOWN = "Unknown"
|
|
ALLOWED = "Allowed"
|
|
DENIED = "Denied"
|
|
OBSERVED = "Observed"
|
|
MODIFIED = "Modified"
|
|
OTHER = "Other"
|
|
|
|
|
|
@value_labeled
|
|
class AlertAnalyticType(models.TextChoices):
|
|
UNKNOWN = "Unknown"
|
|
RULE = "Rule"
|
|
BEHAVIORAL = "Behavioral"
|
|
STATISTICAL = "Statistical"
|
|
LEARNING = "Learning (ML/DL)"
|
|
FINGERPRINTING = "Fingerprinting"
|
|
TAGGING = "Tagging"
|
|
KEYWORD_MATCH = "Keyword Match"
|
|
REGULAR_EXPRESSIONS = "Regular Expressions"
|
|
EXACT_DATA_MATCH = "Exact Data Match"
|
|
PARTIAL_DATA_MATCH = "Partial Data Match"
|
|
INDEXED_DATA_MATCH = "Indexed Data Match"
|
|
OTHER = "Other"
|
|
|
|
|
|
@value_labeled
|
|
class AlertAnalyticState(models.TextChoices):
|
|
UNKNOWN = "Unknown"
|
|
ACTIVE = "Active"
|
|
SUPPRESSED = "Suppressed"
|
|
EXPERIMENTAL = "Experimental"
|
|
OTHER = "Other"
|
|
|
|
|
|
@value_labeled
|
|
class ProductCategory(models.TextChoices):
|
|
DLP = "DLP"
|
|
EMAIL = "Email"
|
|
OT = "OT"
|
|
PROXY = "Proxy"
|
|
UEBA = "UEBA"
|
|
TI = "ThreatIntelligence"
|
|
IAM = "IAM"
|
|
EDR = "EDR"
|
|
NDR = "NDR"
|
|
CLOUD = "Cloud"
|
|
SIEM = "SIEM"
|
|
WAF = "WAF"
|
|
OTHER = "Other"
|
|
|
|
|
|
@value_labeled
|
|
class AlertPolicyType(models.TextChoices):
|
|
IDENTITY_POLICY = "Identity Policy"
|
|
RESOURCE_POLICY = "Resource Policy"
|
|
SERVICE_CONTROL_POLICY = "Service Control Policy"
|
|
ACCESS_CONTROL_POLICY = "Access Control Policy"
|
|
OTHER = "Other"
|
|
|
|
|
|
@value_labeled
|
|
class AlertRiskLevel(models.TextChoices):
|
|
INFO = "Info"
|
|
LOW = "Low"
|
|
MEDIUM = "Medium"
|
|
HIGH = "High"
|
|
CRITICAL = "Critical"
|
|
OTHER = "Other"
|
|
|
|
|
|
@value_labeled
|
|
class AlertStatus(models.TextChoices):
|
|
UNKNOWN = "Unknown"
|
|
NEW = "New"
|
|
IN_PROGRESS = "In Progress"
|
|
SUPPRESSED = "Suppressed"
|
|
RESOLVED = "Resolved"
|
|
ARCHIVED = "Archived"
|
|
DELETED = "Deleted"
|
|
OTHER = "Other"
|
|
|
|
|
|
@value_labeled
|
|
class AlertTactic(models.TextChoices):
|
|
RECONNAISSANCE = "Reconnaissance"
|
|
RESOURCE_DEVELOPMENT = "Resource Development"
|
|
INITIAL_ACCESS = "Initial Access"
|
|
EXECUTION = "Execution"
|
|
PERSISTENCE = "Persistence"
|
|
PRIVILEGE_ESCALATION = "Privilege Escalation"
|
|
DEFENSE_EVASION = "Defense Evasion"
|
|
CREDENTIAL_ACCESS = "Credential Access"
|
|
DISCOVERY = "Discovery"
|
|
LATERAL_MOVEMENT = "Lateral Movement"
|
|
COLLECTION = "Collection"
|
|
COMMAND_AND_CONTROL = "Command and Control"
|
|
EXFILTRATION = "Exfiltration"
|
|
IMPACT = "Impact"
|
|
|
|
|
|
class Alert(BaseModel):
|
|
alert_id = models.CharField(max_length=32, unique=True, editable=False, db_index=True, blank=True, default="", help_text="Record ID e.g. alert_000001, auto-generated, no manual input needed (记录 ID e.g. alert_000001, 系统自动生成,无需手动赋值)")
|
|
case = models.ForeignKey("cases.Case", on_delete=models.CASCADE, related_name="alerts", help_text="Linked case id, reverse association, auto-linked, no manual setting needed (关联案件行 ID,反向关联,自动化关联,无需手动设置)")
|
|
title = models.CharField(max_length=500, blank=True, default="", help_text="Alert title (告警标题)")
|
|
severity = models.CharField(max_length=20, choices=Severity, default=Severity.UNKNOWN, help_text="Source-defined severity (告警来源定义的严重程度)")
|
|
confidence = models.CharField(max_length=20, choices=Confidence, default=Confidence.UNKNOWN, help_text="True-positive confidence (真阳性置信度)")
|
|
impact = models.CharField(max_length=20, choices=Impact, default=Impact.UNKNOWN, help_text="Potential impact (告警潜在影响)")
|
|
disposition = models.CharField(max_length=20, choices=Disposition, default=Disposition.UNKNOWN, help_text="Source disposition (告警源处置结果)")
|
|
action = models.CharField(max_length=20, choices=AlertAction, default=AlertAction.UNKNOWN, help_text="Observed action (告警源的动作)")
|
|
labels = models.JSONField(default=list, blank=True, help_text="Alert labels (告警标签)")
|
|
desc = models.TextField(blank=True, default="", help_text="Alert description (告警描述)")
|
|
first_seen_time = models.DateTimeField(null=True, blank=True, help_text="First observed time (首次观测时间)")
|
|
last_seen_time = models.DateTimeField(null=True, blank=True, help_text="Last observed time (最后观测时间)")
|
|
rule_id = models.CharField(max_length=255, blank=True, default="", help_text="SIEM rule ID (SIEM 规则 ID)")
|
|
rule_name = models.CharField(max_length=255, blank=True, default="", help_text="SIEM rule name (SIEM 规则名称)")
|
|
correlation_uid = models.CharField(max_length=255, blank=True, default="", db_index=True, help_text="Case correlation ID, alerts with the same correlation_uid are linked to the same event (事件关联 ID,相同 correlation_uid 告警关联到同一个事件)")
|
|
src_url = models.URLField(max_length=500, blank=True, default="", help_text="Source alert URL (原始告警 URL)")
|
|
source_uid = models.CharField(max_length=255, blank=True, default="", db_index=True, help_text="Source product ID, can be used to locate the unique alert in the source system (原始告警 唯一ID, 可通过该 ID 在原始来源中定位唯一告警)")
|
|
data_sources = models.JSONField(default=list, blank=True, help_text="Underlying data sources (告警源生成告警的数据来源列表)")
|
|
analytic_name = models.CharField(max_length=255, blank=True, default="", help_text="Analytic engine name (分析引擎名称)")
|
|
analytic_type = models.CharField(max_length=30, choices=AlertAnalyticType, default=AlertAnalyticType.UNKNOWN, help_text="Analytic engine type (分析引擎类型)")
|
|
analytic_state = models.CharField(max_length=20, choices=AlertAnalyticState, blank=True, default="", help_text="Analytic rule state (分析规则状态)")
|
|
analytic_desc = models.TextField(blank=True, default="", help_text="Analytic rule description (分析规则描述)")
|
|
tactic = models.CharField(max_length=100, choices=AlertTactic, blank=True, default="", help_text="Mapped MITRE tactic (映射的 MITRE 战术)")
|
|
technique = models.CharField(max_length=100, blank=True, default="", help_text="Mapped MITRE technique (映射的 MITRE 技术)")
|
|
sub_technique = models.CharField(max_length=100, blank=True, default="", help_text="Mapped MITRE sub-technique (映射的 MITRE 子技术)")
|
|
mitigation = models.TextField(blank=True, default="", help_text="Suggested mitigation (建议的缓解措施)")
|
|
product_category = models.CharField(max_length=30, choices=ProductCategory, blank=True, default="", help_text="Source product category (原始产品类别)")
|
|
product_vendor = models.CharField(max_length=255, blank=True, default="", help_text="Source vendor (原始厂商)")
|
|
product_name = models.CharField(max_length=255, blank=True, default="", help_text="Source product name (原始产品名称)")
|
|
product_feature = models.CharField(max_length=255, blank=True, default="", help_text="Source product feature (原始产品功能)")
|
|
policy_name = models.CharField(max_length=255, blank=True, default="", help_text="Trigger policy name (触发策略名称)")
|
|
policy_type = models.CharField(max_length=30, choices=AlertPolicyType, blank=True, default="", help_text="Trigger policy type (触发策略类型)")
|
|
policy_desc = models.TextField(blank=True, default="", help_text="Trigger policy description (触发策略描述)")
|
|
risk_level = models.CharField(max_length=20, choices=AlertRiskLevel, blank=True, default="", help_text="Assessed risk level (评估的风险等级)")
|
|
status = models.CharField(max_length=20, choices=AlertStatus, default=AlertStatus.NEW, help_text="Alert handling status (告警处理状态)")
|
|
status_detail = models.TextField(blank=True, default="", help_text="Handling status details (处理状态详情)")
|
|
remediation = models.TextField(blank=True, default="", help_text="Remediation advice or record (处置建议或记录)")
|
|
unmapped = models.JSONField(default=dict, blank=True, help_text="Raw unmapped fields, JSON Format (原始未映射字段 JSON 格式)")
|
|
raw_data = models.JSONField(default=dict, blank=True, help_text="Raw alert log JSON (原始告警日志 JSON)")
|
|
|
|
# M2M
|
|
artifacts = models.ManyToManyField("artifacts.Artifact", related_name="alerts", blank=True, help_text="Extracted artifacts (关联表, 提取的实体列表)")
|
|
|
|
def save(self, *args, **kwargs):
|
|
return save_with_readable_id(self, "alert_id", "alert", *args, **kwargs)
|
|
|
|
class Meta:
|
|
db_table = "alerts"
|
|
ordering = ["-created_at"]
|
|
indexes = [
|
|
models.Index(fields=["-created_at", "-id"], name="alert_created_id_idx"),
|
|
models.Index(fields=["-first_seen_time", "-id"], name="alert_first_seen_id_idx"),
|
|
models.Index(
|
|
Coalesce("last_seen_time", "first_seen_time", "created_at"),
|
|
name="alert_event_time_idx",
|
|
),
|
|
]
|
|
|
|
def __str__(self):
|
|
return self.title or str(self.id)
|