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.
- binalyze_air/__init__.py +77 -77
- binalyze_air/apis/__init__.py +67 -27
- binalyze_air/apis/acquisitions.py +107 -0
- binalyze_air/apis/api_tokens.py +49 -0
- binalyze_air/apis/assets.py +161 -0
- binalyze_air/apis/audit_logs.py +26 -0
- binalyze_air/apis/{authentication.py → auth.py} +29 -27
- binalyze_air/apis/auto_asset_tags.py +79 -75
- binalyze_air/apis/backup.py +177 -0
- binalyze_air/apis/baseline.py +46 -0
- binalyze_air/apis/cases.py +225 -0
- binalyze_air/apis/cloud_forensics.py +116 -0
- binalyze_air/apis/event_subscription.py +96 -96
- binalyze_air/apis/evidence.py +249 -53
- binalyze_air/apis/interact.py +153 -36
- binalyze_air/apis/investigation_hub.py +234 -0
- binalyze_air/apis/license.py +104 -0
- binalyze_air/apis/logger.py +83 -0
- binalyze_air/apis/multipart_upload.py +201 -0
- binalyze_air/apis/notifications.py +115 -0
- binalyze_air/apis/organizations.py +267 -0
- binalyze_air/apis/params.py +44 -39
- binalyze_air/apis/policies.py +186 -0
- binalyze_air/apis/preset_filters.py +79 -0
- binalyze_air/apis/recent_activities.py +71 -0
- binalyze_air/apis/relay_server.py +104 -0
- binalyze_air/apis/settings.py +395 -27
- binalyze_air/apis/tasks.py +80 -0
- binalyze_air/apis/triage.py +197 -0
- binalyze_air/apis/user_management.py +183 -74
- binalyze_air/apis/webhook_executions.py +50 -0
- binalyze_air/apis/webhooks.py +322 -230
- binalyze_air/base.py +207 -133
- binalyze_air/client.py +217 -1337
- binalyze_air/commands/__init__.py +175 -145
- binalyze_air/commands/acquisitions.py +661 -387
- binalyze_air/commands/api_tokens.py +55 -0
- binalyze_air/commands/assets.py +324 -362
- binalyze_air/commands/{authentication.py → auth.py} +36 -36
- binalyze_air/commands/auto_asset_tags.py +230 -230
- binalyze_air/commands/backup.py +47 -0
- binalyze_air/commands/baseline.py +32 -396
- binalyze_air/commands/cases.py +609 -602
- binalyze_air/commands/cloud_forensics.py +88 -0
- binalyze_air/commands/event_subscription.py +101 -101
- binalyze_air/commands/evidences.py +918 -988
- binalyze_air/commands/interact.py +172 -58
- binalyze_air/commands/investigation_hub.py +315 -0
- binalyze_air/commands/license.py +183 -0
- binalyze_air/commands/logger.py +126 -0
- binalyze_air/commands/multipart_upload.py +363 -0
- binalyze_air/commands/notifications.py +45 -0
- binalyze_air/commands/organizations.py +200 -221
- binalyze_air/commands/policies.py +175 -203
- binalyze_air/commands/preset_filters.py +55 -0
- binalyze_air/commands/recent_activities.py +32 -0
- binalyze_air/commands/relay_server.py +144 -0
- binalyze_air/commands/settings.py +431 -29
- binalyze_air/commands/tasks.py +95 -56
- binalyze_air/commands/triage.py +224 -360
- binalyze_air/commands/user_management.py +351 -126
- binalyze_air/commands/webhook_executions.py +77 -0
- binalyze_air/config.py +244 -244
- binalyze_air/exceptions.py +49 -49
- binalyze_air/http_client.py +426 -305
- binalyze_air/models/__init__.py +287 -285
- binalyze_air/models/acquisitions.py +365 -250
- binalyze_air/models/api_tokens.py +73 -0
- binalyze_air/models/assets.py +438 -438
- binalyze_air/models/audit.py +247 -272
- binalyze_air/models/audit_logs.py +14 -0
- binalyze_air/models/{authentication.py → auth.py} +69 -69
- binalyze_air/models/auto_asset_tags.py +227 -116
- binalyze_air/models/backup.py +138 -0
- binalyze_air/models/baseline.py +231 -231
- binalyze_air/models/cases.py +275 -275
- binalyze_air/models/cloud_forensics.py +145 -0
- binalyze_air/models/event_subscription.py +170 -171
- binalyze_air/models/evidence.py +65 -65
- binalyze_air/models/evidences.py +367 -348
- binalyze_air/models/interact.py +266 -135
- binalyze_air/models/investigation_hub.py +265 -0
- binalyze_air/models/license.py +150 -0
- binalyze_air/models/logger.py +83 -0
- binalyze_air/models/multipart_upload.py +352 -0
- binalyze_air/models/notifications.py +138 -0
- binalyze_air/models/organizations.py +293 -293
- binalyze_air/models/params.py +153 -127
- binalyze_air/models/policies.py +260 -249
- binalyze_air/models/preset_filters.py +79 -0
- binalyze_air/models/recent_activities.py +70 -0
- binalyze_air/models/relay_server.py +121 -0
- binalyze_air/models/settings.py +538 -84
- binalyze_air/models/tasks.py +215 -149
- binalyze_air/models/triage.py +141 -142
- binalyze_air/models/user_management.py +200 -97
- binalyze_air/models/webhook_executions.py +33 -0
- binalyze_air/queries/__init__.py +121 -133
- binalyze_air/queries/acquisitions.py +155 -155
- binalyze_air/queries/api_tokens.py +46 -0
- binalyze_air/queries/assets.py +186 -105
- binalyze_air/queries/audit.py +400 -416
- binalyze_air/queries/{authentication.py → auth.py} +55 -55
- binalyze_air/queries/auto_asset_tags.py +59 -59
- binalyze_air/queries/backup.py +66 -0
- binalyze_air/queries/baseline.py +21 -185
- binalyze_air/queries/cases.py +292 -292
- binalyze_air/queries/cloud_forensics.py +137 -0
- binalyze_air/queries/event_subscription.py +54 -54
- binalyze_air/queries/evidence.py +139 -139
- binalyze_air/queries/evidences.py +279 -279
- binalyze_air/queries/interact.py +140 -28
- binalyze_air/queries/investigation_hub.py +329 -0
- binalyze_air/queries/license.py +85 -0
- binalyze_air/queries/logger.py +58 -0
- binalyze_air/queries/multipart_upload.py +180 -0
- binalyze_air/queries/notifications.py +71 -0
- binalyze_air/queries/organizations.py +222 -222
- binalyze_air/queries/params.py +154 -115
- binalyze_air/queries/policies.py +149 -149
- binalyze_air/queries/preset_filters.py +60 -0
- binalyze_air/queries/recent_activities.py +44 -0
- binalyze_air/queries/relay_server.py +42 -0
- binalyze_air/queries/settings.py +533 -20
- binalyze_air/queries/tasks.py +125 -81
- binalyze_air/queries/triage.py +230 -230
- binalyze_air/queries/user_management.py +193 -83
- binalyze_air/queries/webhook_executions.py +39 -0
- binalyze_air_sdk-1.0.3.dist-info/METADATA +752 -0
- binalyze_air_sdk-1.0.3.dist-info/RECORD +132 -0
- {binalyze_air_sdk-1.0.2.dist-info → binalyze_air_sdk-1.0.3.dist-info}/WHEEL +1 -1
- binalyze_air/apis/endpoints.py +0 -22
- binalyze_air/apis/evidences.py +0 -216
- binalyze_air/apis/users.py +0 -68
- binalyze_air/commands/users.py +0 -101
- binalyze_air/models/endpoints.py +0 -76
- binalyze_air/models/users.py +0 -82
- binalyze_air/queries/endpoints.py +0 -25
- binalyze_air/queries/users.py +0 -69
- binalyze_air_sdk-1.0.2.dist-info/METADATA +0 -706
- binalyze_air_sdk-1.0.2.dist-info/RECORD +0 -82
- {binalyze_air_sdk-1.0.2.dist-info → binalyze_air_sdk-1.0.3.dist-info}/top_level.txt +0 -0
binalyze_air/queries/interact.py
CHANGED
@@ -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
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|