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
|
@@ -0,0 +1,1085 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LLM-callable tools for case management operations.
|
|
3
|
+
|
|
4
|
+
These functions wrap the generic CaseManagementClient interface and provide
|
|
5
|
+
LLM-friendly error handling and return values.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
from ..api.case_management import (
|
|
13
|
+
Case,
|
|
14
|
+
CaseAssignment,
|
|
15
|
+
CaseComment,
|
|
16
|
+
CaseManagementClient,
|
|
17
|
+
CaseObservable,
|
|
18
|
+
CasePriority,
|
|
19
|
+
CaseSearchQuery,
|
|
20
|
+
CaseStatus,
|
|
21
|
+
CaseSummary,
|
|
22
|
+
)
|
|
23
|
+
from ..core.errors import IntegrationError
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def create_case(
|
|
27
|
+
title: str,
|
|
28
|
+
description: str,
|
|
29
|
+
priority: str = "medium",
|
|
30
|
+
status: str = "open",
|
|
31
|
+
tags: Optional[List[str]] = None,
|
|
32
|
+
alert_id: Optional[str] = None,
|
|
33
|
+
client: CaseManagementClient = None, # type: ignore
|
|
34
|
+
) -> Dict[str, Any]:
|
|
35
|
+
"""
|
|
36
|
+
Create a new case.
|
|
37
|
+
|
|
38
|
+
Tool schema:
|
|
39
|
+
- name: create_case
|
|
40
|
+
- description: Create a new case for investigation. Follows the case standard
|
|
41
|
+
format defined in standards/case_standard.md.
|
|
42
|
+
- parameters:
|
|
43
|
+
- title (str, required): Case title following format: [Alert Type] - [Primary Entity] - [Date/Time]
|
|
44
|
+
- description (str, required): Comprehensive case description
|
|
45
|
+
- priority (str, optional): Case priority (low, medium, high, critical). Default: medium
|
|
46
|
+
- status (str, optional): Case status (open, in_progress, closed). Default: open
|
|
47
|
+
- tags (list[str], optional): Tags for categorization
|
|
48
|
+
- alert_id (str, optional): Associated alert ID if case is created from an alert
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
title: Case title.
|
|
52
|
+
description: Case description.
|
|
53
|
+
priority: Case priority.
|
|
54
|
+
status: Case status.
|
|
55
|
+
tags: Optional tags.
|
|
56
|
+
alert_id: Optional associated alert ID.
|
|
57
|
+
client: The case management client.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Dictionary containing created case details.
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
IntegrationError: If case creation fails.
|
|
64
|
+
"""
|
|
65
|
+
if client is None:
|
|
66
|
+
raise IntegrationError("Case management client not provided")
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
priority_enum = CasePriority(priority.lower())
|
|
70
|
+
status_enum = CaseStatus(status.lower())
|
|
71
|
+
|
|
72
|
+
new_case = Case(
|
|
73
|
+
id=None,
|
|
74
|
+
title=title,
|
|
75
|
+
description=description,
|
|
76
|
+
status=status_enum,
|
|
77
|
+
priority=priority_enum,
|
|
78
|
+
tags=tags,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
created = client.create_case(new_case)
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
"success": True,
|
|
85
|
+
"case_id": created.id,
|
|
86
|
+
"case": {
|
|
87
|
+
"id": created.id,
|
|
88
|
+
"title": created.title,
|
|
89
|
+
"description": created.description,
|
|
90
|
+
"status": created.status.value,
|
|
91
|
+
"priority": created.priority.value,
|
|
92
|
+
"tags": created.tags or [],
|
|
93
|
+
"created_at": created.created_at.isoformat() if created.created_at else None,
|
|
94
|
+
"updated_at": created.updated_at.isoformat() if created.updated_at else None,
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
except ValueError as e:
|
|
98
|
+
raise IntegrationError(f"Invalid priority or status: {str(e)}")
|
|
99
|
+
except Exception as e:
|
|
100
|
+
raise IntegrationError(f"Failed to create case: {str(e)}") from e
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def review_case(
|
|
104
|
+
case_id: str,
|
|
105
|
+
client: CaseManagementClient = None, # type: ignore
|
|
106
|
+
) -> Dict[str, Any]:
|
|
107
|
+
"""
|
|
108
|
+
Review a case by retrieving its full details.
|
|
109
|
+
|
|
110
|
+
Tool schema:
|
|
111
|
+
- name: review_case
|
|
112
|
+
- description: Retrieve and review the full details of a case including
|
|
113
|
+
title, description, status, priority, observables, and comments.
|
|
114
|
+
- parameters:
|
|
115
|
+
- case_id (str, required): The ID of the case to review.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
case_id: The ID of the case to review.
|
|
119
|
+
client: The case management client.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Dictionary containing case details in a format suitable for LLM consumption.
|
|
123
|
+
|
|
124
|
+
Raises:
|
|
125
|
+
IntegrationError: If the case cannot be retrieved.
|
|
126
|
+
"""
|
|
127
|
+
if client is None:
|
|
128
|
+
raise IntegrationError("Case management client not provided")
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
case = client.get_case(case_id)
|
|
132
|
+
timeline = client.get_case_timeline(case_id)
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
"success": True,
|
|
136
|
+
"case": {
|
|
137
|
+
"id": case.id,
|
|
138
|
+
"title": case.title,
|
|
139
|
+
"description": case.description,
|
|
140
|
+
"status": case.status.value,
|
|
141
|
+
"priority": case.priority.value,
|
|
142
|
+
"assignee": case.assignee,
|
|
143
|
+
"tags": case.tags or [],
|
|
144
|
+
"observables": [
|
|
145
|
+
{
|
|
146
|
+
"type": obs.type,
|
|
147
|
+
"value": obs.value,
|
|
148
|
+
"description": obs.description,
|
|
149
|
+
"tags": obs.tags or [],
|
|
150
|
+
}
|
|
151
|
+
for obs in (case.observables or [])
|
|
152
|
+
],
|
|
153
|
+
"created_at": case.created_at.isoformat() if case.created_at else None,
|
|
154
|
+
"updated_at": case.updated_at.isoformat() if case.updated_at else None,
|
|
155
|
+
},
|
|
156
|
+
"timeline": [
|
|
157
|
+
{
|
|
158
|
+
"author": comment.author,
|
|
159
|
+
"content": comment.content,
|
|
160
|
+
"created_at": comment.created_at.isoformat()
|
|
161
|
+
if comment.created_at
|
|
162
|
+
else None,
|
|
163
|
+
}
|
|
164
|
+
for comment in timeline
|
|
165
|
+
],
|
|
166
|
+
}
|
|
167
|
+
except Exception as e:
|
|
168
|
+
error_msg = f"Failed to review case {case_id}: {str(e)}"
|
|
169
|
+
if isinstance(e, IntegrationError):
|
|
170
|
+
raise
|
|
171
|
+
raise IntegrationError(error_msg) from e
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def list_cases(
|
|
175
|
+
status: Optional[str] = None,
|
|
176
|
+
limit: int = 50,
|
|
177
|
+
client: CaseManagementClient = None, # type: ignore
|
|
178
|
+
) -> Dict[str, Any]:
|
|
179
|
+
"""
|
|
180
|
+
List cases, optionally filtered by status.
|
|
181
|
+
|
|
182
|
+
Tool schema:
|
|
183
|
+
- name: list_cases
|
|
184
|
+
- description: List cases from the case management system, optionally
|
|
185
|
+
filtered by status (open, in_progress, closed).
|
|
186
|
+
- parameters:
|
|
187
|
+
- status (str, optional): Filter by status (open, in_progress, closed).
|
|
188
|
+
- limit (int, optional): Maximum number of cases to return (default: 50).
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
status: Optional status filter.
|
|
192
|
+
limit: Maximum number of cases to return.
|
|
193
|
+
client: The case management client.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Dictionary containing list of case summaries.
|
|
197
|
+
|
|
198
|
+
Raises:
|
|
199
|
+
IntegrationError: If listing cases fails.
|
|
200
|
+
"""
|
|
201
|
+
if client is None:
|
|
202
|
+
raise IntegrationError("Case management client not provided")
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
status_enum = None
|
|
206
|
+
if status:
|
|
207
|
+
try:
|
|
208
|
+
status_enum = CaseStatus(status.lower())
|
|
209
|
+
except ValueError:
|
|
210
|
+
raise IntegrationError(f"Invalid status: {status}")
|
|
211
|
+
|
|
212
|
+
cases = client.list_cases(status=status_enum, limit=limit)
|
|
213
|
+
|
|
214
|
+
# Filter out case ID 1 (default demo case) - always ignore it
|
|
215
|
+
filtered_cases = [case for case in cases if str(case.id) != "1"]
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
"success": True,
|
|
219
|
+
"count": len(filtered_cases),
|
|
220
|
+
"cases": [
|
|
221
|
+
{
|
|
222
|
+
"id": case.id,
|
|
223
|
+
"title": case.title,
|
|
224
|
+
"status": case.status.value,
|
|
225
|
+
"priority": case.priority.value,
|
|
226
|
+
"assignee": case.assignee,
|
|
227
|
+
"created_at": case.created_at.isoformat()
|
|
228
|
+
if case.created_at
|
|
229
|
+
else None,
|
|
230
|
+
}
|
|
231
|
+
for case in filtered_cases
|
|
232
|
+
],
|
|
233
|
+
}
|
|
234
|
+
except IntegrationError:
|
|
235
|
+
raise
|
|
236
|
+
except Exception as e:
|
|
237
|
+
raise IntegrationError(f"Failed to list cases: {str(e)}") from e
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def search_cases(
|
|
241
|
+
text: Optional[str] = None,
|
|
242
|
+
status: Optional[str] = None,
|
|
243
|
+
priority: Optional[str] = None,
|
|
244
|
+
tags: Optional[List[str]] = None,
|
|
245
|
+
assignee: Optional[str] = None,
|
|
246
|
+
limit: int = 50,
|
|
247
|
+
client: CaseManagementClient = None, # type: ignore
|
|
248
|
+
) -> Dict[str, Any]:
|
|
249
|
+
"""
|
|
250
|
+
Search for cases using various filters.
|
|
251
|
+
|
|
252
|
+
Tool schema:
|
|
253
|
+
- name: search_cases
|
|
254
|
+
- description: Search for cases using text search, status, priority, tags,
|
|
255
|
+
or assignee filters.
|
|
256
|
+
- parameters:
|
|
257
|
+
- text (str, optional): Text to search for in case title/description.
|
|
258
|
+
- status (str, optional): Filter by status.
|
|
259
|
+
- priority (str, optional): Filter by priority (low, medium, high, critical).
|
|
260
|
+
- tags (list[str], optional): Filter by tags.
|
|
261
|
+
- assignee (str, optional): Filter by assignee.
|
|
262
|
+
- limit (int, optional): Maximum results (default: 50).
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
text: Text search query.
|
|
266
|
+
status: Status filter.
|
|
267
|
+
priority: Priority filter.
|
|
268
|
+
tags: Tags to filter by.
|
|
269
|
+
assignee: Assignee to filter by.
|
|
270
|
+
limit: Maximum results.
|
|
271
|
+
client: The case management client.
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
Dictionary containing search results.
|
|
275
|
+
|
|
276
|
+
Raises:
|
|
277
|
+
IntegrationError: If search fails.
|
|
278
|
+
"""
|
|
279
|
+
if client is None:
|
|
280
|
+
raise IntegrationError("Case management client not provided")
|
|
281
|
+
|
|
282
|
+
try:
|
|
283
|
+
status_enum = None
|
|
284
|
+
if status:
|
|
285
|
+
try:
|
|
286
|
+
status_enum = CaseStatus(status.lower())
|
|
287
|
+
except ValueError:
|
|
288
|
+
raise IntegrationError(f"Invalid status: {status}")
|
|
289
|
+
|
|
290
|
+
priority_enum = None
|
|
291
|
+
if priority:
|
|
292
|
+
try:
|
|
293
|
+
priority_enum = CasePriority(priority.lower())
|
|
294
|
+
except ValueError:
|
|
295
|
+
raise IntegrationError(f"Invalid priority: {priority}")
|
|
296
|
+
|
|
297
|
+
query = CaseSearchQuery(
|
|
298
|
+
text=text,
|
|
299
|
+
status=status_enum,
|
|
300
|
+
priority=priority_enum,
|
|
301
|
+
tags=tags,
|
|
302
|
+
assignee=assignee,
|
|
303
|
+
limit=limit,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
results = client.search_cases(query)
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
"success": True,
|
|
310
|
+
"count": len(results),
|
|
311
|
+
"cases": [
|
|
312
|
+
{
|
|
313
|
+
"id": case.id,
|
|
314
|
+
"title": case.title,
|
|
315
|
+
"status": case.status.value,
|
|
316
|
+
"priority": case.priority.value,
|
|
317
|
+
"assignee": case.assignee,
|
|
318
|
+
"created_at": case.created_at.isoformat()
|
|
319
|
+
if case.created_at
|
|
320
|
+
else None,
|
|
321
|
+
}
|
|
322
|
+
for case in results
|
|
323
|
+
],
|
|
324
|
+
}
|
|
325
|
+
except IntegrationError:
|
|
326
|
+
raise
|
|
327
|
+
except Exception as e:
|
|
328
|
+
raise IntegrationError(f"Failed to search cases: {str(e)}") from e
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def add_case_comment(
|
|
332
|
+
case_id: str,
|
|
333
|
+
content: str,
|
|
334
|
+
author: Optional[str] = None,
|
|
335
|
+
client: CaseManagementClient = None, # type: ignore
|
|
336
|
+
) -> Dict[str, Any]:
|
|
337
|
+
"""
|
|
338
|
+
Add a comment to a case.
|
|
339
|
+
|
|
340
|
+
Tool schema:
|
|
341
|
+
- name: add_case_comment
|
|
342
|
+
- description: Add a comment or note to a case.
|
|
343
|
+
- parameters:
|
|
344
|
+
- case_id (str, required): The ID of the case.
|
|
345
|
+
- content (str, required): The comment content.
|
|
346
|
+
- author (str, optional): The author of the comment.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
case_id: The case ID.
|
|
350
|
+
content: The comment content.
|
|
351
|
+
author: Optional author name.
|
|
352
|
+
client: The case management client.
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
Dictionary with comment details.
|
|
356
|
+
|
|
357
|
+
Raises:
|
|
358
|
+
IntegrationError: If adding comment fails.
|
|
359
|
+
"""
|
|
360
|
+
if client is None:
|
|
361
|
+
raise IntegrationError("Case management client not provided")
|
|
362
|
+
|
|
363
|
+
try:
|
|
364
|
+
comment = client.add_case_comment(case_id, content, author)
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
"success": True,
|
|
368
|
+
"comment": {
|
|
369
|
+
"id": comment.id,
|
|
370
|
+
"case_id": comment.case_id,
|
|
371
|
+
"author": comment.author,
|
|
372
|
+
"content": comment.content,
|
|
373
|
+
"created_at": comment.created_at.isoformat()
|
|
374
|
+
if comment.created_at
|
|
375
|
+
else None,
|
|
376
|
+
},
|
|
377
|
+
}
|
|
378
|
+
except Exception as e:
|
|
379
|
+
raise IntegrationError(f"Failed to add comment to case {case_id}: {str(e)}") from e
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def attach_observable_to_case(
|
|
383
|
+
case_id: str,
|
|
384
|
+
observable_type: str,
|
|
385
|
+
observable_value: str,
|
|
386
|
+
description: Optional[str] = None,
|
|
387
|
+
tags: Optional[List[str]] = None,
|
|
388
|
+
client: CaseManagementClient = None, # type: ignore
|
|
389
|
+
) -> Dict[str, Any]:
|
|
390
|
+
"""
|
|
391
|
+
Attach an observable (IP, hash, domain, etc.) to a case.
|
|
392
|
+
|
|
393
|
+
Tool schema:
|
|
394
|
+
- name: attach_observable_to_case
|
|
395
|
+
- description: Attach an observable such as an IP address, file hash,
|
|
396
|
+
domain, or URL to a case for tracking and analysis.
|
|
397
|
+
- parameters:
|
|
398
|
+
- case_id (str, required): The ID of the case.
|
|
399
|
+
- observable_type (str, required): Type of observable (ip, hash, domain, url, etc.).
|
|
400
|
+
- observable_value (str, required): The value of the observable.
|
|
401
|
+
- description (str, optional): Description of the observable.
|
|
402
|
+
- tags (list[str], optional): Tags for the observable.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
case_id: The case ID.
|
|
406
|
+
observable_type: Type of observable (ip, hash, domain, url, etc.).
|
|
407
|
+
observable_value: The observable value.
|
|
408
|
+
description: Optional description.
|
|
409
|
+
tags: Optional tags.
|
|
410
|
+
client: The case management client.
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
Dictionary with observable details.
|
|
414
|
+
|
|
415
|
+
Raises:
|
|
416
|
+
IntegrationError: If adding observable fails.
|
|
417
|
+
"""
|
|
418
|
+
if client is None:
|
|
419
|
+
raise IntegrationError("Case management client not provided")
|
|
420
|
+
|
|
421
|
+
try:
|
|
422
|
+
observable = CaseObservable(
|
|
423
|
+
type=observable_type,
|
|
424
|
+
value=observable_value,
|
|
425
|
+
description=description,
|
|
426
|
+
tags=tags,
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
added = client.add_case_observable(case_id, observable)
|
|
430
|
+
|
|
431
|
+
return {
|
|
432
|
+
"success": True,
|
|
433
|
+
"observable": {
|
|
434
|
+
"type": added.type,
|
|
435
|
+
"value": added.value,
|
|
436
|
+
"description": added.description,
|
|
437
|
+
"tags": added.tags or [],
|
|
438
|
+
},
|
|
439
|
+
}
|
|
440
|
+
except Exception as e:
|
|
441
|
+
raise IntegrationError(
|
|
442
|
+
f"Failed to attach observable to case {case_id}: {str(e)}"
|
|
443
|
+
) from e
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def update_case_status(
|
|
447
|
+
case_id: str,
|
|
448
|
+
status: str,
|
|
449
|
+
client: CaseManagementClient = None, # type: ignore
|
|
450
|
+
) -> Dict[str, Any]:
|
|
451
|
+
"""
|
|
452
|
+
Update the status of a case.
|
|
453
|
+
|
|
454
|
+
Tool schema:
|
|
455
|
+
- name: update_case_status
|
|
456
|
+
- description: Update the status of a case (open, in_progress, closed).
|
|
457
|
+
- parameters:
|
|
458
|
+
- case_id (str, required): The ID of the case.
|
|
459
|
+
- status (str, required): New status (open, in_progress, closed).
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
case_id: The case ID.
|
|
463
|
+
status: New status value.
|
|
464
|
+
client: The case management client.
|
|
465
|
+
|
|
466
|
+
Returns:
|
|
467
|
+
Dictionary with updated case details.
|
|
468
|
+
|
|
469
|
+
Raises:
|
|
470
|
+
IntegrationError: If status update fails.
|
|
471
|
+
"""
|
|
472
|
+
if client is None:
|
|
473
|
+
raise IntegrationError("Case management client not provided")
|
|
474
|
+
|
|
475
|
+
try:
|
|
476
|
+
status_enum = CaseStatus(status.lower())
|
|
477
|
+
updated = client.update_case_status(case_id, status_enum)
|
|
478
|
+
|
|
479
|
+
return {
|
|
480
|
+
"success": True,
|
|
481
|
+
"case": {
|
|
482
|
+
"id": updated.id,
|
|
483
|
+
"title": updated.title,
|
|
484
|
+
"status": updated.status.value,
|
|
485
|
+
},
|
|
486
|
+
}
|
|
487
|
+
except ValueError:
|
|
488
|
+
raise IntegrationError(f"Invalid status: {status}")
|
|
489
|
+
except Exception as e:
|
|
490
|
+
raise IntegrationError(f"Failed to update case status: {str(e)}") from e
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def assign_case(
|
|
494
|
+
case_id: str,
|
|
495
|
+
assignee: str,
|
|
496
|
+
client: CaseManagementClient = None, # type: ignore
|
|
497
|
+
) -> Dict[str, Any]:
|
|
498
|
+
"""
|
|
499
|
+
Assign a case to a user.
|
|
500
|
+
|
|
501
|
+
Tool schema:
|
|
502
|
+
- name: assign_case
|
|
503
|
+
- description: Assign a case to a specific user or analyst.
|
|
504
|
+
- parameters:
|
|
505
|
+
- case_id (str, required): The ID of the case.
|
|
506
|
+
- assignee (str, required): The username or ID of the assignee.
|
|
507
|
+
|
|
508
|
+
Args:
|
|
509
|
+
case_id: The case ID.
|
|
510
|
+
assignee: The assignee username/ID.
|
|
511
|
+
client: The case management client.
|
|
512
|
+
|
|
513
|
+
Returns:
|
|
514
|
+
Dictionary with assignment details.
|
|
515
|
+
|
|
516
|
+
Raises:
|
|
517
|
+
IntegrationError: If assignment fails.
|
|
518
|
+
"""
|
|
519
|
+
if client is None:
|
|
520
|
+
raise IntegrationError("Case management client not provided")
|
|
521
|
+
|
|
522
|
+
try:
|
|
523
|
+
assignment = client.assign_case(case_id, assignee)
|
|
524
|
+
|
|
525
|
+
return {
|
|
526
|
+
"success": True,
|
|
527
|
+
"assignment": {
|
|
528
|
+
"case_id": assignment.case_id,
|
|
529
|
+
"assignee": assignment.assignee,
|
|
530
|
+
"assigned_at": assignment.assigned_at.isoformat()
|
|
531
|
+
if assignment.assigned_at
|
|
532
|
+
else None,
|
|
533
|
+
},
|
|
534
|
+
}
|
|
535
|
+
except Exception as e:
|
|
536
|
+
raise IntegrationError(f"Failed to assign case {case_id}: {str(e)}") from e
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def get_case_timeline(
|
|
540
|
+
case_id: str,
|
|
541
|
+
client: CaseManagementClient = None, # type: ignore
|
|
542
|
+
) -> Dict[str, Any]:
|
|
543
|
+
"""
|
|
544
|
+
Get the timeline of events/comments for a case.
|
|
545
|
+
|
|
546
|
+
Tool schema:
|
|
547
|
+
- name: get_case_timeline
|
|
548
|
+
- description: Retrieve the timeline of comments and events for a case,
|
|
549
|
+
ordered chronologically.
|
|
550
|
+
- parameters:
|
|
551
|
+
- case_id (str, required): The ID of the case.
|
|
552
|
+
|
|
553
|
+
Args:
|
|
554
|
+
case_id: The case ID.
|
|
555
|
+
client: The case management client.
|
|
556
|
+
|
|
557
|
+
Returns:
|
|
558
|
+
Dictionary containing timeline events.
|
|
559
|
+
|
|
560
|
+
Raises:
|
|
561
|
+
IntegrationError: If retrieving timeline fails.
|
|
562
|
+
"""
|
|
563
|
+
if client is None:
|
|
564
|
+
raise IntegrationError("Case management client not provided")
|
|
565
|
+
|
|
566
|
+
try:
|
|
567
|
+
timeline = client.get_case_timeline(case_id)
|
|
568
|
+
|
|
569
|
+
return {
|
|
570
|
+
"success": True,
|
|
571
|
+
"case_id": case_id,
|
|
572
|
+
"count": len(timeline),
|
|
573
|
+
"timeline": [
|
|
574
|
+
{
|
|
575
|
+
"author": comment.author,
|
|
576
|
+
"content": comment.content,
|
|
577
|
+
"created_at": comment.created_at.isoformat()
|
|
578
|
+
if comment.created_at
|
|
579
|
+
else None,
|
|
580
|
+
}
|
|
581
|
+
for comment in timeline
|
|
582
|
+
],
|
|
583
|
+
}
|
|
584
|
+
except Exception as e:
|
|
585
|
+
raise IntegrationError(f"Failed to get timeline for case {case_id}: {str(e)}") from e
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def add_case_task(
|
|
589
|
+
case_id: str,
|
|
590
|
+
title: str,
|
|
591
|
+
description: str,
|
|
592
|
+
assignee: Optional[str] = None,
|
|
593
|
+
priority: str = "medium",
|
|
594
|
+
status: str = "pending",
|
|
595
|
+
client: CaseManagementClient = None, # type: ignore
|
|
596
|
+
) -> Dict[str, Any]:
|
|
597
|
+
"""
|
|
598
|
+
Add a task to a case.
|
|
599
|
+
|
|
600
|
+
Tool schema:
|
|
601
|
+
- name: add_case_task
|
|
602
|
+
- description: Add a task to a case. Tasks represent actionable items for investigation and response, typically assigned to SOC2 or SOC3 tiers.
|
|
603
|
+
- parameters:
|
|
604
|
+
- case_id (str, required): The ID of the case
|
|
605
|
+
- title (str, required): Task title
|
|
606
|
+
- description (str, required): Task description
|
|
607
|
+
- assignee (str, optional): Assignee ID or SOC tier (e.g., "SOC2", "SOC3")
|
|
608
|
+
- priority (str, optional): Task priority (low, medium, high, critical). Default: medium
|
|
609
|
+
- status (str, optional): Task status (pending, in_progress, completed, blocked). Default: pending
|
|
610
|
+
"""
|
|
611
|
+
if client is None:
|
|
612
|
+
raise IntegrationError("Case management client not provided")
|
|
613
|
+
|
|
614
|
+
if not hasattr(client, "add_case_task"):
|
|
615
|
+
raise IntegrationError("Case management client does not support tasks")
|
|
616
|
+
|
|
617
|
+
try:
|
|
618
|
+
result = client.add_case_task(
|
|
619
|
+
case_id=case_id,
|
|
620
|
+
title=title,
|
|
621
|
+
description=description,
|
|
622
|
+
assignee=assignee,
|
|
623
|
+
priority=priority,
|
|
624
|
+
status=status,
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
return {
|
|
628
|
+
"success": True,
|
|
629
|
+
"case_id": case_id,
|
|
630
|
+
"task": result,
|
|
631
|
+
"message": f"Task '{title}' added to case {case_id}"
|
|
632
|
+
}
|
|
633
|
+
except Exception as e:
|
|
634
|
+
raise IntegrationError(f"Failed to add task to case {case_id}: {str(e)}") from e
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def list_case_tasks(
|
|
638
|
+
case_id: str,
|
|
639
|
+
client: CaseManagementClient = None, # type: ignore
|
|
640
|
+
) -> Dict[str, Any]:
|
|
641
|
+
"""
|
|
642
|
+
List tasks for a case.
|
|
643
|
+
|
|
644
|
+
Tool schema:
|
|
645
|
+
- name: list_case_tasks
|
|
646
|
+
- description: List all tasks associated with a case
|
|
647
|
+
- parameters:
|
|
648
|
+
- case_id (str, required): The ID of the case
|
|
649
|
+
"""
|
|
650
|
+
if client is None:
|
|
651
|
+
raise IntegrationError("Case management client not provided")
|
|
652
|
+
|
|
653
|
+
if not hasattr(client, "list_case_tasks"):
|
|
654
|
+
raise IntegrationError("Case management client does not support tasks")
|
|
655
|
+
|
|
656
|
+
try:
|
|
657
|
+
tasks = client.list_case_tasks(case_id)
|
|
658
|
+
|
|
659
|
+
return {
|
|
660
|
+
"success": True,
|
|
661
|
+
"case_id": case_id,
|
|
662
|
+
"count": len(tasks),
|
|
663
|
+
"tasks": tasks,
|
|
664
|
+
}
|
|
665
|
+
except Exception as e:
|
|
666
|
+
raise IntegrationError(f"Failed to list tasks for case {case_id}: {str(e)}") from e
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
def update_case_task_status(
|
|
670
|
+
case_id: str,
|
|
671
|
+
task_id: str,
|
|
672
|
+
status: str,
|
|
673
|
+
client: CaseManagementClient = None, # type: ignore
|
|
674
|
+
) -> Dict[str, Any]:
|
|
675
|
+
"""
|
|
676
|
+
Update the status of a task.
|
|
677
|
+
|
|
678
|
+
Tool schema:
|
|
679
|
+
- name: update_case_task_status
|
|
680
|
+
- description: Update the status of a task (pending, in_progress, completed, blocked)
|
|
681
|
+
- parameters:
|
|
682
|
+
- case_id (str, required): The ID of the case
|
|
683
|
+
- task_id (str, required): The ID of the task to update
|
|
684
|
+
- status (str, required): New task status (pending, in_progress, completed, blocked)
|
|
685
|
+
"""
|
|
686
|
+
if client is None:
|
|
687
|
+
raise IntegrationError("Case management client not provided")
|
|
688
|
+
|
|
689
|
+
if not hasattr(client, "update_case_task_status"):
|
|
690
|
+
raise IntegrationError("Case management client does not support task status updates")
|
|
691
|
+
|
|
692
|
+
try:
|
|
693
|
+
valid_statuses = ["pending", "in_progress", "completed", "blocked"]
|
|
694
|
+
if status.lower() not in valid_statuses:
|
|
695
|
+
raise IntegrationError(f"Invalid task status: {status}. Valid statuses: {', '.join(valid_statuses)}")
|
|
696
|
+
|
|
697
|
+
result = client.update_case_task_status(
|
|
698
|
+
case_id=case_id,
|
|
699
|
+
task_id=task_id,
|
|
700
|
+
status=status,
|
|
701
|
+
)
|
|
702
|
+
|
|
703
|
+
return {
|
|
704
|
+
"success": True,
|
|
705
|
+
"case_id": case_id,
|
|
706
|
+
"task_id": task_id,
|
|
707
|
+
"task": result,
|
|
708
|
+
"message": f"Task {task_id} status updated to '{status}'"
|
|
709
|
+
}
|
|
710
|
+
except Exception as e:
|
|
711
|
+
raise IntegrationError(f"Failed to update task status: {str(e)}") from e
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
def add_case_asset(
|
|
715
|
+
case_id: str,
|
|
716
|
+
asset_name: str,
|
|
717
|
+
asset_type: str,
|
|
718
|
+
description: Optional[str] = None,
|
|
719
|
+
ip_address: Optional[str] = None,
|
|
720
|
+
hostname: Optional[str] = None,
|
|
721
|
+
tags: Optional[List[str]] = None,
|
|
722
|
+
client: CaseManagementClient = None, # type: ignore
|
|
723
|
+
) -> Dict[str, Any]:
|
|
724
|
+
"""
|
|
725
|
+
Add an asset to a case.
|
|
726
|
+
|
|
727
|
+
Tool schema:
|
|
728
|
+
- name: add_case_asset
|
|
729
|
+
- description: Add an asset (endpoint, server, network, user account, application) to a case
|
|
730
|
+
- parameters:
|
|
731
|
+
- case_id (str, required): The ID of the case
|
|
732
|
+
- asset_name (str, required): Asset name/identifier
|
|
733
|
+
- asset_type (str, required): Asset type (endpoint, server, network, user_account, application)
|
|
734
|
+
- description (str, optional): Asset description
|
|
735
|
+
- ip_address (str, optional): IP address if applicable
|
|
736
|
+
- hostname (str, optional): Hostname if applicable
|
|
737
|
+
- tags (list[str], optional): Tags for the asset
|
|
738
|
+
"""
|
|
739
|
+
if client is None:
|
|
740
|
+
raise IntegrationError("Case management client not provided")
|
|
741
|
+
|
|
742
|
+
if not hasattr(client, "add_case_asset"):
|
|
743
|
+
raise IntegrationError("Case management client does not support assets")
|
|
744
|
+
|
|
745
|
+
try:
|
|
746
|
+
result = client.add_case_asset(
|
|
747
|
+
case_id=case_id,
|
|
748
|
+
asset_name=asset_name,
|
|
749
|
+
asset_type=asset_type,
|
|
750
|
+
description=description,
|
|
751
|
+
ip_address=ip_address,
|
|
752
|
+
hostname=hostname,
|
|
753
|
+
tags=tags,
|
|
754
|
+
)
|
|
755
|
+
|
|
756
|
+
return {
|
|
757
|
+
"success": True,
|
|
758
|
+
"case_id": case_id,
|
|
759
|
+
"asset": result,
|
|
760
|
+
"message": f"Asset '{asset_name}' added to case {case_id}"
|
|
761
|
+
}
|
|
762
|
+
except Exception as e:
|
|
763
|
+
raise IntegrationError(f"Failed to add asset to case {case_id}: {str(e)}") from e
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
def list_case_assets(
|
|
767
|
+
case_id: str,
|
|
768
|
+
client: CaseManagementClient = None, # type: ignore
|
|
769
|
+
) -> Dict[str, Any]:
|
|
770
|
+
"""
|
|
771
|
+
List assets for a case.
|
|
772
|
+
|
|
773
|
+
Tool schema:
|
|
774
|
+
- name: list_case_assets
|
|
775
|
+
- description: List all assets associated with a case
|
|
776
|
+
- parameters:
|
|
777
|
+
- case_id (str, required): The ID of the case
|
|
778
|
+
"""
|
|
779
|
+
if client is None:
|
|
780
|
+
raise IntegrationError("Case management client not provided")
|
|
781
|
+
|
|
782
|
+
if not hasattr(client, "list_case_assets"):
|
|
783
|
+
raise IntegrationError("Case management client does not support assets")
|
|
784
|
+
|
|
785
|
+
try:
|
|
786
|
+
assets = client.list_case_assets(case_id)
|
|
787
|
+
|
|
788
|
+
return {
|
|
789
|
+
"success": True,
|
|
790
|
+
"case_id": case_id,
|
|
791
|
+
"count": len(assets),
|
|
792
|
+
"assets": assets,
|
|
793
|
+
}
|
|
794
|
+
except Exception as e:
|
|
795
|
+
raise IntegrationError(f"Failed to list assets for case {case_id}: {str(e)}") from e
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
def add_case_evidence(
|
|
799
|
+
case_id: str,
|
|
800
|
+
file_path: str,
|
|
801
|
+
description: Optional[str] = None,
|
|
802
|
+
evidence_type: Optional[str] = None,
|
|
803
|
+
client: CaseManagementClient = None, # type: ignore
|
|
804
|
+
) -> Dict[str, Any]:
|
|
805
|
+
"""
|
|
806
|
+
Add evidence (file) to a case.
|
|
807
|
+
|
|
808
|
+
Tool schema:
|
|
809
|
+
- name: add_case_evidence
|
|
810
|
+
- description: Upload and attach evidence (file, log, screenshot, network capture, etc.) to a case
|
|
811
|
+
- parameters:
|
|
812
|
+
- case_id (str, required): The ID of the case
|
|
813
|
+
- file_path (str, required): Path to the evidence file
|
|
814
|
+
- description (str, optional): Description of the evidence
|
|
815
|
+
- evidence_type (str, optional): Type of evidence (file, screenshot, log, network_capture, memory_dump, registry, other)
|
|
816
|
+
"""
|
|
817
|
+
if client is None:
|
|
818
|
+
raise IntegrationError("Case management client not provided")
|
|
819
|
+
|
|
820
|
+
if not hasattr(client, "add_case_evidence"):
|
|
821
|
+
raise IntegrationError("Case management client does not support evidence")
|
|
822
|
+
|
|
823
|
+
try:
|
|
824
|
+
result = client.add_case_evidence(
|
|
825
|
+
case_id=case_id,
|
|
826
|
+
file_path=file_path,
|
|
827
|
+
description=description,
|
|
828
|
+
evidence_type=evidence_type,
|
|
829
|
+
)
|
|
830
|
+
|
|
831
|
+
return {
|
|
832
|
+
"success": True,
|
|
833
|
+
"case_id": case_id,
|
|
834
|
+
"evidence": result,
|
|
835
|
+
"message": f"Evidence file '{file_path}' added to case {case_id}"
|
|
836
|
+
}
|
|
837
|
+
except Exception as e:
|
|
838
|
+
raise IntegrationError(f"Failed to add evidence to case {case_id}: {str(e)}") from e
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
def list_case_evidence(
|
|
842
|
+
case_id: str,
|
|
843
|
+
client: CaseManagementClient = None, # type: ignore
|
|
844
|
+
) -> Dict[str, Any]:
|
|
845
|
+
"""
|
|
846
|
+
List evidence for a case.
|
|
847
|
+
|
|
848
|
+
Tool schema:
|
|
849
|
+
- name: list_case_evidence
|
|
850
|
+
- description: List all evidence files associated with a case
|
|
851
|
+
- parameters:
|
|
852
|
+
- case_id (str, required): The ID of the case
|
|
853
|
+
"""
|
|
854
|
+
if client is None:
|
|
855
|
+
raise IntegrationError("Case management client not provided")
|
|
856
|
+
|
|
857
|
+
if not hasattr(client, "list_case_evidence"):
|
|
858
|
+
raise IntegrationError("Case management client does not support evidence")
|
|
859
|
+
|
|
860
|
+
try:
|
|
861
|
+
evidence = client.list_case_evidence(case_id)
|
|
862
|
+
|
|
863
|
+
return {
|
|
864
|
+
"success": True,
|
|
865
|
+
"case_id": case_id,
|
|
866
|
+
"count": len(evidence),
|
|
867
|
+
"evidence": evidence,
|
|
868
|
+
}
|
|
869
|
+
except Exception as e:
|
|
870
|
+
raise IntegrationError(f"Failed to list evidence for case {case_id}: {str(e)}") from e
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
def update_case(
|
|
874
|
+
case_id: str,
|
|
875
|
+
title: Optional[str] = None,
|
|
876
|
+
description: Optional[str] = None,
|
|
877
|
+
priority: Optional[str] = None,
|
|
878
|
+
status: Optional[str] = None,
|
|
879
|
+
tags: Optional[List[str]] = None,
|
|
880
|
+
assignee: Optional[str] = None,
|
|
881
|
+
client: CaseManagementClient = None, # type: ignore
|
|
882
|
+
) -> Dict[str, Any]:
|
|
883
|
+
"""
|
|
884
|
+
Update a case with new information.
|
|
885
|
+
|
|
886
|
+
Tool schema:
|
|
887
|
+
- name: update_case
|
|
888
|
+
- description: Update a case with new information (title, description, priority, status, tags, assignee)
|
|
889
|
+
- parameters:
|
|
890
|
+
- case_id (str, required): The ID of the case to update
|
|
891
|
+
- title (str, optional): New case title
|
|
892
|
+
- description (str, optional): New case description
|
|
893
|
+
- priority (str, optional): New priority (low, medium, high, critical)
|
|
894
|
+
- status (str, optional): New status (open, in_progress, closed)
|
|
895
|
+
- tags (list[str], optional): New tags list
|
|
896
|
+
- assignee (str, optional): New assignee
|
|
897
|
+
"""
|
|
898
|
+
if client is None:
|
|
899
|
+
raise IntegrationError("Case management client not provided")
|
|
900
|
+
|
|
901
|
+
try:
|
|
902
|
+
updates = {}
|
|
903
|
+
if title is not None:
|
|
904
|
+
updates["title"] = title
|
|
905
|
+
if description is not None:
|
|
906
|
+
updates["description"] = description
|
|
907
|
+
if priority is not None:
|
|
908
|
+
updates["priority"] = CasePriority(priority.lower())
|
|
909
|
+
if status is not None:
|
|
910
|
+
updates["status"] = CaseStatus(status.lower())
|
|
911
|
+
if tags is not None:
|
|
912
|
+
updates["tags"] = tags
|
|
913
|
+
if assignee is not None:
|
|
914
|
+
updates["assignee"] = assignee
|
|
915
|
+
|
|
916
|
+
if not updates:
|
|
917
|
+
raise IntegrationError("No updates provided")
|
|
918
|
+
|
|
919
|
+
updated = client.update_case(case_id, updates)
|
|
920
|
+
|
|
921
|
+
return {
|
|
922
|
+
"success": True,
|
|
923
|
+
"case": {
|
|
924
|
+
"id": updated.id,
|
|
925
|
+
"title": updated.title,
|
|
926
|
+
"description": updated.description,
|
|
927
|
+
"status": updated.status.value,
|
|
928
|
+
"priority": updated.priority.value,
|
|
929
|
+
"tags": updated.tags or [],
|
|
930
|
+
"assignee": updated.assignee,
|
|
931
|
+
"updated_at": updated.updated_at.isoformat() if updated.updated_at else None,
|
|
932
|
+
},
|
|
933
|
+
}
|
|
934
|
+
except ValueError as e:
|
|
935
|
+
raise IntegrationError(f"Invalid priority or status: {str(e)}")
|
|
936
|
+
except Exception as e:
|
|
937
|
+
raise IntegrationError(f"Failed to update case {case_id}: {str(e)}") from e
|
|
938
|
+
|
|
939
|
+
|
|
940
|
+
def link_cases(
|
|
941
|
+
source_case_id: str,
|
|
942
|
+
target_case_id: str,
|
|
943
|
+
link_type: str = "related_to",
|
|
944
|
+
client: CaseManagementClient = None, # type: ignore
|
|
945
|
+
) -> Dict[str, Any]:
|
|
946
|
+
"""
|
|
947
|
+
Link two cases together.
|
|
948
|
+
|
|
949
|
+
Tool schema:
|
|
950
|
+
- name: link_cases
|
|
951
|
+
- description: Link two cases together to indicate a relationship (e.g., duplicate, related, escalated from)
|
|
952
|
+
- parameters:
|
|
953
|
+
- source_case_id (str, required): The ID of the source case
|
|
954
|
+
- target_case_id (str, required): The ID of the target case to link to
|
|
955
|
+
- link_type (str, optional): Type of link (related_to, duplicate_of, escalated_from, child_of, blocked_by). Default: related_to
|
|
956
|
+
"""
|
|
957
|
+
if client is None:
|
|
958
|
+
raise IntegrationError("Case management client not provided")
|
|
959
|
+
|
|
960
|
+
try:
|
|
961
|
+
client.link_cases(source_case_id, target_case_id, link_type)
|
|
962
|
+
|
|
963
|
+
return {
|
|
964
|
+
"success": True,
|
|
965
|
+
"source_case_id": source_case_id,
|
|
966
|
+
"target_case_id": target_case_id,
|
|
967
|
+
"link_type": link_type,
|
|
968
|
+
"message": f"Case {source_case_id} linked to {target_case_id} with type '{link_type}'",
|
|
969
|
+
}
|
|
970
|
+
except Exception as e:
|
|
971
|
+
raise IntegrationError(
|
|
972
|
+
f"Failed to link cases {source_case_id} and {target_case_id}: {str(e)}"
|
|
973
|
+
) from e
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
def add_case_timeline_event(
|
|
977
|
+
case_id: str,
|
|
978
|
+
title: str,
|
|
979
|
+
content: str,
|
|
980
|
+
source: Optional[str] = None,
|
|
981
|
+
category_id: Optional[int] = None,
|
|
982
|
+
tags: Optional[List[str]] = None,
|
|
983
|
+
color: Optional[str] = None,
|
|
984
|
+
event_date: Optional[str] = None,
|
|
985
|
+
include_in_summary: bool = True,
|
|
986
|
+
include_in_graph: bool = True,
|
|
987
|
+
sync_iocs_assets: bool = True,
|
|
988
|
+
asset_ids: Optional[List[int]] = None,
|
|
989
|
+
ioc_ids: Optional[List[int]] = None,
|
|
990
|
+
custom_attributes: Optional[Dict[str, Any]] = None,
|
|
991
|
+
raw: Optional[str] = None,
|
|
992
|
+
tz: Optional[str] = None,
|
|
993
|
+
client: CaseManagementClient = None, # type: ignore
|
|
994
|
+
) -> Dict[str, Any]:
|
|
995
|
+
"""
|
|
996
|
+
Add an event to a case timeline.
|
|
997
|
+
|
|
998
|
+
Tool schema:
|
|
999
|
+
- name: add_case_timeline_event
|
|
1000
|
+
- description: Add an event to a case timeline for tracking investigation activities and milestones
|
|
1001
|
+
- parameters:
|
|
1002
|
+
- case_id (str, required): The ID of the case
|
|
1003
|
+
- title (str, required): Event title
|
|
1004
|
+
- content (str, required): Event content/description
|
|
1005
|
+
- source (str, optional): Event source (e.g., "SamiGPT", "SIEM", "EDR")
|
|
1006
|
+
- category_id (int, optional): Event category ID
|
|
1007
|
+
- tags (list[str], optional): Event tags
|
|
1008
|
+
- color (str, optional): Event color (hex format, e.g., "#1572E899")
|
|
1009
|
+
- event_date (str, optional): Event date in ISO format (defaults to current time)
|
|
1010
|
+
- include_in_summary (bool, optional): Include event in case summary (default: true)
|
|
1011
|
+
- include_in_graph (bool, optional): Include event in case graph (default: true)
|
|
1012
|
+
- sync_iocs_assets (bool, optional): Sync with IOCs and assets (default: true)
|
|
1013
|
+
- asset_ids (list[int], optional): Related asset IDs
|
|
1014
|
+
- ioc_ids (list[int], optional): Related IOC IDs
|
|
1015
|
+
- custom_attributes (dict, optional): Custom attributes
|
|
1016
|
+
- raw (str, optional): Raw event data
|
|
1017
|
+
- tz (str, optional): Timezone (default: "+00:00")
|
|
1018
|
+
"""
|
|
1019
|
+
if client is None:
|
|
1020
|
+
raise IntegrationError("Case management client not provided")
|
|
1021
|
+
|
|
1022
|
+
if not hasattr(client, "add_case_timeline_event"):
|
|
1023
|
+
raise IntegrationError("Case management client does not support timeline events")
|
|
1024
|
+
|
|
1025
|
+
try:
|
|
1026
|
+
result = client.add_case_timeline_event(
|
|
1027
|
+
case_id=case_id,
|
|
1028
|
+
title=title,
|
|
1029
|
+
content=content,
|
|
1030
|
+
source=source,
|
|
1031
|
+
category_id=category_id,
|
|
1032
|
+
tags=tags,
|
|
1033
|
+
color=color,
|
|
1034
|
+
event_date=event_date,
|
|
1035
|
+
include_in_summary=include_in_summary,
|
|
1036
|
+
include_in_graph=include_in_graph,
|
|
1037
|
+
sync_iocs_assets=sync_iocs_assets,
|
|
1038
|
+
asset_ids=asset_ids,
|
|
1039
|
+
ioc_ids=ioc_ids,
|
|
1040
|
+
custom_attributes=custom_attributes,
|
|
1041
|
+
raw=raw,
|
|
1042
|
+
tz=tz,
|
|
1043
|
+
)
|
|
1044
|
+
|
|
1045
|
+
return {
|
|
1046
|
+
"success": True,
|
|
1047
|
+
"case_id": case_id,
|
|
1048
|
+
"event": result,
|
|
1049
|
+
"message": f"Timeline event '{title}' added to case {case_id}",
|
|
1050
|
+
}
|
|
1051
|
+
except Exception as e:
|
|
1052
|
+
raise IntegrationError(f"Failed to add timeline event to case {case_id}: {str(e)}") from e
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
def list_case_timeline_events(
|
|
1056
|
+
case_id: str,
|
|
1057
|
+
client: CaseManagementClient = None, # type: ignore
|
|
1058
|
+
) -> Dict[str, Any]:
|
|
1059
|
+
"""
|
|
1060
|
+
List timeline events for a case.
|
|
1061
|
+
|
|
1062
|
+
Tool schema:
|
|
1063
|
+
- name: list_case_timeline_events
|
|
1064
|
+
- description: List all timeline events associated with a case
|
|
1065
|
+
- parameters:
|
|
1066
|
+
- case_id (str, required): The ID of the case
|
|
1067
|
+
"""
|
|
1068
|
+
if client is None:
|
|
1069
|
+
raise IntegrationError("Case management client not provided")
|
|
1070
|
+
|
|
1071
|
+
if not hasattr(client, "list_case_timeline_events"):
|
|
1072
|
+
raise IntegrationError("Case management client does not support timeline events")
|
|
1073
|
+
|
|
1074
|
+
try:
|
|
1075
|
+
events = client.list_case_timeline_events(case_id)
|
|
1076
|
+
|
|
1077
|
+
return {
|
|
1078
|
+
"success": True,
|
|
1079
|
+
"case_id": case_id,
|
|
1080
|
+
"count": len(events),
|
|
1081
|
+
"events": events,
|
|
1082
|
+
}
|
|
1083
|
+
except Exception as e:
|
|
1084
|
+
raise IntegrationError(f"Failed to list timeline events for case {case_id}: {str(e)}") from e
|
|
1085
|
+
|