binalyze-air-sdk 1.0.1__py3-none-any.whl → 1.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. binalyze_air/__init__.py +77 -77
  2. binalyze_air/apis/__init__.py +67 -27
  3. binalyze_air/apis/acquisitions.py +107 -0
  4. binalyze_air/apis/api_tokens.py +49 -0
  5. binalyze_air/apis/assets.py +161 -0
  6. binalyze_air/apis/audit_logs.py +26 -0
  7. binalyze_air/apis/{authentication.py → auth.py} +29 -27
  8. binalyze_air/apis/auto_asset_tags.py +79 -75
  9. binalyze_air/apis/backup.py +177 -0
  10. binalyze_air/apis/baseline.py +46 -0
  11. binalyze_air/apis/cases.py +225 -0
  12. binalyze_air/apis/cloud_forensics.py +116 -0
  13. binalyze_air/apis/event_subscription.py +96 -96
  14. binalyze_air/apis/evidence.py +249 -53
  15. binalyze_air/apis/interact.py +153 -36
  16. binalyze_air/apis/investigation_hub.py +234 -0
  17. binalyze_air/apis/license.py +104 -0
  18. binalyze_air/apis/logger.py +83 -0
  19. binalyze_air/apis/multipart_upload.py +201 -0
  20. binalyze_air/apis/notifications.py +115 -0
  21. binalyze_air/apis/organizations.py +267 -0
  22. binalyze_air/apis/params.py +44 -39
  23. binalyze_air/apis/policies.py +186 -0
  24. binalyze_air/apis/preset_filters.py +79 -0
  25. binalyze_air/apis/recent_activities.py +71 -0
  26. binalyze_air/apis/relay_server.py +104 -0
  27. binalyze_air/apis/settings.py +395 -27
  28. binalyze_air/apis/tasks.py +80 -0
  29. binalyze_air/apis/triage.py +197 -0
  30. binalyze_air/apis/user_management.py +183 -74
  31. binalyze_air/apis/webhook_executions.py +50 -0
  32. binalyze_air/apis/webhooks.py +322 -230
  33. binalyze_air/base.py +207 -133
  34. binalyze_air/client.py +217 -1337
  35. binalyze_air/commands/__init__.py +175 -145
  36. binalyze_air/commands/acquisitions.py +661 -387
  37. binalyze_air/commands/api_tokens.py +55 -0
  38. binalyze_air/commands/assets.py +324 -362
  39. binalyze_air/commands/{authentication.py → auth.py} +36 -36
  40. binalyze_air/commands/auto_asset_tags.py +230 -230
  41. binalyze_air/commands/backup.py +47 -0
  42. binalyze_air/commands/baseline.py +32 -396
  43. binalyze_air/commands/cases.py +609 -602
  44. binalyze_air/commands/cloud_forensics.py +88 -0
  45. binalyze_air/commands/event_subscription.py +101 -101
  46. binalyze_air/commands/evidences.py +918 -988
  47. binalyze_air/commands/interact.py +172 -58
  48. binalyze_air/commands/investigation_hub.py +315 -0
  49. binalyze_air/commands/license.py +183 -0
  50. binalyze_air/commands/logger.py +126 -0
  51. binalyze_air/commands/multipart_upload.py +363 -0
  52. binalyze_air/commands/notifications.py +45 -0
  53. binalyze_air/commands/organizations.py +200 -221
  54. binalyze_air/commands/policies.py +175 -203
  55. binalyze_air/commands/preset_filters.py +55 -0
  56. binalyze_air/commands/recent_activities.py +32 -0
  57. binalyze_air/commands/relay_server.py +144 -0
  58. binalyze_air/commands/settings.py +431 -29
  59. binalyze_air/commands/tasks.py +95 -56
  60. binalyze_air/commands/triage.py +224 -360
  61. binalyze_air/commands/user_management.py +351 -126
  62. binalyze_air/commands/webhook_executions.py +77 -0
  63. binalyze_air/config.py +244 -244
  64. binalyze_air/exceptions.py +49 -49
  65. binalyze_air/http_client.py +426 -305
  66. binalyze_air/models/__init__.py +287 -285
  67. binalyze_air/models/acquisitions.py +365 -250
  68. binalyze_air/models/api_tokens.py +73 -0
  69. binalyze_air/models/assets.py +438 -438
  70. binalyze_air/models/audit.py +247 -272
  71. binalyze_air/models/audit_logs.py +14 -0
  72. binalyze_air/models/{authentication.py → auth.py} +69 -69
  73. binalyze_air/models/auto_asset_tags.py +227 -116
  74. binalyze_air/models/backup.py +138 -0
  75. binalyze_air/models/baseline.py +231 -231
  76. binalyze_air/models/cases.py +275 -275
  77. binalyze_air/models/cloud_forensics.py +145 -0
  78. binalyze_air/models/event_subscription.py +170 -171
  79. binalyze_air/models/evidence.py +65 -65
  80. binalyze_air/models/evidences.py +367 -348
  81. binalyze_air/models/interact.py +266 -135
  82. binalyze_air/models/investigation_hub.py +265 -0
  83. binalyze_air/models/license.py +150 -0
  84. binalyze_air/models/logger.py +83 -0
  85. binalyze_air/models/multipart_upload.py +352 -0
  86. binalyze_air/models/notifications.py +138 -0
  87. binalyze_air/models/organizations.py +293 -293
  88. binalyze_air/models/params.py +153 -127
  89. binalyze_air/models/policies.py +260 -249
  90. binalyze_air/models/preset_filters.py +79 -0
  91. binalyze_air/models/recent_activities.py +70 -0
  92. binalyze_air/models/relay_server.py +121 -0
  93. binalyze_air/models/settings.py +538 -84
  94. binalyze_air/models/tasks.py +215 -149
  95. binalyze_air/models/triage.py +141 -142
  96. binalyze_air/models/user_management.py +200 -97
  97. binalyze_air/models/webhook_executions.py +33 -0
  98. binalyze_air/queries/__init__.py +121 -133
  99. binalyze_air/queries/acquisitions.py +155 -155
  100. binalyze_air/queries/api_tokens.py +46 -0
  101. binalyze_air/queries/assets.py +186 -105
  102. binalyze_air/queries/audit.py +400 -416
  103. binalyze_air/queries/{authentication.py → auth.py} +55 -55
  104. binalyze_air/queries/auto_asset_tags.py +59 -59
  105. binalyze_air/queries/backup.py +66 -0
  106. binalyze_air/queries/baseline.py +21 -185
  107. binalyze_air/queries/cases.py +292 -292
  108. binalyze_air/queries/cloud_forensics.py +137 -0
  109. binalyze_air/queries/event_subscription.py +54 -54
  110. binalyze_air/queries/evidence.py +139 -139
  111. binalyze_air/queries/evidences.py +279 -279
  112. binalyze_air/queries/interact.py +140 -28
  113. binalyze_air/queries/investigation_hub.py +329 -0
  114. binalyze_air/queries/license.py +85 -0
  115. binalyze_air/queries/logger.py +58 -0
  116. binalyze_air/queries/multipart_upload.py +180 -0
  117. binalyze_air/queries/notifications.py +71 -0
  118. binalyze_air/queries/organizations.py +222 -222
  119. binalyze_air/queries/params.py +154 -115
  120. binalyze_air/queries/policies.py +149 -149
  121. binalyze_air/queries/preset_filters.py +60 -0
  122. binalyze_air/queries/recent_activities.py +44 -0
  123. binalyze_air/queries/relay_server.py +42 -0
  124. binalyze_air/queries/settings.py +533 -20
  125. binalyze_air/queries/tasks.py +125 -81
  126. binalyze_air/queries/triage.py +230 -230
  127. binalyze_air/queries/user_management.py +193 -83
  128. binalyze_air/queries/webhook_executions.py +39 -0
  129. binalyze_air_sdk-1.0.3.dist-info/METADATA +752 -0
  130. binalyze_air_sdk-1.0.3.dist-info/RECORD +132 -0
  131. {binalyze_air_sdk-1.0.1.dist-info → binalyze_air_sdk-1.0.3.dist-info}/WHEEL +1 -1
  132. binalyze_air/apis/endpoints.py +0 -22
  133. binalyze_air/apis/evidences.py +0 -216
  134. binalyze_air/apis/users.py +0 -68
  135. binalyze_air/commands/users.py +0 -101
  136. binalyze_air/models/endpoints.py +0 -76
  137. binalyze_air/models/users.py +0 -82
  138. binalyze_air/queries/endpoints.py +0 -25
  139. binalyze_air/queries/users.py +0 -69
  140. binalyze_air_sdk-1.0.1.dist-info/METADATA +0 -635
  141. binalyze_air_sdk-1.0.1.dist-info/RECORD +0 -82
  142. {binalyze_air_sdk-1.0.1.dist-info → binalyze_air_sdk-1.0.3.dist-info}/top_level.txt +0 -0
@@ -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
- from ..http_client import HTTPClient
13
-
14
-
15
- class GetAcquisitionArtifactsQuery(Query[List[AcquisitionArtifact]]):
16
- """Query to get acquisition artifacts."""
17
-
18
- def __init__(self, http_client: HTTPClient):
19
- self.http_client = http_client
20
-
21
- def execute(self) -> List[AcquisitionArtifact]:
22
- """Execute the query to get acquisition artifacts."""
23
- response: Dict[str, Any] = self.http_client.get("params/acquisition/artifacts")
24
-
25
- # Parse using Pydantic models
26
- artifacts_response = AcquisitionArtifactsResponse.model_validate(response)
27
-
28
- # Flatten the structure into a single list
29
- all_artifacts = []
30
-
31
- # Process all platforms
32
- for platform_name, groups in [
33
- ("windows", artifacts_response.windows),
34
- ("linux", artifacts_response.linux),
35
- ("macos", artifacts_response.macos),
36
- ("aix", artifacts_response.aix)
37
- ]:
38
- for group in groups:
39
- for artifact in group.items:
40
- artifact.group = group.group
41
- artifact.platform = platform_name
42
- all_artifacts.append(artifact)
43
-
44
- return all_artifacts
45
-
46
-
47
- class GetEDiscoveryPatternsQuery(Query[List[EDiscoveryPattern]]):
48
- """Query to get e-discovery patterns."""
49
-
50
- def __init__(self, http_client: HTTPClient):
51
- self.http_client = http_client
52
-
53
- def execute(self) -> List[EDiscoveryPattern]:
54
- """Execute the query to get e-discovery patterns."""
55
- response = self.http_client.get("params/acquisition/e-discovery-patterns")
56
-
57
- # Parse using Pydantic models
58
- categories = [EDiscoveryCategory.model_validate(item) for item in response]
59
-
60
- # Flatten the structure into a single list
61
- all_patterns = []
62
- for category in categories:
63
- for pattern in category.applications:
64
- pattern.category = category.category
65
- all_patterns.append(pattern)
66
-
67
- return all_patterns
68
-
69
-
70
- class GetAcquisitionEvidencesQuery(Query[List[AcquisitionEvidence]]):
71
- """Query to get acquisition evidences."""
72
-
73
- def __init__(self, http_client: HTTPClient):
74
- self.http_client = http_client
75
-
76
- def execute(self) -> List[AcquisitionEvidence]:
77
- """Execute the query to get acquisition evidences."""
78
- response: Dict[str, Any] = self.http_client.get("params/acquisition/evidences")
79
-
80
- # Parse using Pydantic models
81
- evidences_response = AcquisitionEvidencesResponse.model_validate(response)
82
-
83
- # Flatten the structure into a single list
84
- all_evidences = []
85
-
86
- # Process all platforms
87
- for platform_name, groups in [
88
- ("windows", evidences_response.windows),
89
- ("linux", evidences_response.linux),
90
- ("macos", evidences_response.macos),
91
- ("aix", evidences_response.aix)
92
- ]:
93
- for group in groups:
94
- for evidence in group.items:
95
- evidence.group = group.group
96
- evidence.platform = platform_name
97
- all_evidences.append(evidence)
98
-
99
- return all_evidences
100
-
101
-
102
- class GetDroneAnalyzersQuery(Query[List[DroneAnalyzer]]):
103
- """Query to get drone analyzers."""
104
-
105
- def __init__(self, http_client: HTTPClient):
106
- self.http_client = http_client
107
-
108
- def execute(self) -> List[DroneAnalyzer]:
109
- """Execute the query to get drone analyzers."""
110
- response = self.http_client.get("params/drone/analyzers")
111
-
112
- # Parse using Pydantic models with automatic field mapping
113
- analyzers = [DroneAnalyzer.model_validate(item) for item in response]
114
-
115
- return analyzers
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())
@@ -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