regscale-cli 6.27.3.0__py3-none-any.whl → 6.28.1.0__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.
Potentially problematic release.
This version of regscale-cli might be problematic. Click here for more details.
- regscale/_version.py +1 -1
- regscale/core/app/utils/app_utils.py +11 -2
- regscale/dev/cli.py +26 -0
- regscale/dev/version.py +72 -0
- regscale/integrations/commercial/__init__.py +15 -1
- regscale/integrations/commercial/amazon/amazon/__init__.py +0 -0
- regscale/integrations/commercial/amazon/amazon/common.py +204 -0
- regscale/integrations/commercial/amazon/common.py +48 -58
- regscale/integrations/commercial/aws/audit_manager_compliance.py +2671 -0
- regscale/integrations/commercial/aws/cli.py +3093 -55
- regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
- regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
- regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
- regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
- regscale/integrations/commercial/aws/config_compliance.py +914 -0
- regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
- regscale/integrations/commercial/aws/evidence_generator.py +283 -0
- regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
- regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
- regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
- regscale/integrations/commercial/aws/iam_evidence.py +574 -0
- regscale/integrations/commercial/aws/inventory/__init__.py +223 -22
- regscale/integrations/commercial/aws/inventory/base.py +107 -5
- regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
- regscale/integrations/commercial/aws/inventory/resources/compute.py +66 -9
- regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
- regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
- regscale/integrations/commercial/aws/inventory/resources/database.py +106 -31
- regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
- regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
- regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
- regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
- regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
- regscale/integrations/commercial/aws/inventory/resources/networking.py +103 -67
- regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
- regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
- regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
- regscale/integrations/commercial/aws/inventory/resources/storage.py +53 -29
- regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
- regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
- regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
- regscale/integrations/commercial/aws/kms_evidence.py +879 -0
- regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
- regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
- regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
- regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
- regscale/integrations/commercial/aws/org_evidence.py +666 -0
- regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
- regscale/integrations/commercial/aws/s3_evidence.py +632 -0
- regscale/integrations/commercial/aws/scanner.py +851 -206
- regscale/integrations/commercial/aws/security_hub.py +319 -0
- regscale/integrations/commercial/aws/session_manager.py +282 -0
- regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
- regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
- regscale/integrations/commercial/synqly/ticketing.py +27 -0
- regscale/integrations/compliance_integration.py +308 -38
- regscale/integrations/due_date_handler.py +3 -0
- regscale/integrations/scanner_integration.py +399 -84
- regscale/models/integration_models/cisa_kev_data.json +65 -5
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +17 -9
- regscale/models/regscale_models/assessment.py +2 -1
- regscale/models/regscale_models/control_objective.py +74 -5
- regscale/models/regscale_models/file.py +2 -0
- regscale/models/regscale_models/issue.py +2 -5
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/METADATA +1 -1
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/RECORD +113 -34
- tests/regscale/integrations/commercial/aws/__init__.py +0 -0
- tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
- tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
- tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
- tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
- tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
- tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
- tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
- tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
- tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
- tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
- tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
- tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
- tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
- tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
- tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
- tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
- tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
- tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
- tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
- tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
- tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
- tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
- tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
- tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
- tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
- tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
- tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
- tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
- tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
- tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
- tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
- tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
- tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
- tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
- tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
- tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
- tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
- tests/regscale/integrations/commercial/test_aws.py +55 -56
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"""AWS CloudTrail resource collection."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
from botocore.exceptions import ClientError
|
|
7
|
+
|
|
8
|
+
from regscale.integrations.commercial.aws.inventory.base import BaseCollector
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger("regscale")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CloudTrailCollector(BaseCollector):
|
|
14
|
+
"""Collector for AWS CloudTrail resources."""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self, session: Any, region: str, account_id: Optional[str] = None, tags: Optional[Dict[str, str]] = None
|
|
18
|
+
):
|
|
19
|
+
"""
|
|
20
|
+
Initialize CloudTrail collector.
|
|
21
|
+
|
|
22
|
+
:param session: AWS session to use for API calls
|
|
23
|
+
:param str region: AWS region to collect from
|
|
24
|
+
:param str account_id: Optional AWS account ID to filter resources
|
|
25
|
+
:param dict tags: Optional tags to filter resources (key-value pairs)
|
|
26
|
+
"""
|
|
27
|
+
super().__init__(session, region)
|
|
28
|
+
self.account_id = account_id
|
|
29
|
+
self.tags = tags or {}
|
|
30
|
+
|
|
31
|
+
def collect(self) -> Dict[str, Any]:
|
|
32
|
+
"""
|
|
33
|
+
Collect CloudTrail resources.
|
|
34
|
+
|
|
35
|
+
:return: Dictionary containing CloudTrail trails and their status
|
|
36
|
+
:rtype: Dict[str, Any]
|
|
37
|
+
"""
|
|
38
|
+
result = {"Trails": [], "TrailStatuses": {}}
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
client = self._get_client("cloudtrail")
|
|
42
|
+
|
|
43
|
+
# List all trails
|
|
44
|
+
trails = self._list_trails(client)
|
|
45
|
+
|
|
46
|
+
# Get detailed information for each trail
|
|
47
|
+
for trail in trails:
|
|
48
|
+
trail_arn = trail.get("TrailARN", "")
|
|
49
|
+
|
|
50
|
+
# Filter by account ID if specified
|
|
51
|
+
if self.account_id and not self._matches_account_id(trail_arn):
|
|
52
|
+
logger.debug(f"Skipping trail {trail_arn} - does not match account ID {self.account_id}")
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
# Get detailed trail information
|
|
56
|
+
trail_details = self._describe_trail(client, trail_arn)
|
|
57
|
+
if trail_details:
|
|
58
|
+
# Get tags for filtering
|
|
59
|
+
trail_tags = self._get_trail_tags(client, trail_arn)
|
|
60
|
+
|
|
61
|
+
# Filter by tags if specified
|
|
62
|
+
if self.tags and not self._matches_tags(trail_tags):
|
|
63
|
+
logger.debug(f"Skipping trail {trail_arn} - does not match tag filters")
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
trail_details["Tags"] = trail_tags
|
|
67
|
+
# Get trail status
|
|
68
|
+
trail_status = self._get_trail_status(client, trail_arn)
|
|
69
|
+
trail_details["Status"] = trail_status
|
|
70
|
+
|
|
71
|
+
# Get event selectors
|
|
72
|
+
event_selectors = self._get_event_selectors(client, trail_arn)
|
|
73
|
+
trail_details["EventSelectors"] = event_selectors
|
|
74
|
+
|
|
75
|
+
# Add region information
|
|
76
|
+
trail_details["Region"] = self.region
|
|
77
|
+
|
|
78
|
+
result["Trails"].append(trail_details)
|
|
79
|
+
result["TrailStatuses"][trail_arn] = trail_status
|
|
80
|
+
|
|
81
|
+
logger.info(f"Collected {len(result['Trails'])} CloudTrail trail(s) from {self.region}")
|
|
82
|
+
|
|
83
|
+
except ClientError as e:
|
|
84
|
+
self._handle_error(e, "CloudTrail trails")
|
|
85
|
+
except Exception as e:
|
|
86
|
+
logger.error(f"Unexpected error collecting CloudTrail trails: {e}", exc_info=True)
|
|
87
|
+
|
|
88
|
+
return result
|
|
89
|
+
|
|
90
|
+
def _list_trails(self, client: Any) -> List[Dict[str, Any]]:
|
|
91
|
+
"""
|
|
92
|
+
List all CloudTrail trails.
|
|
93
|
+
|
|
94
|
+
:param client: CloudTrail client
|
|
95
|
+
:return: List of trail summaries
|
|
96
|
+
:rtype: List[Dict[str, Any]]
|
|
97
|
+
"""
|
|
98
|
+
try:
|
|
99
|
+
response = client.list_trails()
|
|
100
|
+
return response.get("Trails", [])
|
|
101
|
+
except ClientError as e:
|
|
102
|
+
if e.response["Error"]["Code"] == "AccessDeniedException":
|
|
103
|
+
logger.warning(f"Access denied to list CloudTrail trails in {self.region}")
|
|
104
|
+
return []
|
|
105
|
+
raise
|
|
106
|
+
|
|
107
|
+
def _describe_trail(self, client: Any, trail_arn: str) -> Optional[Dict[str, Any]]:
|
|
108
|
+
"""
|
|
109
|
+
Get detailed information about a specific trail.
|
|
110
|
+
|
|
111
|
+
:param client: CloudTrail client
|
|
112
|
+
:param str trail_arn: Trail ARN
|
|
113
|
+
:return: Trail details or None if not found
|
|
114
|
+
:rtype: Optional[Dict[str, Any]]
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
response = client.describe_trails(trailNameList=[trail_arn])
|
|
118
|
+
trails = response.get("trailList", [])
|
|
119
|
+
return trails[0] if trails else None
|
|
120
|
+
except ClientError as e:
|
|
121
|
+
if e.response["Error"]["Code"] == "TrailNotFoundException":
|
|
122
|
+
logger.warning(f"Trail not found: {trail_arn}")
|
|
123
|
+
return None
|
|
124
|
+
logger.error(f"Error describing trail {trail_arn}: {e}")
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
def _get_trail_status(self, client: Any, trail_arn: str) -> Dict[str, Any]:
|
|
128
|
+
"""
|
|
129
|
+
Get the status of a CloudTrail trail.
|
|
130
|
+
|
|
131
|
+
:param client: CloudTrail client
|
|
132
|
+
:param str trail_arn: Trail ARN
|
|
133
|
+
:return: Trail status information
|
|
134
|
+
:rtype: Dict[str, Any]
|
|
135
|
+
"""
|
|
136
|
+
try:
|
|
137
|
+
response = client.get_trail_status(Name=trail_arn)
|
|
138
|
+
# Remove ResponseMetadata from the result
|
|
139
|
+
response.pop("ResponseMetadata", None)
|
|
140
|
+
return response
|
|
141
|
+
except ClientError as e:
|
|
142
|
+
logger.error(f"Error getting status for trail {trail_arn}: {e}")
|
|
143
|
+
return {}
|
|
144
|
+
|
|
145
|
+
def _get_event_selectors(self, client: Any, trail_arn: str) -> List[Dict[str, Any]]:
|
|
146
|
+
"""
|
|
147
|
+
Get event selectors for a trail.
|
|
148
|
+
|
|
149
|
+
:param client: CloudTrail client
|
|
150
|
+
:param str trail_arn: Trail ARN
|
|
151
|
+
:return: List of event selectors
|
|
152
|
+
:rtype: List[Dict[str, Any]]
|
|
153
|
+
"""
|
|
154
|
+
try:
|
|
155
|
+
response = client.get_event_selectors(TrailName=trail_arn)
|
|
156
|
+
return response.get("EventSelectors", [])
|
|
157
|
+
except ClientError as e:
|
|
158
|
+
logger.error(f"Error getting event selectors for trail {trail_arn}: {e}")
|
|
159
|
+
return []
|
|
160
|
+
|
|
161
|
+
def _get_trail_tags(self, client: Any, trail_arn: str) -> Dict[str, str]:
|
|
162
|
+
"""
|
|
163
|
+
Get tags for a CloudTrail trail.
|
|
164
|
+
|
|
165
|
+
:param client: CloudTrail client
|
|
166
|
+
:param str trail_arn: Trail ARN
|
|
167
|
+
:return: Dictionary of tags (TagKey -> TagValue)
|
|
168
|
+
:rtype: Dict[str, str]
|
|
169
|
+
"""
|
|
170
|
+
try:
|
|
171
|
+
response = client.list_tags(ResourceIdList=[trail_arn])
|
|
172
|
+
resource_tag_list = response.get("ResourceTagList", [])
|
|
173
|
+
if resource_tag_list:
|
|
174
|
+
tags_list = resource_tag_list[0].get("TagsList", [])
|
|
175
|
+
return {tag["Key"]: tag["Value"] for tag in tags_list}
|
|
176
|
+
return {}
|
|
177
|
+
except ClientError as e:
|
|
178
|
+
logger.debug(f"Error getting tags for trail {trail_arn}: {e}")
|
|
179
|
+
return {}
|
|
180
|
+
|
|
181
|
+
def _matches_account_id(self, trail_arn: str) -> bool:
|
|
182
|
+
"""
|
|
183
|
+
Check if trail ARN matches the specified account ID.
|
|
184
|
+
|
|
185
|
+
:param str trail_arn: Trail ARN to check
|
|
186
|
+
:return: True if matches or no account_id filter specified
|
|
187
|
+
:rtype: bool
|
|
188
|
+
"""
|
|
189
|
+
if not self.account_id:
|
|
190
|
+
return True
|
|
191
|
+
|
|
192
|
+
# ARN format: arn:aws:cloudtrail:region:account-id:trail/trail-name
|
|
193
|
+
try:
|
|
194
|
+
arn_parts = trail_arn.split(":")
|
|
195
|
+
if len(arn_parts) >= 5:
|
|
196
|
+
trail_account_id = arn_parts[4]
|
|
197
|
+
return trail_account_id == self.account_id
|
|
198
|
+
except (IndexError, AttributeError):
|
|
199
|
+
logger.warning(f"Could not parse account ID from trail ARN: {trail_arn}")
|
|
200
|
+
|
|
201
|
+
return False
|
|
202
|
+
|
|
203
|
+
def _matches_tags(self, resource_tags: Dict[str, str]) -> bool:
|
|
204
|
+
"""
|
|
205
|
+
Check if resource tags match the specified filter tags.
|
|
206
|
+
|
|
207
|
+
:param dict resource_tags: Tags on the resource
|
|
208
|
+
:return: True if all filter tags match
|
|
209
|
+
:rtype: bool
|
|
210
|
+
"""
|
|
211
|
+
if not self.tags:
|
|
212
|
+
return True
|
|
213
|
+
|
|
214
|
+
# All filter tags must match
|
|
215
|
+
for key, value in self.tags.items():
|
|
216
|
+
if resource_tags.get(key) != value:
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
return True
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class CloudTrailEventsCollector(BaseCollector):
|
|
223
|
+
"""Collector for AWS CloudTrail events."""
|
|
224
|
+
|
|
225
|
+
def __init__(
|
|
226
|
+
self,
|
|
227
|
+
session: Any,
|
|
228
|
+
region: str,
|
|
229
|
+
max_results: int = 50,
|
|
230
|
+
lookup_attributes: Optional[List[Dict[str, str]]] = None,
|
|
231
|
+
start_time: Optional[Any] = None,
|
|
232
|
+
end_time: Optional[Any] = None,
|
|
233
|
+
):
|
|
234
|
+
"""
|
|
235
|
+
Initialize CloudTrail events collector.
|
|
236
|
+
|
|
237
|
+
:param session: AWS session to use for API calls
|
|
238
|
+
:param str region: AWS region to collect from
|
|
239
|
+
:param int max_results: Maximum number of events to return (default 50, max 50)
|
|
240
|
+
:param List[Dict[str, str]] lookup_attributes: Optional lookup attributes to filter events
|
|
241
|
+
:param start_time: Optional start time for event lookup
|
|
242
|
+
:param end_time: Optional end time for event lookup
|
|
243
|
+
"""
|
|
244
|
+
super().__init__(session, region)
|
|
245
|
+
self.max_results = min(max_results, 50) # AWS maximum is 50
|
|
246
|
+
self.lookup_attributes = lookup_attributes or []
|
|
247
|
+
self.start_time = start_time
|
|
248
|
+
self.end_time = end_time
|
|
249
|
+
|
|
250
|
+
def collect(self) -> Dict[str, Any]:
|
|
251
|
+
"""
|
|
252
|
+
Collect CloudTrail events.
|
|
253
|
+
|
|
254
|
+
:return: Dictionary containing CloudTrail events
|
|
255
|
+
:rtype: Dict[str, Any]
|
|
256
|
+
"""
|
|
257
|
+
result = {"Events": [], "EventCount": 0}
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
client = self._get_client("cloudtrail")
|
|
261
|
+
events = self._lookup_events(client)
|
|
262
|
+
result["Events"] = events
|
|
263
|
+
result["EventCount"] = len(events)
|
|
264
|
+
|
|
265
|
+
logger.info(f"Collected {len(events)} CloudTrail event(s) from {self.region}")
|
|
266
|
+
|
|
267
|
+
except ClientError as e:
|
|
268
|
+
self._handle_error(e, "CloudTrail events")
|
|
269
|
+
except Exception as e:
|
|
270
|
+
logger.error(f"Unexpected error collecting CloudTrail events: {e}", exc_info=True)
|
|
271
|
+
|
|
272
|
+
return result
|
|
273
|
+
|
|
274
|
+
def _lookup_events(self, client: Any) -> List[Dict[str, Any]]:
|
|
275
|
+
"""
|
|
276
|
+
Lookup CloudTrail events with pagination support.
|
|
277
|
+
|
|
278
|
+
:param client: CloudTrail client
|
|
279
|
+
:return: List of events
|
|
280
|
+
:rtype: List[Dict[str, Any]]
|
|
281
|
+
"""
|
|
282
|
+
events = []
|
|
283
|
+
next_token = None
|
|
284
|
+
|
|
285
|
+
try:
|
|
286
|
+
while True:
|
|
287
|
+
# Build request parameters
|
|
288
|
+
params = {"MaxResults": self.max_results}
|
|
289
|
+
|
|
290
|
+
if self.lookup_attributes:
|
|
291
|
+
params["LookupAttributes"] = self.lookup_attributes
|
|
292
|
+
|
|
293
|
+
if self.start_time:
|
|
294
|
+
params["StartTime"] = self.start_time
|
|
295
|
+
|
|
296
|
+
if self.end_time:
|
|
297
|
+
params["EndTime"] = self.end_time
|
|
298
|
+
|
|
299
|
+
if next_token:
|
|
300
|
+
params["NextToken"] = next_token
|
|
301
|
+
|
|
302
|
+
response = client.lookup_events(**params)
|
|
303
|
+
events.extend(response.get("Events", []))
|
|
304
|
+
|
|
305
|
+
next_token = response.get("NextToken")
|
|
306
|
+
if not next_token:
|
|
307
|
+
break
|
|
308
|
+
|
|
309
|
+
except ClientError as e:
|
|
310
|
+
if e.response["Error"]["Code"] == "AccessDeniedException":
|
|
311
|
+
logger.warning(f"Access denied to lookup CloudTrail events in {self.region}")
|
|
312
|
+
else:
|
|
313
|
+
logger.error(f"Error looking up CloudTrail events: {e}")
|
|
314
|
+
|
|
315
|
+
return events
|