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
@@ -0,0 +1,137 @@
|
|
1
|
+
"""
|
2
|
+
Cloud Forensics queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Optional, Dict, Any
|
6
|
+
|
7
|
+
from ..base import Query
|
8
|
+
from ..models.cloud_forensics import (
|
9
|
+
CloudAccount, CloudAccountsPaginatedResponse, CloudAccountFilter,
|
10
|
+
CloudAccountSyncResult, CloudVendorSyncResult, CloudVendor
|
11
|
+
)
|
12
|
+
from ..http_client import HTTPClient
|
13
|
+
|
14
|
+
|
15
|
+
class ListCloudAccountsQuery(Query[CloudAccountsPaginatedResponse]):
|
16
|
+
"""Query to list cloud accounts with optional filtering."""
|
17
|
+
|
18
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[CloudAccountFilter] = None):
|
19
|
+
self.http_client = http_client
|
20
|
+
self.filter_params = filter_params or CloudAccountFilter()
|
21
|
+
|
22
|
+
def execute(self) -> CloudAccountsPaginatedResponse:
|
23
|
+
"""Execute the query."""
|
24
|
+
params = {}
|
25
|
+
|
26
|
+
# Add pagination parameters
|
27
|
+
if self.filter_params.page_size is not None:
|
28
|
+
params["pageSize"] = self.filter_params.page_size
|
29
|
+
if self.filter_params.page_number is not None:
|
30
|
+
params["pageNumber"] = self.filter_params.page_number
|
31
|
+
if self.filter_params.sort_type is not None:
|
32
|
+
params["sortType"] = self.filter_params.sort_type
|
33
|
+
if self.filter_params.sort_by is not None:
|
34
|
+
params["sortBy"] = self.filter_params.sort_by
|
35
|
+
|
36
|
+
# Add required filter parameters (API requires these)
|
37
|
+
# Default to AWS and organization ID 0 if not specified
|
38
|
+
cloud_vendor = self.filter_params.cloud_vendor or CloudVendor.AWS
|
39
|
+
# Handle both enum and string values - FIXED
|
40
|
+
if hasattr(cloud_vendor, 'value'):
|
41
|
+
params["filter[cloudVendor]"] = cloud_vendor.value
|
42
|
+
else:
|
43
|
+
params["filter[cloudVendor]"] = cloud_vendor
|
44
|
+
|
45
|
+
organization_ids = self.filter_params.organization_ids or [0]
|
46
|
+
params["filter[organizationIds]"] = ",".join(map(str, organization_ids))
|
47
|
+
|
48
|
+
# Add optional filter parameters
|
49
|
+
if self.filter_params.search_term is not None:
|
50
|
+
params["filter[searchTerm]"] = self.filter_params.search_term
|
51
|
+
if self.filter_params.status is not None:
|
52
|
+
# Handle both enum and string values - FIXED
|
53
|
+
if hasattr(self.filter_params.status, 'value'):
|
54
|
+
params["filter[status]"] = self.filter_params.status.value
|
55
|
+
else:
|
56
|
+
params["filter[status]"] = self.filter_params.status
|
57
|
+
|
58
|
+
response = self.http_client.get("cloud-forensics/accounts", params=params)
|
59
|
+
return CloudAccountsPaginatedResponse(**response["result"])
|
60
|
+
|
61
|
+
|
62
|
+
class GetCloudAccountQuery(Query[CloudAccount]):
|
63
|
+
"""Query to get a specific cloud account by ID."""
|
64
|
+
|
65
|
+
def __init__(self, http_client: HTTPClient, account_id: str):
|
66
|
+
self.http_client = http_client
|
67
|
+
self.account_id = account_id
|
68
|
+
|
69
|
+
def execute(self) -> CloudAccount:
|
70
|
+
"""Execute the query."""
|
71
|
+
response = self.http_client.get(f"cloud-forensics/accounts/{self.account_id}")
|
72
|
+
return CloudAccount(**response["result"])
|
73
|
+
|
74
|
+
|
75
|
+
class ExportCloudAccountsQuery(Query[Dict[str, Any]]):
|
76
|
+
"""Query to export cloud accounts data."""
|
77
|
+
|
78
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[CloudAccountFilter] = None):
|
79
|
+
self.http_client = http_client
|
80
|
+
self.filter_params = filter_params or CloudAccountFilter()
|
81
|
+
|
82
|
+
def execute(self) -> Dict[str, Any]:
|
83
|
+
"""Execute the query."""
|
84
|
+
params = {}
|
85
|
+
|
86
|
+
# Add required filter parameters (API requires these)
|
87
|
+
# Default to AWS and organization ID 0 if not specified
|
88
|
+
cloud_vendor = self.filter_params.cloud_vendor or CloudVendor.AWS
|
89
|
+
# Handle both enum and string values - FIXED
|
90
|
+
if hasattr(cloud_vendor, 'value'):
|
91
|
+
params["filter[cloudVendor]"] = cloud_vendor.value
|
92
|
+
else:
|
93
|
+
params["filter[cloudVendor]"] = cloud_vendor
|
94
|
+
|
95
|
+
organization_ids = self.filter_params.organization_ids or [0]
|
96
|
+
params["filter[organizationIds]"] = ",".join(map(str, organization_ids))
|
97
|
+
|
98
|
+
# Add optional filter parameters for export
|
99
|
+
if self.filter_params.search_term is not None:
|
100
|
+
params["filter[searchTerm]"] = self.filter_params.search_term
|
101
|
+
if self.filter_params.status is not None:
|
102
|
+
# Handle both enum and string values - FIXED
|
103
|
+
if hasattr(self.filter_params.status, 'value'):
|
104
|
+
params["filter[status]"] = self.filter_params.status.value
|
105
|
+
else:
|
106
|
+
params["filter[status]"] = self.filter_params.status
|
107
|
+
|
108
|
+
response = self.http_client.get("cloud-forensics/accounts/export", params=params)
|
109
|
+
return response
|
110
|
+
|
111
|
+
|
112
|
+
class SyncCloudAccountQuery(Query[CloudAccountSyncResult]):
|
113
|
+
"""Query to sync a specific cloud account by ID."""
|
114
|
+
|
115
|
+
def __init__(self, http_client: HTTPClient, account_id: str):
|
116
|
+
self.http_client = http_client
|
117
|
+
self.account_id = account_id
|
118
|
+
|
119
|
+
def execute(self) -> CloudAccountSyncResult:
|
120
|
+
"""Execute the query."""
|
121
|
+
response = self.http_client.get(f"cloud-forensics/accounts/sync/{self.account_id}")
|
122
|
+
return CloudAccountSyncResult(**response["result"])
|
123
|
+
|
124
|
+
|
125
|
+
class SyncCloudAccountsByVendorQuery(Query[CloudVendorSyncResult]):
|
126
|
+
"""Query to sync all cloud accounts by vendor."""
|
127
|
+
|
128
|
+
def __init__(self, http_client: HTTPClient, cloud_vendor: CloudVendor):
|
129
|
+
self.http_client = http_client
|
130
|
+
self.cloud_vendor = cloud_vendor
|
131
|
+
|
132
|
+
def execute(self) -> CloudVendorSyncResult:
|
133
|
+
"""Execute the query."""
|
134
|
+
response = self.http_client.get(f"cloud-forensics/accounts/sync/{self.cloud_vendor.value}/all")
|
135
|
+
# Handle case where result might be None
|
136
|
+
result_data = response.get("result") or {}
|
137
|
+
return CloudVendorSyncResult(**result_data)
|
@@ -1,55 +1,55 @@
|
|
1
|
-
"""
|
2
|
-
Event Subscription queries for the Binalyze AIR SDK.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from typing import List, Optional
|
6
|
-
|
7
|
-
from ..base import Query
|
8
|
-
from ..models.event_subscription import EventSubscription, EventSubscriptionFilter
|
9
|
-
from ..http_client import HTTPClient
|
10
|
-
|
11
|
-
|
12
|
-
class ListEventSubscriptionsQuery(Query[List[EventSubscription]]):
|
13
|
-
"""Query to list event subscriptions."""
|
14
|
-
|
15
|
-
def __init__(self, http_client: HTTPClient, filter_params: Optional[EventSubscriptionFilter] = None):
|
16
|
-
self.http_client = http_client
|
17
|
-
self.filter_params = filter_params or EventSubscriptionFilter()
|
18
|
-
|
19
|
-
def execute(self) -> List[EventSubscription]:
|
20
|
-
"""Execute the query to get event subscriptions."""
|
21
|
-
params = self.filter_params.to_params()
|
22
|
-
response = self.http_client.get("event-subscription", params=params)
|
23
|
-
|
24
|
-
entities = response.get("result", {}).get("entities", [])
|
25
|
-
|
26
|
-
# Use Pydantic parsing with proper field aliasing
|
27
|
-
subscriptions = []
|
28
|
-
for item in entities:
|
29
|
-
# Convert id to string as Pydantic expects
|
30
|
-
if "id" in item:
|
31
|
-
item["id"] = str(item["id"])
|
32
|
-
subscriptions.append(EventSubscription.model_validate(item))
|
33
|
-
|
34
|
-
return subscriptions
|
35
|
-
|
36
|
-
|
37
|
-
class GetEventSubscriptionQuery(Query[EventSubscription]):
|
38
|
-
"""Query to get a specific event subscription."""
|
39
|
-
|
40
|
-
def __init__(self, http_client: HTTPClient, subscription_id: str):
|
41
|
-
self.http_client = http_client
|
42
|
-
self.subscription_id = subscription_id
|
43
|
-
|
44
|
-
def execute(self) -> EventSubscription:
|
45
|
-
"""Execute the query to get a specific event subscription."""
|
46
|
-
response = self.http_client.get(f"event-subscription/{self.subscription_id}")
|
47
|
-
|
48
|
-
entity_data = response.get("result", {})
|
49
|
-
|
50
|
-
# Convert id to string as Pydantic expects
|
51
|
-
if "id" in entity_data:
|
52
|
-
entity_data["id"] = str(entity_data["id"])
|
53
|
-
|
54
|
-
# Use Pydantic parsing with proper field aliasing
|
1
|
+
"""
|
2
|
+
Event Subscription queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List, Optional
|
6
|
+
|
7
|
+
from ..base import Query
|
8
|
+
from ..models.event_subscription import EventSubscription, EventSubscriptionFilter
|
9
|
+
from ..http_client import HTTPClient
|
10
|
+
|
11
|
+
|
12
|
+
class ListEventSubscriptionsQuery(Query[List[EventSubscription]]):
|
13
|
+
"""Query to list event subscriptions."""
|
14
|
+
|
15
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[EventSubscriptionFilter] = None):
|
16
|
+
self.http_client = http_client
|
17
|
+
self.filter_params = filter_params or EventSubscriptionFilter()
|
18
|
+
|
19
|
+
def execute(self) -> List[EventSubscription]:
|
20
|
+
"""Execute the query to get event subscriptions."""
|
21
|
+
params = self.filter_params.to_params()
|
22
|
+
response = self.http_client.get("event-subscription", params=params)
|
23
|
+
|
24
|
+
entities = response.get("result", {}).get("entities", [])
|
25
|
+
|
26
|
+
# Use Pydantic parsing with proper field aliasing
|
27
|
+
subscriptions = []
|
28
|
+
for item in entities:
|
29
|
+
# Convert id to string as Pydantic expects
|
30
|
+
if "id" in item:
|
31
|
+
item["id"] = str(item["id"])
|
32
|
+
subscriptions.append(EventSubscription.model_validate(item))
|
33
|
+
|
34
|
+
return subscriptions
|
35
|
+
|
36
|
+
|
37
|
+
class GetEventSubscriptionQuery(Query[EventSubscription]):
|
38
|
+
"""Query to get a specific event subscription."""
|
39
|
+
|
40
|
+
def __init__(self, http_client: HTTPClient, subscription_id: str):
|
41
|
+
self.http_client = http_client
|
42
|
+
self.subscription_id = subscription_id
|
43
|
+
|
44
|
+
def execute(self) -> EventSubscription:
|
45
|
+
"""Execute the query to get a specific event subscription."""
|
46
|
+
response = self.http_client.get(f"event-subscription/{self.subscription_id}")
|
47
|
+
|
48
|
+
entity_data = response.get("result", {})
|
49
|
+
|
50
|
+
# Convert id to string as Pydantic expects
|
51
|
+
if "id" in entity_data:
|
52
|
+
entity_data["id"] = str(entity_data["id"])
|
53
|
+
|
54
|
+
# Use Pydantic parsing with proper field aliasing
|
55
55
|
return EventSubscription.model_validate(entity_data)
|
binalyze_air/queries/evidence.py
CHANGED
@@ -1,140 +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:
|
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
140
|
raise Exception(f"Evidence report not found for endpoint {self.endpoint_id}, task {self.task_id}: {error_str}")
|