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,360 +1,224 @@
1
- """
2
- Triage-related commands for the Binalyze AIR SDK.
3
- """
4
-
5
- from typing import Dict, Any, Union
6
-
7
- from ..base import Command
8
- from ..models.triage import (
9
- TriageRule, TriageTag, TriageProfile, CreateTriageRuleRequest,
10
- UpdateTriageRuleRequest, CreateTriageTagRequest, CreateTriageProfileRequest
11
- )
12
- from ..http_client import HTTPClient
13
-
14
-
15
- class CreateTriageRuleCommand(Command[TriageRule]):
16
- """Command to create a new triage rule."""
17
-
18
- def __init__(self, http_client: HTTPClient, request: Union[CreateTriageRuleRequest, Dict[str, Any]]):
19
- self.http_client = http_client
20
- self.request = request
21
-
22
- def execute(self) -> TriageRule:
23
- """Execute the command to create a triage rule."""
24
- # Handle both dict and model objects
25
- if isinstance(self.request, dict):
26
- data = self.request.copy()
27
- else:
28
- # Convert SDK model fields to API fields
29
- request_dict = self.request.model_dump(exclude_none=True)
30
- data = {
31
- "description": request_dict.get("description") or request_dict.get("name", ""),
32
- "rule": request_dict.get("rule_content", ""),
33
- "engine": request_dict.get("type", "yara"),
34
- "searchIn": request_dict.get("search_in", "filesystem"),
35
- "organizationIds": [request_dict.get("organization_id", 0)]
36
- }
37
-
38
- response = self.http_client.post("triages/rules", json_data=data)
39
-
40
- entity_data = response.get("result", {})
41
-
42
- mapped_data = {
43
- "id": entity_data.get("_id"),
44
- "name": entity_data.get("description", ""), # API uses description as name
45
- "description": entity_data.get("description"),
46
- "type": entity_data.get("engine"), # API uses engine field
47
- "rule_content": entity_data.get("rule", ""),
48
- "search_in": entity_data.get("searchIn"),
49
- "enabled": entity_data.get("enabled", True),
50
- "severity": entity_data.get("severity", "medium"),
51
- "tags": entity_data.get("tags", []),
52
- "organization_id": entity_data.get("organizationIds", [0])[0] if entity_data.get("organizationIds") else 0,
53
- "organization_ids": entity_data.get("organizationIds", []),
54
- "created_at": entity_data.get("createdAt"),
55
- "updated_at": entity_data.get("updatedAt"),
56
- "created_by": entity_data.get("createdBy"),
57
- "updated_by": entity_data.get("updatedBy"),
58
- "match_count": entity_data.get("matchCount", 0),
59
- "last_match": entity_data.get("lastMatch"),
60
- "deletable": entity_data.get("deletable"),
61
- }
62
-
63
- # Remove None values
64
- mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
65
-
66
- return TriageRule(**mapped_data)
67
-
68
-
69
- class UpdateTriageRuleCommand(Command[TriageRule]):
70
- """Command to update an existing triage rule."""
71
-
72
- def __init__(self, http_client: HTTPClient, rule_id: str, request: Union[UpdateTriageRuleRequest, Dict[str, Any]]):
73
- self.http_client = http_client
74
- self.rule_id = rule_id
75
- self.request = request
76
-
77
- def execute(self) -> TriageRule:
78
- """Execute the command to update a triage rule."""
79
- # Handle both dict and model objects
80
- if isinstance(self.request, dict):
81
- data = self.request.copy()
82
- else:
83
- # Convert SDK model fields to API fields
84
- request_dict = self.request.model_dump(exclude_none=True)
85
- data = {}
86
-
87
- # Map SDK fields to API fields for update
88
- if "description" in request_dict or "name" in request_dict:
89
- data["description"] = request_dict.get("description") or request_dict.get("name", "")
90
- if "rule_content" in request_dict:
91
- data["rule"] = request_dict.get("rule_content")
92
- if "type" in request_dict:
93
- data["engine"] = request_dict.get("type")
94
- if "search_in" in request_dict:
95
- data["searchIn"] = request_dict.get("search_in")
96
- if "organization_id" in request_dict:
97
- data["organizationIds"] = [request_dict.get("organization_id")]
98
- if "enabled" in request_dict:
99
- data["enabled"] = request_dict.get("enabled")
100
-
101
- response = self.http_client.put(f"triages/rules/{self.rule_id}", json_data=data)
102
-
103
- entity_data = response.get("result", {})
104
-
105
- mapped_data = {
106
- "id": entity_data.get("_id"),
107
- "name": entity_data.get("description", ""), # API uses description as name
108
- "description": entity_data.get("description"),
109
- "type": entity_data.get("engine"), # API uses engine field
110
- "rule_content": entity_data.get("rule", ""),
111
- "search_in": entity_data.get("searchIn"),
112
- "enabled": entity_data.get("enabled", True),
113
- "severity": entity_data.get("severity", "medium"),
114
- "tags": entity_data.get("tags", []),
115
- "organization_id": entity_data.get("organizationIds", [0])[0] if entity_data.get("organizationIds") else 0,
116
- "organization_ids": entity_data.get("organizationIds", []),
117
- "created_at": entity_data.get("createdAt"),
118
- "updated_at": entity_data.get("updatedAt"),
119
- "created_by": entity_data.get("createdBy"),
120
- "updated_by": entity_data.get("updatedBy"),
121
- "match_count": entity_data.get("matchCount", 0),
122
- "last_match": entity_data.get("lastMatch"),
123
- "deletable": entity_data.get("deletable"),
124
- }
125
-
126
- # Remove None values
127
- mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
128
-
129
- return TriageRule(**mapped_data)
130
-
131
-
132
- class DeleteTriageRuleCommand(Command[Dict[str, Any]]):
133
- """Command to delete a triage rule."""
134
-
135
- def __init__(self, http_client: HTTPClient, rule_id: str):
136
- self.http_client = http_client
137
- self.rule_id = rule_id
138
-
139
- def execute(self) -> Dict[str, Any]:
140
- """Execute the command to delete a triage rule."""
141
- response = self.http_client.delete(f"triages/rules/{self.rule_id}")
142
- return response
143
-
144
-
145
- class EnableTriageRuleCommand(Command[TriageRule]):
146
- """Command to enable a triage rule."""
147
-
148
- def __init__(self, http_client: HTTPClient, rule_id: str):
149
- self.http_client = http_client
150
- self.rule_id = rule_id
151
-
152
- def execute(self) -> TriageRule:
153
- """Execute the command to enable a triage rule."""
154
- data = {"enabled": True}
155
-
156
- response = self.http_client.put(f"triages/rules/{self.rule_id}", json_data=data)
157
-
158
- entity_data = response.get("result", {})
159
-
160
- mapped_data = {
161
- "id": entity_data.get("_id"),
162
- "name": entity_data.get("name"),
163
- "description": entity_data.get("description"),
164
- "type": entity_data.get("type"),
165
- "rule_content": entity_data.get("ruleContent", ""),
166
- "enabled": entity_data.get("enabled", True),
167
- "severity": entity_data.get("severity", "medium"),
168
- "tags": entity_data.get("tags", []),
169
- "organization_id": entity_data.get("organizationId", 0),
170
- "created_at": entity_data.get("createdAt"),
171
- "updated_at": entity_data.get("updatedAt"),
172
- "created_by": entity_data.get("createdBy"),
173
- "updated_by": entity_data.get("updatedBy"),
174
- "match_count": entity_data.get("matchCount", 0),
175
- "last_match": entity_data.get("lastMatch"),
176
- }
177
-
178
- # Remove None values
179
- mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
180
-
181
- return TriageRule(**mapped_data)
182
-
183
-
184
- class DisableTriageRuleCommand(Command[TriageRule]):
185
- """Command to disable a triage rule."""
186
-
187
- def __init__(self, http_client: HTTPClient, rule_id: str):
188
- self.http_client = http_client
189
- self.rule_id = rule_id
190
-
191
- def execute(self) -> TriageRule:
192
- """Execute the command to disable a triage rule."""
193
- data = {"enabled": False}
194
-
195
- response = self.http_client.put(f"triages/rules/{self.rule_id}", json_data=data)
196
-
197
- entity_data = response.get("result", {})
198
-
199
- mapped_data = {
200
- "id": entity_data.get("_id"),
201
- "name": entity_data.get("name"),
202
- "description": entity_data.get("description"),
203
- "type": entity_data.get("type"),
204
- "rule_content": entity_data.get("ruleContent", ""),
205
- "enabled": entity_data.get("enabled", True),
206
- "severity": entity_data.get("severity", "medium"),
207
- "tags": entity_data.get("tags", []),
208
- "organization_id": entity_data.get("organizationId", 0),
209
- "created_at": entity_data.get("createdAt"),
210
- "updated_at": entity_data.get("updatedAt"),
211
- "created_by": entity_data.get("createdBy"),
212
- "updated_by": entity_data.get("updatedBy"),
213
- "match_count": entity_data.get("matchCount", 0),
214
- "last_match": entity_data.get("lastMatch"),
215
- }
216
-
217
- # Remove None values
218
- mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
219
-
220
- return TriageRule(**mapped_data)
221
-
222
-
223
- class CreateTriageTagCommand(Command[TriageTag]):
224
- """Command to create a new triage tag."""
225
-
226
- def __init__(self, http_client: HTTPClient, request: Union[CreateTriageTagRequest, Dict[str, Any]]):
227
- self.http_client = http_client
228
- self.request = request
229
-
230
- def execute(self) -> TriageTag:
231
- """Execute the command to create a triage tag."""
232
- # Handle both dict and model objects
233
- if isinstance(self.request, dict):
234
- data = self.request
235
- else:
236
- data = self.request.model_dump(exclude_none=True)
237
-
238
- response = self.http_client.post("triages/tags", json_data=data)
239
-
240
- entity_data = response.get("result", {})
241
-
242
- mapped_data = {
243
- "id": entity_data.get("id") or entity_data.get("_id"), # Handle both id and _id
244
- "name": entity_data.get("name"),
245
- "description": entity_data.get("description"),
246
- "color": entity_data.get("color", "#3498db"),
247
- "organization_id": entity_data.get("organizationId", 0),
248
- "created_at": entity_data.get("createdAt"),
249
- "updated_at": entity_data.get("updatedAt"),
250
- "created_by": entity_data.get("createdBy", "Unknown"), # Provide default for required field
251
- "usage_count": entity_data.get("usageCount") or entity_data.get("count", 0), # Handle both count and usageCount
252
- }
253
-
254
- # Remove None values but keep defaults for required fields
255
- mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
256
-
257
- return TriageTag(**mapped_data)
258
-
259
-
260
- class DeleteTriageTagCommand(Command[Dict[str, Any]]):
261
- """Command to delete a triage tag."""
262
-
263
- def __init__(self, http_client: HTTPClient, tag_id: str):
264
- self.http_client = http_client
265
- self.tag_id = tag_id
266
-
267
- def execute(self) -> Dict[str, Any]:
268
- """Execute the command to delete a triage tag."""
269
- response = self.http_client.delete(f"triage/tags/{self.tag_id}")
270
- return response
271
-
272
-
273
- class CreateTriageProfileCommand(Command[TriageProfile]):
274
- """Command to create a new triage profile."""
275
-
276
- def __init__(self, http_client: HTTPClient, request: Union[CreateTriageProfileRequest, Dict[str, Any]]):
277
- self.http_client = http_client
278
- self.request = request
279
-
280
- def execute(self) -> TriageProfile:
281
- """Execute the command to create a triage profile."""
282
- # Handle both dict and model objects
283
- if isinstance(self.request, dict):
284
- data = self.request
285
- else:
286
- data = self.request.model_dump(exclude_none=True)
287
-
288
- response = self.http_client.post("triage/profiles", json_data=data)
289
-
290
- entity_data = response.get("result", {})
291
-
292
- mapped_data = {
293
- "id": entity_data.get("_id"),
294
- "name": entity_data.get("name"),
295
- "description": entity_data.get("description"),
296
- "rules": entity_data.get("rules", []),
297
- "enabled": entity_data.get("enabled", True),
298
- "organization_id": entity_data.get("organizationId", 0),
299
- "created_at": entity_data.get("createdAt"),
300
- "updated_at": entity_data.get("updatedAt"),
301
- "created_by": entity_data.get("createdBy"),
302
- "updated_by": entity_data.get("updatedBy"),
303
- }
304
-
305
- # Remove None values
306
- mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
307
-
308
- return TriageProfile(**mapped_data)
309
-
310
-
311
- class UpdateTriageProfileCommand(Command[TriageProfile]):
312
- """Command to update an existing triage profile."""
313
-
314
- def __init__(self, http_client: HTTPClient, profile_id: str, request: Union[CreateTriageProfileRequest, Dict[str, Any]]):
315
- self.http_client = http_client
316
- self.profile_id = profile_id
317
- self.request = request
318
-
319
- def execute(self) -> TriageProfile:
320
- """Execute the command to update a triage profile."""
321
- # Handle both dict and model objects
322
- if isinstance(self.request, dict):
323
- data = self.request
324
- else:
325
- data = self.request.model_dump(exclude_none=True)
326
-
327
- response = self.http_client.put(f"triage/profiles/{self.profile_id}", json_data=data)
328
-
329
- entity_data = response.get("result", {})
330
-
331
- mapped_data = {
332
- "id": entity_data.get("_id"),
333
- "name": entity_data.get("name"),
334
- "description": entity_data.get("description"),
335
- "rules": entity_data.get("rules", []),
336
- "enabled": entity_data.get("enabled", True),
337
- "organization_id": entity_data.get("organizationId", 0),
338
- "created_at": entity_data.get("createdAt"),
339
- "updated_at": entity_data.get("updatedAt"),
340
- "created_by": entity_data.get("createdBy"),
341
- "updated_by": entity_data.get("updatedBy"),
342
- }
343
-
344
- # Remove None values
345
- mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
346
-
347
- return TriageProfile(**mapped_data)
348
-
349
-
350
- class DeleteTriageProfileCommand(Command[Dict[str, Any]]):
351
- """Command to delete a triage profile."""
352
-
353
- def __init__(self, http_client: HTTPClient, profile_id: str):
354
- self.http_client = http_client
355
- self.profile_id = profile_id
356
-
357
- def execute(self) -> Dict[str, Any]:
358
- """Execute the command to delete a triage profile."""
359
- response = self.http_client.delete(f"triage/profiles/{self.profile_id}")
360
- return response
1
+ """
2
+ Triage-related commands for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import Dict, Any, Union
6
+
7
+ from ..base import Command
8
+ from ..models.triage import (
9
+ TriageRule, TriageTag, TriageProfile, CreateTriageRuleRequest,
10
+ UpdateTriageRuleRequest, CreateTriageTagRequest, CreateTriageProfileRequest
11
+ )
12
+ from ..http_client import HTTPClient
13
+
14
+
15
+ class CreateTriageRuleCommand(Command[TriageRule]):
16
+ """Command to create a new triage rule."""
17
+
18
+ def __init__(self, http_client: HTTPClient, request: Union[CreateTriageRuleRequest, Dict[str, Any]]):
19
+ self.http_client = http_client
20
+ self.request = request
21
+
22
+ def execute(self) -> TriageRule:
23
+ """Execute the command to create a triage rule."""
24
+ # Handle both dict and model objects
25
+ if isinstance(self.request, dict):
26
+ data = self.request.copy()
27
+ else:
28
+ # Convert SDK model fields to API fields
29
+ request_dict = self.request.model_dump(exclude_none=True)
30
+ data = {
31
+ "description": request_dict.get("description") or request_dict.get("name", ""),
32
+ "rule": request_dict.get("rule_content", ""),
33
+ "engine": request_dict.get("type", "yara"),
34
+ "searchIn": request_dict.get("search_in", "filesystem"),
35
+ "organizationIds": [request_dict.get("organization_id", 0)]
36
+ }
37
+
38
+ response = self.http_client.post("triages/rules", json_data=data)
39
+
40
+ entity_data = response.get("result", {})
41
+
42
+ mapped_data = {
43
+ "id": entity_data.get("_id"),
44
+ "name": entity_data.get("description", ""), # API uses description as name
45
+ "description": entity_data.get("description"),
46
+ "type": entity_data.get("engine"), # API uses engine field
47
+ "rule_content": entity_data.get("rule", ""),
48
+ "search_in": entity_data.get("searchIn"),
49
+ "enabled": entity_data.get("enabled", True),
50
+ "severity": entity_data.get("severity", "medium"),
51
+ "tags": entity_data.get("tags", []),
52
+ "organization_id": entity_data.get("organizationIds", [0])[0] if entity_data.get("organizationIds") else 0,
53
+ "organization_ids": entity_data.get("organizationIds", []),
54
+ "created_at": entity_data.get("createdAt"),
55
+ "updated_at": entity_data.get("updatedAt"),
56
+ "created_by": entity_data.get("createdBy"),
57
+ "updated_by": entity_data.get("updatedBy"),
58
+ "match_count": entity_data.get("matchCount", 0),
59
+ "last_match": entity_data.get("lastMatch"),
60
+ "deletable": entity_data.get("deletable"),
61
+ }
62
+
63
+ # Remove None values
64
+ mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
65
+
66
+ return TriageRule(**mapped_data)
67
+
68
+
69
+ class UpdateTriageRuleCommand(Command[TriageRule]):
70
+ """Command to update an existing triage rule."""
71
+
72
+ def __init__(self, http_client: HTTPClient, rule_id: str, request: Union[UpdateTriageRuleRequest, Dict[str, Any]]):
73
+ self.http_client = http_client
74
+ self.rule_id = rule_id
75
+ self.request = request
76
+
77
+ def execute(self) -> TriageRule:
78
+ """Execute the command to update a triage rule."""
79
+ # Handle both dict and model objects
80
+ if isinstance(self.request, dict):
81
+ data = self.request.copy()
82
+ else:
83
+ # Convert SDK model fields to API fields
84
+ request_dict = self.request.model_dump(exclude_none=True)
85
+ data = {}
86
+
87
+ # Map SDK fields to API fields for update
88
+ if "description" in request_dict or "name" in request_dict:
89
+ data["description"] = request_dict.get("description") or request_dict.get("name", "")
90
+ if "rule_content" in request_dict:
91
+ data["rule"] = request_dict.get("rule_content")
92
+ if "type" in request_dict:
93
+ data["engine"] = request_dict.get("type")
94
+ if "search_in" in request_dict:
95
+ data["searchIn"] = request_dict.get("search_in")
96
+ if "organization_id" in request_dict:
97
+ data["organizationIds"] = [request_dict.get("organization_id")]
98
+ if "enabled" in request_dict:
99
+ data["enabled"] = request_dict.get("enabled")
100
+
101
+ response = self.http_client.put(f"triages/rules/{self.rule_id}", json_data=data)
102
+
103
+ entity_data = response.get("result", {})
104
+
105
+ mapped_data = {
106
+ "id": entity_data.get("_id"),
107
+ "name": entity_data.get("description", ""), # API uses description as name
108
+ "description": entity_data.get("description"),
109
+ "type": entity_data.get("engine"), # API uses engine field
110
+ "rule_content": entity_data.get("rule", ""),
111
+ "search_in": entity_data.get("searchIn"),
112
+ "enabled": entity_data.get("enabled", True),
113
+ "severity": entity_data.get("severity", "medium"),
114
+ "tags": entity_data.get("tags", []),
115
+ "organization_id": entity_data.get("organizationIds", [0])[0] if entity_data.get("organizationIds") else 0,
116
+ "organization_ids": entity_data.get("organizationIds", []),
117
+ "created_at": entity_data.get("createdAt"),
118
+ "updated_at": entity_data.get("updatedAt"),
119
+ "created_by": entity_data.get("createdBy"),
120
+ "updated_by": entity_data.get("updatedBy"),
121
+ "match_count": entity_data.get("matchCount", 0),
122
+ "last_match": entity_data.get("lastMatch"),
123
+ "deletable": entity_data.get("deletable"),
124
+ }
125
+
126
+ # Remove None values
127
+ mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
128
+
129
+ return TriageRule(**mapped_data)
130
+
131
+
132
+ class DeleteTriageRuleCommand(Command[Dict[str, Any]]):
133
+ """Command to delete a triage rule."""
134
+
135
+ def __init__(self, http_client: HTTPClient, rule_id: str):
136
+ self.http_client = http_client
137
+ self.rule_id = rule_id
138
+
139
+ def execute(self) -> Dict[str, Any]:
140
+ """Execute the command to delete a triage rule."""
141
+ response = self.http_client.delete(f"triages/rules/{self.rule_id}")
142
+ return response
143
+
144
+
145
+ class EnableTriageRuleCommand(Command[TriageRule]):
146
+ """Command to enable a triage rule."""
147
+
148
+ def __init__(self, http_client: HTTPClient, rule_id: str):
149
+ self.http_client = http_client
150
+ self.rule_id = rule_id
151
+
152
+ def execute(self) -> TriageRule:
153
+ """Execute the command to enable a triage rule."""
154
+ data = {"enabled": True}
155
+
156
+ response = self.http_client.put(f"triages/rules/{self.rule_id}", json_data=data)
157
+
158
+ entity_data = response.get("result", {})
159
+
160
+ mapped_data = {
161
+ "id": entity_data.get("_id"),
162
+ "name": entity_data.get("name"),
163
+ "description": entity_data.get("description"),
164
+ "type": entity_data.get("type"),
165
+ "rule_content": entity_data.get("ruleContent", ""),
166
+ "enabled": entity_data.get("enabled", True),
167
+ "severity": entity_data.get("severity", "medium"),
168
+ "tags": entity_data.get("tags", []),
169
+ "organization_id": entity_data.get("organizationId", 0),
170
+ "created_at": entity_data.get("createdAt"),
171
+ "updated_at": entity_data.get("updatedAt"),
172
+ "created_by": entity_data.get("createdBy"),
173
+ "updated_by": entity_data.get("updatedBy"),
174
+ "match_count": entity_data.get("matchCount", 0),
175
+ "last_match": entity_data.get("lastMatch"),
176
+ }
177
+
178
+ # Remove None values
179
+ mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
180
+
181
+ return TriageRule(**mapped_data)
182
+
183
+
184
+
185
+
186
+
187
+ class CreateTriageTagCommand(Command[TriageTag]):
188
+ """Command to create a new triage tag."""
189
+
190
+ def __init__(self, http_client: HTTPClient, request: Union[CreateTriageTagRequest, Dict[str, Any]]):
191
+ self.http_client = http_client
192
+ self.request = request
193
+
194
+ def execute(self) -> TriageTag:
195
+ """Execute the command to create a triage tag."""
196
+ # Handle both dict and model objects
197
+ if isinstance(self.request, dict):
198
+ data = self.request
199
+ else:
200
+ data = self.request.model_dump(exclude_none=True, by_alias=True)
201
+
202
+ response = self.http_client.post("triages/tags", json_data=data)
203
+
204
+ entity_data = response.get("result", {})
205
+
206
+ mapped_data = {
207
+ "id": entity_data.get("id") or entity_data.get("_id"), # Handle both id and _id
208
+ "name": entity_data.get("name"),
209
+ "description": entity_data.get("description"),
210
+ "color": entity_data.get("color", "#3498db"),
211
+ "organization_id": entity_data.get("organizationId", 0),
212
+ "created_at": entity_data.get("createdAt"),
213
+ "updated_at": entity_data.get("updatedAt"),
214
+ "created_by": entity_data.get("createdBy", "Unknown"), # Provide default for required field
215
+ "usage_count": entity_data.get("usageCount") or entity_data.get("count", 0), # Handle both count and usageCount
216
+ }
217
+
218
+ # Remove None values but keep defaults for required fields
219
+ mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
220
+
221
+ return TriageTag(**mapped_data)
222
+
223
+
224
+