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,276 @@
1
+ """
2
+ Case-related data models for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import List, Optional, Dict, Any, Literal, Union
6
+ from datetime import datetime
7
+ from enum import Enum
8
+ from pydantic import Field, field_validator
9
+
10
+ from ..base import AIRBaseModel, Filter
11
+
12
+
13
+ class CaseStatus(str, Enum):
14
+ """Case status."""
15
+ OPEN = "open"
16
+ CLOSED = "closed"
17
+ ARCHIVED = "archived"
18
+
19
+
20
+ class CaseNote(AIRBaseModel):
21
+ """Case note model."""
22
+
23
+ id: str = Field(alias="_id")
24
+ case_id: str = Field(alias="caseId")
25
+ value: str
26
+ written_at: Optional[datetime] = Field(default=None, alias="writtenAt")
27
+ written_by: Optional[str] = Field(default=None, alias="writtenBy")
28
+ created_at: Optional[datetime] = Field(default=None, alias="createdAt")
29
+ updated_at: Optional[datetime] = Field(default=None, alias="updatedAt")
30
+
31
+
32
+ class Case(AIRBaseModel):
33
+ """Case model."""
34
+
35
+ id: str = Field(alias="_id")
36
+ name: str
37
+ status: CaseStatus = CaseStatus.OPEN
38
+ owner_user_id: str = Field(alias="ownerUserId")
39
+ owner_user: Optional[Any] = Field(default=None, alias="ownerUser")
40
+ assigned_user_ids: List[str] = Field(default=[], alias="assignedUserIds")
41
+ assigned_users: List[Any] = Field(default=[], alias="assignedUsers")
42
+ visibility: str
43
+ organization_id: int = Field(default=0, alias="organizationId")
44
+ notes: List[Any] = []
45
+ endpoint_count: int = Field(default=0, alias="endpointCount")
46
+ task_count: int = Field(default=0, alias="taskCount")
47
+ acquisition_task_count: int = Field(default=0, alias="acquisitionTaskCount")
48
+ created_at: Optional[datetime] = Field(default=None, alias="createdAt")
49
+ updated_at: Optional[datetime] = Field(default=None, alias="updatedAt")
50
+ closed_at: Optional[datetime] = Field(default=None, alias="closedAt")
51
+ archived_at: Optional[datetime] = Field(default=None, alias="archivedAt")
52
+ # Optional fields that may not be in all responses
53
+ source: Optional[str] = None
54
+ started_on: Optional[datetime] = Field(default=None, alias="startedOn")
55
+ total_days: int = Field(default=0, alias="totalDays")
56
+ total_endpoints: int = Field(default=0, alias="totalEndpoints")
57
+ closed_on: Optional[datetime] = Field(default=None, alias="closedOn")
58
+
59
+
60
+ class CaseActivity(AIRBaseModel):
61
+ """Case activity model."""
62
+
63
+ id: Optional[str] = Field(default=None, alias="_id")
64
+ case_id: Optional[str] = Field(default=None, alias="caseId")
65
+ user_id: Optional[str] = Field(default=None, alias="userId")
66
+ user: Optional[Any] = None
67
+ activity_type: Optional[str] = Field(default=None, alias="activityType")
68
+ description: Optional[str] = None
69
+ details: Dict[str, Any] = {}
70
+ created_at: Optional[datetime] = Field(default=None, alias="createdAt")
71
+ # Legacy fields that may still exist
72
+ updated_at: Optional[datetime] = Field(default=None, alias="updatedAt")
73
+ type: Optional[str] = None
74
+ performed_by: Optional[str] = Field(default=None, alias="performedBy")
75
+ data: Optional[Any] = None
76
+ organization_ids: List[int] = Field(default=[], alias="organizationIds")
77
+
78
+
79
+ class CaseEndpoint(AIRBaseModel):
80
+ """Case endpoint model."""
81
+
82
+ platform: str
83
+ tags: List[str] = []
84
+ isolation_status: str = Field(alias="isolationStatus")
85
+ id: str = Field(alias="_id")
86
+ created_at: Optional[datetime] = Field(default=None, alias="createdAt")
87
+ updated_at: Optional[datetime] = Field(default=None, alias="updatedAt")
88
+ organization_id: int = Field(default=0, alias="organizationId")
89
+ ip_address: Optional[str] = Field(default=None, alias="ipAddress")
90
+ name: str
91
+ group_id: Optional[str] = Field(default=None, alias="groupId")
92
+ group_full_path: Optional[str] = Field(default=None, alias="groupFullPath")
93
+ os: str
94
+ is_server: bool = Field(default=False, alias="isServer")
95
+ is_managed: bool = Field(default=True, alias="isManaged")
96
+ last_seen: Optional[datetime] = Field(default=None, alias="lastSeen")
97
+ version: Optional[str] = None
98
+ version_no: Optional[int] = Field(default=None, alias="versionNo")
99
+ registered_at: Optional[datetime] = Field(default=None, alias="registeredAt")
100
+ security_token: Optional[str] = Field(default=None, alias="securityToken")
101
+ online_status: str = Field(alias="onlineStatus")
102
+ issues: List[str] = []
103
+ label: Optional[str] = None
104
+ waiting_for_version_update_fix: bool = Field(default=False, alias="waitingForVersionUpdateFix")
105
+
106
+
107
+ class CaseTask(AIRBaseModel):
108
+ """Case task model."""
109
+
110
+ id: str = Field(alias="_id")
111
+ task_id: str = Field(alias="taskId")
112
+ name: str
113
+ type: str
114
+ endpoint_id: str = Field(alias="endpointId")
115
+ endpoint_name: str = Field(alias="endpointName")
116
+ organization_id: int = Field(default=0, alias="organizationId")
117
+ status: str
118
+ recurrence: Optional[str] = None
119
+ progress: int = 0
120
+ duration: Optional[int] = None
121
+ case_ids: List[str] = Field(default=[], alias="caseIds")
122
+ is_comparable: bool = Field(default=False, alias="isComparable")
123
+ created_at: Optional[datetime] = Field(default=None, alias="createdAt")
124
+ updated_at: Optional[datetime] = Field(default=None, alias="updatedAt")
125
+ response: Optional[Any] = None
126
+
127
+
128
+ class UserProfile(AIRBaseModel):
129
+ """User profile model."""
130
+
131
+ name: str
132
+ surname: str
133
+ department: str
134
+
135
+
136
+ class Role(AIRBaseModel):
137
+ """User role model."""
138
+
139
+ id: str = Field(alias="_id")
140
+ name: str
141
+ created_by: str = Field(alias="createdBy")
142
+ tag: str
143
+ privilege_types: List[str] = Field(default=[], alias="privilegeTypes")
144
+ created_at: Optional[datetime] = Field(default=None, alias="createdAt")
145
+ updated_at: Optional[datetime] = Field(default=None, alias="updatedAt")
146
+
147
+
148
+ class User(AIRBaseModel):
149
+ """User model."""
150
+
151
+ id: str = Field(alias="_id")
152
+ email: str
153
+ username: str
154
+ organization_ids: Union[List[int], str] = Field(default=[], alias="organizationIds")
155
+ strategy: str
156
+ profile: UserProfile
157
+ created_at: Optional[datetime] = Field(default=None, alias="createdAt")
158
+ updated_at: Optional[datetime] = Field(default=None, alias="updatedAt")
159
+ roles: List[Role] = []
160
+
161
+ @field_validator('organization_ids', mode='before')
162
+ @classmethod
163
+ def parse_organization_ids(cls, v):
164
+ """Handle organizationIds which can be a list of ints or the string 'ALL'."""
165
+ if isinstance(v, str):
166
+ # Handle the "ALL" case - return as is for now
167
+ return v
168
+ elif isinstance(v, list):
169
+ # Ensure all items are integers
170
+ return [int(item) if not isinstance(item, int) else item for item in v]
171
+ else:
172
+ # Default to empty list for other cases
173
+ return []
174
+
175
+
176
+ class CaseFilter(Filter):
177
+ """Filter for case queries."""
178
+
179
+ name: Optional[str] = None
180
+ status: Optional[List[CaseStatus]] = None
181
+ owner_user_id: Optional[str] = None
182
+ assigned_user_ids: Optional[List[str]] = None
183
+ visibility: Optional[str] = None
184
+ source: Optional[str] = None
185
+
186
+
187
+ class CaseActivityFilter(Filter):
188
+ """Filter for case activity queries."""
189
+
190
+ performed_by: Optional[List[str]] = None # User IDs who performed the activities
191
+ types: Optional[List[str]] = None # Activity types to filter
192
+ search_term: Optional[str] = None # Search term for activity descriptions
193
+ occurred_at: Optional[str] = None # Date range filter
194
+ page_number: Optional[int] = Field(default=1, ge=1) # Page number (min: 1)
195
+ page_size: Optional[int] = Field(default=10, ge=1) # Page size (min: 1)
196
+ sort_by: Optional[str] = Field(default="createdAt") # Sort field
197
+ sort_type: Optional[Literal["ASC", "DESC"]] = Field(default="ASC") # Sort direction
198
+
199
+
200
+ class CaseEndpointFilter(Filter):
201
+ """Filter for case endpoint queries."""
202
+
203
+ organization_ids: Optional[List[int]] = None # Required organization IDs
204
+ search_term: Optional[str] = None # Search term for endpoint names
205
+ name: Optional[str] = None # Endpoint name filter
206
+ ip_address: Optional[str] = None # IP address filter
207
+ group_id: Optional[str] = None # Group ID filter
208
+ group_full_path: Optional[str] = None # Group full path filter
209
+ label: Optional[str] = None # Label filter
210
+ last_seen: Optional[str] = None # Last seen date range (e.g., "2023-03-06T21:00:00.000Z,2023-03-24T21:00:00.000Z")
211
+ managed_status: Optional[List[str]] = None # Managed status filter (unmanaged, managed, off-network)
212
+ isolation_status: Optional[List[str]] = None # Isolation status filter (isolating, isolated, unisolating, unisolated)
213
+ platform: Optional[List[str]] = None # Platform filter (windows, linux, darwin)
214
+ issue: Optional[List[str]] = None # Issue filter (unreachable, old-version, update-required)
215
+ online_status: Optional[List[str]] = None # Online status filter (online, offline)
216
+ tags: Optional[List[str]] = None # Tags filter
217
+ version: Optional[str] = None # Version filter
218
+ policy: Optional[str] = None # Policy filter
219
+ included_endpoint_ids: Optional[List[str]] = None # Included endpoint IDs
220
+ excluded_endpoint_ids: Optional[List[str]] = None # Excluded endpoint IDs
221
+ aws_regions: Optional[List[str]] = None # AWS regions filter
222
+ azure_regions: Optional[List[str]] = None # Azure regions filter
223
+ page_number: Optional[int] = Field(default=1, ge=1) # Page number (min: 1)
224
+ page_size: Optional[int] = Field(default=10, ge=1) # Page size (min: 1)
225
+ sort_by: Optional[str] = Field(default="createdAt") # Sort field
226
+ sort_type: Optional[Literal["ASC", "DESC"]] = Field(default="ASC") # Sort direction
227
+
228
+
229
+ class CaseTaskFilter(Filter):
230
+ """Filter for case task queries."""
231
+
232
+ organization_ids: Optional[List[int]] = None # Required organization IDs
233
+ search_term: Optional[str] = None # Search term for task names
234
+ name: Optional[str] = None # Task name filter
235
+ endpoint_ids: Optional[List[str]] = None # Endpoint IDs filter
236
+ execution_type: Optional[str] = None # Execution type filter (instant, scheduled)
237
+ status: Optional[str] = None # Status filter (scheduled, assigned, processing, completed, failed, expired, cancelled, compressing, uploading, analyzing, partially-completed)
238
+ type: Optional[str] = None # Task type filter (acquisition, offline-acquisition, triage, offline-triage, investigation, interact-shell, baseline-comparison, baseline-acquisition, acquire-image)
239
+ asset_names: Optional[str] = None # Comma separated asset names
240
+ started_by: Optional[str] = None # Started by user filter
241
+ page_number: Optional[int] = Field(default=1, ge=1) # Page number (min: 1)
242
+ page_size: Optional[int] = Field(default=10, ge=1) # Page size (min: 1)
243
+ sort_by: Optional[str] = Field(default="createdAt") # Sort field
244
+ sort_type: Optional[Literal["ASC", "DESC"]] = Field(default="ASC") # Sort direction
245
+
246
+
247
+ class CaseUserFilter(Filter):
248
+ """Filter for case user queries."""
249
+
250
+ organization_ids: Optional[List[int]] = None # Required organization IDs
251
+ search_term: Optional[str] = None # Search term for user filtering
252
+ page_number: Optional[int] = Field(default=1, ge=1) # Page number (min: 1)
253
+ page_size: Optional[int] = Field(default=10, ge=1) # Page size (min: 1)
254
+ sort_by: Optional[str] = Field(default="createdAt") # Sort field
255
+ sort_type: Optional[Literal["ASC", "DESC"]] = Field(default="ASC") # Sort direction
256
+
257
+
258
+ class CreateCaseRequest(AIRBaseModel):
259
+ """Request model for creating a case."""
260
+
261
+ organization_id: int = 0
262
+ name: str
263
+ owner_user_id: str
264
+ visibility: str
265
+ assigned_user_ids: List[str] = []
266
+
267
+
268
+ class UpdateCaseRequest(AIRBaseModel):
269
+ """Request model for updating a case."""
270
+
271
+ name: Optional[str] = None
272
+ owner_user_id: Optional[str] = None
273
+ visibility: Optional[str] = None
274
+ assigned_user_ids: Optional[List[str]] = None
275
+ status: Optional[CaseStatus] = None
276
+ notes: Optional[List[Any]] = None
@@ -0,0 +1,76 @@
1
+ """
2
+ Endpoints API models for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import List, Optional, Dict, Any
6
+ from datetime import datetime
7
+ from enum import Enum
8
+
9
+ from ..base import AIRBaseModel, Filter
10
+
11
+
12
+ class TagType(str, Enum):
13
+ """Endpoint tag types."""
14
+ SYSTEM = "system"
15
+ USER = "user"
16
+ CUSTOM = "custom"
17
+ AUTO = "auto"
18
+
19
+
20
+ class TagScope(str, Enum):
21
+ """Tag scope."""
22
+ GLOBAL = "global"
23
+ ORGANIZATION = "organization"
24
+ GROUP = "group"
25
+
26
+
27
+ class EndpointTag(AIRBaseModel):
28
+ """Endpoint tag model."""
29
+
30
+ id: str
31
+ name: str
32
+ description: Optional[str] = None
33
+ color: Optional[str] = None
34
+ tag_type: TagType = TagType.USER
35
+ scope: TagScope = TagScope.ORGANIZATION
36
+ organization_id: Optional[int] = None
37
+ created_by: Optional[str] = None
38
+ created_at: Optional[datetime] = None
39
+ updated_at: Optional[datetime] = None
40
+ usage_count: int = 0
41
+ is_system: bool = False
42
+ is_deletable: bool = True
43
+ metadata: Optional[Dict[str, Any]] = None
44
+ rules: Optional[Dict[str, Any]] = None # Auto-tagging rules
45
+
46
+
47
+ class CreateEndpointTagRequest(AIRBaseModel):
48
+ """Request model for creating endpoint tags."""
49
+
50
+ name: str
51
+ description: Optional[str] = None
52
+ color: Optional[str] = None
53
+ tag_type: TagType = TagType.USER
54
+ scope: TagScope = TagScope.ORGANIZATION
55
+ organization_id: Optional[int] = None
56
+ metadata: Optional[Dict[str, Any]] = None
57
+ rules: Optional[Dict[str, Any]] = None
58
+
59
+
60
+ class UpdateEndpointTagRequest(AIRBaseModel):
61
+ """Request model for updating endpoint tags."""
62
+
63
+ name: Optional[str] = None
64
+ description: Optional[str] = None
65
+ color: Optional[str] = None
66
+ metadata: Optional[Dict[str, Any]] = None
67
+ rules: Optional[Dict[str, Any]] = None
68
+
69
+
70
+ class EndpointTagFilter(Filter):
71
+ """Filter for endpoint tag queries."""
72
+
73
+ name: Optional[str] = None
74
+ tag_type: Optional[TagType] = None
75
+ scope: Optional[TagScope] = None
76
+ created_by: Optional[str] = None
@@ -0,0 +1,172 @@
1
+ """
2
+ Event Subscription API models for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import List, Optional, Dict, Any
6
+ from datetime import datetime
7
+ from enum import Enum
8
+ from pydantic import Field, field_validator
9
+
10
+ from ..base import AIRBaseModel, Filter
11
+
12
+
13
+ class SubscriptionStatus(str, Enum):
14
+ """Event subscription status."""
15
+ ACTIVE = "active"
16
+ INACTIVE = "inactive"
17
+ PENDING = "pending"
18
+ FAILED = "failed"
19
+
20
+
21
+ class EventType(str, Enum):
22
+ """Event types for subscriptions."""
23
+ # Real API event names from JSON specification
24
+ DEPLOYMENT_TOKEN_REGENERATED = "DeploymentTokenRegeneratedEvent"
25
+ TASK_PROCESSING_FAILED = "TaskProcessingFailedEvent"
26
+ TASK_PROCESSING_COMPLETED = "TaskProcessingCompletedEvent"
27
+ CASE_FILE_SAVED = "CaseFileSavedEvent"
28
+
29
+ # Additional common event types (these may exist in the system)
30
+ ASSET_CREATED = "AssetCreatedEvent"
31
+ ASSET_UPDATED = "AssetUpdatedEvent"
32
+ ASSET_DELETED = "AssetDeletedEvent"
33
+ CASE_CREATED = "CaseCreatedEvent"
34
+ CASE_UPDATED = "CaseUpdatedEvent"
35
+ CASE_CLOSED = "CaseClosedEvent"
36
+ TASK_STARTED = "TaskStartedEvent"
37
+ POLICY_EXECUTED = "PolicyExecutedEvent"
38
+ ALERT_TRIGGERED = "AlertTriggeredEvent"
39
+
40
+
41
+ class DeliveryMethod(str, Enum):
42
+ """Event delivery methods."""
43
+ WEBHOOK = "webhook"
44
+ EMAIL = "email"
45
+ SYSLOG = "syslog"
46
+
47
+
48
+ class EventSubscription(AIRBaseModel):
49
+ """Event subscription model."""
50
+
51
+ id: str = Field(alias="id") # API returns int, but we'll convert to string
52
+ name: str
53
+ description: Optional[str] = None
54
+ event_types: Optional[List[EventType]] = Field(default=[], alias="events") # API: events -> SDK: event_types
55
+ delivery_method: Optional[DeliveryMethod] = Field(default=None, alias="deliveryMethod")
56
+ endpoint_url: Optional[str] = Field(default=None, alias="url") # API: url -> SDK: endpoint_url
57
+ email_addresses: Optional[List[str]] = Field(default=None, alias="emailAddresses")
58
+ syslog_server: Optional[str] = Field(default=None, alias="syslogServer")
59
+ status: Optional[SubscriptionStatus] = None
60
+ active: Optional[bool] = None # API uses active boolean instead of status enum
61
+ organization_id: Optional[int] = Field(default=None, alias="organizationId")
62
+ created_by: Optional[str] = Field(default=None, alias="createdBy")
63
+ created_at: Optional[datetime] = Field(default=None, alias="createdAt")
64
+ updated_at: Optional[datetime] = Field(default=None, alias="updatedAt")
65
+ last_triggered: Optional[datetime] = Field(default=None, alias="lastTriggered")
66
+ trigger_count: int = Field(default=0, alias="triggerCount")
67
+ retry_count: int = Field(default=3, alias="retryCount")
68
+ retry_interval: int = Field(default=300, alias="retryInterval") # seconds
69
+ headers: Optional[Dict[str, str]] = None # For webhook headers
70
+ authentication: Optional[Dict[str, Any]] = None
71
+ filters: Optional[Dict[str, Any]] = None # Event filtering criteria
72
+ url: Optional[str] = None # Keep original API field name for backward compatibility
73
+ auth_token: Optional[str] = Field(default=None, alias="authToken")
74
+
75
+ @field_validator('id', mode='before')
76
+ @classmethod
77
+ def convert_id_to_string(cls, v):
78
+ """Convert ID from int to string as API returns numbers."""
79
+ return str(v) if v is not None else v
80
+
81
+
82
+ class CreateEventSubscriptionRequest(AIRBaseModel):
83
+ """Request model for creating event subscriptions."""
84
+
85
+ name: str
86
+ description: Optional[str] = None
87
+ event_types: List[EventType]
88
+ delivery_method: DeliveryMethod
89
+ endpoint_url: Optional[str] = None
90
+ email_addresses: Optional[List[str]] = None
91
+ syslog_server: Optional[str] = None
92
+ organization_id: int
93
+ retry_count: Optional[int] = 3
94
+ retry_interval: Optional[int] = 300
95
+ headers: Optional[Dict[str, str]] = None
96
+ authentication: Optional[Dict[str, Any]] = None
97
+ filters: Optional[Dict[str, Any]] = None
98
+
99
+
100
+ class UpdateEventSubscriptionRequest(AIRBaseModel):
101
+ """Request model for updating event subscriptions."""
102
+
103
+ name: Optional[str] = None
104
+ description: Optional[str] = None
105
+ event_types: Optional[List[EventType]] = None
106
+ delivery_method: Optional[DeliveryMethod] = None
107
+ endpoint_url: Optional[str] = None
108
+ email_addresses: Optional[List[str]] = None
109
+ syslog_server: Optional[str] = None
110
+ status: Optional[SubscriptionStatus] = None
111
+ retry_count: Optional[int] = None
112
+ retry_interval: Optional[int] = None
113
+ headers: Optional[Dict[str, str]] = None
114
+ authentication: Optional[Dict[str, Any]] = None
115
+ filters: Optional[Dict[str, Any]] = None
116
+
117
+
118
+ class EventSubscriptionFilter(Filter):
119
+ """Filter for event subscription queries."""
120
+
121
+ name: Optional[str] = None
122
+ event_type: Optional[EventType] = None
123
+ delivery_method: Optional[DeliveryMethod] = None
124
+ status: Optional[SubscriptionStatus] = None
125
+ created_by: Optional[str] = None
126
+ is_active: Optional[bool] = None # API uses isActive
127
+ url: Optional[str] = None # API supports URL filtering
128
+
129
+ def to_params(self) -> Dict[str, Any]:
130
+ """Convert filter to API parameters with proper field mapping."""
131
+ params = {}
132
+
133
+ # Pagination parameters (not in filter namespace)
134
+ if self.page_number is not None:
135
+ params["pageNumber"] = self.page_number
136
+ if self.page_size is not None:
137
+ params["pageSize"] = self.page_size
138
+ if self.sort_by is not None:
139
+ params["sortBy"] = self.sort_by
140
+ if self.sort_type is not None:
141
+ params["sortType"] = self.sort_type
142
+
143
+ # Filter parameters with proper field mapping
144
+ field_mappings = {
145
+ "search_term": "searchTerm",
146
+ "organization_ids": "organizationId", # API expects single organizationId in filter
147
+ "name": "name",
148
+ "event_type": "eventType",
149
+ "delivery_method": "deliveryMethod",
150
+ "status": "status",
151
+ "created_by": "createdBy",
152
+ "is_active": "isActive",
153
+ "url": "url"
154
+ }
155
+
156
+ for field_name, field_value in self.model_dump(exclude_none=True).items():
157
+ # Skip pagination/sorting fields as they're handled above
158
+ if field_name in ["page_number", "page_size", "sort_by", "sort_type"]:
159
+ continue
160
+
161
+ if field_value is not None:
162
+ api_field_name = field_mappings.get(field_name, field_name)
163
+
164
+ # Special handling for organization_ids - use first ID for organizationId filter
165
+ if field_name == "organization_ids" and isinstance(field_value, list) and len(field_value) > 0:
166
+ params[f"filter[{api_field_name}]"] = str(field_value[0])
167
+ elif isinstance(field_value, list):
168
+ params[f"filter[{api_field_name}]"] = ",".join(map(str, field_value))
169
+ else:
170
+ params[f"filter[{api_field_name}]"] = str(field_value)
171
+
172
+ return params
@@ -0,0 +1,66 @@
1
+ """
2
+ Evidence-related data models for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import Optional, List, Dict, Any
6
+ from datetime import datetime
7
+
8
+ from ..base import AIRBaseModel
9
+
10
+
11
+ class EvidencePPC(AIRBaseModel):
12
+ """Evidence PPC (Portable Pre-processor Configuration) model for binary file downloads."""
13
+
14
+ endpoint_id: str
15
+ task_id: str
16
+ content: bytes # Binary content of the PPC file
17
+ content_type: Optional[str] = None # MIME type of the file
18
+ content_length: Optional[int] = None # Size of the file in bytes
19
+ filename: Optional[str] = None # Suggested filename
20
+
21
+ def save_to_file(self, filepath: str) -> bool:
22
+ """Save the PPC content to a file."""
23
+ try:
24
+ with open(filepath, 'wb') as f:
25
+ f.write(self.content)
26
+ return True
27
+ except Exception:
28
+ return False
29
+
30
+
31
+ class EvidenceReportFileInfo(AIRBaseModel):
32
+ """Evidence report file info model."""
33
+
34
+ endpoint_id: str
35
+ task_id: str
36
+ file_name: Optional[str] = None
37
+ file_size: Optional[int] = None
38
+ created_at: Optional[datetime] = None
39
+ status: Optional[str] = None
40
+ # Additional fields from API
41
+ file_path: Optional[str] = None # Full path to the file
42
+ encoding: Optional[str] = None # File encoding (e.g., "7bit")
43
+ mime_type: Optional[str] = None # MIME type (e.g., "application/octet-stream")
44
+ file_hash: Optional[str] = None # File hash/checksum
45
+ organization_id: Optional[int] = None # Organization ID
46
+ is_purged: Optional[bool] = None # Whether file has been purged
47
+
48
+
49
+ class EvidenceReport(AIRBaseModel):
50
+ """Evidence report model."""
51
+
52
+ endpoint_id: str
53
+ task_id: str
54
+ content: bytes # Binary content of the report file
55
+ content_type: Optional[str] = None
56
+ content_length: Optional[int] = None
57
+ filename: Optional[str] = None
58
+
59
+ def save_to_file(self, filepath: str) -> bool:
60
+ """Save the report content to a file."""
61
+ try:
62
+ with open(filepath, 'wb') as f:
63
+ f.write(self.content)
64
+ return True
65
+ except Exception:
66
+ return False