binalyze-air-sdk 1.0.1__py3-none-any.whl → 1.0.3__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 (142) hide show
  1. binalyze_air/__init__.py +77 -77
  2. binalyze_air/apis/__init__.py +67 -27
  3. binalyze_air/apis/acquisitions.py +107 -0
  4. binalyze_air/apis/api_tokens.py +49 -0
  5. binalyze_air/apis/assets.py +161 -0
  6. binalyze_air/apis/audit_logs.py +26 -0
  7. binalyze_air/apis/{authentication.py → auth.py} +29 -27
  8. binalyze_air/apis/auto_asset_tags.py +79 -75
  9. binalyze_air/apis/backup.py +177 -0
  10. binalyze_air/apis/baseline.py +46 -0
  11. binalyze_air/apis/cases.py +225 -0
  12. binalyze_air/apis/cloud_forensics.py +116 -0
  13. binalyze_air/apis/event_subscription.py +96 -96
  14. binalyze_air/apis/evidence.py +249 -53
  15. binalyze_air/apis/interact.py +153 -36
  16. binalyze_air/apis/investigation_hub.py +234 -0
  17. binalyze_air/apis/license.py +104 -0
  18. binalyze_air/apis/logger.py +83 -0
  19. binalyze_air/apis/multipart_upload.py +201 -0
  20. binalyze_air/apis/notifications.py +115 -0
  21. binalyze_air/apis/organizations.py +267 -0
  22. binalyze_air/apis/params.py +44 -39
  23. binalyze_air/apis/policies.py +186 -0
  24. binalyze_air/apis/preset_filters.py +79 -0
  25. binalyze_air/apis/recent_activities.py +71 -0
  26. binalyze_air/apis/relay_server.py +104 -0
  27. binalyze_air/apis/settings.py +395 -27
  28. binalyze_air/apis/tasks.py +80 -0
  29. binalyze_air/apis/triage.py +197 -0
  30. binalyze_air/apis/user_management.py +183 -74
  31. binalyze_air/apis/webhook_executions.py +50 -0
  32. binalyze_air/apis/webhooks.py +322 -230
  33. binalyze_air/base.py +207 -133
  34. binalyze_air/client.py +217 -1337
  35. binalyze_air/commands/__init__.py +175 -145
  36. binalyze_air/commands/acquisitions.py +661 -387
  37. binalyze_air/commands/api_tokens.py +55 -0
  38. binalyze_air/commands/assets.py +324 -362
  39. binalyze_air/commands/{authentication.py → auth.py} +36 -36
  40. binalyze_air/commands/auto_asset_tags.py +230 -230
  41. binalyze_air/commands/backup.py +47 -0
  42. binalyze_air/commands/baseline.py +32 -396
  43. binalyze_air/commands/cases.py +609 -602
  44. binalyze_air/commands/cloud_forensics.py +88 -0
  45. binalyze_air/commands/event_subscription.py +101 -101
  46. binalyze_air/commands/evidences.py +918 -988
  47. binalyze_air/commands/interact.py +172 -58
  48. binalyze_air/commands/investigation_hub.py +315 -0
  49. binalyze_air/commands/license.py +183 -0
  50. binalyze_air/commands/logger.py +126 -0
  51. binalyze_air/commands/multipart_upload.py +363 -0
  52. binalyze_air/commands/notifications.py +45 -0
  53. binalyze_air/commands/organizations.py +200 -221
  54. binalyze_air/commands/policies.py +175 -203
  55. binalyze_air/commands/preset_filters.py +55 -0
  56. binalyze_air/commands/recent_activities.py +32 -0
  57. binalyze_air/commands/relay_server.py +144 -0
  58. binalyze_air/commands/settings.py +431 -29
  59. binalyze_air/commands/tasks.py +95 -56
  60. binalyze_air/commands/triage.py +224 -360
  61. binalyze_air/commands/user_management.py +351 -126
  62. binalyze_air/commands/webhook_executions.py +77 -0
  63. binalyze_air/config.py +244 -244
  64. binalyze_air/exceptions.py +49 -49
  65. binalyze_air/http_client.py +426 -305
  66. binalyze_air/models/__init__.py +287 -285
  67. binalyze_air/models/acquisitions.py +365 -250
  68. binalyze_air/models/api_tokens.py +73 -0
  69. binalyze_air/models/assets.py +438 -438
  70. binalyze_air/models/audit.py +247 -272
  71. binalyze_air/models/audit_logs.py +14 -0
  72. binalyze_air/models/{authentication.py → auth.py} +69 -69
  73. binalyze_air/models/auto_asset_tags.py +227 -116
  74. binalyze_air/models/backup.py +138 -0
  75. binalyze_air/models/baseline.py +231 -231
  76. binalyze_air/models/cases.py +275 -275
  77. binalyze_air/models/cloud_forensics.py +145 -0
  78. binalyze_air/models/event_subscription.py +170 -171
  79. binalyze_air/models/evidence.py +65 -65
  80. binalyze_air/models/evidences.py +367 -348
  81. binalyze_air/models/interact.py +266 -135
  82. binalyze_air/models/investigation_hub.py +265 -0
  83. binalyze_air/models/license.py +150 -0
  84. binalyze_air/models/logger.py +83 -0
  85. binalyze_air/models/multipart_upload.py +352 -0
  86. binalyze_air/models/notifications.py +138 -0
  87. binalyze_air/models/organizations.py +293 -293
  88. binalyze_air/models/params.py +153 -127
  89. binalyze_air/models/policies.py +260 -249
  90. binalyze_air/models/preset_filters.py +79 -0
  91. binalyze_air/models/recent_activities.py +70 -0
  92. binalyze_air/models/relay_server.py +121 -0
  93. binalyze_air/models/settings.py +538 -84
  94. binalyze_air/models/tasks.py +215 -149
  95. binalyze_air/models/triage.py +141 -142
  96. binalyze_air/models/user_management.py +200 -97
  97. binalyze_air/models/webhook_executions.py +33 -0
  98. binalyze_air/queries/__init__.py +121 -133
  99. binalyze_air/queries/acquisitions.py +155 -155
  100. binalyze_air/queries/api_tokens.py +46 -0
  101. binalyze_air/queries/assets.py +186 -105
  102. binalyze_air/queries/audit.py +400 -416
  103. binalyze_air/queries/{authentication.py → auth.py} +55 -55
  104. binalyze_air/queries/auto_asset_tags.py +59 -59
  105. binalyze_air/queries/backup.py +66 -0
  106. binalyze_air/queries/baseline.py +21 -185
  107. binalyze_air/queries/cases.py +292 -292
  108. binalyze_air/queries/cloud_forensics.py +137 -0
  109. binalyze_air/queries/event_subscription.py +54 -54
  110. binalyze_air/queries/evidence.py +139 -139
  111. binalyze_air/queries/evidences.py +279 -279
  112. binalyze_air/queries/interact.py +140 -28
  113. binalyze_air/queries/investigation_hub.py +329 -0
  114. binalyze_air/queries/license.py +85 -0
  115. binalyze_air/queries/logger.py +58 -0
  116. binalyze_air/queries/multipart_upload.py +180 -0
  117. binalyze_air/queries/notifications.py +71 -0
  118. binalyze_air/queries/organizations.py +222 -222
  119. binalyze_air/queries/params.py +154 -115
  120. binalyze_air/queries/policies.py +149 -149
  121. binalyze_air/queries/preset_filters.py +60 -0
  122. binalyze_air/queries/recent_activities.py +44 -0
  123. binalyze_air/queries/relay_server.py +42 -0
  124. binalyze_air/queries/settings.py +533 -20
  125. binalyze_air/queries/tasks.py +125 -81
  126. binalyze_air/queries/triage.py +230 -230
  127. binalyze_air/queries/user_management.py +193 -83
  128. binalyze_air/queries/webhook_executions.py +39 -0
  129. binalyze_air_sdk-1.0.3.dist-info/METADATA +752 -0
  130. binalyze_air_sdk-1.0.3.dist-info/RECORD +132 -0
  131. {binalyze_air_sdk-1.0.1.dist-info → binalyze_air_sdk-1.0.3.dist-info}/WHEEL +1 -1
  132. binalyze_air/apis/endpoints.py +0 -22
  133. binalyze_air/apis/evidences.py +0 -216
  134. binalyze_air/apis/users.py +0 -68
  135. binalyze_air/commands/users.py +0 -101
  136. binalyze_air/models/endpoints.py +0 -76
  137. binalyze_air/models/users.py +0 -82
  138. binalyze_air/queries/endpoints.py +0 -25
  139. binalyze_air/queries/users.py +0 -69
  140. binalyze_air_sdk-1.0.1.dist-info/METADATA +0 -635
  141. binalyze_air_sdk-1.0.1.dist-info/RECORD +0 -82
  142. {binalyze_air_sdk-1.0.1.dist-info → binalyze_air_sdk-1.0.3.dist-info}/top_level.txt +0 -0
@@ -1,58 +1,172 @@
1
- """
2
- Interact commands for the Binalyze AIR SDK.
3
- """
4
-
5
- from typing import Dict, Any, Union
6
-
7
- from ..base import Command
8
- from ..models.interact import (
9
- InteractiveShellTaskResponse, AssignInteractiveShellTaskRequest,
10
- ShellTaskResponse, AssignShellTaskRequest # Legacy models
11
- )
12
- from ..http_client import HTTPClient
13
-
14
-
15
- class AssignInteractiveShellTaskCommand(Command[InteractiveShellTaskResponse]):
16
- """Command to assign an interactive shell task."""
17
-
18
- def __init__(self, http_client: HTTPClient, request: Union[AssignInteractiveShellTaskRequest, Dict[str, Any]]):
19
- self.http_client = http_client
20
- self.request = request
21
-
22
- def execute(self) -> InteractiveShellTaskResponse:
23
- """Execute the command to assign an interactive shell task."""
24
- # Handle both dict and model objects
25
- if isinstance(self.request, dict):
26
- payload = self.request
27
- else:
28
- payload = self.request.model_dump(exclude_none=True, by_alias=True)
29
-
30
- response = self.http_client.post("interact/shell/assign-task", json_data=payload)
31
-
32
- if response.get("success"):
33
- result_data = response.get("result", {})
34
- # Use Pydantic parsing with proper field aliasing
35
- return InteractiveShellTaskResponse.model_validate(result_data)
36
-
37
- raise Exception(f"Failed to assign interactive shell task: {response.get('errors', [])}")
38
-
39
-
40
- # Legacy command for backward compatibility (deprecated)
41
- class AssignShellTaskCommand(Command[ShellTaskResponse]):
42
- """Command to assign a shell interaction task (legacy)."""
43
-
44
- def __init__(self, http_client: HTTPClient, request: Union[AssignShellTaskRequest, Dict[str, Any]]):
45
- self.http_client = http_client
46
- self.request = request
47
-
48
- def execute(self) -> ShellTaskResponse:
49
- """Execute the command to assign a shell task (legacy)."""
50
- # Handle both dict and model objects
51
- if isinstance(self.request, dict):
52
- payload = self.request
53
- else:
54
- payload = self.request.model_dump(exclude_none=True)
55
-
56
- response = self.http_client.post("interact/shell/assign-task", json_data=payload)
57
-
58
- return ShellTaskResponse(**response.get("result", {}))
1
+ """
2
+ Interact commands for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import Dict, Any, Union, List, Optional
6
+ import json
7
+
8
+ from ..base import Command
9
+ from ..models.interact import (
10
+ InteractiveShellTaskResponse, AssignInteractiveShellTaskRequest,
11
+ ExecuteCommandRequest, ExecuteCommandResponse, LibraryFile,
12
+ InterruptCommandRequest, CloseSessionRequest, FileExistsResponse
13
+ )
14
+ from ..http_client import HTTPClient
15
+
16
+
17
+ class AssignInteractiveShellTaskCommand(Command[InteractiveShellTaskResponse]):
18
+ """Command to assign an interactive shell task."""
19
+
20
+ def __init__(self, http_client: HTTPClient, request: Union[AssignInteractiveShellTaskRequest, Dict[str, Any]]):
21
+ self.http_client = http_client
22
+ self.request = request
23
+
24
+ def execute(self) -> InteractiveShellTaskResponse:
25
+ """Execute the command to assign an interactive shell task."""
26
+ # Handle both dict and model objects
27
+ if isinstance(self.request, dict):
28
+ payload = self.request
29
+ else:
30
+ payload = self.request.model_dump(exclude_none=True, by_alias=True)
31
+
32
+ response = self.http_client.post("interact/shell/assign-task", json_data=payload)
33
+
34
+ if response.get("success"):
35
+ result_data = response.get("result", {})
36
+ # Use Pydantic parsing with proper field aliasing
37
+ return InteractiveShellTaskResponse.model_validate(result_data)
38
+
39
+ raise Exception(f"Failed to assign interactive shell task: {response.get('errors', [])}")
40
+
41
+
42
+ # LIBRARY FILE COMMANDS
43
+
44
+ class UploadFileToLibraryCommand(Command[Optional[LibraryFile]]):
45
+ """Command to upload a file to the library."""
46
+
47
+ def __init__(self, http_client: HTTPClient, file_content: bytes, filename: str, organization_ids: Optional[List[int]] = None):
48
+ self.http_client = http_client
49
+ self.file_content = file_content
50
+ self.filename = filename
51
+ self.organization_ids = organization_ids
52
+
53
+ def execute(self) -> Optional[LibraryFile]:
54
+ """Execute the command to upload a file to the library."""
55
+ # Prepare multipart form data
56
+ files = {"file": (self.filename, self.file_content)}
57
+ data = {}
58
+
59
+ if self.organization_ids:
60
+ # API expects organizationIds as JSON string array, not comma-separated
61
+ data["organizationIds"] = json.dumps(self.organization_ids)
62
+
63
+ response = self.http_client.upload_multipart("interact/library/upload", files=files, data=data)
64
+
65
+ result_data = response.get("result")
66
+ # Handle null result properly - API may return null on success
67
+ if result_data is None:
68
+ return None
69
+
70
+ return LibraryFile.model_validate(result_data)
71
+
72
+
73
+ class DeleteFileFromLibraryCommand(Command[Dict[str, Any]]):
74
+ """Command to delete a file from the library."""
75
+
76
+ def __init__(self, http_client: HTTPClient, filename: str):
77
+ self.http_client = http_client
78
+ self.filename = filename
79
+
80
+ def execute(self) -> Dict[str, Any]:
81
+ """Execute the command to delete a file from the library."""
82
+ params = {"filename": self.filename}
83
+ response = self.http_client.delete("interact/library/delete", params=params)
84
+
85
+ return response
86
+
87
+
88
+ # SHELL SESSION COMMANDS
89
+
90
+ class ExecuteCommandCommand(Command[ExecuteCommandResponse]):
91
+ """Command to execute a command in a shell session."""
92
+
93
+ def __init__(self, http_client: HTTPClient, session_id: str, request: Union[ExecuteCommandRequest, Dict[str, Any]]):
94
+ self.http_client = http_client
95
+ self.session_id = session_id
96
+ self.request = request
97
+
98
+ def execute(self) -> ExecuteCommandResponse:
99
+ """Execute the command in the shell session."""
100
+ # Handle both dict and model objects
101
+ if isinstance(self.request, dict):
102
+ payload = self.request
103
+ else:
104
+ payload = self.request.model_dump(exclude_none=True, by_alias=True)
105
+
106
+ response = self.http_client.post(
107
+ f"interact/shell/sessions/{self.session_id}/execute-command",
108
+ json_data=payload
109
+ )
110
+
111
+ result_data = response.get("result", {})
112
+ return ExecuteCommandResponse.model_validate(result_data)
113
+
114
+
115
+ class ExecuteAsyncCommandCommand(Command[ExecuteCommandResponse]):
116
+ """Command to execute an async command in a shell session."""
117
+
118
+ def __init__(self, http_client: HTTPClient, session_id: str, request: Union[ExecuteCommandRequest, Dict[str, Any]]):
119
+ self.http_client = http_client
120
+ self.session_id = session_id
121
+ self.request = request
122
+
123
+ def execute(self) -> ExecuteCommandResponse:
124
+ """Execute the async command in the shell session."""
125
+ # Handle both dict and model objects
126
+ if isinstance(self.request, dict):
127
+ payload = self.request
128
+ else:
129
+ payload = self.request.model_dump(exclude_none=True, by_alias=True)
130
+
131
+ response = self.http_client.post(
132
+ f"interact/shell/sessions/{self.session_id}/execute-async-command",
133
+ json_data=payload
134
+ )
135
+
136
+ result_data = response.get("result", {})
137
+ return ExecuteCommandResponse.model_validate(result_data)
138
+
139
+
140
+ class InterruptCommandCommand(Command[Dict[str, Any]]):
141
+ """Command to interrupt a command in a shell session."""
142
+
143
+ def __init__(self, http_client: HTTPClient, session_id: str, message_id: str):
144
+ self.http_client = http_client
145
+ self.session_id = session_id
146
+ self.message_id = message_id
147
+
148
+ def execute(self) -> Dict[str, Any]:
149
+ """Execute the command to interrupt a running command."""
150
+ response = self.http_client.post(
151
+ f"interact/shell/sessions/{self.session_id}/messages/{self.message_id}/interrupt-command",
152
+ json_data={}
153
+ )
154
+
155
+ return response
156
+
157
+
158
+ class CloseSessionCommand(Command[Dict[str, Any]]):
159
+ """Command to close a shell session."""
160
+
161
+ def __init__(self, http_client: HTTPClient, session_id: str):
162
+ self.http_client = http_client
163
+ self.session_id = session_id
164
+
165
+ def execute(self) -> Dict[str, Any]:
166
+ """Execute the command to close a shell session."""
167
+ response = self.http_client.post(
168
+ f"interact/shell/sessions/{self.session_id}/close",
169
+ json_data={}
170
+ )
171
+
172
+ return response
@@ -0,0 +1,315 @@
1
+ """
2
+ Investigation Hub commands for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import Dict, Any, List
6
+
7
+ from ..base import Command
8
+ from ..models.investigation_hub import (
9
+ Investigation, UpdateInvestigationRequest, FlagEvidenceRequest,
10
+ AddNoteRequest, CreateCommentRequest, InvestigationComment,
11
+ MarkActivityAsReadRequest, CreateAdvancedFilterRequest,
12
+ UpdateAdvancedFilterRequest, AdvancedFilter, ExportRequest
13
+ )
14
+ from ..http_client import HTTPClient
15
+
16
+
17
+ class UpdateInvestigationCommand(Command[Investigation]):
18
+ """Command to update an investigation."""
19
+
20
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
21
+ request: UpdateInvestigationRequest):
22
+ self.http_client = http_client
23
+ self.investigation_id = investigation_id
24
+ self.request = request
25
+
26
+ def execute(self) -> Investigation:
27
+ """Execute the command."""
28
+ response = self.http_client.put(
29
+ f"investigation-hub/investigations/{self.investigation_id}",
30
+ json_data=self.request.model_dump(by_alias=True, exclude_none=True)
31
+ )
32
+ return Investigation(**response["result"])
33
+
34
+
35
+ class DeleteInvestigationCommand(Command[Dict[str, Any]]):
36
+ """Command to delete an investigation."""
37
+
38
+ def __init__(self, http_client: HTTPClient, investigation_id: str):
39
+ self.http_client = http_client
40
+ self.investigation_id = investigation_id
41
+
42
+ def execute(self) -> Dict[str, Any]:
43
+ """Execute the command."""
44
+ response = self.http_client.delete(f"investigation-hub/investigations/{self.investigation_id}")
45
+ return response
46
+
47
+
48
+ class FlagEvidenceCommand(Command[Dict[str, Any]]):
49
+ """Command to flag evidence records."""
50
+
51
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
52
+ request: FlagEvidenceRequest):
53
+ self.http_client = http_client
54
+ self.investigation_id = investigation_id
55
+ self.request = request
56
+
57
+ def execute(self) -> Dict[str, Any]:
58
+ """Execute the command."""
59
+ response = self.http_client.post(
60
+ f"investigation-hub/investigations/{self.investigation_id}/section/flag",
61
+ json_data=self.request.model_dump(by_alias=True, exclude_none=True)
62
+ )
63
+ return response
64
+
65
+
66
+ class UnflagEvidenceCommand(Command[Dict[str, Any]]):
67
+ """Command to unflag evidence records."""
68
+
69
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
70
+ records: List[Dict[str, Any]], section: str):
71
+ self.http_client = http_client
72
+ self.investigation_id = investigation_id
73
+ self.records = records
74
+ self.section = section
75
+
76
+ def execute(self) -> Dict[str, Any]:
77
+ """Execute the command."""
78
+ payload = {
79
+ "records": self.records,
80
+ "section": self.section
81
+ }
82
+ response = self.http_client.delete(
83
+ f"investigation-hub/investigations/{self.investigation_id}/section/flag",
84
+ json_data=payload
85
+ )
86
+ return response
87
+
88
+
89
+ class AddNoteToEvidenceCommand(Command[Dict[str, Any]]):
90
+ """Command to add a note to evidence records."""
91
+
92
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
93
+ request: AddNoteRequest):
94
+ self.http_client = http_client
95
+ self.investigation_id = investigation_id
96
+ self.request = request
97
+
98
+ def execute(self) -> Dict[str, Any]:
99
+ """Execute the command."""
100
+ response = self.http_client.put(
101
+ f"investigation-hub/investigations/{self.investigation_id}/notes",
102
+ json_data=self.request.model_dump(by_alias=True, exclude_none=True)
103
+ )
104
+ return response
105
+
106
+
107
+ class CreateCommentCommand(Command[InvestigationComment]):
108
+ """Command to create a comment in an investigation."""
109
+
110
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
111
+ request: CreateCommentRequest):
112
+ self.http_client = http_client
113
+ self.investigation_id = investigation_id
114
+ self.request = request
115
+
116
+ def execute(self) -> InvestigationComment:
117
+ """Execute the command."""
118
+ response = self.http_client.post(
119
+ f"investigation-hub/investigations/{self.investigation_id}/comments",
120
+ json_data=self.request.model_dump(by_alias=True, exclude_none=True)
121
+ )
122
+ return InvestigationComment(**response["result"])
123
+
124
+
125
+ class UpdateCommentCommand(Command[InvestigationComment]):
126
+ """Command to update a comment in an investigation."""
127
+
128
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
129
+ comment_id: str, content: str):
130
+ self.http_client = http_client
131
+ self.investigation_id = investigation_id
132
+ self.comment_id = comment_id
133
+ self.content = content
134
+
135
+ def execute(self) -> InvestigationComment:
136
+ """Execute the command."""
137
+ payload = {"content": self.content}
138
+ response = self.http_client.put(
139
+ f"investigation-hub/investigations/{self.investigation_id}/comments/{self.comment_id}",
140
+ json_data=payload
141
+ )
142
+ return InvestigationComment(**response["result"])
143
+
144
+
145
+ class DeleteCommentCommand(Command[Dict[str, Any]]):
146
+ """Command to delete a comment from an investigation."""
147
+
148
+ def __init__(self, http_client: HTTPClient, investigation_id: str, comment_id: str):
149
+ self.http_client = http_client
150
+ self.investigation_id = investigation_id
151
+ self.comment_id = comment_id
152
+
153
+ def execute(self) -> Dict[str, Any]:
154
+ """Execute the command."""
155
+ response = self.http_client.delete(
156
+ f"investigation-hub/investigations/{self.investigation_id}/comments/{self.comment_id}"
157
+ )
158
+ return response
159
+
160
+
161
+ class MarkActivitiesAsReadCommand(Command[Dict[str, Any]]):
162
+ """Command to mark activities as read."""
163
+
164
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
165
+ request: MarkActivityAsReadRequest):
166
+ self.http_client = http_client
167
+ self.investigation_id = investigation_id
168
+ self.request = request
169
+
170
+ def execute(self) -> Dict[str, Any]:
171
+ """Execute the command."""
172
+ response = self.http_client.post(
173
+ f"investigation-hub/investigations/{self.investigation_id}/activities/mark-as-read",
174
+ json_data=self.request.model_dump(by_alias=True, exclude_none=True)
175
+ )
176
+ return response
177
+
178
+
179
+ class CreateAdvancedFilterCommand(Command[AdvancedFilter]):
180
+ """Command to create an advanced filter."""
181
+
182
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
183
+ request: CreateAdvancedFilterRequest):
184
+ self.http_client = http_client
185
+ self.investigation_id = investigation_id
186
+ self.request = request
187
+
188
+ def execute(self) -> AdvancedFilter:
189
+ """Execute the command."""
190
+ response = self.http_client.post(
191
+ f"investigation-hub/advanced-filters",
192
+ json_data=self.request.model_dump(by_alias=True, exclude_none=True)
193
+ )
194
+ return AdvancedFilter(**response["result"])
195
+
196
+
197
+ class UpdateAdvancedFilterCommand(Command[AdvancedFilter]):
198
+ """Command to update an advanced filter."""
199
+
200
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
201
+ filter_id: int, request: UpdateAdvancedFilterRequest):
202
+ self.http_client = http_client
203
+ self.investigation_id = investigation_id
204
+ self.filter_id = filter_id
205
+ self.request = request
206
+
207
+ def execute(self) -> AdvancedFilter:
208
+ """Execute the command."""
209
+ response = self.http_client.put(
210
+ f"investigation-hub/advanced-filters/{self.filter_id}",
211
+ json_data=self.request.model_dump(by_alias=True, exclude_none=True)
212
+ )
213
+ return AdvancedFilter(**response["result"])
214
+
215
+
216
+ class DeleteAdvancedFilterCommand(Command[Dict[str, Any]]):
217
+ """Command to delete an advanced filter."""
218
+
219
+ def __init__(self, http_client: HTTPClient, investigation_id: str, filter_id: int):
220
+ self.http_client = http_client
221
+ self.investigation_id = investigation_id
222
+ self.filter_id = filter_id
223
+
224
+ def execute(self) -> Dict[str, Any]:
225
+ """Execute the command."""
226
+ response = self.http_client.delete(
227
+ f"investigation-hub/advanced-filters/{self.filter_id}"
228
+ )
229
+ return response
230
+
231
+
232
+ class ExportEvidenceCommand(Command[Dict[str, Any]]):
233
+ """Command to export evidence data."""
234
+
235
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
236
+ request: ExportRequest):
237
+ self.http_client = http_client
238
+ self.investigation_id = investigation_id
239
+ self.request = request
240
+
241
+ def execute(self) -> Dict[str, Any]:
242
+ """Execute the command."""
243
+ response = self.http_client.post(
244
+ f"investigation-hub/investigations/{self.investigation_id}/export-flags",
245
+ json_data=self.request.model_dump(by_alias=True, exclude_none=True)
246
+ )
247
+ return response
248
+
249
+
250
+ class GenerateReportCommand(Command[Dict[str, Any]]):
251
+ """Command to generate an investigation report."""
252
+
253
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
254
+ report_type: str = "comprehensive", include_evidence: bool = True):
255
+ self.http_client = http_client
256
+ self.investigation_id = investigation_id
257
+ self.report_type = report_type
258
+ self.include_evidence = include_evidence
259
+
260
+ def execute(self) -> Dict[str, Any]:
261
+ """Execute the command."""
262
+ payload = {
263
+ "reportType": self.report_type,
264
+ "includeEvidence": self.include_evidence
265
+ }
266
+ response = self.http_client.post(
267
+ f"investigation-hub/investigations/{self.investigation_id}/generate-report",
268
+ json_data=payload
269
+ )
270
+ return response
271
+
272
+
273
+ class RefreshInvestigationCommand(Command[Investigation]):
274
+ """Command to refresh/rebuild an investigation."""
275
+
276
+ def __init__(self, http_client: HTTPClient, investigation_id: str):
277
+ self.http_client = http_client
278
+ self.investigation_id = investigation_id
279
+
280
+ def execute(self) -> Investigation:
281
+ """Execute the command."""
282
+ response = self.http_client.post(
283
+ f"investigation-hub/investigations/{self.investigation_id}/refresh"
284
+ )
285
+ return Investigation(**response["result"])
286
+
287
+
288
+ class ArchiveInvestigationCommand(Command[Dict[str, Any]]):
289
+ """Command to archive an investigation."""
290
+
291
+ def __init__(self, http_client: HTTPClient, investigation_id: str):
292
+ self.http_client = http_client
293
+ self.investigation_id = investigation_id
294
+
295
+ def execute(self) -> Dict[str, Any]:
296
+ """Execute the command."""
297
+ response = self.http_client.post(
298
+ f"investigation-hub/investigations/{self.investigation_id}/archive"
299
+ )
300
+ return response
301
+
302
+
303
+ class RestoreInvestigationCommand(Command[Investigation]):
304
+ """Command to restore an archived investigation."""
305
+
306
+ def __init__(self, http_client: HTTPClient, investigation_id: str):
307
+ self.http_client = http_client
308
+ self.investigation_id = investigation_id
309
+
310
+ def execute(self) -> Investigation:
311
+ """Execute the command."""
312
+ response = self.http_client.post(
313
+ f"investigation-hub/investigations/{self.investigation_id}/restore"
314
+ )
315
+ return Investigation(**response["result"])