mirror of
https://github.com/FunnyWolf/agentic-soc-platform.git
synced 2026-08-22 13:12:56 +02:00
691 lines
36 KiB
Python
691 lines
36 KiB
Python
"""Reusable mock data importer.
|
|
|
|
Run from ``backend``:
|
|
|
|
.\\.venv\\Scripts\\python.exe manage.py shell -c "from mock.import_mock_data import run; run()"
|
|
|
|
The script is safe to run repeatedly. Each run creates a new mock batch instead
|
|
of overwriting earlier mock data. Audit logs are generated with the
|
|
``automation`` user as actor.
|
|
"""
|
|
|
|
import json
|
|
from datetime import timedelta
|
|
|
|
from django.utils import timezone
|
|
|
|
from apps.accounts.models import User
|
|
from apps.alerts.models import (
|
|
Alert,
|
|
AlertAction,
|
|
AlertAnalyticState,
|
|
AlertAnalyticType,
|
|
AlertPolicyType,
|
|
AlertRiskLevel,
|
|
AlertStatus,
|
|
Confidence,
|
|
Disposition,
|
|
Impact,
|
|
ProductCategory,
|
|
Severity,
|
|
)
|
|
from apps.artifacts.models import Artifact, ArtifactName, ArtifactRole, ArtifactType
|
|
from apps.audit.context import audit_actor
|
|
from apps.cases.models import (
|
|
Case,
|
|
CaseCategory,
|
|
CaseConfidence,
|
|
CaseImpact,
|
|
CasePriority,
|
|
CaseSeverity,
|
|
CaseStatus,
|
|
CaseVerdict,
|
|
)
|
|
from apps.comments.services import create_record_comment
|
|
from apps.enrichments.models import Enrichment, EnrichmentProvider, EnrichmentType
|
|
from apps.knowledge.models import Knowledge, KnowledgeSource
|
|
from apps.playbooks.models import Playbook, PlaybookJobStatus
|
|
|
|
MOCK_TAG = "mock-data"
|
|
MOCK_MARKER = "[mock-data]"
|
|
MOCK_CORRELATION_PREFIX = "MOCK-CORR-"
|
|
MOCK_ALERT_SOURCE_PREFIX = "MOCK-SRC-"
|
|
MOCK_ENRICHMENT_UID_PREFIX = "mock:"
|
|
MOCK_PLAYBOOK_JOB_PREFIX = "mock-job-"
|
|
|
|
PLAYBOOK_INVESTIGATION = "Investigation"
|
|
PLAYBOOK_KNOWLEDGE_EXTRACTION = "Knowledge Extraction"
|
|
PLAYBOOK_TI_ENRICHMENT = "Threat Intelligence Enrichment"
|
|
PLAYBOOK_CMDB_ENRICHMENT = "CMDB Enrichment"
|
|
|
|
|
|
def minutes(value):
|
|
return timedelta(minutes=value)
|
|
|
|
|
|
def hours(value):
|
|
return timedelta(hours=value)
|
|
|
|
|
|
def days(value):
|
|
return timedelta(days=value)
|
|
|
|
|
|
def new_batch_id():
|
|
return timezone.now().strftime("%Y%m%d%H%M%S%f")
|
|
|
|
|
|
def batch_tag(batch_id):
|
|
return f"{MOCK_TAG}:{batch_id}"
|
|
|
|
|
|
CASE_SPECS = [
|
|
{
|
|
"key": "ransomware-finance",
|
|
"title": "Critical ransomware indicators on finance workstation",
|
|
"category": CaseCategory.EDR,
|
|
"severity": CaseSeverity.CRITICAL,
|
|
"confidence": CaseConfidence.HIGH,
|
|
"impact": CaseImpact.CRITICAL,
|
|
"priority": CasePriority.CRITICAL,
|
|
"status": CaseStatus.IN_PROGRESS,
|
|
"verdict": CaseVerdict.SECURITY_RISK,
|
|
"assignee": "alice.chen",
|
|
"tags": ["ransomware", "finance", "endpoint", "critical"],
|
|
"first_seen_offset": days(2) + hours(4),
|
|
"detection_delay": minutes(11),
|
|
"ack_delay": minutes(7),
|
|
"resolution_delay": None,
|
|
"summary": "Containment started. Endpoint isolated and credential reset requested for the affected user.",
|
|
"description": "EDR detected shadow copy deletion, suspicious encryption activity, and ransom note creation on a finance endpoint.",
|
|
"ai": {"hypothesis": "Active ransomware behavior", "confidence": "High", "recommended_actions": ["isolate host", "preserve disk image", "reset credentials"]},
|
|
},
|
|
{
|
|
"key": "cloud-impossible-travel",
|
|
"title": "Impossible travel followed by privileged cloud console access",
|
|
"category": CaseCategory.IAM,
|
|
"severity": CaseSeverity.HIGH,
|
|
"confidence": CaseConfidence.HIGH,
|
|
"impact": CaseImpact.HIGH,
|
|
"priority": CasePriority.HIGH,
|
|
"status": CaseStatus.NEW,
|
|
"verdict": CaseVerdict.UNKNOWN,
|
|
"assignee": "bob.li",
|
|
"tags": ["identity", "cloud", "aws", "impossible-travel"],
|
|
"first_seen_offset": hours(18),
|
|
"detection_delay": minutes(24),
|
|
"ack_delay": None,
|
|
"resolution_delay": None,
|
|
"summary": "",
|
|
"description": "Identity provider sign-in telemetry shows impossible travel and subsequent AWS IAM policy enumeration.",
|
|
"ai": {"hypothesis": "Credential compromise", "entities": ["user", "aws role"], "confidence": "Medium"},
|
|
},
|
|
{
|
|
"key": "dns-tunneling",
|
|
"title": "Suspicious outbound DNS tunneling pattern",
|
|
"category": CaseCategory.NDR,
|
|
"severity": CaseSeverity.MEDIUM,
|
|
"confidence": CaseConfidence.MEDIUM,
|
|
"impact": CaseImpact.MEDIUM,
|
|
"priority": CasePriority.MEDIUM,
|
|
"status": CaseStatus.ON_HOLD,
|
|
"verdict": CaseVerdict.SUSPICIOUS,
|
|
"assignee": "maya.singh",
|
|
"tags": ["dns", "tunneling", "ndr"],
|
|
"first_seen_offset": days(3) + hours(6),
|
|
"detection_delay": hours(2) + minutes(10),
|
|
"ack_delay": minutes(46),
|
|
"resolution_delay": None,
|
|
"summary": "Waiting for endpoint owner confirmation before blocking the domain at DNS layer.",
|
|
"description": "NDR detected high-volume TXT queries to a newly registered domain with encoded subdomain labels.",
|
|
"ai": {"hypothesis": "Possible DNS tunneling", "confidence": "Medium", "open_questions": ["business justification"]},
|
|
},
|
|
{
|
|
"key": "executive-phishing",
|
|
"title": "Phishing campaign targeting executives",
|
|
"category": CaseCategory.EMAIL,
|
|
"severity": CaseSeverity.HIGH,
|
|
"confidence": CaseConfidence.HIGH,
|
|
"impact": CaseImpact.MEDIUM,
|
|
"priority": CasePriority.HIGH,
|
|
"status": CaseStatus.RESOLVED,
|
|
"verdict": CaseVerdict.TRUE_POSITIVE,
|
|
"assignee": "alice.chen",
|
|
"tags": ["phishing", "executive", "email"],
|
|
"first_seen_offset": days(5) + hours(1),
|
|
"detection_delay": minutes(18),
|
|
"ack_delay": minutes(12),
|
|
"resolution_delay": hours(6) + minutes(35),
|
|
"summary": "Campaign confirmed. Sender blocked, URLs submitted for takedown, and affected recipients notified.",
|
|
"description": "Multiple mailbox detections share the same sender infrastructure, payload URL pattern, and executive recipient group.",
|
|
"ai": {"hypothesis": "Coordinated phishing campaign", "confidence": "High", "actions_completed": ["block sender", "quarantine mail", "notify recipients"]},
|
|
},
|
|
{
|
|
"key": "dlp-source-code",
|
|
"title": "Source code archive uploaded to personal cloud storage",
|
|
"category": CaseCategory.DLP,
|
|
"severity": CaseSeverity.HIGH,
|
|
"confidence": CaseConfidence.HIGH,
|
|
"impact": CaseImpact.HIGH,
|
|
"priority": CasePriority.HIGH,
|
|
"status": CaseStatus.IN_PROGRESS,
|
|
"verdict": CaseVerdict.SUSPICIOUS,
|
|
"assignee": "bob.li",
|
|
"tags": ["dlp", "source-code", "cloud-storage"],
|
|
"first_seen_offset": hours(9),
|
|
"detection_delay": minutes(4),
|
|
"ack_delay": minutes(18),
|
|
"resolution_delay": None,
|
|
"summary": "Legal hold request opened. Waiting for manager confirmation and endpoint collection.",
|
|
"description": "DLP detected a compressed repository archive uploaded from a developer workstation to unsanctioned storage.",
|
|
"ai": {"hypothesis": "Possible data exfiltration", "confidence": "Medium", "data_types": ["source code", "secrets candidate"]},
|
|
},
|
|
{
|
|
"key": "waf-sql-injection",
|
|
"title": "SQL injection attempts against customer portal",
|
|
"category": CaseCategory.WAF,
|
|
"severity": CaseSeverity.MEDIUM,
|
|
"confidence": CaseConfidence.HIGH,
|
|
"impact": CaseImpact.MEDIUM,
|
|
"priority": CasePriority.MEDIUM,
|
|
"status": CaseStatus.RESOLVED,
|
|
"verdict": CaseVerdict.TRUE_POSITIVE,
|
|
"assignee": "maya.singh",
|
|
"tags": ["waf", "sql-injection", "portal"],
|
|
"first_seen_offset": days(1) + hours(8),
|
|
"detection_delay": minutes(2),
|
|
"ack_delay": minutes(9),
|
|
"resolution_delay": hours(1) + minutes(30),
|
|
"summary": "WAF rule blocked all observed requests. Source ASN added to temporary block list.",
|
|
"description": "WAF detected repeated SQL injection payloads against login and search endpoints.",
|
|
"ai": {"hypothesis": "Automated SQL injection probing", "confidence": "High", "impact": "Blocked at WAF"},
|
|
},
|
|
{
|
|
"key": "proxy-malware-download",
|
|
"title": "Suspicious malware download over corporate proxy",
|
|
"category": CaseCategory.PROXY,
|
|
"severity": CaseSeverity.MEDIUM,
|
|
"confidence": CaseConfidence.MEDIUM,
|
|
"impact": CaseImpact.MEDIUM,
|
|
"priority": CasePriority.MEDIUM,
|
|
"status": CaseStatus.CLOSED,
|
|
"verdict": CaseVerdict.FALSE_POSITIVE,
|
|
"assignee": "alice.chen",
|
|
"tags": ["proxy", "download", "false-positive"],
|
|
"first_seen_offset": days(8),
|
|
"detection_delay": minutes(16),
|
|
"ack_delay": minutes(25),
|
|
"resolution_delay": hours(2) + minutes(5),
|
|
"summary": "Confirmed sanctioned red-team file retrieval. Knowledge updated for future suppression.",
|
|
"description": "Proxy alert matched a malware test filename downloaded by a red-team workstation.",
|
|
"ai": {"hypothesis": "Possible malware download", "confidence": "Low", "analyst_override": "Red-team activity"},
|
|
},
|
|
{
|
|
"key": "ti-c2-domain",
|
|
"title": "Threat intelligence match for C2 domain in DNS logs",
|
|
"category": CaseCategory.TI,
|
|
"severity": CaseSeverity.HIGH,
|
|
"confidence": CaseConfidence.HIGH,
|
|
"impact": CaseImpact.HIGH,
|
|
"priority": CasePriority.HIGH,
|
|
"status": CaseStatus.NEW,
|
|
"verdict": CaseVerdict.SUSPICIOUS,
|
|
"assignee": "bob.li",
|
|
"tags": ["threat-intelligence", "c2", "dns"],
|
|
"first_seen_offset": hours(3),
|
|
"detection_delay": minutes(8),
|
|
"ack_delay": None,
|
|
"resolution_delay": None,
|
|
"summary": "",
|
|
"description": "Threat intelligence provider flagged a queried domain as likely command-and-control infrastructure.",
|
|
"ai": {"hypothesis": "C2 beacon candidate", "confidence": "Medium", "needs": ["endpoint process tree", "DNS history"]},
|
|
},
|
|
]
|
|
|
|
ARTIFACT_SPECS = [
|
|
("fin-wks-023.corp.example", ArtifactType.HOSTNAME, ArtifactRole.AFFECTED, ArtifactName.AFFECTED_HOST),
|
|
("finance.user@example.com", ArtifactType.USER_NAME, ArtifactRole.ACTOR, ArtifactName.SOURCE_USER),
|
|
("vssadmin.exe delete shadows /all /quiet", ArtifactType.COMMAND_LINE, ArtifactRole.RELATED, ArtifactName.PROCESS_COMMAND_LINE),
|
|
("C:\\Users\\Public\\README_RESTORE_FILES.txt", ArtifactType.FILE_PATH, ArtifactRole.RELATED, ArtifactName.FILE_PATH),
|
|
("44d88612fea8a8f36de82e1278abb02f", ArtifactType.HASH, ArtifactRole.RELATED, ArtifactName.FILE_HASH),
|
|
("185.199.110.153", ArtifactType.IP_ADDRESS, ArtifactRole.ACTOR, ArtifactName.SOURCE_IP),
|
|
("arn:aws:iam::111122223333:role/SecurityAudit", ArtifactType.RESOURCE, ArtifactRole.TARGET, ArtifactName.CLOUD_ROLE),
|
|
("d3f4c2a9.exfil-example.net", ArtifactType.HOSTNAME, ArtifactRole.RELATED, ArtifactName.DNS_QUERY_NAME),
|
|
("benefits-update@payroll-secure.example", ArtifactType.EMAIL_ADDRESS, ArtifactRole.ACTOR, ArtifactName.SENDER_EMAIL),
|
|
("https://login-m365-security.example/verify", ArtifactType.URL_STRING, ArtifactRole.RELATED, ArtifactName.PHISHING_URL),
|
|
("dev-wks-044.corp.example", ArtifactType.HOSTNAME, ArtifactRole.AFFECTED, ArtifactName.AFFECTED_HOST),
|
|
("repo-secrets-2026.zip", ArtifactType.FILE_NAME, ArtifactRole.RELATED, ArtifactName.FILE_NAME),
|
|
("customer-portal.example.com", ArtifactType.HOSTNAME, ArtifactRole.TARGET, ArtifactName.DESTINATION_HOST),
|
|
("203.0.113.45", ArtifactType.IP_ADDRESS, ArtifactRole.ACTOR, ArtifactName.SOURCE_IP),
|
|
("redteam-workstation-07", ArtifactType.HOSTNAME, ArtifactRole.RELATED, ArtifactName.HOSTNAME),
|
|
("c2-payment-check.example", ArtifactType.HOSTNAME, ArtifactRole.RELATED, ArtifactName.DOMAIN),
|
|
]
|
|
|
|
|
|
def ensure_users():
|
|
users = {}
|
|
for username, full_name in [
|
|
("alice.chen", "Alice Chen"),
|
|
("bob.li", "Bob Li"),
|
|
("maya.singh", "Maya Singh"),
|
|
("liam.ops", "Liam Ops"),
|
|
("automation", "ASP Automation"),
|
|
]:
|
|
first_name, last_name = full_name.split(" ", 1)
|
|
user, _created = User.objects.get_or_create(username=username)
|
|
user.first_name = first_name
|
|
user.last_name = last_name
|
|
user.email = f"{username}@example.com"
|
|
user.mobile_phone = "+86-138-0000-0000" if username != "automation" else ""
|
|
user.set_password("mockpass")
|
|
user.save()
|
|
users[username] = user
|
|
return users
|
|
|
|
|
|
def metric_text(spec):
|
|
ttd = spec["detection_delay"]
|
|
tta = spec["ack_delay"]
|
|
ttr = spec["resolution_delay"]
|
|
parts = [f"TTD={format_delta(ttd)}"]
|
|
parts.append(f"TTA={format_delta(tta)}" if tta else "TTA=pending")
|
|
parts.append(f"TTR={format_delta(ttr)}" if ttr else "TTR=pending")
|
|
return ", ".join(parts)
|
|
|
|
|
|
def format_delta(value):
|
|
total_minutes = int(value.total_seconds() // 60)
|
|
days_part, remainder = divmod(total_minutes, 1440)
|
|
hours_part, minutes_part = divmod(remainder, 60)
|
|
chunks = []
|
|
if days_part:
|
|
chunks.append(f"{days_part}d")
|
|
if hours_part:
|
|
chunks.append(f"{hours_part}h")
|
|
if minutes_part or not chunks:
|
|
chunks.append(f"{minutes_part}m")
|
|
return "".join(chunks)
|
|
|
|
|
|
def create_cases(users, batch_id):
|
|
now = timezone.now()
|
|
cases = {}
|
|
current_batch_tag = batch_tag(batch_id)
|
|
for index, spec in enumerate(CASE_SPECS, start=1):
|
|
first_seen = now - spec["first_seen_offset"]
|
|
detected_at = first_seen + spec["detection_delay"]
|
|
acknowledged_at = detected_at + spec["ack_delay"] if spec["ack_delay"] else None
|
|
closed_at = acknowledged_at + spec["resolution_delay"] if acknowledged_at and spec["resolution_delay"] else None
|
|
case = Case.objects.create(
|
|
title=f"{spec['title']} ({batch_id})",
|
|
severity=spec["severity"],
|
|
severity_ai=spec["severity"],
|
|
confidence=spec["confidence"],
|
|
confidence_ai=spec["confidence"],
|
|
impact=spec["impact"],
|
|
impact_ai=spec["impact"],
|
|
priority=spec["priority"],
|
|
priority_ai=spec["priority"],
|
|
description=f"{spec['description']} Response metrics: {metric_text(spec)}.",
|
|
category=spec["category"],
|
|
tags=[MOCK_TAG, current_batch_tag, *spec["tags"]],
|
|
status=spec["status"],
|
|
verdict=spec["verdict"],
|
|
verdict_ai=spec["verdict"] if spec["verdict"] != CaseVerdict.UNKNOWN else CaseVerdict.SUSPICIOUS,
|
|
summary=spec["summary"],
|
|
assignee=users[spec["assignee"]],
|
|
acknowledged_time=acknowledged_at,
|
|
closed_time=closed_at,
|
|
correlation_uid=f"{MOCK_CORRELATION_PREFIX}{batch_id}-{index:02d}-{spec['key'].upper()}",
|
|
investigation_report_ai_json=json.dumps(
|
|
{
|
|
**spec["ai"],
|
|
"metrics": {
|
|
"ttd": format_delta(spec["detection_delay"]),
|
|
"tta": format_delta(spec["ack_delay"]) if spec["ack_delay"] else None,
|
|
"ttr": format_delta(spec["resolution_delay"]) if spec["resolution_delay"] else None,
|
|
},
|
|
},
|
|
ensure_ascii=False,
|
|
),
|
|
)
|
|
Case.objects.filter(pk=case.pk).update(created_at=detected_at, updated_at=closed_at or acknowledged_at or detected_at)
|
|
case.refresh_from_db()
|
|
cases[spec["key"]] = {"case": case, "first_seen": first_seen, "detected_at": detected_at}
|
|
return cases
|
|
|
|
|
|
def create_artifacts():
|
|
artifacts = {}
|
|
for value, type_, role, name in ARTIFACT_SPECS:
|
|
artifacts[value] = Artifact.objects.create(name=name, type=type_, role=role, value=value)
|
|
return artifacts
|
|
|
|
|
|
def alert_payload(case, case_key, alert_index, title, severity, confidence, impact, disposition, action, product_category, analytic_type, first_seen, last_seen, tactic, technique):
|
|
return {
|
|
"case": case,
|
|
"title": title,
|
|
"severity": severity,
|
|
"confidence": confidence,
|
|
"impact": impact,
|
|
"disposition": disposition,
|
|
"action": action,
|
|
"labels": [MOCK_TAG, "soc", case.category.lower(), severity.lower()],
|
|
"desc": f"{title}. Detection includes telemetry correlation, entity context, and recommended triage actions.",
|
|
"first_seen_time": first_seen,
|
|
"last_seen_time": last_seen,
|
|
"rule_id": f"MOCK-{case.category}-{alert_index:03d}",
|
|
"rule_name": title,
|
|
"correlation_uid": case.correlation_uid,
|
|
"src_url": f"https://console.example.local/alerts/{case_key}/{alert_index:03d}",
|
|
"source_uid": f"{MOCK_ALERT_SOURCE_PREFIX}{case.correlation_uid}-{case_key}-{alert_index:03d}",
|
|
"data_sources": [product_category.lower(), "mock.telemetry"],
|
|
"analytic_name": f"{title} Analytic",
|
|
"analytic_type": analytic_type,
|
|
"analytic_state": AlertAnalyticState.ACTIVE,
|
|
"analytic_desc": "Mock analytic generated from realistic SOC detection logic.",
|
|
"tactic": tactic,
|
|
"technique": technique,
|
|
"mitigation": "Validate scope, preserve evidence, and apply containment according to severity.",
|
|
"product_category": product_category,
|
|
"product_vendor": product_vendor(product_category),
|
|
"product_name": product_name(product_category),
|
|
"product_feature": product_feature(product_category),
|
|
"policy_name": f"Mock Detection Policy {alert_index:03d}",
|
|
"policy_type": policy_type(product_category),
|
|
"policy_desc": "Policy tuned for enterprise SOC mock telemetry.",
|
|
"risk_level": risk_level_from_severity(severity),
|
|
"status": status_for_case(case.status),
|
|
"status_detail": f"{MOCK_MARKER} Generated alert for {case.case_id}",
|
|
"remediation": "Follow the playbook, confirm business context, and document analyst conclusion.",
|
|
"unmapped": {"mock_case_key": case_key, "mock_alert_index": alert_index, "parser": "mock-importer"},
|
|
"raw_data": {"event_id": f"mock-{case_key}-{alert_index:03d}", "case": case.case_id, "title": title},
|
|
}
|
|
|
|
|
|
def create_alerts(cases, artifacts, batch_id):
|
|
specs = [
|
|
("ransomware-finance", "Shadow copy deletion and mass file rename", Severity.CRITICAL, Confidence.HIGH, Impact.CRITICAL, Disposition.DETECTED, AlertAction.OBSERVED, ProductCategory.EDR, AlertAnalyticType.BEHAVIORAL, ["fin-wks-023.corp.example", "finance.user@example.com", "vssadmin.exe delete shadows /all /quiet", "44d88612fea8a8f36de82e1278abb02f"], "Impact", "T1490 - Inhibit System Recovery"),
|
|
("ransomware-finance", "Ransom note created in public user directory", Severity.HIGH, Confidence.HIGH, Impact.HIGH, Disposition.LOGGED, AlertAction.OBSERVED, ProductCategory.EDR, AlertAnalyticType.RULE, ["fin-wks-023.corp.example", "C:\\Users\\Public\\README_RESTORE_FILES.txt"], "Impact", "T1486 - Data Encrypted for Impact"),
|
|
("cloud-impossible-travel", "Impossible travel with MFA fatigue indicators", Severity.HIGH, Confidence.HIGH, Impact.HIGH, Disposition.ALERT, AlertAction.OBSERVED, ProductCategory.IAM, AlertAnalyticType.STATISTICAL, ["finance.user@example.com", "arn:aws:iam::111122223333:role/SecurityAudit", "185.199.110.153"], "Credential Access", "T1110 - Brute Force"),
|
|
("cloud-impossible-travel", "AWS IAM policy enumeration after suspicious login", Severity.MEDIUM, Confidence.MEDIUM, Impact.HIGH, Disposition.LOGGED, AlertAction.OBSERVED, ProductCategory.CLOUD, AlertAnalyticType.RULE, ["arn:aws:iam::111122223333:role/SecurityAudit"], "Discovery", "T1087 - Account Discovery"),
|
|
("dns-tunneling", "High entropy DNS TXT query burst", Severity.MEDIUM, Confidence.MEDIUM, Impact.MEDIUM, Disposition.LOGGED, AlertAction.OBSERVED, ProductCategory.NDR, AlertAnalyticType.RULE, ["d3f4c2a9.exfil-example.net", "fin-wks-023.corp.example"], "Command and Control", "T1071.004 - DNS"),
|
|
("executive-phishing", "Executive mailbox phishing link clicked", Severity.HIGH, Confidence.HIGH, Impact.MEDIUM, Disposition.QUARANTINED, AlertAction.DENIED, ProductCategory.EMAIL, AlertAnalyticType.KEYWORD_MATCH, ["benefits-update@payroll-secure.example", "https://login-m365-security.example/verify"], "Initial Access", "T1566.002 - Spearphishing Link"),
|
|
("executive-phishing", "Lookalike sender domain newly registered", Severity.MEDIUM, Confidence.MEDIUM, Impact.LOW, Disposition.TAGGED, AlertAction.OBSERVED, ProductCategory.EMAIL, AlertAnalyticType.FINGERPRINTING, ["benefits-update@payroll-secure.example"], "Resource Development", "T1583.001 - Domains"),
|
|
("dlp-source-code", "Repository archive uploaded to unsanctioned storage", Severity.HIGH, Confidence.HIGH, Impact.HIGH, Disposition.ALERT, AlertAction.OBSERVED, ProductCategory.DLP, AlertAnalyticType.EXACT_DATA_MATCH, ["dev-wks-044.corp.example", "repo-secrets-2026.zip"], "Exfiltration", "T1567.002 - Exfiltration to Cloud Storage"),
|
|
("waf-sql-injection", "SQL injection blocked at customer portal", Severity.MEDIUM, Confidence.HIGH, Impact.MEDIUM, Disposition.BLOCKED, AlertAction.DENIED, ProductCategory.WAF, AlertAnalyticType.REGULAR_EXPRESSIONS, ["customer-portal.example.com", "203.0.113.45"], "Initial Access", "T1190 - Exploit Public-Facing Application"),
|
|
("proxy-malware-download", "Malware test file downloaded via proxy", Severity.MEDIUM, Confidence.MEDIUM, Impact.MEDIUM, Disposition.ALERT, AlertAction.OBSERVED, ProductCategory.PROXY, AlertAnalyticType.KEYWORD_MATCH, ["redteam-workstation-07", "44d88612fea8a8f36de82e1278abb02f"], "Execution", "T1204.002 - Malicious File"),
|
|
("ti-c2-domain", "Threat intelligence C2 domain match", Severity.HIGH, Confidence.HIGH, Impact.HIGH, Disposition.ALERT, AlertAction.OBSERVED, ProductCategory.TI, AlertAnalyticType.RULE, ["c2-payment-check.example", "fin-wks-023.corp.example"], "Command and Control", "T1071 - Application Layer Protocol"),
|
|
]
|
|
|
|
alerts = []
|
|
case_alert_counts = {}
|
|
for index, spec in enumerate(specs, start=1):
|
|
case_key, title, severity, confidence, impact, disposition, action, product_category, analytic_type, artifact_values, tactic, technique = spec
|
|
timeline = cases[case_key]
|
|
case = timeline["case"]
|
|
case_alert_counts[case_key] = case_alert_counts.get(case_key, 0) + 1
|
|
case_alert_index = case_alert_counts[case_key]
|
|
detection_window = timeline["detected_at"] - timeline["first_seen"]
|
|
first_seen = timeline["first_seen"] + min(minutes((case_alert_index - 1) * 3), detection_window)
|
|
last_seen = min(first_seen + minutes(35), timeline["detected_at"])
|
|
alert = Alert.objects.create(
|
|
**alert_payload(
|
|
case,
|
|
case_key,
|
|
index,
|
|
title,
|
|
severity,
|
|
confidence,
|
|
impact,
|
|
disposition,
|
|
action,
|
|
product_category,
|
|
analytic_type,
|
|
first_seen,
|
|
last_seen,
|
|
tactic,
|
|
technique,
|
|
)
|
|
)
|
|
alert.artifacts.set([artifacts[value] for value in artifact_values if value in artifacts])
|
|
Alert.objects.filter(pk=alert.pk).update(created_at=timeline["detected_at"] + minutes(index), updated_at=timeline["detected_at"] + minutes(index))
|
|
alert.refresh_from_db()
|
|
alerts.append(alert)
|
|
return alerts
|
|
|
|
|
|
def status_for_case(status):
|
|
if status == CaseStatus.CLOSED:
|
|
return AlertStatus.ARCHIVED
|
|
if status == CaseStatus.RESOLVED:
|
|
return AlertStatus.RESOLVED
|
|
if status == CaseStatus.IN_PROGRESS:
|
|
return AlertStatus.IN_PROGRESS
|
|
if status == CaseStatus.ON_HOLD:
|
|
return AlertStatus.SUPPRESSED
|
|
return AlertStatus.NEW
|
|
|
|
|
|
def risk_level_from_severity(severity):
|
|
return {
|
|
Severity.CRITICAL: AlertRiskLevel.CRITICAL,
|
|
Severity.HIGH: AlertRiskLevel.HIGH,
|
|
Severity.MEDIUM: AlertRiskLevel.MEDIUM,
|
|
Severity.LOW: AlertRiskLevel.LOW,
|
|
Severity.INFORMATIONAL: AlertRiskLevel.INFO,
|
|
}.get(severity, AlertRiskLevel.OTHER)
|
|
|
|
|
|
def product_vendor(category):
|
|
return {
|
|
ProductCategory.EDR: "CrowdStrike",
|
|
ProductCategory.IAM: "Okta",
|
|
ProductCategory.NDR: "Vectra",
|
|
ProductCategory.EMAIL: "Proofpoint",
|
|
ProductCategory.CLOUD: "AWS",
|
|
ProductCategory.DLP: "Microsoft Purview",
|
|
ProductCategory.WAF: "Cloudflare",
|
|
ProductCategory.PROXY: "Zscaler",
|
|
ProductCategory.TI: "AlienVaultOTX",
|
|
}.get(category, "ASP Mock")
|
|
|
|
|
|
def product_name(category):
|
|
return {
|
|
ProductCategory.EDR: "Falcon",
|
|
ProductCategory.IAM: "Workforce Identity",
|
|
ProductCategory.NDR: "Cognito",
|
|
ProductCategory.EMAIL: "TAP",
|
|
ProductCategory.CLOUD: "CloudTrail",
|
|
ProductCategory.DLP: "Purview DLP",
|
|
ProductCategory.WAF: "WAF",
|
|
ProductCategory.PROXY: "Internet Access",
|
|
ProductCategory.TI: "OTX",
|
|
}.get(category, "Mock Telemetry")
|
|
|
|
|
|
def product_feature(category):
|
|
return {
|
|
ProductCategory.EDR: "Process Monitoring",
|
|
ProductCategory.IAM: "Sign-in Risk",
|
|
ProductCategory.NDR: "DNS Analytics",
|
|
ProductCategory.EMAIL: "Email Security",
|
|
ProductCategory.CLOUD: "IAM Auditing",
|
|
ProductCategory.DLP: "File Exfiltration",
|
|
ProductCategory.WAF: "HTTP Firewall",
|
|
ProductCategory.PROXY: "Web Proxy",
|
|
ProductCategory.TI: "IOC Matching",
|
|
}.get(category, "Detection")
|
|
|
|
|
|
def policy_type(category):
|
|
if category in {ProductCategory.IAM, ProductCategory.CLOUD}:
|
|
return AlertPolicyType.IDENTITY_POLICY
|
|
if category in {ProductCategory.WAF, ProductCategory.PROXY}:
|
|
return AlertPolicyType.ACCESS_CONTROL_POLICY
|
|
if category == ProductCategory.DLP:
|
|
return AlertPolicyType.RESOURCE_POLICY
|
|
return AlertPolicyType.OTHER
|
|
|
|
|
|
def create_enrichment(content_object, name, type_, provider, value, desc, data, batch_id):
|
|
parent_kwargs = {content_object._meta.model_name: content_object}
|
|
return Enrichment.objects.create(
|
|
**parent_kwargs,
|
|
name=name,
|
|
type=type_,
|
|
provider=provider,
|
|
uid=f"{MOCK_ENRICHMENT_UID_PREFIX}{batch_id}:{content_object._meta.model_name}:{provider}:{value}",
|
|
value=value,
|
|
desc=desc,
|
|
data=data,
|
|
)
|
|
|
|
|
|
def create_enrichments(cases, alerts, artifacts, batch_id):
|
|
case_map = {key: item["case"] for key, item in cases.items()}
|
|
enrichments = [
|
|
create_enrichment(case_map["ransomware-finance"], "EDR host isolation status", EnrichmentType.REMEDIATION, EnrichmentProvider.CROWDSTRIKE_FALCON, "fin-wks-023 isolated", "Host isolation confirmed in EDR console.", {"isolated": True, "policy": "containment"}, batch_id),
|
|
create_enrichment(case_map["cloud-impossible-travel"], "Okta user risk profile", EnrichmentType.IDENTITY, EnrichmentProvider.OKTA, "finance.user@example.com", "User shows recent impossible travel and high risk score.", {"risk_score": 87, "mfa_pushes": 9}, batch_id),
|
|
create_enrichment(case_map["dns-tunneling"], "Passive DNS history", EnrichmentType.PASSIVE_DNS, EnrichmentProvider.SECURITYTRAILS, "exfil-example.net", "Domain registered recently with sparse passive DNS history.", {"first_seen_days": 3}, batch_id),
|
|
create_enrichment(case_map["executive-phishing"], "Proofpoint campaign cluster", EnrichmentType.CORRELATION, EnrichmentProvider.PROOFPOINT, "exec-phish-campaign", "Mail security clustered related executive phishing messages.", {"messages": 18, "recipients": 12}, batch_id),
|
|
create_enrichment(case_map["dlp-source-code"], "Source code sensitivity summary", EnrichmentType.OBSERVATION, EnrichmentProvider.MICROSOFT_GRAPH, "repo-secrets-2026.zip", "Archive includes repositories with possible credential material.", {"repos": 4, "secret_candidates": 11}, batch_id),
|
|
create_enrichment(case_map["waf-sql-injection"], "WAF block summary", EnrichmentType.DETECTION, EnrichmentProvider.CLOUDFLARE, "customer portal SQLi", "Requests were blocked before reaching the origin.", {"blocked": 127, "source_asn": 64512}, batch_id),
|
|
create_enrichment(case_map["ti-c2-domain"], "OTX pulse context", EnrichmentType.THREAT_INTELLIGENCE, EnrichmentProvider.ALIENVAULT_OTX, "c2-payment-check.example", "Domain appears in recent C2 pulse collections.", {"pulse_count": 4, "risk": "high"}, batch_id),
|
|
]
|
|
|
|
for provider, value, desc in [
|
|
(EnrichmentProvider.VIRUSTOTAL, "44d88612fea8a8f36de82e1278abb02f", "Hash has multiple malicious detections."),
|
|
(EnrichmentProvider.ABUSEIPDB, "185.199.110.153", "Source IP has recent abuse reports."),
|
|
(EnrichmentProvider.DOMAINTOOLS, "payroll-secure.example", "Domain was registered recently and uses privacy-protected WHOIS."),
|
|
]:
|
|
enrichments.append(create_enrichment(alerts[0], f"{provider} alert enrichment", EnrichmentType.THREAT_INTELLIGENCE, provider, value, desc, {"confidence": "High"}, batch_id))
|
|
|
|
for artifact_value, provider, type_, desc in [
|
|
("fin-wks-023.corp.example", EnrichmentProvider.INTERNAL_CMDB, EnrichmentType.CMDB, "Finance workstation owned by APAC finance team."),
|
|
("finance.user@example.com", EnrichmentProvider.MICROSOFT_ENTRA_ID, EnrichmentType.IDENTITY, "User is member of Finance and privileged approval groups."),
|
|
("c2-payment-check.example", EnrichmentProvider.ALIENVAULT_OTX, EnrichmentType.THREAT_INTELLIGENCE, "High-risk C2 infrastructure candidate."),
|
|
("customer-portal.example.com", EnrichmentProvider.INTERNAL_CMDB, EnrichmentType.ASSET, "Customer-facing portal with high business criticality."),
|
|
]:
|
|
if artifact_value in artifacts:
|
|
artifact = artifacts[artifact_value]
|
|
enrichments.append(create_enrichment(artifact, f"{provider} artifact context", type_, provider, artifact.value, desc, {"artifact": artifact.artifact_id}, batch_id))
|
|
return enrichments
|
|
|
|
|
|
def create_knowledge(cases, batch_id):
|
|
case_map = {key: item["case"] for key, item in cases.items()}
|
|
current_batch_tag = batch_tag(batch_id)
|
|
return [
|
|
Knowledge.objects.create(
|
|
title="Ransomware endpoint containment checklist",
|
|
body="# Ransomware endpoint containment\n\n1. Isolate the endpoint in EDR.\n2. Preserve disk and memory evidence.\n3. Reset affected credentials.\n4. Search for lateral movement indicators.\n5. Document recovery and closure summary.",
|
|
source=KnowledgeSource.MANUAL,
|
|
tags=[MOCK_TAG, current_batch_tag, "ransomware", "edr", "containment"],
|
|
),
|
|
Knowledge.objects.create(
|
|
title="Cloud identity impossible travel triage guide",
|
|
body="# Impossible travel triage\n\nValidate geolocation, VPN usage, device fingerprint, MFA events, and privileged role activity. Escalate when impossible travel is followed by policy or role enumeration.",
|
|
source=KnowledgeSource.MANUAL,
|
|
tags=[MOCK_TAG, current_batch_tag, "identity", "cloud", "iam", "mfa"],
|
|
expires_at=timezone.now() + days(180),
|
|
),
|
|
Knowledge.objects.create(
|
|
title="Executive phishing campaign response notes",
|
|
body="# Executive phishing response\n\nCluster by sender domain, URL pattern, and recipient group. Preserve headers, quarantine related messages, and notify executive assistants.",
|
|
source=KnowledgeSource.CASE,
|
|
case=case_map["executive-phishing"],
|
|
tags=[MOCK_TAG, current_batch_tag, "phishing", "executive", "knowledge-extraction"],
|
|
),
|
|
]
|
|
|
|
|
|
def create_playbooks(cases, users, batch_id):
|
|
case_map = {key: item["case"] for key, item in cases.items()}
|
|
specs = [
|
|
(case_map["ransomware-finance"], PLAYBOOK_INVESTIGATION, users["automation"], "Generate final ransomware investigation summary.", PlaybookJobStatus.SUCCESS, "Investigation completed: active ransomware behavior confirmed."),
|
|
(case_map["executive-phishing"], PLAYBOOK_KNOWLEDGE_EXTRACTION, users["alice.chen"], "Extract reusable phishing response knowledge.", PlaybookJobStatus.SUCCESS, "Knowledge created: Executive phishing campaign response notes."),
|
|
(case_map["ti-c2-domain"], PLAYBOOK_TI_ENRICHMENT, users["bob.li"], "Enrich all case artifacts with threat intelligence.", PlaybookJobStatus.RUNNING, "Threat intelligence enrichment running for case artifacts."),
|
|
(case_map["cloud-impossible-travel"], PLAYBOOK_CMDB_ENRICHMENT, users["bob.li"], "Lookup CMDB owner and cloud account context.", PlaybookJobStatus.PENDING, "Queued for CMDB lookup."),
|
|
(case_map["proxy-malware-download"], PLAYBOOK_INVESTIGATION, users["automation"], "Review red-team false positive history.", PlaybookJobStatus.FAILED, "ValueError: Case has insufficient linked evidence for automated investigation."),
|
|
]
|
|
playbooks = []
|
|
for index, (case, name, user, user_input, status, remark) in enumerate(specs, start=1):
|
|
playbooks.append(Playbook.objects.create(
|
|
case=case,
|
|
name=name,
|
|
user=user,
|
|
user_input=user_input,
|
|
job_status=status,
|
|
job_id=f"{MOCK_PLAYBOOK_JOB_PREFIX}{batch_id}-{index:03d}",
|
|
remark=remark,
|
|
))
|
|
return playbooks
|
|
|
|
|
|
def create_comments(cases, users, batch_id):
|
|
case_map = {key: item["case"] for key, item in cases.items()}
|
|
for key, author, body, mentions in [
|
|
("ransomware-finance", "alice.chen", f"{MOCK_MARKER} batch={batch_id} Endpoint isolated. Need credential reset confirmation from identity team.", ["bob.li"]),
|
|
("cloud-impossible-travel", "bob.li", f"{MOCK_MARKER} batch={batch_id} Waiting for user callback. MFA fatigue indicators need SIEM validation.", ["maya.singh"]),
|
|
("dns-tunneling", "maya.singh", f"{MOCK_MARKER} batch={batch_id} DNS owner says this host runs a legacy telemetry agent; keeping case on hold.", []),
|
|
("executive-phishing", "alice.chen", f"{MOCK_MARKER} batch={batch_id} Campaign closed. Knowledge extracted for future executive phishing waves.", []),
|
|
("dlp-source-code", "bob.li", f"{MOCK_MARKER} batch={batch_id} Manager review requested before containment decision.", ["alice.chen"]),
|
|
]:
|
|
create_record_comment(
|
|
author=users[author],
|
|
content_object=case_map[key],
|
|
body=body,
|
|
mentions=[users[name] for name in mentions],
|
|
)
|
|
|
|
|
|
def exercise_audit_examples(cases, alerts, artifacts, batch_id):
|
|
case = cases["cloud-impossible-travel"]["case"]
|
|
case.summary = "Initial identity triage queued; waiting for user verification and MFA reset confirmation."
|
|
case.save(update_fields=["summary", "updated_at"])
|
|
alerts[2].status_detail = "Analyst confirmed suspicious sign-in context and requested session revocation."
|
|
alerts[2].save(update_fields=["status_detail", "updated_at"])
|
|
|
|
if "185.199.110.153" in artifacts:
|
|
alerts[0].artifacts.add(artifacts["185.199.110.153"])
|
|
if "44d88612fea8a8f36de82e1278abb02f" in artifacts:
|
|
alerts[0].artifacts.remove(artifacts["44d88612fea8a8f36de82e1278abb02f"])
|
|
|
|
transient = create_enrichment(
|
|
cases["ransomware-finance"]["case"],
|
|
"Transient deleted enrichment demo",
|
|
EnrichmentType.OBSERVATION,
|
|
EnrichmentProvider.INTERNAL_SIRP,
|
|
f"deleted-demo-{batch_id}",
|
|
"This enrichment is created and deleted by the mock importer to demonstrate deleted relationship audit logs.",
|
|
{"demo": "deleted relationship event"},
|
|
batch_id,
|
|
)
|
|
transient.delete()
|
|
|
|
|
|
def run():
|
|
batch_id = new_batch_id()
|
|
users = ensure_users()
|
|
with audit_actor(users["automation"]):
|
|
cases = create_cases(users, batch_id)
|
|
artifacts = create_artifacts()
|
|
alerts = create_alerts(cases, artifacts, batch_id)
|
|
enrichments = create_enrichments(cases, alerts, artifacts, batch_id)
|
|
knowledge_records = create_knowledge(cases, batch_id)
|
|
playbooks = create_playbooks(cases, users, batch_id)
|
|
create_comments(cases, users, batch_id)
|
|
exercise_audit_examples(cases, alerts, artifacts, batch_id)
|
|
print(
|
|
f"Imported mock batch {batch_id}: "
|
|
f"{len(cases)} cases, "
|
|
f"{len(alerts)} alerts, "
|
|
f"{len(artifacts)} artifacts, "
|
|
f"{len(enrichments)} enrichments, "
|
|
f"{len(knowledge_records)} knowledge records, "
|
|
f"{len(playbooks)} playbooks."
|
|
)
|