binalyze-air-sdk 1.0.1__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 (82) hide show
  1. binalyze_air/__init__.py +77 -0
  2. binalyze_air/apis/__init__.py +27 -0
  3. binalyze_air/apis/authentication.py +27 -0
  4. binalyze_air/apis/auto_asset_tags.py +75 -0
  5. binalyze_air/apis/endpoints.py +22 -0
  6. binalyze_air/apis/event_subscription.py +97 -0
  7. binalyze_air/apis/evidence.py +53 -0
  8. binalyze_air/apis/evidences.py +216 -0
  9. binalyze_air/apis/interact.py +36 -0
  10. binalyze_air/apis/params.py +40 -0
  11. binalyze_air/apis/settings.py +27 -0
  12. binalyze_air/apis/user_management.py +74 -0
  13. binalyze_air/apis/users.py +68 -0
  14. binalyze_air/apis/webhooks.py +231 -0
  15. binalyze_air/base.py +133 -0
  16. binalyze_air/client.py +1338 -0
  17. binalyze_air/commands/__init__.py +146 -0
  18. binalyze_air/commands/acquisitions.py +387 -0
  19. binalyze_air/commands/assets.py +363 -0
  20. binalyze_air/commands/authentication.py +37 -0
  21. binalyze_air/commands/auto_asset_tags.py +231 -0
  22. binalyze_air/commands/baseline.py +396 -0
  23. binalyze_air/commands/cases.py +603 -0
  24. binalyze_air/commands/event_subscription.py +102 -0
  25. binalyze_air/commands/evidences.py +988 -0
  26. binalyze_air/commands/interact.py +58 -0
  27. binalyze_air/commands/organizations.py +221 -0
  28. binalyze_air/commands/policies.py +203 -0
  29. binalyze_air/commands/settings.py +29 -0
  30. binalyze_air/commands/tasks.py +56 -0
  31. binalyze_air/commands/triage.py +360 -0
  32. binalyze_air/commands/user_management.py +126 -0
  33. binalyze_air/commands/users.py +101 -0
  34. binalyze_air/config.py +245 -0
  35. binalyze_air/exceptions.py +50 -0
  36. binalyze_air/http_client.py +306 -0
  37. binalyze_air/models/__init__.py +285 -0
  38. binalyze_air/models/acquisitions.py +251 -0
  39. binalyze_air/models/assets.py +439 -0
  40. binalyze_air/models/audit.py +273 -0
  41. binalyze_air/models/authentication.py +70 -0
  42. binalyze_air/models/auto_asset_tags.py +117 -0
  43. binalyze_air/models/baseline.py +232 -0
  44. binalyze_air/models/cases.py +276 -0
  45. binalyze_air/models/endpoints.py +76 -0
  46. binalyze_air/models/event_subscription.py +172 -0
  47. binalyze_air/models/evidence.py +66 -0
  48. binalyze_air/models/evidences.py +349 -0
  49. binalyze_air/models/interact.py +136 -0
  50. binalyze_air/models/organizations.py +294 -0
  51. binalyze_air/models/params.py +128 -0
  52. binalyze_air/models/policies.py +250 -0
  53. binalyze_air/models/settings.py +84 -0
  54. binalyze_air/models/tasks.py +149 -0
  55. binalyze_air/models/triage.py +143 -0
  56. binalyze_air/models/user_management.py +97 -0
  57. binalyze_air/models/users.py +82 -0
  58. binalyze_air/queries/__init__.py +134 -0
  59. binalyze_air/queries/acquisitions.py +156 -0
  60. binalyze_air/queries/assets.py +105 -0
  61. binalyze_air/queries/audit.py +417 -0
  62. binalyze_air/queries/authentication.py +56 -0
  63. binalyze_air/queries/auto_asset_tags.py +60 -0
  64. binalyze_air/queries/baseline.py +185 -0
  65. binalyze_air/queries/cases.py +293 -0
  66. binalyze_air/queries/endpoints.py +25 -0
  67. binalyze_air/queries/event_subscription.py +55 -0
  68. binalyze_air/queries/evidence.py +140 -0
  69. binalyze_air/queries/evidences.py +280 -0
  70. binalyze_air/queries/interact.py +28 -0
  71. binalyze_air/queries/organizations.py +223 -0
  72. binalyze_air/queries/params.py +115 -0
  73. binalyze_air/queries/policies.py +150 -0
  74. binalyze_air/queries/settings.py +20 -0
  75. binalyze_air/queries/tasks.py +82 -0
  76. binalyze_air/queries/triage.py +231 -0
  77. binalyze_air/queries/user_management.py +83 -0
  78. binalyze_air/queries/users.py +69 -0
  79. binalyze_air_sdk-1.0.1.dist-info/METADATA +635 -0
  80. binalyze_air_sdk-1.0.1.dist-info/RECORD +82 -0
  81. binalyze_air_sdk-1.0.1.dist-info/WHEEL +5 -0
  82. binalyze_air_sdk-1.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,439 @@
1
+ """
2
+ Asset-related data models for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import List, Optional, Dict, Any, Union
6
+ from datetime import datetime
7
+ from enum import Enum
8
+ from pydantic import Field
9
+
10
+ from ..base import AIRBaseModel, Filter
11
+
12
+
13
+ class OnlineStatus(str, Enum):
14
+ """Asset online status."""
15
+ ONLINE = "online"
16
+ OFFLINE = "offline"
17
+
18
+
19
+ class Platform(str, Enum):
20
+ """Asset platform."""
21
+ WINDOWS = "windows"
22
+ LINUX = "linux"
23
+ DARWIN = "darwin" # Fixed: API uses 'darwin' not 'macos'
24
+ AIX = "aix"
25
+ DISK_IMAGE = "disk-image" # API also returns this value
26
+
27
+
28
+ class IsolationStatus(str, Enum):
29
+ """Asset isolation status."""
30
+ ISOLATING = "isolating"
31
+ ISOLATED = "isolated"
32
+ UNISOLATING = "unisolating"
33
+ UNISOLATED = "unisolated"
34
+
35
+
36
+ class ManagedStatus(str, Enum):
37
+ """Asset managed status."""
38
+ MANAGED = "managed"
39
+ UNMANAGED = "unmanaged"
40
+ OFF_NETWORK = "off-network" # Added missing status
41
+
42
+
43
+ class IssueType(str, Enum):
44
+ """Asset issue types."""
45
+ UNREACHABLE = "unreachable"
46
+ OLD_VERSION = "old-version"
47
+ UPDATE_REQUIRED = "update-required"
48
+
49
+
50
+ class Tag(AIRBaseModel):
51
+ """Tag model for assets."""
52
+ id: Optional[str] = Field(alias="_id", default=None)
53
+ name: str
54
+ color: Optional[str] = None
55
+ assignee_count: Optional[int] = Field(alias="assigneeCount", default=None) # NEW
56
+ organization_id: Optional[int] = Field(alias="organizationId", default=None)
57
+ created_at: Optional[datetime] = Field(alias="createdAt", default=None) # NEW
58
+ updated_at: Optional[datetime] = Field(alias="updatedAt", default=None) # NEW
59
+
60
+
61
+ class SystemResources(AIRBaseModel):
62
+ """System resources information."""
63
+ cpu: Optional[Dict[str, Any]] = None
64
+ ram: Optional[Dict[str, Any]] = None
65
+ disks: Optional[List[Dict[str, Any]]] = None
66
+
67
+
68
+ class NetworkInterface(AIRBaseModel):
69
+ """Network interface information."""
70
+ name: str
71
+ mac_addr: Optional[str] = Field(alias="macAddr", default=None)
72
+ ipv4_addrs: Optional[List[str]] = Field(alias="ipv4Addrs", default_factory=list)
73
+ ipv6_addrs: Optional[List[str]] = Field(alias="ipv6Addrs", default_factory=list)
74
+
75
+
76
+ class AssetFeatures(AIRBaseModel):
77
+ """Asset feature capabilities."""
78
+ isolation_supported: Optional[bool] = Field(alias="isolationSupported", default=None)
79
+ full_disk_access_enabled: Optional[bool] = Field(alias="fullDiskAccessEnabled", default=None)
80
+
81
+
82
+ class CloudInfo(AIRBaseModel):
83
+ """Cloud provider information."""
84
+ # This will be populated based on actual API response structure
85
+ provider: Optional[str] = None
86
+ region: Optional[str] = None
87
+ instance_id: Optional[str] = None
88
+
89
+
90
+ class Asset(AIRBaseModel):
91
+ """Asset (endpoint) model."""
92
+
93
+ # Basic identification
94
+ id: str = Field(alias="_id")
95
+ name: str
96
+ os: Optional[str] = None # Make optional since some assets don't have this
97
+ platform: Platform
98
+ ip_address: Optional[str] = Field(alias="ipAddress", default=None)
99
+ organization_id: int = Field(alias="organizationId", default=0)
100
+
101
+ # Group and labeling
102
+ group_id: Optional[str] = Field(alias="groupId", default=None)
103
+ group_full_path: Optional[str] = Field(alias="groupFullPath", default=None)
104
+ label: Optional[str] = None
105
+
106
+ # Server and management status
107
+ is_server: bool = Field(alias="isServer", default=False)
108
+ is_managed: bool = Field(alias="isManaged", default=True)
109
+ agent_installed: Optional[bool] = Field(alias="agentInstalled", default=None)
110
+ managed_status: ManagedStatus = Field(alias="managedStatus", default=ManagedStatus.MANAGED)
111
+
112
+ # Network information
113
+ net_interfaces: Optional[List[NetworkInterface]] = Field(alias="netInterfaces", default_factory=list)
114
+
115
+ # Timestamps
116
+ last_seen: Optional[datetime] = Field(alias="lastSeen", default=None)
117
+ registered_at: Optional[datetime] = Field(alias="registeredAt", default=None)
118
+ created_at: Optional[datetime] = Field(alias="createdAt", default=None)
119
+ updated_at: Optional[datetime] = Field(alias="updatedAt", default=None)
120
+
121
+ # Version and architecture
122
+ version: Optional[str] = None
123
+ version_no: Optional[int] = Field(alias="versionNo", default=None)
124
+ build_arch: Optional[str] = Field(alias="buildArch", default=None)
125
+
126
+ # Security and authentication
127
+ security_token: Optional[str] = Field(alias="securityToken", default=None)
128
+ online_status: OnlineStatus = Field(alias="onlineStatus", default=OnlineStatus.OFFLINE)
129
+ isolation_status: IsolationStatus = Field(alias="isolationStatus", default=IsolationStatus.UNISOLATED)
130
+
131
+ # System resources and capabilities
132
+ system_resources: Optional[SystemResources] = Field(alias="systemResources", default=None)
133
+ features: Optional[AssetFeatures] = None
134
+
135
+ # Cloud and infrastructure
136
+ cloud: Optional[CloudInfo] = None
137
+ has_evidence: Optional[bool] = Field(alias="hasEvidence", default=None)
138
+ relay_server_id: Optional[str] = Field(alias="relayServerId", default=None)
139
+ connection_route: Optional[str] = Field(alias="connectionRoute", default=None)
140
+
141
+ # Asset classification
142
+ asset_id: Optional[str] = Field(alias="assetId", default=None)
143
+ asset_type: Optional[str] = Field(alias="assetType", default=None)
144
+ timezone_offset: Optional[int] = Field(alias="timezoneOffset", default=None)
145
+
146
+ # Vendor information
147
+ vendor_id: Optional[str] = Field(alias="vendorId", default=None)
148
+ vendor_device_id: Optional[str] = Field(alias="vendorDeviceId", default=None)
149
+ responder_id: Optional[str] = Field(alias="responderId", default=None)
150
+
151
+ # Update management
152
+ excluded_from_updates: Optional[bool] = Field(alias="excludedFromUpdates", default=None)
153
+ unsupported_os_to_update: Optional[bool] = Field(alias="unsupportedOsToUpdate", default=None)
154
+ version_updating: Optional[bool] = Field(alias="versionUpdating", default=None)
155
+ deploying: Optional[bool] = Field(alias="deploying", default=None)
156
+ waiting_for_version_update_fix: bool = Field(alias="waitingForVersionUpdateFix", default=False)
157
+
158
+ # Issues, tags, and policies
159
+ issues: List[str] = []
160
+ tags: List[Union[str, Tag]] = []
161
+ policies: List[Any] = []
162
+
163
+
164
+ class AssetDetail(Asset):
165
+ """Detailed asset information."""
166
+ pass # Same as Asset for now, can be extended
167
+
168
+
169
+ class TaskDurations(AIRBaseModel):
170
+ """Task duration information."""
171
+ processing: Optional[int] = None
172
+ uploading: Optional[int] = None
173
+ analyzing: Optional[int] = None
174
+ compressing: Optional[int] = None
175
+
176
+
177
+ class TaskMetadata(AIRBaseModel):
178
+ """Task metadata information."""
179
+ purged: Optional[bool] = None
180
+ has_case_db: Optional[bool] = Field(alias="hasCaseDb", default=None)
181
+ has_case_ppc: Optional[bool] = Field(alias="hasCasePpc", default=None)
182
+ has_drone_data: Optional[bool] = Field(alias="hasDroneData", default=None)
183
+ isolation: Optional[bool] = None
184
+ import_status: Optional[str] = Field(alias="importStatus", default=None)
185
+ disk_usage_in_bytes: Optional[int] = Field(alias="diskUsageInBytes", default=None)
186
+
187
+
188
+ class TaskResponse(AIRBaseModel):
189
+ """Task response information."""
190
+ error_message: Optional[str] = Field(alias="errorMessage", default=None)
191
+ case_directory: Optional[str] = Field(alias="caseDirectory", default=None)
192
+ match_count: Optional[int] = Field(alias="matchCount", default=None)
193
+ result: Optional[Dict[str, Any]] = None
194
+
195
+
196
+ class AssetTask(AIRBaseModel):
197
+ """Task associated with an asset."""
198
+
199
+ # Basic identification
200
+ id: str = Field(alias="_id")
201
+ task_id: str = Field(alias="taskId")
202
+ name: str
203
+ type: str
204
+ endpoint_id: str = Field(alias="endpointId")
205
+ endpoint_name: str = Field(alias="endpointName")
206
+ organization_id: int = Field(alias="organizationId", default=0)
207
+
208
+ # Status and progress
209
+ status: str
210
+ recurrence: Optional[str] = None
211
+ progress: int = 0
212
+
213
+ # Duration information
214
+ duration: Optional[int] = None # Legacy single duration field
215
+ durations: Optional[TaskDurations] = None # NEW - Complex duration object
216
+
217
+ # Task metadata
218
+ metadata: Optional[TaskMetadata] = None # NEW - Task metadata
219
+
220
+ # Relationships
221
+ case_ids: Optional[List[str]] = Field(alias="caseIds", default_factory=list)
222
+
223
+ # Timestamps and users
224
+ created_at: Optional[datetime] = Field(alias="createdAt", default=None)
225
+ created_by: Optional[str] = Field(alias="createdBy", default=None) # NEW
226
+ updated_at: Optional[datetime] = Field(alias="updatedAt", default=None)
227
+ updated_by: Optional[str] = Field(alias="updatedBy", default=None) # NEW - Not in response but likely exists
228
+
229
+ # Response data
230
+ response: Optional[TaskResponse] = None # Enhanced response structure
231
+
232
+
233
+ class AssetFilter(Filter):
234
+ """Filter for asset queries - matches API documentation exactly."""
235
+
236
+ # Basic search and identification
237
+ search_term: Optional[str] = None
238
+ name: Optional[str] = None
239
+ ip_address: Optional[str] = None
240
+
241
+ # Group and organization
242
+ group_id: Optional[str] = None
243
+ group_full_path: Optional[str] = None
244
+ organization_ids: List[int] = [] # Required by API
245
+
246
+ # Status filters (arrays as per API)
247
+ managed_status: Optional[List[str]] = None # ["managed", "unmanaged", "off-network"]
248
+ isolation_status: Optional[List[str]] = None # ["isolating", "isolated", "unisolating", "unisolated"]
249
+ online_status: Optional[List[str]] = None # ["online", "offline"]
250
+
251
+ # Platform and technical details
252
+ platform: Optional[List[str]] = None # ["windows", "linux", "darwin"]
253
+ issue: Optional[List[str]] = None # ["unreachable", "old-version", "update-required"] - Changed to List
254
+ version: Optional[str] = None
255
+ policy: Optional[str] = None
256
+
257
+ # Tags and labels
258
+ tags: Optional[List[str]] = None
259
+ tag_id: Optional[str] = None # Added missing field - required by some endpoints
260
+ label: Optional[str] = None # Added missing field
261
+
262
+ # Endpoint targeting
263
+ included_endpoint_ids: Optional[List[str]] = None
264
+ excluded_endpoint_ids: Optional[List[str]] = None
265
+
266
+ # Date/time filters - Added missing fields
267
+ last_seen_before: Optional[str] = None # ISO 8601 format
268
+ last_seen_after: Optional[str] = None # ISO 8601 format
269
+ last_seen_between: Optional[str] = None # Comma-separated range
270
+ last_seen: Optional[str] = None # For backward compatibility
271
+
272
+ # Cloud provider filters - Added missing fields
273
+ aws_regions: Optional[List[str]] = None
274
+ azure_regions: Optional[List[str]] = None
275
+
276
+ # Special filters - Added missing field
277
+ unisolated: Optional[str] = None # Added per API docs
278
+
279
+ def to_params(self) -> Dict[str, Any]:
280
+ """Convert filter to API parameters."""
281
+ # Start with base pagination/sorting parameters from parent class
282
+ params = super().to_params()
283
+
284
+ # Add asset-specific filter parameters
285
+ if self.search_term is not None:
286
+ params["filter[searchTerm]"] = self.search_term
287
+ if self.name is not None:
288
+ params["filter[name]"] = self.name
289
+ if self.ip_address is not None:
290
+ params["filter[ipAddress]"] = self.ip_address
291
+
292
+ # Group parameters
293
+ if self.group_id is not None:
294
+ params["filter[groupId]"] = self.group_id
295
+ if self.group_full_path is not None:
296
+ params["filter[groupFullPath]"] = self.group_full_path
297
+
298
+ # Organization IDs (required)
299
+ if self.organization_ids:
300
+ params["filter[organizationIds]"] = ",".join(map(str, self.organization_ids))
301
+
302
+ # Status arrays
303
+ if self.managed_status:
304
+ params["filter[managedStatus]"] = ",".join(self.managed_status)
305
+ if self.isolation_status:
306
+ params["filter[isolationStatus]"] = ",".join(self.isolation_status)
307
+ if self.online_status:
308
+ params["filter[onlineStatus]"] = ",".join(self.online_status)
309
+
310
+ # Platform and technical
311
+ if self.platform:
312
+ params["filter[platform]"] = ",".join(self.platform)
313
+ if self.issue:
314
+ params["filter[issue]"] = ",".join(self.issue)
315
+ if self.version is not None:
316
+ params["filter[version]"] = self.version
317
+ if self.policy is not None:
318
+ params["filter[policy]"] = self.policy
319
+
320
+ # Tags and labels
321
+ if self.tags:
322
+ params["filter[tags]"] = ",".join(self.tags)
323
+ if self.tag_id is not None:
324
+ params["filter[tagId]"] = self.tag_id
325
+ if self.label is not None:
326
+ params["filter[label]"] = self.label
327
+
328
+ # Endpoint targeting
329
+ if self.included_endpoint_ids:
330
+ params["filter[includedEndpointIds]"] = ",".join(self.included_endpoint_ids)
331
+ if self.excluded_endpoint_ids:
332
+ params["filter[excludedEndpointIds]"] = ",".join(self.excluded_endpoint_ids)
333
+
334
+ # Date/time filters
335
+ if self.last_seen_before is not None:
336
+ params["filter[lastSeenBefore]"] = self.last_seen_before
337
+ if self.last_seen_after is not None:
338
+ params["filter[lastSeenAfter]"] = self.last_seen_after
339
+ if self.last_seen_between is not None:
340
+ params["filter[lastSeenBetween]"] = self.last_seen_between
341
+ if self.last_seen is not None:
342
+ params["filter[lastSeen]"] = self.last_seen
343
+
344
+ # Cloud provider filters
345
+ if self.aws_regions:
346
+ params["filter[awsRegions]"] = ",".join(self.aws_regions)
347
+ if self.azure_regions:
348
+ params["filter[azureRegions]"] = ",".join(self.azure_regions)
349
+
350
+ # Special filters
351
+ if self.unisolated is not None:
352
+ params["filter[unisolated]"] = self.unisolated
353
+
354
+ return params
355
+
356
+ def to_filter_dict(self) -> Dict[str, Any]:
357
+ """Convert filter to dictionary for command payloads - matches API specification exactly."""
358
+ # Include ALL fields as per API specification, with empty defaults
359
+ filter_dict = {
360
+ # Basic search fields (empty strings as defaults)
361
+ "searchTerm": self.search_term or "",
362
+ "name": self.name or "",
363
+ "ipAddress": self.ip_address or "",
364
+ "groupId": self.group_id or "",
365
+ "groupFullPath": self.group_full_path or "",
366
+
367
+ # Required array fields (with proper defaults)
368
+ "organizationIds": self.organization_ids or [0], # Keep as integers
369
+ "managedStatus": self.managed_status or ["managed"], # Default to managed
370
+ "isolationStatus": self.isolation_status or [],
371
+ "onlineStatus": self.online_status or [],
372
+ "platform": self.platform or [],
373
+ "tags": self.tags or [],
374
+
375
+ # Technical fields (empty strings as defaults)
376
+ "issue": self.issue[0] if self.issue and len(self.issue) > 0 else "", # API expects string not array
377
+ "version": self.version or "",
378
+ "policy": self.policy or "",
379
+ "tagId": self.tag_id or "", # Added missing field
380
+
381
+ # Endpoint targeting (with proper defaults)
382
+ "includedEndpointIds": self.included_endpoint_ids or [],
383
+ "excludedEndpointIds": self.excluded_endpoint_ids or [],
384
+ }
385
+
386
+ return filter_dict
387
+
388
+
389
+ class AssetTaskFilter(Filter):
390
+ """Filter for asset task queries - matches API specification exactly."""
391
+
392
+ # Search and identification
393
+ search_term: Optional[str] = None
394
+ name: Optional[str] = None
395
+ endpoint_ids: Optional[List[str]] = None
396
+
397
+ # Task properties
398
+ status: Optional[List[str]] = None # scheduled, assigned, processing, completed, failed, expired, cancelled, compressing, uploading, analyzing, partially-completed
399
+ type: Optional[List[str]] = None # acquisition, offline-acquisition, triage, offline-triage, investigation, interact-shell, baseline-comparison, baseline-acquisition, acquire-image
400
+ execution_type: Optional[List[str]] = None # instant, scheduled
401
+
402
+ # Task metadata
403
+ has_drone_data: Optional[str] = None # yes, no
404
+
405
+ def to_params(self) -> Dict[str, Any]:
406
+ """Convert filter to API parameters."""
407
+ params = {}
408
+
409
+ # Pagination parameters (not in filter namespace) - only if set
410
+ if self.page_number is not None:
411
+ params["pageNumber"] = self.page_number
412
+ if self.page_size is not None:
413
+ params["pageSize"] = self.page_size
414
+ if self.sort_by is not None:
415
+ params["sortBy"] = self.sort_by
416
+ if self.sort_type is not None:
417
+ params["sortType"] = self.sort_type
418
+
419
+ # Add task-specific filter parameters (use API field names)
420
+ if self.search_term is not None:
421
+ params["filter[searchTerm]"] = self.search_term
422
+ if self.name is not None:
423
+ params["filter[name]"] = self.name
424
+ if self.endpoint_ids:
425
+ params["filter[endpointIds]"] = ",".join(self.endpoint_ids)
426
+
427
+ # Status and type arrays
428
+ if self.status:
429
+ params["filter[status]"] = ",".join(self.status)
430
+ if self.type:
431
+ params["filter[type]"] = ",".join(self.type)
432
+ if self.execution_type:
433
+ params["filter[executionType]"] = ",".join(self.execution_type)
434
+
435
+ # Special filters
436
+ if self.has_drone_data is not None:
437
+ params["filter[hasDroneData]"] = self.has_drone_data
438
+
439
+ return params