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.
- binalyze_air/__init__.py +77 -0
- binalyze_air/apis/__init__.py +27 -0
- binalyze_air/apis/authentication.py +27 -0
- binalyze_air/apis/auto_asset_tags.py +75 -0
- binalyze_air/apis/endpoints.py +22 -0
- binalyze_air/apis/event_subscription.py +97 -0
- binalyze_air/apis/evidence.py +53 -0
- binalyze_air/apis/evidences.py +216 -0
- binalyze_air/apis/interact.py +36 -0
- binalyze_air/apis/params.py +40 -0
- binalyze_air/apis/settings.py +27 -0
- binalyze_air/apis/user_management.py +74 -0
- binalyze_air/apis/users.py +68 -0
- binalyze_air/apis/webhooks.py +231 -0
- binalyze_air/base.py +133 -0
- binalyze_air/client.py +1338 -0
- binalyze_air/commands/__init__.py +146 -0
- binalyze_air/commands/acquisitions.py +387 -0
- binalyze_air/commands/assets.py +363 -0
- binalyze_air/commands/authentication.py +37 -0
- binalyze_air/commands/auto_asset_tags.py +231 -0
- binalyze_air/commands/baseline.py +396 -0
- binalyze_air/commands/cases.py +603 -0
- binalyze_air/commands/event_subscription.py +102 -0
- binalyze_air/commands/evidences.py +988 -0
- binalyze_air/commands/interact.py +58 -0
- binalyze_air/commands/organizations.py +221 -0
- binalyze_air/commands/policies.py +203 -0
- binalyze_air/commands/settings.py +29 -0
- binalyze_air/commands/tasks.py +56 -0
- binalyze_air/commands/triage.py +360 -0
- binalyze_air/commands/user_management.py +126 -0
- binalyze_air/commands/users.py +101 -0
- binalyze_air/config.py +245 -0
- binalyze_air/exceptions.py +50 -0
- binalyze_air/http_client.py +306 -0
- binalyze_air/models/__init__.py +285 -0
- binalyze_air/models/acquisitions.py +251 -0
- binalyze_air/models/assets.py +439 -0
- binalyze_air/models/audit.py +273 -0
- binalyze_air/models/authentication.py +70 -0
- binalyze_air/models/auto_asset_tags.py +117 -0
- binalyze_air/models/baseline.py +232 -0
- binalyze_air/models/cases.py +276 -0
- binalyze_air/models/endpoints.py +76 -0
- binalyze_air/models/event_subscription.py +172 -0
- binalyze_air/models/evidence.py +66 -0
- binalyze_air/models/evidences.py +349 -0
- binalyze_air/models/interact.py +136 -0
- binalyze_air/models/organizations.py +294 -0
- binalyze_air/models/params.py +128 -0
- binalyze_air/models/policies.py +250 -0
- binalyze_air/models/settings.py +84 -0
- binalyze_air/models/tasks.py +149 -0
- binalyze_air/models/triage.py +143 -0
- binalyze_air/models/user_management.py +97 -0
- binalyze_air/models/users.py +82 -0
- binalyze_air/queries/__init__.py +134 -0
- binalyze_air/queries/acquisitions.py +156 -0
- binalyze_air/queries/assets.py +105 -0
- binalyze_air/queries/audit.py +417 -0
- binalyze_air/queries/authentication.py +56 -0
- binalyze_air/queries/auto_asset_tags.py +60 -0
- binalyze_air/queries/baseline.py +185 -0
- binalyze_air/queries/cases.py +293 -0
- binalyze_air/queries/endpoints.py +25 -0
- binalyze_air/queries/event_subscription.py +55 -0
- binalyze_air/queries/evidence.py +140 -0
- binalyze_air/queries/evidences.py +280 -0
- binalyze_air/queries/interact.py +28 -0
- binalyze_air/queries/organizations.py +223 -0
- binalyze_air/queries/params.py +115 -0
- binalyze_air/queries/policies.py +150 -0
- binalyze_air/queries/settings.py +20 -0
- binalyze_air/queries/tasks.py +82 -0
- binalyze_air/queries/triage.py +231 -0
- binalyze_air/queries/user_management.py +83 -0
- binalyze_air/queries/users.py +69 -0
- binalyze_air_sdk-1.0.1.dist-info/METADATA +635 -0
- binalyze_air_sdk-1.0.1.dist-info/RECORD +82 -0
- binalyze_air_sdk-1.0.1.dist-info/WHEEL +5 -0
- binalyze_air_sdk-1.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,140 @@
|
|
1
|
+
"""
|
2
|
+
Evidence-related queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from ..base import Query
|
6
|
+
from ..models.evidence import EvidencePPC, EvidenceReportFileInfo, EvidenceReport
|
7
|
+
from ..http_client import HTTPClient
|
8
|
+
|
9
|
+
|
10
|
+
class GetEvidencePPCQuery(Query[EvidencePPC]):
|
11
|
+
"""Query to get case evidence PPC."""
|
12
|
+
|
13
|
+
def __init__(self, http_client: HTTPClient, endpoint_id: str, task_id: str):
|
14
|
+
self.http_client = http_client
|
15
|
+
self.endpoint_id = endpoint_id
|
16
|
+
self.task_id = task_id
|
17
|
+
|
18
|
+
def execute(self) -> EvidencePPC:
|
19
|
+
"""Execute the get evidence PPC query."""
|
20
|
+
try:
|
21
|
+
# Use binary HTTP client for file downloads
|
22
|
+
response = self.http_client.get_binary(f"evidence/case/ppc/{self.endpoint_id}/{self.task_id}")
|
23
|
+
|
24
|
+
# Extract content information
|
25
|
+
content_type = response.headers.get('Content-Type', 'application/octet-stream')
|
26
|
+
filename = response.headers.get('Content-Disposition')
|
27
|
+
if filename and 'filename=' in filename:
|
28
|
+
filename = filename.split('filename=')[1].strip('"')
|
29
|
+
else:
|
30
|
+
filename = f"ppc_{self.endpoint_id}_{self.task_id}.zip"
|
31
|
+
|
32
|
+
return EvidencePPC(
|
33
|
+
endpoint_id=self.endpoint_id,
|
34
|
+
task_id=self.task_id,
|
35
|
+
content=response.content,
|
36
|
+
content_type=content_type,
|
37
|
+
content_length=len(response.content) if response.content else 0,
|
38
|
+
filename=filename
|
39
|
+
)
|
40
|
+
|
41
|
+
except Exception as e:
|
42
|
+
# Re-raise with more specific error information
|
43
|
+
error_str = str(e)
|
44
|
+
if "404" in error_str or "not found" in error_str.lower():
|
45
|
+
raise Exception(f"Evidence PPC not found (HTTP 404): No task(s) found by provided id(s)")
|
46
|
+
else:
|
47
|
+
raise Exception(f"Evidence PPC not found for endpoint {self.endpoint_id}, task {self.task_id}: {error_str}")
|
48
|
+
|
49
|
+
|
50
|
+
class GetEvidenceReportFileInfoQuery(Query[EvidenceReportFileInfo]):
|
51
|
+
"""Query to get case evidence report file info."""
|
52
|
+
|
53
|
+
def __init__(self, http_client: HTTPClient, endpoint_id: str, task_id: str):
|
54
|
+
self.http_client = http_client
|
55
|
+
self.endpoint_id = endpoint_id
|
56
|
+
self.task_id = task_id
|
57
|
+
|
58
|
+
def execute(self) -> EvidenceReportFileInfo:
|
59
|
+
"""Execute the get evidence report file info query."""
|
60
|
+
try:
|
61
|
+
response = self.http_client.get(f"evidence/case/report-file-info/{self.endpoint_id}/{self.task_id}")
|
62
|
+
|
63
|
+
if response.get("success"):
|
64
|
+
file_info_data = response.get("result", {})
|
65
|
+
|
66
|
+
# Map API fields to SDK model fields
|
67
|
+
return EvidenceReportFileInfo(
|
68
|
+
endpoint_id=self.endpoint_id,
|
69
|
+
task_id=self.task_id,
|
70
|
+
file_name=file_info_data.get("name"),
|
71
|
+
file_size=file_info_data.get("size"),
|
72
|
+
created_at=file_info_data.get("timestampResponse"),
|
73
|
+
status="available" if not file_info_data.get("purged", False) else "purged",
|
74
|
+
# Additional fields from API
|
75
|
+
file_path=file_info_data.get("path"),
|
76
|
+
encoding=file_info_data.get("encoding"),
|
77
|
+
mime_type=file_info_data.get("mimeType"),
|
78
|
+
file_hash=file_info_data.get("hash"),
|
79
|
+
organization_id=file_info_data.get("organizationId"),
|
80
|
+
is_purged=file_info_data.get("purged", False)
|
81
|
+
)
|
82
|
+
|
83
|
+
# Handle error response with detailed information from API
|
84
|
+
errors = response.get("errors", [])
|
85
|
+
status_code = response.get("statusCode", "Unknown")
|
86
|
+
error_message = "; ".join(errors) if errors else "Unknown error"
|
87
|
+
|
88
|
+
raise Exception(f"Evidence report file info not found (HTTP {status_code}): {error_message}")
|
89
|
+
|
90
|
+
except Exception as e:
|
91
|
+
# Check if this is already our formatted exception
|
92
|
+
if "Evidence report file info not found" in str(e):
|
93
|
+
raise e
|
94
|
+
|
95
|
+
# Handle HTTP client exceptions and format them consistently
|
96
|
+
error_str = str(e)
|
97
|
+
if "404" in error_str or "not found" in error_str.lower():
|
98
|
+
raise Exception(f"Evidence report file info not found (HTTP 404): No task(s) found by provided id(s)")
|
99
|
+
else:
|
100
|
+
raise Exception(f"Evidence report file info not found for endpoint {self.endpoint_id}, task {self.task_id}: {error_str}")
|
101
|
+
|
102
|
+
|
103
|
+
class GetEvidenceReportQuery(Query[EvidenceReport]):
|
104
|
+
"""Query to get case evidence report."""
|
105
|
+
|
106
|
+
def __init__(self, http_client: HTTPClient, endpoint_id: str, task_id: str):
|
107
|
+
self.http_client = http_client
|
108
|
+
self.endpoint_id = endpoint_id
|
109
|
+
self.task_id = task_id
|
110
|
+
|
111
|
+
def execute(self) -> EvidenceReport:
|
112
|
+
"""Execute the get evidence report query."""
|
113
|
+
try:
|
114
|
+
# Use binary HTTP client for file downloads
|
115
|
+
response = self.http_client.get_binary(f"evidence/case/report/{self.endpoint_id}/{self.task_id}")
|
116
|
+
|
117
|
+
# Extract content information
|
118
|
+
content_type = response.headers.get('Content-Type', 'application/octet-stream')
|
119
|
+
filename = response.headers.get('Content-Disposition')
|
120
|
+
if filename and 'filename=' in filename:
|
121
|
+
filename = filename.split('filename=')[1].strip('"')
|
122
|
+
else:
|
123
|
+
filename = f"report_{self.endpoint_id}_{self.task_id}"
|
124
|
+
|
125
|
+
return EvidenceReport(
|
126
|
+
endpoint_id=self.endpoint_id,
|
127
|
+
task_id=self.task_id,
|
128
|
+
content=response.content,
|
129
|
+
content_type=content_type,
|
130
|
+
content_length=len(response.content) if response.content else 0,
|
131
|
+
filename=filename
|
132
|
+
)
|
133
|
+
|
134
|
+
except Exception as e:
|
135
|
+
# Re-raise with more specific error information
|
136
|
+
error_str = str(e)
|
137
|
+
if "404" in error_str or "not found" in error_str.lower():
|
138
|
+
raise Exception(f"Evidence report not found (HTTP 404): No task(s) found by provided id(s)")
|
139
|
+
else:
|
140
|
+
raise Exception(f"Evidence report not found for endpoint {self.endpoint_id}, task {self.task_id}: {error_str}")
|
@@ -0,0 +1,280 @@
|
|
1
|
+
"""
|
2
|
+
Evidences/Repositories-related queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List, Optional
|
6
|
+
|
7
|
+
from ..base import Query
|
8
|
+
from ..models.evidences import (
|
9
|
+
EvidenceRepository, AmazonS3Repository, AzureStorageRepository,
|
10
|
+
FTPSRepository, SFTPRepository, SMBRepository, RepositoryFilter
|
11
|
+
)
|
12
|
+
from ..http_client import HTTPClient
|
13
|
+
|
14
|
+
|
15
|
+
class ListRepositoriesQuery(Query[List[EvidenceRepository]]):
|
16
|
+
"""Query to list evidence repositories."""
|
17
|
+
|
18
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[RepositoryFilter] = None, organization_ids: Optional[List[int]] = None):
|
19
|
+
self.http_client = http_client
|
20
|
+
self.filter_params = filter_params
|
21
|
+
self.organization_ids = organization_ids or [0]
|
22
|
+
|
23
|
+
def execute(self) -> List[EvidenceRepository]:
|
24
|
+
"""Execute the list repositories query."""
|
25
|
+
params = {}
|
26
|
+
if self.filter_params:
|
27
|
+
params = self.filter_params.to_params()
|
28
|
+
else:
|
29
|
+
# Add required organization IDs filter if no filter provided
|
30
|
+
params["filter[organizationIds]"] = ",".join(map(str, self.organization_ids))
|
31
|
+
|
32
|
+
# Ensure consistent sorting to match API defaults
|
33
|
+
if "sortBy" not in params:
|
34
|
+
params["sortBy"] = "createdAt"
|
35
|
+
if "sortType" not in params:
|
36
|
+
params["sortType"] = "ASC"
|
37
|
+
|
38
|
+
response = self.http_client.get("evidences/repositories", params=params)
|
39
|
+
|
40
|
+
if response.get("success"):
|
41
|
+
repositories_data = response.get("result", {}).get("entities", [])
|
42
|
+
|
43
|
+
# Use Pydantic parsing with proper field aliasing
|
44
|
+
repositories = []
|
45
|
+
for repo_data in repositories_data:
|
46
|
+
repositories.append(EvidenceRepository.model_validate(repo_data))
|
47
|
+
|
48
|
+
return repositories
|
49
|
+
|
50
|
+
return []
|
51
|
+
|
52
|
+
|
53
|
+
class GetRepositoryQuery(Query[EvidenceRepository]):
|
54
|
+
"""Query to get evidence repository by ID."""
|
55
|
+
|
56
|
+
def __init__(self, http_client: HTTPClient, repository_id: str):
|
57
|
+
self.http_client = http_client
|
58
|
+
self.repository_id = repository_id
|
59
|
+
|
60
|
+
def execute(self) -> EvidenceRepository:
|
61
|
+
"""Execute the get repository query."""
|
62
|
+
response = self.http_client.get(f"evidences/repositories/{self.repository_id}")
|
63
|
+
|
64
|
+
if response.get("success"):
|
65
|
+
repository_data = response.get("result", {})
|
66
|
+
|
67
|
+
# Use Pydantic parsing with proper field aliasing
|
68
|
+
return EvidenceRepository.model_validate(repository_data)
|
69
|
+
|
70
|
+
raise Exception(f"Evidence repository not found: {self.repository_id}")
|
71
|
+
|
72
|
+
|
73
|
+
# Amazon S3 Repository Queries
|
74
|
+
|
75
|
+
class ListAmazonS3RepositoriesQuery(Query[List[AmazonS3Repository]]):
|
76
|
+
"""Query to list Amazon S3 repositories."""
|
77
|
+
|
78
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[RepositoryFilter] = None):
|
79
|
+
self.http_client = http_client
|
80
|
+
self.filter_params = filter_params
|
81
|
+
|
82
|
+
def execute(self) -> List[AmazonS3Repository]:
|
83
|
+
"""Execute the list Amazon S3 repositories query."""
|
84
|
+
params = {}
|
85
|
+
if self.filter_params:
|
86
|
+
params = self.filter_params.model_dump(exclude_none=True)
|
87
|
+
|
88
|
+
response = self.http_client.get("evidences/repositories/amazon-s3", params=params)
|
89
|
+
|
90
|
+
if response.get("success"):
|
91
|
+
repositories_data = response.get("result", {}).get("entities", [])
|
92
|
+
return [AmazonS3Repository(**repo) for repo in repositories_data]
|
93
|
+
|
94
|
+
return []
|
95
|
+
|
96
|
+
|
97
|
+
class GetAmazonS3RepositoryQuery(Query[AmazonS3Repository]):
|
98
|
+
"""Query to get Amazon S3 repository by ID."""
|
99
|
+
|
100
|
+
def __init__(self, http_client: HTTPClient, repository_id: str):
|
101
|
+
self.http_client = http_client
|
102
|
+
self.repository_id = repository_id
|
103
|
+
|
104
|
+
def execute(self) -> AmazonS3Repository:
|
105
|
+
"""Execute the get Amazon S3 repository query."""
|
106
|
+
response = self.http_client.get(f"evidences/repositories/amazon-s3/{self.repository_id}")
|
107
|
+
|
108
|
+
if response.get("success"):
|
109
|
+
repository_data = response.get("result", {})
|
110
|
+
return AmazonS3Repository(**repository_data)
|
111
|
+
|
112
|
+
raise Exception(f"Amazon S3 repository not found: {self.repository_id}")
|
113
|
+
|
114
|
+
|
115
|
+
# Azure Storage Repository Queries
|
116
|
+
|
117
|
+
class ListAzureStorageRepositoriesQuery(Query[List[AzureStorageRepository]]):
|
118
|
+
"""Query to list Azure Storage repositories."""
|
119
|
+
|
120
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[RepositoryFilter] = None):
|
121
|
+
self.http_client = http_client
|
122
|
+
self.filter_params = filter_params
|
123
|
+
|
124
|
+
def execute(self) -> List[AzureStorageRepository]:
|
125
|
+
"""Execute the list Azure Storage repositories query."""
|
126
|
+
params = {}
|
127
|
+
if self.filter_params:
|
128
|
+
params = self.filter_params.model_dump(exclude_none=True)
|
129
|
+
|
130
|
+
response = self.http_client.get("evidences/repositories/azure-storage", params=params)
|
131
|
+
|
132
|
+
if response.get("success"):
|
133
|
+
repositories_data = response.get("result", {}).get("entities", [])
|
134
|
+
return [AzureStorageRepository(**repo) for repo in repositories_data]
|
135
|
+
|
136
|
+
return []
|
137
|
+
|
138
|
+
|
139
|
+
class GetAzureStorageRepositoryQuery(Query[AzureStorageRepository]):
|
140
|
+
"""Query to get Azure Storage repository by ID."""
|
141
|
+
|
142
|
+
def __init__(self, http_client: HTTPClient, repository_id: str):
|
143
|
+
self.http_client = http_client
|
144
|
+
self.repository_id = repository_id
|
145
|
+
|
146
|
+
def execute(self) -> AzureStorageRepository:
|
147
|
+
"""Execute the get Azure Storage repository query."""
|
148
|
+
response = self.http_client.get(f"evidences/repositories/azure-storage/{self.repository_id}")
|
149
|
+
|
150
|
+
if response.get("success"):
|
151
|
+
repository_data = response.get("result", {})
|
152
|
+
return AzureStorageRepository(**repository_data)
|
153
|
+
|
154
|
+
raise Exception(f"Azure Storage repository not found: {self.repository_id}")
|
155
|
+
|
156
|
+
|
157
|
+
# FTPS Repository Queries
|
158
|
+
|
159
|
+
class ListFTPSRepositoriesQuery(Query[List[FTPSRepository]]):
|
160
|
+
"""Query to list FTPS repositories."""
|
161
|
+
|
162
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[RepositoryFilter] = None):
|
163
|
+
self.http_client = http_client
|
164
|
+
self.filter_params = filter_params
|
165
|
+
|
166
|
+
def execute(self) -> List[FTPSRepository]:
|
167
|
+
"""Execute the list FTPS repositories query."""
|
168
|
+
params = {}
|
169
|
+
if self.filter_params:
|
170
|
+
params = self.filter_params.model_dump(exclude_none=True)
|
171
|
+
|
172
|
+
response = self.http_client.get("evidences/repositories/ftps", params=params)
|
173
|
+
|
174
|
+
if response.get("success"):
|
175
|
+
repositories_data = response.get("result", {}).get("entities", [])
|
176
|
+
return [FTPSRepository(**repo) for repo in repositories_data]
|
177
|
+
|
178
|
+
return []
|
179
|
+
|
180
|
+
|
181
|
+
class GetFTPSRepositoryQuery(Query[FTPSRepository]):
|
182
|
+
"""Query to get FTPS repository by ID."""
|
183
|
+
|
184
|
+
def __init__(self, http_client: HTTPClient, repository_id: str):
|
185
|
+
self.http_client = http_client
|
186
|
+
self.repository_id = repository_id
|
187
|
+
|
188
|
+
def execute(self) -> FTPSRepository:
|
189
|
+
"""Execute the get FTPS repository query."""
|
190
|
+
response = self.http_client.get(f"evidences/repositories/ftps/{self.repository_id}")
|
191
|
+
|
192
|
+
if response.get("success"):
|
193
|
+
repository_data = response.get("result", {})
|
194
|
+
return FTPSRepository(**repository_data)
|
195
|
+
|
196
|
+
raise Exception(f"FTPS repository not found: {self.repository_id}")
|
197
|
+
|
198
|
+
|
199
|
+
# SFTP Repository Queries
|
200
|
+
|
201
|
+
class ListSFTPRepositoriesQuery(Query[List[SFTPRepository]]):
|
202
|
+
"""Query to list SFTP repositories."""
|
203
|
+
|
204
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[RepositoryFilter] = None):
|
205
|
+
self.http_client = http_client
|
206
|
+
self.filter_params = filter_params
|
207
|
+
|
208
|
+
def execute(self) -> List[SFTPRepository]:
|
209
|
+
"""Execute the list SFTP repositories query."""
|
210
|
+
params = {}
|
211
|
+
if self.filter_params:
|
212
|
+
params = self.filter_params.model_dump(exclude_none=True)
|
213
|
+
|
214
|
+
response = self.http_client.get("evidences/repositories/sftp", params=params)
|
215
|
+
|
216
|
+
if response.get("success"):
|
217
|
+
repositories_data = response.get("result", {}).get("entities", [])
|
218
|
+
return [SFTPRepository(**repo) for repo in repositories_data]
|
219
|
+
|
220
|
+
return []
|
221
|
+
|
222
|
+
|
223
|
+
class GetSFTPRepositoryQuery(Query[SFTPRepository]):
|
224
|
+
"""Query to get SFTP repository by ID."""
|
225
|
+
|
226
|
+
def __init__(self, http_client: HTTPClient, repository_id: str):
|
227
|
+
self.http_client = http_client
|
228
|
+
self.repository_id = repository_id
|
229
|
+
|
230
|
+
def execute(self) -> SFTPRepository:
|
231
|
+
"""Execute the get SFTP repository query."""
|
232
|
+
response = self.http_client.get(f"evidences/repositories/sftp/{self.repository_id}")
|
233
|
+
|
234
|
+
if response.get("success"):
|
235
|
+
repository_data = response.get("result", {})
|
236
|
+
return SFTPRepository(**repository_data)
|
237
|
+
|
238
|
+
raise Exception(f"SFTP repository not found: {self.repository_id}")
|
239
|
+
|
240
|
+
|
241
|
+
# SMB Repository Queries
|
242
|
+
|
243
|
+
class ListSMBRepositoriesQuery(Query[List[SMBRepository]]):
|
244
|
+
"""Query to list SMB repositories."""
|
245
|
+
|
246
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[RepositoryFilter] = None):
|
247
|
+
self.http_client = http_client
|
248
|
+
self.filter_params = filter_params
|
249
|
+
|
250
|
+
def execute(self) -> List[SMBRepository]:
|
251
|
+
"""Execute the list SMB repositories query."""
|
252
|
+
params = {}
|
253
|
+
if self.filter_params:
|
254
|
+
params = self.filter_params.model_dump(exclude_none=True)
|
255
|
+
|
256
|
+
response = self.http_client.get("evidences/repositories/smb", params=params)
|
257
|
+
|
258
|
+
if response.get("success"):
|
259
|
+
repositories_data = response.get("result", {}).get("entities", [])
|
260
|
+
return [SMBRepository(**repo) for repo in repositories_data]
|
261
|
+
|
262
|
+
return []
|
263
|
+
|
264
|
+
|
265
|
+
class GetSMBRepositoryQuery(Query[SMBRepository]):
|
266
|
+
"""Query to get SMB repository by ID."""
|
267
|
+
|
268
|
+
def __init__(self, http_client: HTTPClient, repository_id: str):
|
269
|
+
self.http_client = http_client
|
270
|
+
self.repository_id = repository_id
|
271
|
+
|
272
|
+
def execute(self) -> SMBRepository:
|
273
|
+
"""Execute the get SMB repository query."""
|
274
|
+
response = self.http_client.get(f"evidences/repositories/smb/{self.repository_id}")
|
275
|
+
|
276
|
+
if response.get("success"):
|
277
|
+
repository_data = response.get("result", {})
|
278
|
+
return SMBRepository(**repository_data)
|
279
|
+
|
280
|
+
raise Exception(f"SMB repository not found: {self.repository_id}")
|
@@ -0,0 +1,28 @@
|
|
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}")
|
@@ -0,0 +1,223 @@
|
|
1
|
+
"""
|
2
|
+
Organization-related queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List, Optional
|
6
|
+
|
7
|
+
from ..base import Query
|
8
|
+
from ..models.organizations import (
|
9
|
+
Organization, OrganizationUser, OrganizationRole, OrganizationLicense,
|
10
|
+
OrganizationSettings, OrganizationFilter, OrganizationsPaginatedResponse,
|
11
|
+
FilterOption, OrganizationUsersPaginatedResponse, CheckOrganizationNameExistsResponse,
|
12
|
+
ShareableDeploymentInfoResponse
|
13
|
+
)
|
14
|
+
from ..http_client import HTTPClient
|
15
|
+
|
16
|
+
|
17
|
+
class ListOrganizationsQuery(Query[OrganizationsPaginatedResponse]):
|
18
|
+
"""Query to list organizations with optional filtering."""
|
19
|
+
|
20
|
+
def __init__(self, http_client: HTTPClient, page: int = 1, page_size: int = 10,
|
21
|
+
sort_by: str = "name", order: str = "asc", filter_params: Optional[OrganizationFilter] = None):
|
22
|
+
self.http_client = http_client
|
23
|
+
self.page = page
|
24
|
+
self.page_size = page_size
|
25
|
+
self.sort_by = sort_by
|
26
|
+
self.order = order
|
27
|
+
self.filter_params = filter_params or OrganizationFilter()
|
28
|
+
|
29
|
+
def execute(self) -> OrganizationsPaginatedResponse:
|
30
|
+
"""Execute the query to list organizations with complete parameter support."""
|
31
|
+
# Build query parameters
|
32
|
+
params = {
|
33
|
+
"pageNumber": self.page,
|
34
|
+
"pageSize": self.page_size,
|
35
|
+
"sortBy": self.sort_by,
|
36
|
+
"sortType": self.order.upper(), # ASC or DESC
|
37
|
+
}
|
38
|
+
|
39
|
+
# Add filter parameters using proper API structure
|
40
|
+
filter_params = self.filter_params.to_params()
|
41
|
+
params.update(filter_params)
|
42
|
+
|
43
|
+
response = self.http_client.get("organizations", params=params)
|
44
|
+
|
45
|
+
result = response.get("result", {})
|
46
|
+
|
47
|
+
# Use Pydantic model_validate for automatic field mapping
|
48
|
+
return OrganizationsPaginatedResponse.model_validate(result)
|
49
|
+
|
50
|
+
|
51
|
+
class GetOrganizationQuery(Query[Organization]):
|
52
|
+
"""Query to get a specific organization by ID."""
|
53
|
+
|
54
|
+
def __init__(self, http_client: HTTPClient, organization_id: str):
|
55
|
+
self.http_client = http_client
|
56
|
+
self.organization_id = organization_id
|
57
|
+
|
58
|
+
def execute(self) -> Organization:
|
59
|
+
"""Execute the query to get organization details."""
|
60
|
+
response = self.http_client.get(f"organizations/{self.organization_id}")
|
61
|
+
|
62
|
+
entity_data = response.get("result", {})
|
63
|
+
|
64
|
+
# Use Pydantic model_validate for automatic field mapping via aliases
|
65
|
+
return Organization.model_validate(entity_data)
|
66
|
+
|
67
|
+
|
68
|
+
class GetOrganizationUsersQuery(Query[OrganizationUsersPaginatedResponse]):
|
69
|
+
"""Query to get users for a specific organization with complete field mapping."""
|
70
|
+
|
71
|
+
def __init__(self, http_client: HTTPClient, organization_id: str, page: int = 1, page_size: int = 10):
|
72
|
+
self.http_client = http_client
|
73
|
+
self.organization_id = organization_id
|
74
|
+
self.page = page
|
75
|
+
self.page_size = page_size
|
76
|
+
|
77
|
+
def execute(self) -> OrganizationUsersPaginatedResponse:
|
78
|
+
"""Execute the query to get organization users with complete field mapping."""
|
79
|
+
# Add pagination parameters
|
80
|
+
params = {
|
81
|
+
"pageNumber": self.page,
|
82
|
+
"pageSize": self.page_size
|
83
|
+
}
|
84
|
+
|
85
|
+
response = self.http_client.get(f"organizations/{self.organization_id}/users", params=params)
|
86
|
+
|
87
|
+
result = response.get("result", {})
|
88
|
+
|
89
|
+
# Use Pydantic model_validate for automatic field mapping
|
90
|
+
return OrganizationUsersPaginatedResponse.model_validate(result)
|
91
|
+
|
92
|
+
|
93
|
+
class CheckOrganizationNameExistsQuery(Query[CheckOrganizationNameExistsResponse]):
|
94
|
+
"""Query to check if an organization name exists."""
|
95
|
+
|
96
|
+
def __init__(self, http_client: HTTPClient, name: str):
|
97
|
+
self.http_client = http_client
|
98
|
+
self.name = name
|
99
|
+
|
100
|
+
def execute(self) -> CheckOrganizationNameExistsResponse:
|
101
|
+
"""Execute the query to check organization name availability."""
|
102
|
+
params = {"name": self.name}
|
103
|
+
|
104
|
+
response = self.http_client.get("organizations/check", params=params)
|
105
|
+
|
106
|
+
# Use Pydantic model_validate for automatic field mapping
|
107
|
+
return CheckOrganizationNameExistsResponse.model_validate(response)
|
108
|
+
|
109
|
+
|
110
|
+
class GetShareableDeploymentInfoQuery(Query[ShareableDeploymentInfoResponse]):
|
111
|
+
"""Query to get shareable deployment information by token."""
|
112
|
+
|
113
|
+
def __init__(self, http_client: HTTPClient, token: str):
|
114
|
+
self.http_client = http_client
|
115
|
+
self.token = token
|
116
|
+
|
117
|
+
def execute(self) -> ShareableDeploymentInfoResponse:
|
118
|
+
"""Execute the query to get shareable deployment information."""
|
119
|
+
params = {"token": self.token}
|
120
|
+
|
121
|
+
response = self.http_client.get("organizations/shareable-deployment", params=params)
|
122
|
+
|
123
|
+
# Use Pydantic model_validate for automatic field mapping
|
124
|
+
return ShareableDeploymentInfoResponse.model_validate(response)
|
125
|
+
|
126
|
+
|
127
|
+
class GetOrganizationRolesQuery(Query[List[OrganizationRole]]):
|
128
|
+
"""Query to get roles for a specific organization."""
|
129
|
+
|
130
|
+
def __init__(self, http_client: HTTPClient, organization_id: str):
|
131
|
+
self.http_client = http_client
|
132
|
+
self.organization_id = organization_id
|
133
|
+
|
134
|
+
def execute(self) -> List[OrganizationRole]:
|
135
|
+
"""Execute the query to get organization roles."""
|
136
|
+
response = self.http_client.get(f"organizations/{self.organization_id}/roles")
|
137
|
+
|
138
|
+
entities = response.get("result", {}).get("entities", [])
|
139
|
+
|
140
|
+
roles = []
|
141
|
+
for entity_data in entities:
|
142
|
+
mapped_data = {
|
143
|
+
"id": entity_data.get("_id"),
|
144
|
+
"name": entity_data.get("name"),
|
145
|
+
"description": entity_data.get("description"),
|
146
|
+
"organization_id": entity_data.get("organizationId"),
|
147
|
+
"permissions": entity_data.get("permissions", []),
|
148
|
+
"created_at": entity_data.get("createdAt"),
|
149
|
+
"updated_at": entity_data.get("updatedAt"),
|
150
|
+
"created_by": entity_data.get("createdBy"),
|
151
|
+
"is_system": entity_data.get("isSystem", False),
|
152
|
+
"user_count": entity_data.get("userCount", 0),
|
153
|
+
}
|
154
|
+
|
155
|
+
# Remove None values
|
156
|
+
mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
|
157
|
+
|
158
|
+
roles.append(OrganizationRole(**mapped_data))
|
159
|
+
|
160
|
+
return roles
|
161
|
+
|
162
|
+
|
163
|
+
class GetOrganizationLicensesQuery(Query[List[OrganizationLicense]]):
|
164
|
+
"""Query to get licenses for a specific organization."""
|
165
|
+
|
166
|
+
def __init__(self, http_client: HTTPClient, organization_id: str):
|
167
|
+
self.http_client = http_client
|
168
|
+
self.organization_id = organization_id
|
169
|
+
|
170
|
+
def execute(self) -> List[OrganizationLicense]:
|
171
|
+
"""Execute the query to get organization licenses."""
|
172
|
+
response = self.http_client.get(f"organizations/{self.organization_id}/licenses")
|
173
|
+
|
174
|
+
entities = response.get("result", {}).get("entities", [])
|
175
|
+
|
176
|
+
licenses = []
|
177
|
+
for entity_data in entities:
|
178
|
+
mapped_data = {
|
179
|
+
"id": entity_data.get("_id"),
|
180
|
+
"organization_id": entity_data.get("organizationId"),
|
181
|
+
"license_type": entity_data.get("licenseType"),
|
182
|
+
"total_licenses": entity_data.get("totalLicenses", 0),
|
183
|
+
"used_licenses": entity_data.get("usedLicenses", 0),
|
184
|
+
"valid_from": entity_data.get("validFrom"),
|
185
|
+
"valid_until": entity_data.get("validUntil"),
|
186
|
+
"features": entity_data.get("features", []),
|
187
|
+
"is_active": entity_data.get("isActive", True),
|
188
|
+
}
|
189
|
+
|
190
|
+
# Remove None values
|
191
|
+
mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
|
192
|
+
|
193
|
+
licenses.append(OrganizationLicense(**mapped_data))
|
194
|
+
|
195
|
+
return licenses
|
196
|
+
|
197
|
+
|
198
|
+
class GetOrganizationSettingsQuery(Query[OrganizationSettings]):
|
199
|
+
"""Query to get settings for a specific organization."""
|
200
|
+
|
201
|
+
def __init__(self, http_client: HTTPClient, organization_id: str):
|
202
|
+
self.http_client = http_client
|
203
|
+
self.organization_id = organization_id
|
204
|
+
|
205
|
+
def execute(self) -> OrganizationSettings:
|
206
|
+
"""Execute the query to get organization settings."""
|
207
|
+
response = self.http_client.get(f"organizations/{self.organization_id}/settings")
|
208
|
+
|
209
|
+
entity_data = response.get("result", {})
|
210
|
+
|
211
|
+
mapped_data = {
|
212
|
+
"organization_id": entity_data.get("organizationId"),
|
213
|
+
"retention_policy": entity_data.get("retentionPolicy", {}),
|
214
|
+
"security_settings": entity_data.get("securitySettings", {}),
|
215
|
+
"notification_settings": entity_data.get("notificationSettings", {}),
|
216
|
+
"api_settings": entity_data.get("apiSettings", {}),
|
217
|
+
"custom_settings": entity_data.get("customSettings", {}),
|
218
|
+
}
|
219
|
+
|
220
|
+
# Remove None values
|
221
|
+
mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
|
222
|
+
|
223
|
+
return OrganizationSettings(**mapped_data)
|