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.
Files changed (85) hide show
  1. iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/METADATA +410 -0
  2. iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/RECORD +85 -0
  3. iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/WHEEL +5 -0
  4. iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/entry_points.txt +2 -0
  5. iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/licenses/LICENSE +21 -0
  6. iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/top_level.txt +1 -0
  7. src/__init__.py +8 -0
  8. src/ai_controller/README.md +139 -0
  9. src/ai_controller/__init__.py +12 -0
  10. src/ai_controller/agent_executor.py +596 -0
  11. src/ai_controller/cli/__init__.py +2 -0
  12. src/ai_controller/cli/main.py +243 -0
  13. src/ai_controller/session_manager.py +409 -0
  14. src/ai_controller/web/__init__.py +2 -0
  15. src/ai_controller/web/server.py +1181 -0
  16. src/ai_controller/web/static/css/README.md +102 -0
  17. src/api/__init__.py +13 -0
  18. src/api/case_management.py +271 -0
  19. src/api/edr.py +187 -0
  20. src/api/kb.py +136 -0
  21. src/api/siem.py +308 -0
  22. src/core/__init__.py +10 -0
  23. src/core/config.py +242 -0
  24. src/core/config_storage.py +684 -0
  25. src/core/dto.py +50 -0
  26. src/core/errors.py +36 -0
  27. src/core/logging.py +128 -0
  28. src/integrations/__init__.py +8 -0
  29. src/integrations/case_management/__init__.py +5 -0
  30. src/integrations/case_management/iris/__init__.py +11 -0
  31. src/integrations/case_management/iris/iris_client.py +885 -0
  32. src/integrations/case_management/iris/iris_http.py +274 -0
  33. src/integrations/case_management/iris/iris_mapper.py +263 -0
  34. src/integrations/case_management/iris/iris_models.py +128 -0
  35. src/integrations/case_management/thehive/__init__.py +8 -0
  36. src/integrations/case_management/thehive/thehive_client.py +193 -0
  37. src/integrations/case_management/thehive/thehive_http.py +147 -0
  38. src/integrations/case_management/thehive/thehive_mapper.py +190 -0
  39. src/integrations/case_management/thehive/thehive_models.py +125 -0
  40. src/integrations/cti/__init__.py +6 -0
  41. src/integrations/cti/local_tip/__init__.py +10 -0
  42. src/integrations/cti/local_tip/local_tip_client.py +90 -0
  43. src/integrations/cti/local_tip/local_tip_http.py +110 -0
  44. src/integrations/cti/opencti/__init__.py +10 -0
  45. src/integrations/cti/opencti/opencti_client.py +101 -0
  46. src/integrations/cti/opencti/opencti_http.py +418 -0
  47. src/integrations/edr/__init__.py +6 -0
  48. src/integrations/edr/elastic_defend/__init__.py +6 -0
  49. src/integrations/edr/elastic_defend/elastic_defend_client.py +351 -0
  50. src/integrations/edr/elastic_defend/elastic_defend_http.py +162 -0
  51. src/integrations/eng/__init__.py +10 -0
  52. src/integrations/eng/clickup/__init__.py +8 -0
  53. src/integrations/eng/clickup/clickup_client.py +513 -0
  54. src/integrations/eng/clickup/clickup_http.py +156 -0
  55. src/integrations/eng/github/__init__.py +8 -0
  56. src/integrations/eng/github/github_client.py +169 -0
  57. src/integrations/eng/github/github_http.py +158 -0
  58. src/integrations/eng/trello/__init__.py +8 -0
  59. src/integrations/eng/trello/trello_client.py +207 -0
  60. src/integrations/eng/trello/trello_http.py +162 -0
  61. src/integrations/kb/__init__.py +12 -0
  62. src/integrations/kb/fs_kb_client.py +313 -0
  63. src/integrations/siem/__init__.py +6 -0
  64. src/integrations/siem/elastic/__init__.py +6 -0
  65. src/integrations/siem/elastic/elastic_client.py +3319 -0
  66. src/integrations/siem/elastic/elastic_http.py +165 -0
  67. src/mcp/README.md +183 -0
  68. src/mcp/TOOLS.md +2827 -0
  69. src/mcp/__init__.py +13 -0
  70. src/mcp/__main__.py +18 -0
  71. src/mcp/agent_profiles.py +408 -0
  72. src/mcp/flow_agent_profiles.py +424 -0
  73. src/mcp/mcp_server.py +4086 -0
  74. src/mcp/rules_engine.py +487 -0
  75. src/mcp/runbook_manager.py +264 -0
  76. src/orchestrator/__init__.py +11 -0
  77. src/orchestrator/incident_workflow.py +244 -0
  78. src/orchestrator/tools_case.py +1085 -0
  79. src/orchestrator/tools_cti.py +359 -0
  80. src/orchestrator/tools_edr.py +315 -0
  81. src/orchestrator/tools_eng.py +378 -0
  82. src/orchestrator/tools_kb.py +156 -0
  83. src/orchestrator/tools_siem.py +1709 -0
  84. src/web/__init__.py +8 -0
  85. src/web/config_server.py +511 -0
@@ -0,0 +1,424 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Script to generate an execution flow diagram for agent profiles defined in
4
+ `config/agent_profiles.json` (as used by `agent_profiles.py`).
5
+
6
+ The script creates Graphviz DOT and SVG files that visualize:
7
+ - The overall Agent Profile Manager
8
+ - Each agent profile, grouped by SOC tier
9
+ - Each agent's starting runbook and additional runbooks
10
+ - Case-specific sub-runbooks that can be called from starting runbooks
11
+ - Routing rules that map events/states to specific agents
12
+ - Example CLI output so an AI can easily understand how the script behaves
13
+
14
+ Output files are written to the top-level `execution_flow/` directory:
15
+ - `execution_flow/agent_profiles_flow.dot`
16
+ - `execution_flow/agent_profiles_flow.svg`
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import os
22
+ import re
23
+ import subprocess
24
+ from pathlib import Path
25
+ from typing import Dict, Any, List
26
+
27
+
28
+ def get_project_root() -> Path:
29
+ """Return the project root based on this file location."""
30
+ # This file lives at: <project_root>/src/mcp/flow_agent_profiles.py
31
+ return Path(__file__).resolve().parents[2]
32
+
33
+
34
+ def load_agent_profiles_config() -> Dict[str, Any]:
35
+ """
36
+ Load the agent profiles configuration.
37
+
38
+ This mirrors the default behavior of `AgentProfileManager`:
39
+ - If `config/agent_profiles.json` exists, load it.
40
+ - Otherwise, fall back to the baked-in default configuration.
41
+ """
42
+ project_root = get_project_root()
43
+ config_path = project_root / "config" / "agent_profiles.json"
44
+
45
+ if config_path.exists():
46
+ import json
47
+
48
+ with config_path.open("r", encoding="utf-8") as f:
49
+ config = json.load(f)
50
+
51
+ return config
52
+
53
+ # Fallback: mirror `_save_default_config` from `agent_profiles.py`
54
+ return {
55
+ "agents": {
56
+ "soc1_triage_agent": {
57
+ "name": "SOC1 Triage Agent",
58
+ "tier": "soc1",
59
+ "description": "Handles initial alert triage and false positive identification",
60
+ "capabilities": [
61
+ "initial_triage",
62
+ "basic_enrichment",
63
+ "false_positive_identification",
64
+ ],
65
+ "runbooks": [
66
+ "soc1/triage/initial_alert_triage",
67
+ "soc1/enrichment/ioc_enrichment",
68
+ "soc1/remediation/close_false_positive",
69
+ ],
70
+ "case_runbooks": [
71
+ "soc1/cases/suspicious_login_triage",
72
+ "soc1/cases/malware_initial_triage",
73
+ ],
74
+ "decision_authority": {
75
+ "close_false_positives": True,
76
+ "close_benign_true_positives": True,
77
+ "escalate_to_soc2": True,
78
+ "escalate_to_soc3": False,
79
+ "containment_actions": False,
80
+ "forensic_collection": False,
81
+ },
82
+ "auto_select_runbook": True,
83
+ "max_concurrent_cases": 10,
84
+ },
85
+ "soc2_investigation_agent": {
86
+ "name": "SOC2 Investigation Agent",
87
+ "tier": "soc2",
88
+ "description": "Performs deep investigation and correlation analysis",
89
+ "capabilities": [
90
+ "deep_investigation",
91
+ "correlation_analysis",
92
+ "threat_hunting",
93
+ "containment_recommendations",
94
+ ],
95
+ "runbooks": [
96
+ "soc2/investigation/case_analysis",
97
+ ],
98
+ "case_runbooks": [
99
+ "soc2/cases/malware_deep_analysis",
100
+ "soc2/cases/suspicious_login_investigation",
101
+ ],
102
+ "decision_authority": {
103
+ "close_false_positives": True,
104
+ "close_benign_true_positives": True,
105
+ "escalate_to_soc2": False,
106
+ "escalate_to_soc3": True,
107
+ "containment_actions": False,
108
+ "forensic_collection": False,
109
+ },
110
+ "auto_select_runbook": True,
111
+ "max_concurrent_cases": 5,
112
+ },
113
+ "soc3_response_agent": {
114
+ "name": "SOC3 Response Agent",
115
+ "tier": "soc3",
116
+ "description": "Executes incident response and containment actions",
117
+ "capabilities": [
118
+ "incident_response",
119
+ "containment_execution",
120
+ "forensic_collection",
121
+ ],
122
+ "runbooks": [
123
+ "soc3/response/endpoint_isolation",
124
+ "soc3/response/process_termination",
125
+ "soc3/forensics/artifact_collection",
126
+ ],
127
+ "decision_authority": {
128
+ "close_false_positives": True,
129
+ "close_benign_true_positives": True,
130
+ "escalate_to_soc2": False,
131
+ "escalate_to_soc3": False,
132
+ "containment_actions": True,
133
+ "forensic_collection": True,
134
+ },
135
+ "auto_select_runbook": True,
136
+ "max_concurrent_cases": 3,
137
+ },
138
+ },
139
+ "routing_rules": {
140
+ "new_alert": "soc1_triage_agent",
141
+ "review_cases": "soc2_investigation_agent",
142
+ "requires_containment": "soc3_response_agent",
143
+ "forensic_collection": "soc3_response_agent",
144
+ },
145
+ }
146
+
147
+
148
+ def sanitize_id(prefix: str, value: str) -> str:
149
+ """Return a Graphviz-safe node ID based on a prefix and raw value."""
150
+ safe = re.sub(r"[^a-zA-Z0-9_]", "_", value)
151
+ return f"{prefix}_{safe}"
152
+
153
+
154
+ def build_example_output_lines(
155
+ output_dir: Path, dot_file: Path, svg_file: Path, config_source: str
156
+ ) -> List[str]:
157
+ """
158
+ Build example CLI output lines to embed in the DOT/SVG for AI consumption.
159
+
160
+ These are examples of what the script prints when run, not necessarily an
161
+ exact capture of any particular execution.
162
+ """
163
+ rel_output_dir = os.path.relpath(str(output_dir), str(get_project_root()))
164
+ rel_dot = os.path.relpath(str(dot_file), str(get_project_root()))
165
+ rel_svg = os.path.relpath(str(svg_file), str(get_project_root()))
166
+
167
+ return [
168
+ "python src/mcp/flow_agent_profiles.py",
169
+ f"Reading agent profiles config from: {config_source}",
170
+ "✓ Loaded 3 agent profiles",
171
+ "✓ Loaded 4 routing rules",
172
+ f"Generating DOT and SVG in: {rel_output_dir}",
173
+ f"✓ DOT file created: {rel_dot}",
174
+ f"✓ SVG file created: {rel_svg}",
175
+ "✓ Agent profiles flow diagram generation complete!",
176
+ ]
177
+
178
+
179
+ def create_dot_file(config: Dict[str, Any], output_dir: Path) -> Path:
180
+ """Create a Graphviz DOT file that visualizes the agent profiles flow."""
181
+ agents = config.get("agents", {})
182
+ routing_rules = config.get("routing_rules", {})
183
+
184
+ dot_file = output_dir / "agent_profiles_flow.dot"
185
+ svg_file = output_dir / "agent_profiles_flow.svg"
186
+
187
+ # Determine where config came from (for example output text)
188
+ project_root = get_project_root()
189
+ real_config_path = project_root / "config" / "agent_profiles.json"
190
+ if real_config_path.exists():
191
+ config_source = os.path.relpath(str(real_config_path), str(project_root))
192
+ else:
193
+ config_source = "<default in code (no config/agent_profiles.json found)>"
194
+
195
+ example_output_lines = build_example_output_lines(
196
+ output_dir=output_dir,
197
+ dot_file=dot_file,
198
+ svg_file=svg_file,
199
+ config_source=config_source,
200
+ )
201
+
202
+ dot_lines: List[str] = [
203
+ "digraph AgentProfilesFlow {",
204
+ ' rankdir=LR;',
205
+ ' node [shape=box, style=rounded, fontname="Arial"];',
206
+ ' edge [fontname="Arial"];',
207
+ "",
208
+ " // Agent Profile Manager entry point",
209
+ ' manager [label="AgentProfileManager\\n(loads config, routes cases)", '
210
+ 'shape=ellipse, style=filled, fillcolor=lightgreen];',
211
+ "",
212
+ " // Example CLI output for AI understanding of this script",
213
+ ]
214
+
215
+ # Embed example CLI output as comments (these are preserved in the DOT and SVG)
216
+ for line in example_output_lines:
217
+ dot_lines.append(f" // {line}")
218
+
219
+ dot_lines.extend(
220
+ [
221
+ "",
222
+ " // Also expose example CLI output as a dedicated node for visualization",
223
+ ]
224
+ )
225
+ example_label = "\\n".join(example_output_lines).replace('"', '\\"')
226
+ dot_lines.extend(
227
+ [
228
+ " subgraph cluster_example_output {",
229
+ ' label="Example CLI Output (for AI)";',
230
+ " style=dashed;",
231
+ " color=gray;",
232
+ ' fontname="Arial";',
233
+ f' example_output [shape=note, style=filled, fillcolor=lightgray, '
234
+ f'fontname="Courier New", label="{example_label}"];',
235
+ " }",
236
+ "",
237
+ " // Agent profiles grouped by SOC tier",
238
+ ]
239
+ )
240
+
241
+ # Group agents by tier and create nodes
242
+ tier_colors = {
243
+ "soc1": "lightblue",
244
+ "soc2": "lightyellow",
245
+ "soc3": "lightcoral",
246
+ }
247
+
248
+ agent_node_ids: Dict[str, str] = {}
249
+
250
+ for agent_id, agent_cfg in agents.items():
251
+ tier = agent_cfg.get("tier", "unknown")
252
+ color = tier_colors.get(tier, "white")
253
+ name = agent_cfg.get("name", agent_id)
254
+ description = agent_cfg.get("description", "")
255
+ runbooks = agent_cfg.get("runbooks", [])
256
+
257
+ label_lines = [
258
+ f"{agent_id}",
259
+ f"{name}",
260
+ f"[{tier.upper()}]",
261
+ ]
262
+ if description:
263
+ label_lines.append(description)
264
+ label = "\\n".join(label_lines).replace('"', '\\"')
265
+
266
+ node_id = sanitize_id("agent", agent_id)
267
+ agent_node_ids[agent_id] = node_id
268
+
269
+ dot_lines.append(
270
+ f' {node_id} [label="{label}", style=filled, fillcolor={color}];'
271
+ )
272
+ dot_lines.append(f" manager -> {node_id};")
273
+
274
+ # Main runbooks as separate nodes (connected directly to agent)
275
+ for rb in runbooks:
276
+ rb_id = sanitize_id("runbook", rb)
277
+ rb_label = rb.replace('"', '\\"')
278
+ dot_lines.append(
279
+ f' {rb_id} [label="{rb_label}", shape=note, style=filled, '
280
+ f'fillcolor=white];'
281
+ )
282
+ dot_lines.append(f" {node_id} -> {rb_id};")
283
+
284
+ # Case runbooks as sub-runbooks (connected to main runbooks, shown with different style)
285
+ case_runbooks = agent_cfg.get("case_runbooks", [])
286
+ if case_runbooks:
287
+ # For SOC1, case runbooks connect to initial_alert_triage (the starting runbook)
288
+ # For SOC2, case runbooks connect to case_analysis (the starting runbook)
289
+ main_runbook_id = None
290
+ if tier == "soc1":
291
+ # Find initial_alert_triage runbook (SOC1 starting runbook)
292
+ for rb in runbooks:
293
+ if "initial_alert_triage" in rb:
294
+ main_runbook_id = sanitize_id("runbook", rb)
295
+ break
296
+ elif tier == "soc2":
297
+ # Find case_analysis runbook (SOC2 starting runbook)
298
+ for rb in runbooks:
299
+ if "case_analysis" in rb:
300
+ main_runbook_id = sanitize_id("runbook", rb)
301
+ break
302
+
303
+ if main_runbook_id:
304
+ for case_rb in case_runbooks:
305
+ case_rb_id = sanitize_id("case_runbook", case_rb)
306
+ case_rb_label = case_rb.replace('"', '\\"')
307
+ # Show case runbooks with different style (smaller, different color)
308
+ dot_lines.append(
309
+ f' {case_rb_id} [label="{case_rb_label}", shape=note, '
310
+ f'style=filled, fillcolor=lightcyan, fontsize=10];'
311
+ )
312
+ # Connect case runbook to main runbook with dashed line to show hierarchy
313
+ dot_lines.append(
314
+ f" {main_runbook_id} -> {case_rb_id} [style=dashed, color=gray, "
315
+ f'label="can call"];'
316
+ )
317
+
318
+ dot_lines.append("") # spacing between agents
319
+
320
+ # Routing rules section
321
+ if routing_rules:
322
+ dot_lines.append(" // Routing rules -> agents")
323
+ for rule_name, target_agent in routing_rules.items():
324
+ rule_id = sanitize_id("route", rule_name)
325
+ rule_label = rule_name.replace("_", " ").title().replace('"', '\\"')
326
+ dot_lines.append(
327
+ f' {rule_id} [label="Routing Rule\\n{rule_label}", '
328
+ f'shape=diamond, style=filled, fillcolor=lightgray];'
329
+ )
330
+ dot_lines.append(f" manager -> {rule_id};")
331
+
332
+ target_node = agent_node_ids.get(target_agent)
333
+ if target_node:
334
+ dot_lines.append(f" {rule_id} -> {target_node};")
335
+ else:
336
+ # Fallback if config references an unknown agent
337
+ unknown_id = sanitize_id("agent_missing", target_agent)
338
+ dot_lines.append(
339
+ f' {unknown_id} [label="Missing Agent\\n{target_agent}", '
340
+ f'shape=box, style=filled, fillcolor=red];'
341
+ )
342
+ dot_lines.append(f" {rule_id} -> {unknown_id};")
343
+
344
+ dot_lines.append("}")
345
+
346
+ dot_file.write_text("\n".join(dot_lines), encoding="utf-8")
347
+ return dot_file
348
+
349
+
350
+ def generate_svg(dot_file: Path, output_dir: Path) -> Path | None:
351
+ """Generate an SVG file from a DOT file using Graphviz."""
352
+ svg_file = output_dir / "agent_profiles_flow.svg"
353
+
354
+ try:
355
+ subprocess.run(
356
+ ["dot", "-Tsvg", "-o", str(svg_file), str(dot_file)],
357
+ capture_output=True,
358
+ text=True,
359
+ check=True,
360
+ )
361
+ print(f"✓ SVG file generated: {svg_file}")
362
+ return svg_file
363
+ except subprocess.CalledProcessError as e:
364
+ print(f"✗ Error generating SVG: {e.stderr}")
365
+ print(
366
+ "Make sure Graphviz is installed: "
367
+ "brew install graphviz (macOS) or apt-get install graphviz (Linux)"
368
+ )
369
+ return None
370
+ except FileNotFoundError:
371
+ print("✗ Graphviz 'dot' command not found.")
372
+ print(
373
+ "Install Graphviz: "
374
+ "brew install graphviz (macOS) or apt-get install graphviz (Linux)"
375
+ )
376
+ return None
377
+
378
+
379
+ def main() -> int:
380
+ """Main entry point to generate the agent profiles flow diagram."""
381
+ project_root = get_project_root()
382
+ output_dir = project_root / "execution_flow"
383
+ output_dir.mkdir(parents=True, exist_ok=True)
384
+
385
+ config_path = project_root / "config" / "agent_profiles.json"
386
+ if config_path.exists():
387
+ print(f"Reading agent profiles config: {config_path}")
388
+ else:
389
+ print(
390
+ "No config/agent_profiles.json found – using default in-code profiles "
391
+ "(as defined in src/mcp/agent_profiles.py)."
392
+ )
393
+
394
+ config = load_agent_profiles_config()
395
+ agents = config.get("agents", {})
396
+ routing_rules = config.get("routing_rules", {})
397
+
398
+ print(f"✓ Loaded {len(agents)} agent profiles")
399
+ print(f"✓ Loaded {len(routing_rules)} routing rules")
400
+ print(f"Generating DOT and SVG files in: {output_dir}")
401
+
402
+ dot_file = create_dot_file(config, output_dir)
403
+ print(f"✓ DOT file created: {dot_file}")
404
+
405
+ svg_file = generate_svg(dot_file, output_dir)
406
+
407
+ if svg_file:
408
+ print("\n✓ Agent profiles flow diagram generation complete!")
409
+ print(f" - DOT file: {dot_file}")
410
+ print(f" - SVG file: {svg_file}")
411
+ return 0
412
+
413
+ print(
414
+ "\n⚠ DOT file created but SVG generation failed. "
415
+ "You can manually convert it using:"
416
+ )
417
+ print(f" dot -Tsvg {dot_file} -o {output_dir / 'agent_profiles_flow.svg'}")
418
+ return 1
419
+
420
+
421
+ if __name__ == "__main__":
422
+ raise SystemExit(main())
423
+
424
+