binalyze-air-sdk 1.0.2__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.2.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.2.dist-info/METADATA +0 -706
  141. binalyze_air_sdk-1.0.2.dist-info/RECORD +0 -82
  142. {binalyze_air_sdk-1.0.2.dist-info → binalyze_air_sdk-1.0.3.dist-info}/top_level.txt +0 -0
@@ -1,28 +1,140 @@
1
- """
2
- Interact queries for the Binalyze AIR SDK.
3
- """
4
-
5
- from typing import List, Optional
6
-
7
- from ..base import Query
8
- from ..models.interact import ShellInteraction
9
- from ..http_client import HTTPClient
10
-
11
-
12
- class GetShellInteractionQuery(Query[ShellInteraction]):
13
- """Query to get a specific shell interaction."""
14
-
15
- def __init__(self, http_client: HTTPClient, interaction_id: str):
16
- self.http_client = http_client
17
- self.interaction_id = interaction_id
18
-
19
- def execute(self) -> ShellInteraction:
20
- """Execute the query to get a specific shell interaction."""
21
- response = self.http_client.get(f"interact/shell/{self.interaction_id}")
22
-
23
- if response.get("success"):
24
- result_data = response.get("result", {})
25
- # Use Pydantic parsing with proper field aliasing
26
- return ShellInteraction.model_validate(result_data)
27
-
28
- raise Exception(f"Shell interaction not found: {self.interaction_id}")
1
+ """
2
+ Interact queries for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import List, Optional, Dict, Any
6
+
7
+ from ..base import Query
8
+ from ..models.interact import (
9
+ ShellInteraction, LibraryFile, LibraryFileFilter,
10
+ InteractCommand, CommandMessage, FileExistsResponse
11
+ )
12
+ from ..http_client import HTTPClient
13
+
14
+
15
+ class GetShellInteractionQuery(Query[ShellInteraction]):
16
+ """Query to get a specific shell interaction."""
17
+
18
+ def __init__(self, http_client: HTTPClient, interaction_id: str):
19
+ self.http_client = http_client
20
+ self.interaction_id = interaction_id
21
+
22
+ def execute(self) -> ShellInteraction:
23
+ """Execute the query to get a specific shell interaction."""
24
+ response = self.http_client.get(f"interact/shell/{self.interaction_id}")
25
+
26
+ if response.get("success"):
27
+ result_data = response.get("result", {})
28
+ # Use Pydantic parsing with proper field aliasing
29
+ return ShellInteraction.model_validate(result_data)
30
+
31
+ raise Exception(f"Shell interaction not found: {self.interaction_id}")
32
+
33
+
34
+ # LIBRARY FILE QUERIES
35
+
36
+ class ListLibraryFilesQuery(Query[List[LibraryFile]]):
37
+ """Query to list library files."""
38
+
39
+ def __init__(self, http_client: HTTPClient, filter_params: Optional[LibraryFileFilter] = None):
40
+ self.http_client = http_client
41
+ self.filter_params = filter_params or LibraryFileFilter()
42
+
43
+ def execute(self) -> List[LibraryFile]:
44
+ """Execute the query to get library files."""
45
+ params = self.filter_params.to_params()
46
+ response = self.http_client.get("interact/library/files", params=params)
47
+
48
+ entities = response.get("result", {}).get("entities", [])
49
+
50
+ # Use Pydantic parsing with proper field aliasing
51
+ files = []
52
+ for item in entities:
53
+ files.append(LibraryFile.model_validate(item))
54
+
55
+ return files
56
+
57
+
58
+ class DownloadLibraryFileQuery(Query[bytes]):
59
+ """Query to download a library file."""
60
+
61
+ def __init__(self, http_client: HTTPClient, file_id: str):
62
+ self.http_client = http_client
63
+ self.file_id = file_id
64
+
65
+ def execute(self) -> bytes:
66
+ """Execute the query to download a library file."""
67
+ params = {"fileId": self.file_id}
68
+ response = self.http_client.get("interact/library/download", params=params)
69
+
70
+ # Return file content as bytes
71
+ if isinstance(response, bytes):
72
+ return response
73
+
74
+ # If response is JSON, convert to bytes
75
+ return str(response).encode('utf-8')
76
+
77
+
78
+ class CheckFileExistsQuery(Query[bool]):
79
+ """Query to check if a file exists in library."""
80
+
81
+ def __init__(self, http_client: HTTPClient, name: str, sha256: Optional[str] = None, organization_ids: Optional[List[int]] = None):
82
+ self.http_client = http_client
83
+ self.name = name
84
+ self.sha256 = sha256
85
+ self.organization_ids = organization_ids or [0] # Default to organization 0
86
+
87
+ def execute(self) -> bool:
88
+ """Execute the query to check if file exists."""
89
+ import json
90
+ params = {"name": self.name}
91
+ if self.sha256:
92
+ params["sha256"] = self.sha256
93
+
94
+ # API requires organizationIds as JSON string array
95
+ params["organizationIds"] = json.dumps(self.organization_ids)
96
+
97
+ response = self.http_client.get("interact/library/check", params=params)
98
+
99
+ # API returns simple boolean response, not object
100
+ return response.get("result", False)
101
+
102
+
103
+ # SHELL SESSION QUERIES
104
+
105
+ class GetCommandMessageQuery(Query[CommandMessage]):
106
+ """Query to get a command message from a session."""
107
+
108
+ def __init__(self, http_client: HTTPClient, session_id: str, message_id: str):
109
+ self.http_client = http_client
110
+ self.session_id = session_id
111
+ self.message_id = message_id
112
+
113
+ def execute(self) -> CommandMessage:
114
+ """Execute the query to get a command message."""
115
+ response = self.http_client.get(
116
+ f"interact/shell/sessions/{self.session_id}/messages/{self.message_id}"
117
+ )
118
+
119
+ result_data = response.get("result", {})
120
+ return CommandMessage.model_validate(result_data)
121
+
122
+
123
+ class ListInteractCommandsQuery(Query[List[InteractCommand]]):
124
+ """Query to list available interact commands."""
125
+
126
+ def __init__(self, http_client: HTTPClient):
127
+ self.http_client = http_client
128
+
129
+ def execute(self) -> List[InteractCommand]:
130
+ """Execute the query to get available interact commands."""
131
+ response = self.http_client.get("interact/commands")
132
+
133
+ commands_data = response.get("result", [])
134
+
135
+ # Use Pydantic parsing with proper field aliasing
136
+ commands = []
137
+ for item in commands_data:
138
+ commands.append(InteractCommand.model_validate(item))
139
+
140
+ return commands
@@ -0,0 +1,329 @@
1
+ """
2
+ Investigation Hub queries for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import Optional, List, Dict, Any
6
+
7
+ from ..base import Query
8
+ from ..models.investigation_hub import (
9
+ Investigation, InvestigationAsset, FlagSummary, EvidenceSection,
10
+ EvidenceStructure, SQLQueryResult, FindingsSummary, FindingsStructure,
11
+ FindingsResult, FindingsRequest, MitreMatch, InvestigationComment,
12
+ InvestigationActivity, AdvancedFilter
13
+ )
14
+ from ..http_client import HTTPClient
15
+
16
+
17
+ class GetInvestigationQuery(Query[Investigation]):
18
+ """Query to get a specific investigation by ID."""
19
+
20
+ def __init__(self, http_client: HTTPClient, investigation_id: str):
21
+ self.http_client = http_client
22
+ self.investigation_id = investigation_id
23
+
24
+ def execute(self) -> Investigation:
25
+ """Execute the query."""
26
+ response = self.http_client.get(f"investigation-hub/investigations/{self.investigation_id}")
27
+ return Investigation(**response["result"])
28
+
29
+
30
+ class GetInvestigationAssetsQuery(Query[List[InvestigationAsset]]):
31
+ """Query to get assets for a specific investigation."""
32
+
33
+ def __init__(self, http_client: HTTPClient, investigation_id: str):
34
+ self.http_client = http_client
35
+ self.investigation_id = investigation_id
36
+
37
+ def execute(self) -> List[InvestigationAsset]:
38
+ """Execute the query."""
39
+ response = self.http_client.get(f"investigation-hub/investigations/{self.investigation_id}/assets")
40
+ return [InvestigationAsset(**asset) for asset in response["result"]]
41
+
42
+
43
+ class GetInvestigationFlagSummaryQuery(Query[List[FlagSummary]]):
44
+ """Query to get flag summary for a specific investigation."""
45
+
46
+ def __init__(self, http_client: HTTPClient, investigation_id: str):
47
+ self.http_client = http_client
48
+ self.investigation_id = investigation_id
49
+
50
+ def execute(self) -> List[FlagSummary]:
51
+ """Execute the query."""
52
+ response = self.http_client.post(f"investigation-hub/investigations/{self.investigation_id}/flags-summary")
53
+ return [FlagSummary(**flag) for flag in response["result"]]
54
+
55
+
56
+ class GetEvidenceSectionsQuery(Query[List[EvidenceSection]]):
57
+ """Query to get evidence sections for a specific investigation with task assignment IDs."""
58
+
59
+ def __init__(self, http_client: HTTPClient, investigation_id: str, task_assignment_ids: List[str]):
60
+ self.http_client = http_client
61
+ self.investigation_id = investigation_id
62
+ self.task_assignment_ids = task_assignment_ids
63
+
64
+ def execute(self) -> List[EvidenceSection]:
65
+ """Execute the query."""
66
+ payload = {"taskAssignmentIds": self.task_assignment_ids}
67
+ response = self.http_client.post(
68
+ f"investigation-hub/investigations/{self.investigation_id}/sections",
69
+ json_data=payload
70
+ )
71
+ return [EvidenceSection(**section) for section in response["result"]]
72
+
73
+
74
+ class GetEvidenceStructureQuery(Query[List[EvidenceStructure]]):
75
+ """Query to get evidence structure for a specific section."""
76
+
77
+ def __init__(self, http_client: HTTPClient, investigation_id: str, section: str):
78
+ self.http_client = http_client
79
+ self.investigation_id = investigation_id
80
+ self.section = section
81
+
82
+ def execute(self) -> List[EvidenceStructure]:
83
+ """Execute the query."""
84
+ response = self.http_client.get(
85
+ f"investigation-hub/investigations/{self.investigation_id}/sections/{self.section}/structure"
86
+ )
87
+ return [EvidenceStructure(**structure) for structure in response["result"]]
88
+
89
+
90
+ class ExecuteSQLQuery(Query[SQLQueryResult]):
91
+ """Query to execute SQL against investigation database."""
92
+
93
+ def __init__(self, http_client: HTTPClient, investigation_id: str, query: str,
94
+ page_size: int = 10, page_number: int = 1):
95
+ self.http_client = http_client
96
+ self.investigation_id = investigation_id
97
+ self.query = query
98
+ self.page_size = page_size
99
+ self.page_number = page_number
100
+
101
+ def execute(self) -> SQLQueryResult:
102
+ """Execute the query."""
103
+ payload = {
104
+ "query": self.query,
105
+ "pageSize": self.page_size,
106
+ "pageNumber": self.page_number
107
+ }
108
+ response = self.http_client.post(
109
+ f"investigation-hub/investigations/{self.investigation_id}/execute-sql-query",
110
+ json_data=payload
111
+ )
112
+ return SQLQueryResult(**response["result"])
113
+
114
+
115
+ class GetFindingsSummaryQuery(Query[FindingsSummary]):
116
+ """Query to get findings summary for a specific investigation."""
117
+
118
+ def __init__(self, http_client: HTTPClient, investigation_id: str):
119
+ self.http_client = http_client
120
+ self.investigation_id = investigation_id
121
+
122
+ def execute(self) -> FindingsSummary:
123
+ """Execute the query."""
124
+ # Use default payload for findings summary
125
+ payload = {
126
+ "take": 50,
127
+ "skip": 0,
128
+ "filter": [],
129
+ "globalFilter": {
130
+ "assignmentIds": [],
131
+ "flagIds": [],
132
+ "verdictScores": [],
133
+ "createdBy": [],
134
+ "mitreTechniqueIds": [],
135
+ "mitreTacticIds": []
136
+ },
137
+ "sort": [{"column": "verdict_score", "order": "desc"}]
138
+ }
139
+ response = self.http_client.post(
140
+ f"investigation-hub/investigations/{self.investigation_id}/findings/summary",
141
+ json_data=payload
142
+ )
143
+ return FindingsSummary(**response["result"])
144
+
145
+
146
+ class GetMitreMatchesQuery(Query[List[MitreMatch]]):
147
+ """Query to get MITRE ATT&CK matches for a specific investigation."""
148
+
149
+ def __init__(self, http_client: HTTPClient, investigation_id: str):
150
+ self.http_client = http_client
151
+ self.investigation_id = investigation_id
152
+
153
+ def execute(self) -> List[MitreMatch]:
154
+ """Execute the query."""
155
+ # Use default payload for MITRE matches
156
+ payload = {
157
+ "take": 50,
158
+ "skip": 0,
159
+ "filter": [],
160
+ "globalFilter": {
161
+ "assignmentIds": [],
162
+ "flagIds": [],
163
+ "verdictScores": [],
164
+ "createdBy": [],
165
+ "mitreTechniqueIds": [],
166
+ "mitreTacticIds": []
167
+ },
168
+ "sort": [{"column": "verdict_score", "order": "desc"}]
169
+ }
170
+ response = self.http_client.post(
171
+ f"investigation-hub/investigations/{self.investigation_id}/findings/mitre-matches",
172
+ json_data=payload
173
+ )
174
+ return [MitreMatch(**match) for match in response["result"]]
175
+
176
+
177
+ class GetInvestigationCommentsQuery(Query[List[InvestigationComment]]):
178
+ """Query to get comments for a specific investigation evidence."""
179
+
180
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
181
+ evidence_id: Optional[str] = None):
182
+ self.http_client = http_client
183
+ self.investigation_id = investigation_id
184
+ self.evidence_id = evidence_id or "artifacts" # Default to artifacts if not specified
185
+
186
+ def execute(self) -> List[InvestigationComment]:
187
+ """Execute the query."""
188
+ # Add optional query parameters from API spec
189
+ params = {
190
+ "taskAssignmentId": "test_task_1",
191
+ "objectId": "1"
192
+ }
193
+
194
+ response = self.http_client.get(
195
+ f"investigation-hub/investigations/{self.investigation_id}/evidence/{self.evidence_id}/comments",
196
+ params=params
197
+ )
198
+ return [InvestigationComment(**comment) for comment in response["result"]]
199
+
200
+
201
+ class GetInvestigationActivitiesQuery(Query[List[InvestigationActivity]]):
202
+ """Query to get activities for a specific investigation."""
203
+
204
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
205
+ page_size: int = 20, page_number: int = 1):
206
+ self.http_client = http_client
207
+ self.investigation_id = investigation_id
208
+ self.page_size = page_size
209
+ self.page_number = page_number
210
+
211
+ def execute(self) -> List[InvestigationActivity]:
212
+ """Execute the query."""
213
+ params = {
214
+ "pageSize": self.page_size,
215
+ "pageNumber": self.page_number
216
+ }
217
+ response = self.http_client.get(
218
+ f"investigation-hub/investigations/{self.investigation_id}/activities",
219
+ params=params
220
+ )
221
+ return [InvestigationActivity(**activity) for activity in response["result"]]
222
+
223
+
224
+ class GetAdvancedFiltersQuery(Query[List[AdvancedFilter]]):
225
+ """Query to get advanced filters (organization-wide, not investigation-specific)."""
226
+
227
+ def __init__(self, http_client: HTTPClient, investigation_id: Optional[str] = None):
228
+ self.http_client = http_client
229
+ # Investigation ID is not needed for this endpoint but kept for compatibility
230
+ self.investigation_id = investigation_id
231
+
232
+ def execute(self) -> List[AdvancedFilter]:
233
+ """Execute the query."""
234
+ # Add required query parameters from API validation
235
+ params = {
236
+ "organizationId": 0, # Required parameter
237
+ "tableName": "artifacts" # Required parameter
238
+ }
239
+ response = self.http_client.get("investigation-hub/advanced-filters", params=params)
240
+ return [AdvancedFilter(**filter_item) for filter_item in response["result"]["entities"]]
241
+
242
+
243
+ class GetAdvancedFilterQuery(Query[AdvancedFilter]):
244
+ """Query to get a specific advanced filter by ID."""
245
+
246
+ def __init__(self, http_client: HTTPClient, investigation_id: str, filter_id: int):
247
+ self.http_client = http_client
248
+ self.investigation_id = investigation_id
249
+ self.filter_id = filter_id
250
+
251
+ def execute(self) -> AdvancedFilter:
252
+ """Execute the query."""
253
+ response = self.http_client.get(
254
+ f"investigation-hub/advanced-filters/{self.filter_id}"
255
+ )
256
+ return AdvancedFilter(**response["result"])
257
+
258
+
259
+ class GetEvidenceRecordsQuery(Query[Dict[str, Any]]):
260
+ """Query to get evidence records with filtering."""
261
+
262
+ def __init__(self, http_client: HTTPClient, investigation_id: str, section: str,
263
+ filters: Optional[Dict[str, Any]] = None, page_size: int = 50,
264
+ page_number: int = 1):
265
+ self.http_client = http_client
266
+ self.investigation_id = investigation_id
267
+ self.section = section
268
+ self.filters = filters or {}
269
+ self.page_size = page_size
270
+ self.page_number = page_number
271
+
272
+ def execute(self) -> Dict[str, Any]:
273
+ """Execute the query."""
274
+ payload = {
275
+ "take": self.page_size,
276
+ "skip": (self.page_number - 1) * self.page_size,
277
+ "filter": [],
278
+ "globalFilter": {
279
+ "assignmentIds": [],
280
+ "flagIds": [],
281
+ "verdictScores": [],
282
+ "createdBy": [],
283
+ "mitreTechniqueIds": [],
284
+ "mitreTacticIds": []
285
+ },
286
+ "sort": [{"column": "verdict_score", "order": "desc"}]
287
+ }
288
+ # Merge any additional filters provided
289
+ if self.filters:
290
+ payload.update(self.filters)
291
+
292
+ response = self.http_client.post(
293
+ f"investigation-hub/investigations/{self.investigation_id}/sections/{self.section}",
294
+ json_data=payload
295
+ )
296
+ return response["result"]
297
+
298
+
299
+ class GetFindingsStructureQuery(Query[FindingsStructure]):
300
+ """Query to get findings structure for a specific investigation."""
301
+
302
+ def __init__(self, http_client: HTTPClient, investigation_id: str):
303
+ self.http_client = http_client
304
+ self.investigation_id = investigation_id
305
+
306
+ def execute(self) -> FindingsStructure:
307
+ """Execute the query."""
308
+ response = self.http_client.get(
309
+ f"investigation-hub/investigations/{self.investigation_id}/findings/structure"
310
+ )
311
+ return FindingsStructure(**response["result"])
312
+
313
+
314
+ class GetFindingsQuery(Query[FindingsResult]):
315
+ """Query to get findings for a specific investigation."""
316
+
317
+ def __init__(self, http_client: HTTPClient, investigation_id: str,
318
+ request: FindingsRequest):
319
+ self.http_client = http_client
320
+ self.investigation_id = investigation_id
321
+ self.request = request
322
+
323
+ def execute(self) -> FindingsResult:
324
+ """Execute the query."""
325
+ response = self.http_client.post(
326
+ f"investigation-hub/investigations/{self.investigation_id}/findings",
327
+ json_data=self.request.model_dump(by_alias=True, exclude_none=True)
328
+ )
329
+ return FindingsResult(**response["result"])
@@ -0,0 +1,85 @@
1
+ """License query classes for Binalyze AIR SDK."""
2
+
3
+ from typing import Dict, Any, Optional
4
+ from ..models.license import LicenseUpdateRequest
5
+
6
+
7
+ class LicenseQuery:
8
+ """Base query class for license operations."""
9
+
10
+ def __init__(self):
11
+ """Initialize base license query."""
12
+ self._params = {}
13
+
14
+ def build_params(self) -> Dict[str, Any]:
15
+ """Build query parameters.
16
+
17
+ Returns:
18
+ Dictionary of query parameters
19
+ """
20
+ return self._params.copy()
21
+
22
+
23
+ class GetLicenseQuery(LicenseQuery):
24
+ """Query for retrieving license information.
25
+
26
+ This query retrieves current license details including:
27
+ - License key and activation status
28
+ - Expiration dates and remaining time
29
+ - Device and client usage limits
30
+ - Customer information
31
+ """
32
+
33
+ def __init__(self):
34
+ """Initialize get license query."""
35
+ super().__init__()
36
+
37
+ def build_params(self) -> Dict[str, Any]:
38
+ """Build parameters for get license request.
39
+
40
+ Returns:
41
+ Empty dictionary as GET license requires no parameters
42
+ """
43
+ return {}
44
+
45
+
46
+ class SetLicenseQuery(LicenseQuery):
47
+ """Query for setting/updating license key.
48
+
49
+ This query allows updating the license key for the system.
50
+ """
51
+
52
+ def __init__(self, license_key: str):
53
+ """Initialize set license query.
54
+
55
+ Args:
56
+ license_key: The new license key to set
57
+ """
58
+ super().__init__()
59
+ self._license_key = license_key
60
+
61
+ def build_body(self) -> Dict[str, Any]:
62
+ """Build request body for set license request.
63
+
64
+ Returns:
65
+ Dictionary containing license key for API request
66
+ """
67
+ request = LicenseUpdateRequest(license_key=self._license_key)
68
+ return request.to_dict()
69
+
70
+ def build_params(self) -> Dict[str, Any]:
71
+ """Build parameters for set license request.
72
+
73
+ Returns:
74
+ Empty dictionary as POST license uses body, not params
75
+ """
76
+ return {}
77
+
78
+ @property
79
+ def license_key(self) -> str:
80
+ """Get the license key.
81
+
82
+ Returns:
83
+ The license key to be set
84
+ """
85
+ return self._license_key
@@ -0,0 +1,58 @@
1
+ """Logger query classes for Binalyze AIR SDK."""
2
+
3
+ from typing import Dict, Any, Optional
4
+ from ..models.logger import LogDownloadRequest
5
+
6
+
7
+ class LoggerQuery:
8
+ """Base query class for logger operations."""
9
+
10
+ def __init__(self):
11
+ """Initialize base logger query."""
12
+ self._params = {}
13
+
14
+ def build_params(self) -> Dict[str, Any]:
15
+ """Build query parameters.
16
+
17
+ Returns:
18
+ Dictionary of query parameters
19
+ """
20
+ return self._params.copy()
21
+
22
+
23
+ class DownloadLogsQuery(LoggerQuery):
24
+ """Query for downloading application logs as ZIP file.
25
+
26
+ This query downloads system logs which can include:
27
+ - Application logs
28
+ - Error logs
29
+ - System diagnostic information
30
+ - Debug logs (if enabled)
31
+ """
32
+
33
+ def __init__(self, latest_log_file: bool = False):
34
+ """Initialize download logs query.
35
+
36
+ Args:
37
+ latest_log_file: Whether to download only the latest log file
38
+ """
39
+ super().__init__()
40
+ self._latest_log_file = latest_log_file
41
+
42
+ def build_params(self) -> Dict[str, Any]:
43
+ """Build parameters for download logs request.
44
+
45
+ Returns:
46
+ Dictionary containing query parameters
47
+ """
48
+ request = LogDownloadRequest(latest_log_file=self._latest_log_file)
49
+ return request.to_dict()
50
+
51
+ @property
52
+ def latest_log_file(self) -> bool:
53
+ """Get the latest log file flag.
54
+
55
+ Returns:
56
+ Whether to download only the latest log file
57
+ """
58
+ return self._latest_log_file