iflow-mcp-m507_ai-soc-agent 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/METADATA +410 -0
- iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/RECORD +85 -0
- iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/WHEEL +5 -0
- iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/entry_points.txt +2 -0
- iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/licenses/LICENSE +21 -0
- iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/top_level.txt +1 -0
- src/__init__.py +8 -0
- src/ai_controller/README.md +139 -0
- src/ai_controller/__init__.py +12 -0
- src/ai_controller/agent_executor.py +596 -0
- src/ai_controller/cli/__init__.py +2 -0
- src/ai_controller/cli/main.py +243 -0
- src/ai_controller/session_manager.py +409 -0
- src/ai_controller/web/__init__.py +2 -0
- src/ai_controller/web/server.py +1181 -0
- src/ai_controller/web/static/css/README.md +102 -0
- src/api/__init__.py +13 -0
- src/api/case_management.py +271 -0
- src/api/edr.py +187 -0
- src/api/kb.py +136 -0
- src/api/siem.py +308 -0
- src/core/__init__.py +10 -0
- src/core/config.py +242 -0
- src/core/config_storage.py +684 -0
- src/core/dto.py +50 -0
- src/core/errors.py +36 -0
- src/core/logging.py +128 -0
- src/integrations/__init__.py +8 -0
- src/integrations/case_management/__init__.py +5 -0
- src/integrations/case_management/iris/__init__.py +11 -0
- src/integrations/case_management/iris/iris_client.py +885 -0
- src/integrations/case_management/iris/iris_http.py +274 -0
- src/integrations/case_management/iris/iris_mapper.py +263 -0
- src/integrations/case_management/iris/iris_models.py +128 -0
- src/integrations/case_management/thehive/__init__.py +8 -0
- src/integrations/case_management/thehive/thehive_client.py +193 -0
- src/integrations/case_management/thehive/thehive_http.py +147 -0
- src/integrations/case_management/thehive/thehive_mapper.py +190 -0
- src/integrations/case_management/thehive/thehive_models.py +125 -0
- src/integrations/cti/__init__.py +6 -0
- src/integrations/cti/local_tip/__init__.py +10 -0
- src/integrations/cti/local_tip/local_tip_client.py +90 -0
- src/integrations/cti/local_tip/local_tip_http.py +110 -0
- src/integrations/cti/opencti/__init__.py +10 -0
- src/integrations/cti/opencti/opencti_client.py +101 -0
- src/integrations/cti/opencti/opencti_http.py +418 -0
- src/integrations/edr/__init__.py +6 -0
- src/integrations/edr/elastic_defend/__init__.py +6 -0
- src/integrations/edr/elastic_defend/elastic_defend_client.py +351 -0
- src/integrations/edr/elastic_defend/elastic_defend_http.py +162 -0
- src/integrations/eng/__init__.py +10 -0
- src/integrations/eng/clickup/__init__.py +8 -0
- src/integrations/eng/clickup/clickup_client.py +513 -0
- src/integrations/eng/clickup/clickup_http.py +156 -0
- src/integrations/eng/github/__init__.py +8 -0
- src/integrations/eng/github/github_client.py +169 -0
- src/integrations/eng/github/github_http.py +158 -0
- src/integrations/eng/trello/__init__.py +8 -0
- src/integrations/eng/trello/trello_client.py +207 -0
- src/integrations/eng/trello/trello_http.py +162 -0
- src/integrations/kb/__init__.py +12 -0
- src/integrations/kb/fs_kb_client.py +313 -0
- src/integrations/siem/__init__.py +6 -0
- src/integrations/siem/elastic/__init__.py +6 -0
- src/integrations/siem/elastic/elastic_client.py +3319 -0
- src/integrations/siem/elastic/elastic_http.py +165 -0
- src/mcp/README.md +183 -0
- src/mcp/TOOLS.md +2827 -0
- src/mcp/__init__.py +13 -0
- src/mcp/__main__.py +18 -0
- src/mcp/agent_profiles.py +408 -0
- src/mcp/flow_agent_profiles.py +424 -0
- src/mcp/mcp_server.py +4086 -0
- src/mcp/rules_engine.py +487 -0
- src/mcp/runbook_manager.py +264 -0
- src/orchestrator/__init__.py +11 -0
- src/orchestrator/incident_workflow.py +244 -0
- src/orchestrator/tools_case.py +1085 -0
- src/orchestrator/tools_cti.py +359 -0
- src/orchestrator/tools_edr.py +315 -0
- src/orchestrator/tools_eng.py +378 -0
- src/orchestrator/tools_kb.py +156 -0
- src/orchestrator/tools_siem.py +1709 -0
- src/web/__init__.py +8 -0
- src/web/config_server.py +511 -0
src/mcp/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP (Model Context Protocol) server and rules engine for SamiGPT.
|
|
3
|
+
|
|
4
|
+
This package contains:
|
|
5
|
+
- mcp_server.py: MCP server implementation that exposes SamiGPT skills as tools
|
|
6
|
+
- rules_engine.py: Rules/workflow engine for automated investigations
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .mcp_server import SamiGPTMCPServer, configure_mcp_logging
|
|
10
|
+
from .rules_engine import RulesEngine
|
|
11
|
+
|
|
12
|
+
__all__ = ["SamiGPTMCPServer", "configure_mcp_logging", "RulesEngine"]
|
|
13
|
+
|
src/mcp/__main__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Entry point for the MCP server."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from .mcp_server import main
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def cli():
|
|
10
|
+
"""Synchronous entry point for the MCP server."""
|
|
11
|
+
try:
|
|
12
|
+
asyncio.run(main())
|
|
13
|
+
except KeyboardInterrupt:
|
|
14
|
+
sys.exit(0)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if __name__ == "__main__":
|
|
18
|
+
cli()
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent Profile Manager for SOC tier-based agent configuration.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
from ..core.errors import IntegrationError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class DecisionAuthority:
|
|
17
|
+
"""Decision authority for an agent."""
|
|
18
|
+
close_false_positives: bool = False
|
|
19
|
+
close_benign_true_positives: bool = False
|
|
20
|
+
escalate_to_soc2: bool = False
|
|
21
|
+
escalate_to_soc3: bool = False
|
|
22
|
+
containment_actions: bool = False
|
|
23
|
+
forensic_collection: bool = False
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class AgentProfile:
|
|
28
|
+
"""Represents an agent profile configuration."""
|
|
29
|
+
name: str
|
|
30
|
+
tier: str # "soc1", "soc2", "soc3"
|
|
31
|
+
description: str
|
|
32
|
+
capabilities: List[str]
|
|
33
|
+
runbooks: List[str]
|
|
34
|
+
decision_authority: DecisionAuthority
|
|
35
|
+
auto_select_runbook: bool = True
|
|
36
|
+
max_concurrent_cases: int = 10
|
|
37
|
+
|
|
38
|
+
def get_starting_runbook(self) -> Optional[str]:
|
|
39
|
+
"""
|
|
40
|
+
Get the starting runbook for this agent profile.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Starting runbook path, or None if not defined
|
|
44
|
+
"""
|
|
45
|
+
# Starting runbooks are the first/main runbook for each tier
|
|
46
|
+
starting_runbooks = {
|
|
47
|
+
"soc1": "soc1/triage/initial_alert_triage",
|
|
48
|
+
"soc2": "soc2/investigation/case_analysis",
|
|
49
|
+
# SOC3 doesn't have a single starting runbook - it's action-based
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if self.tier in starting_runbooks:
|
|
53
|
+
return starting_runbooks[self.tier]
|
|
54
|
+
|
|
55
|
+
# For SOC3 or other tiers, return first runbook if available
|
|
56
|
+
if self.runbooks:
|
|
57
|
+
return self.runbooks[0]
|
|
58
|
+
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
def can_execute_runbook(self, runbook_name: str) -> bool:
|
|
62
|
+
"""Check if agent can execute a runbook."""
|
|
63
|
+
# Check if runbook is in agent's runbook list
|
|
64
|
+
if runbook_name in self.runbooks:
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
# Check if runbook matches agent's tier (includes case-specific runbooks like soc2/cases/malware_deep_analysis)
|
|
68
|
+
if f"/{self.tier}/" in runbook_name:
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
# Check case-specific runbooks (e.g., soc2/cases/malware_deep_analysis)
|
|
72
|
+
# Case-specific runbooks are sub-runbooks that can be executed by the same tier
|
|
73
|
+
if f"/{self.tier}/cases/" in runbook_name:
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
def select_runbook_for_alert(self, alert_type: str, alert_details: Dict[str, Any]) -> Optional[str]:
|
|
79
|
+
"""
|
|
80
|
+
Auto-select appropriate runbook based on alert type.
|
|
81
|
+
|
|
82
|
+
For SOC1 and SOC2, returns the starting runbook.
|
|
83
|
+
For SOC3, selects based on required action.
|
|
84
|
+
"""
|
|
85
|
+
if not self.auto_select_runbook:
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
# For SOC1 and SOC2, use the starting runbook
|
|
89
|
+
if self.tier in ["soc1", "soc2"]:
|
|
90
|
+
return self.get_starting_runbook()
|
|
91
|
+
|
|
92
|
+
# For SOC3 - select based on required action
|
|
93
|
+
if self.tier == "soc3":
|
|
94
|
+
recommended_actions = alert_details.get("recommended_actions", [])
|
|
95
|
+
if isinstance(recommended_actions, str):
|
|
96
|
+
recommended_actions = [recommended_actions]
|
|
97
|
+
|
|
98
|
+
if any("isolate" in str(action).lower() for action in recommended_actions):
|
|
99
|
+
return "soc3/response/endpoint_isolation"
|
|
100
|
+
elif any("terminate" in str(action).lower() for action in recommended_actions):
|
|
101
|
+
return "soc3/response/process_termination"
|
|
102
|
+
elif any("forensic" in str(action).lower() for action in recommended_actions):
|
|
103
|
+
return "soc3/forensics/artifact_collection"
|
|
104
|
+
|
|
105
|
+
# Default: return starting runbook (first runbook)
|
|
106
|
+
return self.get_starting_runbook()
|
|
107
|
+
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class AgentProfileManager:
|
|
112
|
+
"""Manages agent profiles and routing."""
|
|
113
|
+
|
|
114
|
+
def __init__(self, config_path: Optional[str] = None):
|
|
115
|
+
"""
|
|
116
|
+
Initialize agent profile manager.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
config_path: Path to agent profiles config file.
|
|
120
|
+
"""
|
|
121
|
+
if config_path is None:
|
|
122
|
+
# Default to config/agent_profiles.json relative to project root
|
|
123
|
+
project_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
|
124
|
+
config_path = os.path.join(project_root, "config", "agent_profiles.json")
|
|
125
|
+
|
|
126
|
+
self.config_path = config_path
|
|
127
|
+
self.profiles: Dict[str, AgentProfile] = {}
|
|
128
|
+
self.routing_rules: Dict[str, str] = {}
|
|
129
|
+
self._load_profiles()
|
|
130
|
+
|
|
131
|
+
def _load_profiles(self) -> None:
|
|
132
|
+
"""Load agent profiles from config file."""
|
|
133
|
+
if not os.path.exists(self.config_path):
|
|
134
|
+
# Create default profiles
|
|
135
|
+
self._create_default_profiles()
|
|
136
|
+
# Save default config
|
|
137
|
+
self._save_default_config()
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
with open(self.config_path, "r") as f:
|
|
142
|
+
config = json.load(f)
|
|
143
|
+
except Exception as e:
|
|
144
|
+
raise IntegrationError(f"Failed to load agent profiles config: {e}")
|
|
145
|
+
|
|
146
|
+
# Load agent profiles
|
|
147
|
+
for agent_id, agent_config in config.get("agents", {}).items():
|
|
148
|
+
decision_auth = DecisionAuthority(
|
|
149
|
+
**agent_config.get("decision_authority", {})
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
profile = AgentProfile(
|
|
153
|
+
name=agent_config["name"],
|
|
154
|
+
tier=agent_config["tier"],
|
|
155
|
+
description=agent_config.get("description", ""),
|
|
156
|
+
capabilities=agent_config.get("capabilities", []),
|
|
157
|
+
runbooks=agent_config.get("runbooks", []),
|
|
158
|
+
decision_authority=decision_auth,
|
|
159
|
+
auto_select_runbook=agent_config.get("auto_select_runbook", True),
|
|
160
|
+
max_concurrent_cases=agent_config.get("max_concurrent_cases", 10)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
self.profiles[agent_id] = profile
|
|
164
|
+
|
|
165
|
+
# Load routing rules
|
|
166
|
+
self.routing_rules = config.get("routing_rules", {})
|
|
167
|
+
|
|
168
|
+
def _create_default_profiles(self) -> None:
|
|
169
|
+
"""Create default agent profiles."""
|
|
170
|
+
# SOC1 Profile
|
|
171
|
+
soc1_profile = AgentProfile(
|
|
172
|
+
name="SOC1 Triage Agent",
|
|
173
|
+
tier="soc1",
|
|
174
|
+
description="Handles initial alert triage and false positive identification",
|
|
175
|
+
capabilities=["initial_triage", "basic_enrichment", "false_positive_identification"],
|
|
176
|
+
runbooks=[
|
|
177
|
+
"soc1/triage/initial_alert_triage",
|
|
178
|
+
"soc1/enrichment/ioc_enrichment",
|
|
179
|
+
"soc1/remediation/close_false_positive"
|
|
180
|
+
],
|
|
181
|
+
decision_authority=DecisionAuthority(
|
|
182
|
+
close_false_positives=True,
|
|
183
|
+
close_benign_true_positives=True,
|
|
184
|
+
escalate_to_soc2=True,
|
|
185
|
+
escalate_to_soc3=False,
|
|
186
|
+
containment_actions=False,
|
|
187
|
+
forensic_collection=False
|
|
188
|
+
),
|
|
189
|
+
auto_select_runbook=True,
|
|
190
|
+
max_concurrent_cases=10
|
|
191
|
+
)
|
|
192
|
+
self.profiles["soc1_triage_agent"] = soc1_profile
|
|
193
|
+
|
|
194
|
+
# SOC2 Profile
|
|
195
|
+
soc2_profile = AgentProfile(
|
|
196
|
+
name="SOC2 Investigation Agent",
|
|
197
|
+
tier="soc2",
|
|
198
|
+
description="Performs deep investigation and correlation analysis",
|
|
199
|
+
capabilities=["deep_investigation", "correlation_analysis", "threat_hunting", "containment_recommendations"],
|
|
200
|
+
runbooks=[
|
|
201
|
+
"soc2/investigation/case_analysis"
|
|
202
|
+
],
|
|
203
|
+
decision_authority=DecisionAuthority(
|
|
204
|
+
close_false_positives=True,
|
|
205
|
+
close_benign_true_positives=True,
|
|
206
|
+
escalate_to_soc2=False,
|
|
207
|
+
escalate_to_soc3=True,
|
|
208
|
+
containment_actions=False,
|
|
209
|
+
forensic_collection=False
|
|
210
|
+
),
|
|
211
|
+
auto_select_runbook=True,
|
|
212
|
+
max_concurrent_cases=5
|
|
213
|
+
)
|
|
214
|
+
self.profiles["soc2_investigation_agent"] = soc2_profile
|
|
215
|
+
|
|
216
|
+
# SOC3 Profile
|
|
217
|
+
soc3_profile = AgentProfile(
|
|
218
|
+
name="SOC3 Response Agent",
|
|
219
|
+
tier="soc3",
|
|
220
|
+
description="Executes incident response and containment actions",
|
|
221
|
+
capabilities=["incident_response", "containment_execution", "forensic_collection"],
|
|
222
|
+
runbooks=[
|
|
223
|
+
"soc3/response/endpoint_isolation",
|
|
224
|
+
"soc3/response/process_termination",
|
|
225
|
+
"soc3/forensics/artifact_collection"
|
|
226
|
+
],
|
|
227
|
+
decision_authority=DecisionAuthority(
|
|
228
|
+
close_false_positives=True,
|
|
229
|
+
close_benign_true_positives=True,
|
|
230
|
+
escalate_to_soc2=False,
|
|
231
|
+
escalate_to_soc3=False,
|
|
232
|
+
containment_actions=True,
|
|
233
|
+
forensic_collection=True
|
|
234
|
+
),
|
|
235
|
+
auto_select_runbook=True,
|
|
236
|
+
max_concurrent_cases=3
|
|
237
|
+
)
|
|
238
|
+
self.profiles["soc3_response_agent"] = soc3_profile
|
|
239
|
+
|
|
240
|
+
def _save_default_config(self) -> None:
|
|
241
|
+
"""Save default configuration to file."""
|
|
242
|
+
config = {
|
|
243
|
+
"agents": {
|
|
244
|
+
"soc1_triage_agent": {
|
|
245
|
+
"name": "SOC1 Triage Agent",
|
|
246
|
+
"tier": "soc1",
|
|
247
|
+
"description": "Handles initial alert triage and false positive identification",
|
|
248
|
+
"capabilities": ["initial_triage", "basic_enrichment", "false_positive_identification"],
|
|
249
|
+
"runbooks": [
|
|
250
|
+
"soc1/triage/initial_alert_triage",
|
|
251
|
+
"soc1/enrichment/ioc_enrichment",
|
|
252
|
+
"soc1/remediation/close_false_positive"
|
|
253
|
+
],
|
|
254
|
+
"case_runbooks": [
|
|
255
|
+
"soc1/cases/suspicious_login_triage",
|
|
256
|
+
"soc1/cases/malware_initial_triage"
|
|
257
|
+
],
|
|
258
|
+
"decision_authority": {
|
|
259
|
+
"close_false_positives": True,
|
|
260
|
+
"close_benign_true_positives": True,
|
|
261
|
+
"escalate_to_soc2": True,
|
|
262
|
+
"escalate_to_soc3": False,
|
|
263
|
+
"containment_actions": False,
|
|
264
|
+
"forensic_collection": False
|
|
265
|
+
},
|
|
266
|
+
"auto_select_runbook": True,
|
|
267
|
+
"max_concurrent_cases": 10
|
|
268
|
+
},
|
|
269
|
+
"soc2_investigation_agent": {
|
|
270
|
+
"name": "SOC2 Investigation Agent",
|
|
271
|
+
"tier": "soc2",
|
|
272
|
+
"description": "Performs deep investigation and correlation analysis",
|
|
273
|
+
"capabilities": ["deep_investigation", "correlation_analysis", "threat_hunting", "containment_recommendations"],
|
|
274
|
+
"runbooks": [
|
|
275
|
+
"soc2/investigation/case_analysis"
|
|
276
|
+
],
|
|
277
|
+
"case_runbooks": [
|
|
278
|
+
"soc2/cases/malware_deep_analysis",
|
|
279
|
+
"soc2/cases/suspicious_login_investigation"
|
|
280
|
+
],
|
|
281
|
+
"decision_authority": {
|
|
282
|
+
"close_false_positives": True,
|
|
283
|
+
"close_benign_true_positives": True,
|
|
284
|
+
"escalate_to_soc2": False,
|
|
285
|
+
"escalate_to_soc3": True,
|
|
286
|
+
"containment_actions": False,
|
|
287
|
+
"forensic_collection": False
|
|
288
|
+
},
|
|
289
|
+
"auto_select_runbook": True,
|
|
290
|
+
"max_concurrent_cases": 5
|
|
291
|
+
},
|
|
292
|
+
"soc3_response_agent": {
|
|
293
|
+
"name": "SOC3 Response Agent",
|
|
294
|
+
"tier": "soc3",
|
|
295
|
+
"description": "Executes incident response and containment actions",
|
|
296
|
+
"capabilities": ["incident_response", "containment_execution", "forensic_collection"],
|
|
297
|
+
"runbooks": [
|
|
298
|
+
"soc3/response/endpoint_isolation",
|
|
299
|
+
"soc3/response/process_termination",
|
|
300
|
+
"soc3/forensics/artifact_collection"
|
|
301
|
+
],
|
|
302
|
+
"decision_authority": {
|
|
303
|
+
"close_false_positives": True,
|
|
304
|
+
"close_benign_true_positives": True,
|
|
305
|
+
"escalate_to_soc2": False,
|
|
306
|
+
"escalate_to_soc3": False,
|
|
307
|
+
"containment_actions": True,
|
|
308
|
+
"forensic_collection": True
|
|
309
|
+
},
|
|
310
|
+
"auto_select_runbook": True,
|
|
311
|
+
"max_concurrent_cases": 3
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
"routing_rules": {
|
|
315
|
+
"new_alert": "soc1_triage_agent",
|
|
316
|
+
"review_cases": "soc2_investigation_agent",
|
|
317
|
+
"requires_containment": "soc3_response_agent",
|
|
318
|
+
"forensic_collection": "soc3_response_agent"
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
# Ensure config directory exists
|
|
323
|
+
os.makedirs(os.path.dirname(self.config_path), exist_ok=True)
|
|
324
|
+
|
|
325
|
+
with open(self.config_path, "w") as f:
|
|
326
|
+
json.dump(config, f, indent=2)
|
|
327
|
+
|
|
328
|
+
def get_profile(self, agent_id: str) -> Optional[AgentProfile]:
|
|
329
|
+
"""Get agent profile by ID."""
|
|
330
|
+
return self.profiles.get(agent_id)
|
|
331
|
+
|
|
332
|
+
def list_profiles(self) -> List[Dict[str, Any]]:
|
|
333
|
+
"""List all agent profiles."""
|
|
334
|
+
return [
|
|
335
|
+
{
|
|
336
|
+
"agent_id": agent_id,
|
|
337
|
+
"name": profile.name,
|
|
338
|
+
"tier": profile.tier,
|
|
339
|
+
"description": profile.description,
|
|
340
|
+
"capabilities": profile.capabilities,
|
|
341
|
+
"runbook_count": len(profile.runbooks),
|
|
342
|
+
"decision_authority": {
|
|
343
|
+
"close_false_positives": profile.decision_authority.close_false_positives,
|
|
344
|
+
"escalate_to_soc2": profile.decision_authority.escalate_to_soc2,
|
|
345
|
+
"escalate_to_soc3": profile.decision_authority.escalate_to_soc3,
|
|
346
|
+
"containment_actions": profile.decision_authority.containment_actions,
|
|
347
|
+
"forensic_collection": profile.decision_authority.forensic_collection
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
for agent_id, profile in self.profiles.items()
|
|
351
|
+
]
|
|
352
|
+
|
|
353
|
+
def route_to_agent(
|
|
354
|
+
self,
|
|
355
|
+
case_id: Optional[str] = None,
|
|
356
|
+
alert_id: Optional[str] = None,
|
|
357
|
+
alert_type: Optional[str] = None,
|
|
358
|
+
case_status: Optional[str] = None
|
|
359
|
+
) -> Optional[str]:
|
|
360
|
+
"""
|
|
361
|
+
Route a case/alert to appropriate agent.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
case_id: Case ID
|
|
365
|
+
alert_id: Alert ID
|
|
366
|
+
alert_type: Type of alert
|
|
367
|
+
case_status: Current case status
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
Agent ID to handle the case/alert
|
|
371
|
+
"""
|
|
372
|
+
# Check routing rules
|
|
373
|
+
# Priority 1: Response actions (SOC3) - containment and forensic collection
|
|
374
|
+
if case_status and "containment" in case_status.lower():
|
|
375
|
+
return self.routing_rules.get("requires_containment", "soc3_response_agent")
|
|
376
|
+
|
|
377
|
+
if case_status and "forensic" in case_status.lower():
|
|
378
|
+
return self.routing_rules.get("forensic_collection", "soc3_response_agent")
|
|
379
|
+
|
|
380
|
+
# Priority 2: Case review (SOC2) - SOC2 always starts by reviewing cases
|
|
381
|
+
if case_id:
|
|
382
|
+
return self.routing_rules.get("review_cases", "soc2_investigation_agent")
|
|
383
|
+
|
|
384
|
+
# Priority 3: New alerts (SOC1) - default for new alerts without cases
|
|
385
|
+
return self.routing_rules.get("new_alert", "soc1_triage_agent")
|
|
386
|
+
|
|
387
|
+
def get_agent_for_tier(self, tier: str) -> Optional[AgentProfile]:
|
|
388
|
+
"""Get agent profile for a specific SOC tier."""
|
|
389
|
+
for profile in self.profiles.values():
|
|
390
|
+
if profile.tier == tier:
|
|
391
|
+
return profile
|
|
392
|
+
return None
|
|
393
|
+
|
|
394
|
+
def get_starting_runbook_for_tier(self, tier: str) -> Optional[str]:
|
|
395
|
+
"""
|
|
396
|
+
Get the starting runbook for a specific SOC tier.
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
tier: SOC tier (soc1, soc2, soc3)
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
Starting runbook path, or None if not found
|
|
403
|
+
"""
|
|
404
|
+
profile = self.get_agent_for_tier(tier)
|
|
405
|
+
if profile:
|
|
406
|
+
return profile.get_starting_runbook()
|
|
407
|
+
return None
|
|
408
|
+
|