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/params.py
CHANGED
@@ -1,115 +1,154 @@
|
|
1
|
-
"""
|
2
|
-
Params queries for the Binalyze AIR SDK.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from typing import List, Dict, Any, Union
|
6
|
-
|
7
|
-
from ..base import Query
|
8
|
-
from ..models.params import (
|
9
|
-
AcquisitionArtifact, EDiscoveryPattern, AcquisitionEvidence, DroneAnalyzer,
|
10
|
-
AcquisitionArtifactsResponse, EDiscoveryCategory, AcquisitionEvidencesResponse
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
("
|
35
|
-
("
|
36
|
-
("
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
artifact.
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
]
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
1
|
+
"""
|
2
|
+
Params queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List, Dict, Any, Union
|
6
|
+
|
7
|
+
from ..base import Query
|
8
|
+
from ..models.params import (
|
9
|
+
AcquisitionArtifact, EDiscoveryPattern, AcquisitionEvidence, DroneAnalyzer,
|
10
|
+
AcquisitionArtifactsResponse, EDiscoveryCategory, AcquisitionEvidencesResponse,
|
11
|
+
MitreAttackTactic, MitreAttackTechnique, MitreAttackResponse
|
12
|
+
)
|
13
|
+
from ..http_client import HTTPClient
|
14
|
+
|
15
|
+
|
16
|
+
class GetAcquisitionArtifactsQuery(Query[List[AcquisitionArtifact]]):
|
17
|
+
"""Query to get acquisition artifacts."""
|
18
|
+
|
19
|
+
def __init__(self, http_client: HTTPClient):
|
20
|
+
self.http_client = http_client
|
21
|
+
|
22
|
+
def execute(self) -> List[AcquisitionArtifact]:
|
23
|
+
"""Execute the query to get acquisition artifacts."""
|
24
|
+
response: Dict[str, Any] = self.http_client.get("params/acquisition/artifacts")
|
25
|
+
|
26
|
+
# Parse using Pydantic models
|
27
|
+
artifacts_response = AcquisitionArtifactsResponse.model_validate(response)
|
28
|
+
|
29
|
+
# Flatten the structure into a single list
|
30
|
+
all_artifacts = []
|
31
|
+
|
32
|
+
# Process all platforms
|
33
|
+
for platform_name, groups in [
|
34
|
+
("windows", artifacts_response.windows),
|
35
|
+
("linux", artifacts_response.linux),
|
36
|
+
("macos", artifacts_response.macos),
|
37
|
+
("aix", artifacts_response.aix)
|
38
|
+
]:
|
39
|
+
for group in groups:
|
40
|
+
for artifact in group.items:
|
41
|
+
artifact.group = group.group
|
42
|
+
artifact.platform = platform_name
|
43
|
+
all_artifacts.append(artifact)
|
44
|
+
|
45
|
+
return all_artifacts
|
46
|
+
|
47
|
+
|
48
|
+
class GetEDiscoveryPatternsQuery(Query[List[EDiscoveryPattern]]):
|
49
|
+
"""Query to get e-discovery patterns."""
|
50
|
+
|
51
|
+
def __init__(self, http_client: HTTPClient):
|
52
|
+
self.http_client = http_client
|
53
|
+
|
54
|
+
def execute(self) -> List[EDiscoveryPattern]:
|
55
|
+
"""Execute the query to get e-discovery patterns."""
|
56
|
+
response = self.http_client.get("params/acquisition/e-discovery-patterns")
|
57
|
+
|
58
|
+
# Extract the result array from the API response
|
59
|
+
if isinstance(response, dict) and "result" in response:
|
60
|
+
categories_data = response["result"]
|
61
|
+
else:
|
62
|
+
# Fallback for direct array response
|
63
|
+
categories_data = response
|
64
|
+
|
65
|
+
# Parse using Pydantic models
|
66
|
+
categories = [EDiscoveryCategory.model_validate(item) for item in categories_data]
|
67
|
+
|
68
|
+
# Flatten the structure into a single list
|
69
|
+
all_patterns = []
|
70
|
+
for category in categories:
|
71
|
+
for pattern in category.applications:
|
72
|
+
pattern.category = category.category
|
73
|
+
all_patterns.append(pattern)
|
74
|
+
|
75
|
+
return all_patterns
|
76
|
+
|
77
|
+
|
78
|
+
class GetAcquisitionEvidencesQuery(Query[List[AcquisitionEvidence]]):
|
79
|
+
"""Query to get acquisition evidences."""
|
80
|
+
|
81
|
+
def __init__(self, http_client: HTTPClient):
|
82
|
+
self.http_client = http_client
|
83
|
+
|
84
|
+
def execute(self) -> List[AcquisitionEvidence]:
|
85
|
+
"""Execute the query to get acquisition evidences."""
|
86
|
+
response: Dict[str, Any] = self.http_client.get("params/acquisition/evidences")
|
87
|
+
|
88
|
+
# Parse using Pydantic models
|
89
|
+
evidences_response = AcquisitionEvidencesResponse.model_validate(response)
|
90
|
+
|
91
|
+
# Flatten the structure into a single list
|
92
|
+
all_evidences = []
|
93
|
+
|
94
|
+
# Process all platforms
|
95
|
+
for platform_name, groups in [
|
96
|
+
("windows", evidences_response.windows),
|
97
|
+
("linux", evidences_response.linux),
|
98
|
+
("macos", evidences_response.macos),
|
99
|
+
("aix", evidences_response.aix)
|
100
|
+
]:
|
101
|
+
for group in groups:
|
102
|
+
for evidence in group.items:
|
103
|
+
evidence.group = group.group
|
104
|
+
evidence.platform = platform_name
|
105
|
+
all_evidences.append(evidence)
|
106
|
+
|
107
|
+
return all_evidences
|
108
|
+
|
109
|
+
|
110
|
+
class GetDroneAnalyzersQuery(Query[List[DroneAnalyzer]]):
|
111
|
+
"""Query to get drone analyzers."""
|
112
|
+
|
113
|
+
def __init__(self, http_client: HTTPClient):
|
114
|
+
self.http_client = http_client
|
115
|
+
|
116
|
+
def execute(self) -> List[DroneAnalyzer]:
|
117
|
+
"""Execute the query to get drone analyzers."""
|
118
|
+
response = self.http_client.get("params/drone/analyzers")
|
119
|
+
|
120
|
+
# Extract the result array from the API response
|
121
|
+
if isinstance(response, dict) and "result" in response:
|
122
|
+
analyzers_data = response["result"]
|
123
|
+
else:
|
124
|
+
# Fallback for direct array response
|
125
|
+
analyzers_data = response
|
126
|
+
|
127
|
+
# Parse using Pydantic models with automatic field mapping
|
128
|
+
analyzers = [DroneAnalyzer.model_validate(item) for item in analyzers_data]
|
129
|
+
|
130
|
+
return analyzers
|
131
|
+
|
132
|
+
|
133
|
+
class GetMitreAttackTacticsQuery(Query[List[MitreAttackTactic]]):
|
134
|
+
"""Query to get MITRE ATT&CK tactics."""
|
135
|
+
|
136
|
+
def __init__(self, http_client: HTTPClient):
|
137
|
+
self.http_client = http_client
|
138
|
+
|
139
|
+
def execute(self) -> List[MitreAttackTactic]:
|
140
|
+
"""Execute the query to get MITRE ATT&CK tactics."""
|
141
|
+
response = self.http_client.get("params/mitre-attack/tactics")
|
142
|
+
|
143
|
+
# Extract the result from the API response
|
144
|
+
if isinstance(response, dict) and "result" in response:
|
145
|
+
mitre_data = response["result"]
|
146
|
+
else:
|
147
|
+
# Fallback for direct response
|
148
|
+
mitre_data = response
|
149
|
+
|
150
|
+
# Parse using Pydantic models
|
151
|
+
mitre_response = MitreAttackResponse.model_validate(mitre_data)
|
152
|
+
|
153
|
+
# Return only the tactics as a list
|
154
|
+
return list(mitre_response.tactics.values())
|
binalyze_air/queries/policies.py
CHANGED
@@ -1,150 +1,150 @@
|
|
1
|
-
"""
|
2
|
-
Policy-related queries for the Binalyze AIR SDK.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from typing import List, Optional
|
6
|
-
|
7
|
-
from ..base import Query
|
8
|
-
from ..models.policies import (
|
9
|
-
Policy, PolicyFilter, PolicyAssignment, PolicyExecution,
|
10
|
-
PolicyRule, PolicyCondition, PolicyAction,
|
11
|
-
PoliciesPaginatedResponse, PolicyMatchStats
|
12
|
-
)
|
13
|
-
from ..http_client import HTTPClient
|
14
|
-
|
15
|
-
|
16
|
-
class ListPoliciesQuery(Query[PoliciesPaginatedResponse]):
|
17
|
-
"""Query to list policies with optional filtering."""
|
18
|
-
|
19
|
-
def __init__(self, http_client: HTTPClient, filter_params: Optional[PolicyFilter] = None, organization_ids: Optional[List[int]] = None):
|
20
|
-
self.http_client = http_client
|
21
|
-
self.filter_params = filter_params or PolicyFilter()
|
22
|
-
|
23
|
-
# Fix API-001: Ensure organizationIds are always provided to prevent 500 error
|
24
|
-
if organization_ids is None or len(organization_ids) == 0:
|
25
|
-
self.organization_ids = [0] # Default to organization 0
|
26
|
-
else:
|
27
|
-
self.organization_ids = organization_ids
|
28
|
-
|
29
|
-
def execute(self) -> PoliciesPaginatedResponse:
|
30
|
-
"""Execute the query to list policies."""
|
31
|
-
# Validate organization_ids before making API call
|
32
|
-
if not self.organization_ids or len(self.organization_ids) == 0:
|
33
|
-
from ..exceptions import ValidationError
|
34
|
-
raise ValidationError(
|
35
|
-
"organizationIds parameter is required for listing policies. "
|
36
|
-
"Please provide at least one organization ID."
|
37
|
-
)
|
38
|
-
|
39
|
-
params = self.filter_params.to_params()
|
40
|
-
|
41
|
-
# Add organization IDs
|
42
|
-
params["filter[organizationIds]"] = ",".join(map(str, self.organization_ids))
|
43
|
-
|
44
|
-
response = self.http_client.get("policies", params=params)
|
45
|
-
|
46
|
-
# Parse using Pydantic models with automatic field mapping
|
47
|
-
return PoliciesPaginatedResponse.model_validate(response.get("result", {}))
|
48
|
-
|
49
|
-
|
50
|
-
class GetPolicyQuery(Query[Policy]):
|
51
|
-
"""Query to get a specific policy by ID."""
|
52
|
-
|
53
|
-
def __init__(self, http_client: HTTPClient, policy_id: str):
|
54
|
-
self.http_client = http_client
|
55
|
-
self.policy_id = policy_id
|
56
|
-
|
57
|
-
def execute(self) -> Policy:
|
58
|
-
"""Execute the query to get policy details."""
|
59
|
-
response = self.http_client.get(f"policies/{self.policy_id}")
|
60
|
-
|
61
|
-
# Parse using Pydantic models with automatic field mapping
|
62
|
-
return Policy.model_validate(response.get("result", {}))
|
63
|
-
|
64
|
-
|
65
|
-
class GetPolicyAssignmentsQuery(Query[List[PolicyAssignment]]):
|
66
|
-
"""Query to get policy assignments."""
|
67
|
-
|
68
|
-
def __init__(self, http_client: HTTPClient, policy_id: str):
|
69
|
-
self.http_client = http_client
|
70
|
-
self.policy_id = policy_id
|
71
|
-
|
72
|
-
def execute(self) -> List[PolicyAssignment]:
|
73
|
-
"""Execute the query to get policy assignments."""
|
74
|
-
response = self.http_client.get(f"policies/{self.policy_id}/assignments")
|
75
|
-
|
76
|
-
entities = response.get("result", {}).get("entities", [])
|
77
|
-
|
78
|
-
assignments = []
|
79
|
-
for entity_data in entities:
|
80
|
-
mapped_data = {
|
81
|
-
"id": entity_data.get("_id"),
|
82
|
-
"policy_id": entity_data.get("policyId"),
|
83
|
-
"endpoint_id": entity_data.get("endpointId"),
|
84
|
-
"assigned_at": entity_data.get("assignedAt"),
|
85
|
-
"assigned_by": entity_data.get("assignedBy"),
|
86
|
-
"status": entity_data.get("status", "active"),
|
87
|
-
}
|
88
|
-
|
89
|
-
# Remove None values
|
90
|
-
mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
|
91
|
-
|
92
|
-
assignments.append(PolicyAssignment(**mapped_data))
|
93
|
-
|
94
|
-
return assignments
|
95
|
-
|
96
|
-
|
97
|
-
class GetPolicyExecutionsQuery(Query[List[PolicyExecution]]):
|
98
|
-
"""Query to get policy execution history."""
|
99
|
-
|
100
|
-
def __init__(self, http_client: HTTPClient, policy_id: str, limit: Optional[int] = None):
|
101
|
-
self.http_client = http_client
|
102
|
-
self.policy_id = policy_id
|
103
|
-
self.limit = limit
|
104
|
-
|
105
|
-
def execute(self) -> List[PolicyExecution]:
|
106
|
-
"""Execute the query to get policy executions."""
|
107
|
-
params = {}
|
108
|
-
if self.limit:
|
109
|
-
params["limit"] = str(self.limit)
|
110
|
-
|
111
|
-
response = self.http_client.get(f"policies/{self.policy_id}/executions", params=params)
|
112
|
-
|
113
|
-
entities = response.get("result", {}).get("entities", [])
|
114
|
-
|
115
|
-
executions = []
|
116
|
-
for entity_data in entities:
|
117
|
-
mapped_data = {
|
118
|
-
"id": entity_data.get("_id"),
|
119
|
-
"policy_id": entity_data.get("policyId"),
|
120
|
-
"endpoint_id": entity_data.get("endpointId"),
|
121
|
-
"executed_at": entity_data.get("executedAt"),
|
122
|
-
"status": entity_data.get("status"),
|
123
|
-
"result": entity_data.get("result", {}),
|
124
|
-
"errors": entity_data.get("errors", []),
|
125
|
-
"duration": entity_data.get("duration"),
|
126
|
-
}
|
127
|
-
|
128
|
-
# Remove None values
|
129
|
-
mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
|
130
|
-
|
131
|
-
executions.append(PolicyExecution(**mapped_data))
|
132
|
-
|
133
|
-
return executions
|
134
|
-
|
135
|
-
|
136
|
-
class GetPolicyMatchStatsQuery(Query[PolicyMatchStats]):
|
137
|
-
"""Query to get policy match statistics."""
|
138
|
-
|
139
|
-
def __init__(self, http_client: HTTPClient, filter_params: Optional[PolicyFilter] = None):
|
140
|
-
self.http_client = http_client
|
141
|
-
self.filter_params = filter_params or PolicyFilter()
|
142
|
-
|
143
|
-
def execute(self) -> PolicyMatchStats:
|
144
|
-
"""Execute the query to get policy match statistics."""
|
145
|
-
params = self.filter_params.to_params()
|
146
|
-
|
147
|
-
response = self.http_client.get("policies/match-stats", params=params)
|
148
|
-
|
149
|
-
# Parse using Pydantic models with automatic field mapping
|
1
|
+
"""
|
2
|
+
Policy-related queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List, Optional
|
6
|
+
|
7
|
+
from ..base import Query
|
8
|
+
from ..models.policies import (
|
9
|
+
Policy, PolicyFilter, PolicyAssignment, PolicyExecution,
|
10
|
+
PolicyRule, PolicyCondition, PolicyAction,
|
11
|
+
PoliciesPaginatedResponse, PolicyMatchStats
|
12
|
+
)
|
13
|
+
from ..http_client import HTTPClient
|
14
|
+
|
15
|
+
|
16
|
+
class ListPoliciesQuery(Query[PoliciesPaginatedResponse]):
|
17
|
+
"""Query to list policies with optional filtering."""
|
18
|
+
|
19
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[PolicyFilter] = None, organization_ids: Optional[List[int]] = None):
|
20
|
+
self.http_client = http_client
|
21
|
+
self.filter_params = filter_params or PolicyFilter()
|
22
|
+
|
23
|
+
# Fix API-001: Ensure organizationIds are always provided to prevent 500 error
|
24
|
+
if organization_ids is None or len(organization_ids) == 0:
|
25
|
+
self.organization_ids = [0] # Default to organization 0
|
26
|
+
else:
|
27
|
+
self.organization_ids = organization_ids
|
28
|
+
|
29
|
+
def execute(self) -> PoliciesPaginatedResponse:
|
30
|
+
"""Execute the query to list policies."""
|
31
|
+
# Validate organization_ids before making API call
|
32
|
+
if not self.organization_ids or len(self.organization_ids) == 0:
|
33
|
+
from ..exceptions import ValidationError
|
34
|
+
raise ValidationError(
|
35
|
+
"organizationIds parameter is required for listing policies. "
|
36
|
+
"Please provide at least one organization ID."
|
37
|
+
)
|
38
|
+
|
39
|
+
params = self.filter_params.to_params()
|
40
|
+
|
41
|
+
# Add organization IDs
|
42
|
+
params["filter[organizationIds]"] = ",".join(map(str, self.organization_ids))
|
43
|
+
|
44
|
+
response = self.http_client.get("policies", params=params)
|
45
|
+
|
46
|
+
# Parse using Pydantic models with automatic field mapping
|
47
|
+
return PoliciesPaginatedResponse.model_validate(response.get("result", {}))
|
48
|
+
|
49
|
+
|
50
|
+
class GetPolicyQuery(Query[Policy]):
|
51
|
+
"""Query to get a specific policy by ID."""
|
52
|
+
|
53
|
+
def __init__(self, http_client: HTTPClient, policy_id: str):
|
54
|
+
self.http_client = http_client
|
55
|
+
self.policy_id = policy_id
|
56
|
+
|
57
|
+
def execute(self) -> Policy:
|
58
|
+
"""Execute the query to get policy details."""
|
59
|
+
response = self.http_client.get(f"policies/{self.policy_id}")
|
60
|
+
|
61
|
+
# Parse using Pydantic models with automatic field mapping
|
62
|
+
return Policy.model_validate(response.get("result", {}))
|
63
|
+
|
64
|
+
|
65
|
+
class GetPolicyAssignmentsQuery(Query[List[PolicyAssignment]]):
|
66
|
+
"""Query to get policy assignments."""
|
67
|
+
|
68
|
+
def __init__(self, http_client: HTTPClient, policy_id: str):
|
69
|
+
self.http_client = http_client
|
70
|
+
self.policy_id = policy_id
|
71
|
+
|
72
|
+
def execute(self) -> List[PolicyAssignment]:
|
73
|
+
"""Execute the query to get policy assignments."""
|
74
|
+
response = self.http_client.get(f"policies/{self.policy_id}/assignments")
|
75
|
+
|
76
|
+
entities = response.get("result", {}).get("entities", [])
|
77
|
+
|
78
|
+
assignments = []
|
79
|
+
for entity_data in entities:
|
80
|
+
mapped_data = {
|
81
|
+
"id": entity_data.get("_id"),
|
82
|
+
"policy_id": entity_data.get("policyId"),
|
83
|
+
"endpoint_id": entity_data.get("endpointId"),
|
84
|
+
"assigned_at": entity_data.get("assignedAt"),
|
85
|
+
"assigned_by": entity_data.get("assignedBy"),
|
86
|
+
"status": entity_data.get("status", "active"),
|
87
|
+
}
|
88
|
+
|
89
|
+
# Remove None values
|
90
|
+
mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
|
91
|
+
|
92
|
+
assignments.append(PolicyAssignment(**mapped_data))
|
93
|
+
|
94
|
+
return assignments
|
95
|
+
|
96
|
+
|
97
|
+
class GetPolicyExecutionsQuery(Query[List[PolicyExecution]]):
|
98
|
+
"""Query to get policy execution history."""
|
99
|
+
|
100
|
+
def __init__(self, http_client: HTTPClient, policy_id: str, limit: Optional[int] = None):
|
101
|
+
self.http_client = http_client
|
102
|
+
self.policy_id = policy_id
|
103
|
+
self.limit = limit
|
104
|
+
|
105
|
+
def execute(self) -> List[PolicyExecution]:
|
106
|
+
"""Execute the query to get policy executions."""
|
107
|
+
params = {}
|
108
|
+
if self.limit:
|
109
|
+
params["limit"] = str(self.limit)
|
110
|
+
|
111
|
+
response = self.http_client.get(f"policies/{self.policy_id}/executions", params=params)
|
112
|
+
|
113
|
+
entities = response.get("result", {}).get("entities", [])
|
114
|
+
|
115
|
+
executions = []
|
116
|
+
for entity_data in entities:
|
117
|
+
mapped_data = {
|
118
|
+
"id": entity_data.get("_id"),
|
119
|
+
"policy_id": entity_data.get("policyId"),
|
120
|
+
"endpoint_id": entity_data.get("endpointId"),
|
121
|
+
"executed_at": entity_data.get("executedAt"),
|
122
|
+
"status": entity_data.get("status"),
|
123
|
+
"result": entity_data.get("result", {}),
|
124
|
+
"errors": entity_data.get("errors", []),
|
125
|
+
"duration": entity_data.get("duration"),
|
126
|
+
}
|
127
|
+
|
128
|
+
# Remove None values
|
129
|
+
mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
|
130
|
+
|
131
|
+
executions.append(PolicyExecution(**mapped_data))
|
132
|
+
|
133
|
+
return executions
|
134
|
+
|
135
|
+
|
136
|
+
class GetPolicyMatchStatsQuery(Query[PolicyMatchStats]):
|
137
|
+
"""Query to get policy match statistics."""
|
138
|
+
|
139
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[PolicyFilter] = None):
|
140
|
+
self.http_client = http_client
|
141
|
+
self.filter_params = filter_params or PolicyFilter()
|
142
|
+
|
143
|
+
def execute(self) -> PolicyMatchStats:
|
144
|
+
"""Execute the query to get policy match statistics."""
|
145
|
+
params = self.filter_params.to_params()
|
146
|
+
|
147
|
+
response = self.http_client.get("policies/match-stats", params=params)
|
148
|
+
|
149
|
+
# Parse using Pydantic models with automatic field mapping
|
150
150
|
return PolicyMatchStats.model_validate(response.get("result", {}))
|
@@ -0,0 +1,60 @@
|
|
1
|
+
"""
|
2
|
+
Preset Filters queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Optional
|
6
|
+
|
7
|
+
from ..base import Query
|
8
|
+
from ..models.preset_filters import PresetFilter, PresetFiltersList, PresetFiltersFilter
|
9
|
+
from ..http_client import HTTPClient
|
10
|
+
|
11
|
+
|
12
|
+
class GetPresetFiltersQuery(Query[PresetFiltersList]):
|
13
|
+
"""Query to get preset filters."""
|
14
|
+
|
15
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[PresetFiltersFilter] = None):
|
16
|
+
self.http_client = http_client
|
17
|
+
self.filter_params = filter_params or PresetFiltersFilter()
|
18
|
+
|
19
|
+
def execute(self) -> PresetFiltersList:
|
20
|
+
"""Execute the query to get preset filters."""
|
21
|
+
params = {}
|
22
|
+
|
23
|
+
# Add filter parameters
|
24
|
+
if self.filter_params.organization_id is not None:
|
25
|
+
params['filter[organizationId]'] = str(self.filter_params.organization_id)
|
26
|
+
if self.filter_params.type:
|
27
|
+
params['filter[type]'] = self.filter_params.type
|
28
|
+
if self.filter_params.name:
|
29
|
+
params['filter[name]'] = self.filter_params.name
|
30
|
+
if self.filter_params.created_by:
|
31
|
+
params['filter[createdBy]'] = self.filter_params.created_by
|
32
|
+
|
33
|
+
# Add pagination parameters
|
34
|
+
if self.filter_params.page_size:
|
35
|
+
params['pageSize'] = str(self.filter_params.page_size)
|
36
|
+
if self.filter_params.page_number:
|
37
|
+
params['pageNumber'] = str(self.filter_params.page_number)
|
38
|
+
if self.filter_params.sort_by:
|
39
|
+
params['sortBy'] = self.filter_params.sort_by
|
40
|
+
if self.filter_params.sort_type:
|
41
|
+
params['sortType'] = self.filter_params.sort_type
|
42
|
+
|
43
|
+
response = self.http_client.get('/preset-filters', params=params)
|
44
|
+
return PresetFiltersList(**response['result'])
|
45
|
+
|
46
|
+
|
47
|
+
class GetPresetFilterByIdQuery(Query[Optional[PresetFilter]]):
|
48
|
+
"""Query to get a specific preset filter by ID."""
|
49
|
+
|
50
|
+
def __init__(self, http_client: HTTPClient, filter_id: str):
|
51
|
+
self.http_client = http_client
|
52
|
+
self.filter_id = filter_id
|
53
|
+
|
54
|
+
def execute(self) -> Optional[PresetFilter]:
|
55
|
+
"""Execute the query to get a preset filter by ID."""
|
56
|
+
try:
|
57
|
+
response = self.http_client.get(f'/preset-filters/{self.filter_id}')
|
58
|
+
return PresetFilter(**response['result'])
|
59
|
+
except Exception:
|
60
|
+
return None
|