regscale-cli 6.27.2.0__py3-none-any.whl → 6.28.0.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/application.py +1 -0
- regscale/core/app/internal/control_editor.py +73 -21
- regscale/core/app/internal/login.py +4 -1
- regscale/core/app/internal/model_editor.py +219 -64
- regscale/core/app/utils/app_utils.py +11 -2
- regscale/core/login.py +21 -4
- regscale/core/utils/date.py +77 -1
- 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 +853 -205
- 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/query_builder.py +4 -1
- regscale/integrations/compliance_integration.py +308 -38
- regscale/integrations/control_matcher.py +78 -23
- regscale/integrations/due_date_handler.py +3 -0
- regscale/integrations/public/csam/csam.py +572 -763
- regscale/integrations/public/csam/csam_agency_defined.py +179 -0
- regscale/integrations/public/csam/csam_common.py +154 -0
- regscale/integrations/public/csam/csam_controls.py +432 -0
- regscale/integrations/public/csam/csam_poam.py +124 -0
- regscale/integrations/public/fedramp/click.py +17 -4
- regscale/integrations/public/fedramp/fedramp_cis_crm.py +271 -62
- regscale/integrations/public/fedramp/poam/scanner.py +74 -7
- regscale/integrations/scanner_integration.py +415 -85
- regscale/models/integration_models/cisa_kev_data.json +80 -20
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +44 -3
- regscale/models/integration_models/synqly_models/ocsf_mapper.py +41 -12
- regscale/models/platform.py +3 -0
- regscale/models/regscale_models/__init__.py +5 -0
- regscale/models/regscale_models/assessment.py +2 -1
- regscale/models/regscale_models/component.py +1 -1
- regscale/models/regscale_models/control_implementation.py +55 -24
- 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/models/regscale_models/organization.py +3 -0
- regscale/models/regscale_models/regscale_model.py +17 -5
- regscale/models/regscale_models/security_plan.py +1 -0
- regscale/regscale.py +11 -1
- {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.28.0.0.dist-info}/METADATA +1 -1
- {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.28.0.0.dist-info}/RECORD +140 -57
- tests/regscale/core/test_login.py +171 -4
- 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
- tests/regscale/integrations/test_control_matcher.py +24 -0
- tests/regscale/models/test_control_implementation.py +118 -3
- {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.28.0.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.28.0.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.28.0.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.28.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"""Unit tests for AWS GuardDuty collector."""
|
|
2
|
+
|
|
3
|
+
import unittest
|
|
4
|
+
from unittest.mock import MagicMock, patch
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from botocore.exceptions import ClientError
|
|
8
|
+
|
|
9
|
+
from regscale.integrations.commercial.aws.inventory.resources.guardduty import GuardDutyCollector
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestGuardDutyCollector(unittest.TestCase):
|
|
13
|
+
"""Test cases for GuardDutyCollector."""
|
|
14
|
+
|
|
15
|
+
def setUp(self):
|
|
16
|
+
"""Set up test fixtures."""
|
|
17
|
+
self.mock_session = MagicMock()
|
|
18
|
+
self.region = "us-east-1"
|
|
19
|
+
self.account_id = "123456789012"
|
|
20
|
+
self.collector = GuardDutyCollector(self.mock_session, self.region, self.account_id)
|
|
21
|
+
|
|
22
|
+
def test_init(self):
|
|
23
|
+
"""Test GuardDutyCollector initialization."""
|
|
24
|
+
assert self.collector.session == self.mock_session
|
|
25
|
+
assert self.collector.region == self.region
|
|
26
|
+
assert self.collector.account_id == self.account_id
|
|
27
|
+
|
|
28
|
+
def test_init_without_account_id(self):
|
|
29
|
+
"""Test GuardDutyCollector initialization without account ID."""
|
|
30
|
+
collector = GuardDutyCollector(self.mock_session, self.region)
|
|
31
|
+
assert collector.account_id is None
|
|
32
|
+
|
|
33
|
+
@patch("regscale.integrations.commercial.aws.inventory.resources.guardduty.logger")
|
|
34
|
+
def test_collect_success(self, mock_logger):
|
|
35
|
+
"""Test successful collection of GuardDuty resources."""
|
|
36
|
+
# Setup mock client
|
|
37
|
+
mock_client = MagicMock()
|
|
38
|
+
self.mock_session.client.return_value = mock_client
|
|
39
|
+
|
|
40
|
+
detector_id = "test-detector-123"
|
|
41
|
+
mock_client.list_detectors.return_value = {"DetectorIds": [detector_id]}
|
|
42
|
+
|
|
43
|
+
mock_client.get_detector.return_value = {
|
|
44
|
+
"Status": "ENABLED",
|
|
45
|
+
"ServiceRole": "arn:aws:iam::123456789012:role/service-role",
|
|
46
|
+
"AccountId": self.account_id,
|
|
47
|
+
"ResponseMetadata": {"RequestId": "test"},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
mock_client.list_findings.return_value = {"FindingIds": ["finding-1", "finding-2"]}
|
|
51
|
+
|
|
52
|
+
mock_client.get_findings.return_value = {
|
|
53
|
+
"Findings": [
|
|
54
|
+
{
|
|
55
|
+
"Id": "finding-1",
|
|
56
|
+
"Type": "UnauthorizedAccess:IAMUser/MaliciousIPCaller.Custom",
|
|
57
|
+
"Severity": 8.0,
|
|
58
|
+
"AccountId": self.account_id,
|
|
59
|
+
},
|
|
60
|
+
{"Id": "finding-2", "Type": "Recon:EC2/PortProbeUnprotectedPort", "Severity": 5.0},
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
mock_client.list_members.return_value = {"Members": []}
|
|
65
|
+
|
|
66
|
+
# Execute
|
|
67
|
+
result = self.collector.collect()
|
|
68
|
+
|
|
69
|
+
# Verify
|
|
70
|
+
assert "Detectors" in result
|
|
71
|
+
assert "Findings" in result
|
|
72
|
+
assert "Members" in result
|
|
73
|
+
assert len(result["Detectors"]) == 1
|
|
74
|
+
assert len(result["Findings"]) == 2
|
|
75
|
+
assert result["Detectors"][0]["DetectorId"] == detector_id
|
|
76
|
+
assert result["Detectors"][0]["Region"] == self.region
|
|
77
|
+
assert "ResponseMetadata" not in result["Detectors"][0]
|
|
78
|
+
|
|
79
|
+
@patch("regscale.integrations.commercial.aws.inventory.resources.guardduty.logger")
|
|
80
|
+
def test_collect_filters_by_account_id(self, mock_logger):
|
|
81
|
+
"""Test that collection filters detectors by account ID."""
|
|
82
|
+
# Setup mock client
|
|
83
|
+
mock_client = MagicMock()
|
|
84
|
+
self.mock_session.client.return_value = mock_client
|
|
85
|
+
|
|
86
|
+
mock_client.list_detectors.return_value = {"DetectorIds": ["detector-1", "detector-2"]}
|
|
87
|
+
|
|
88
|
+
# Return different account IDs for each detector
|
|
89
|
+
mock_client.get_detector.side_effect = [
|
|
90
|
+
{"Status": "ENABLED", "AccountId": self.account_id, "ServiceRole": "arn"},
|
|
91
|
+
{"Status": "ENABLED", "AccountId": "999999999999", "ServiceRole": "arn"},
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
mock_client.list_findings.return_value = {"FindingIds": []}
|
|
95
|
+
mock_client.list_members.return_value = {"Members": []}
|
|
96
|
+
|
|
97
|
+
# Execute
|
|
98
|
+
result = self.collector.collect()
|
|
99
|
+
|
|
100
|
+
# Verify - should only have one detector (the matching account)
|
|
101
|
+
assert len(result["Detectors"]) == 1
|
|
102
|
+
assert result["Detectors"][0]["AccountId"] == self.account_id
|
|
103
|
+
|
|
104
|
+
@patch("regscale.integrations.commercial.aws.inventory.resources.guardduty.logger")
|
|
105
|
+
def test_collect_no_account_filter(self, mock_logger):
|
|
106
|
+
"""Test collection without account ID filter."""
|
|
107
|
+
# Create collector without account ID
|
|
108
|
+
collector = GuardDutyCollector(self.mock_session, self.region)
|
|
109
|
+
|
|
110
|
+
# Setup mock client
|
|
111
|
+
mock_client = MagicMock()
|
|
112
|
+
self.mock_session.client.return_value = mock_client
|
|
113
|
+
|
|
114
|
+
mock_client.list_detectors.return_value = {"DetectorIds": ["detector-1", "detector-2"]}
|
|
115
|
+
|
|
116
|
+
mock_client.get_detector.side_effect = [
|
|
117
|
+
{"Status": "ENABLED", "AccountId": "111111111111"},
|
|
118
|
+
{"Status": "ENABLED", "AccountId": "222222222222"},
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
mock_client.list_findings.return_value = {"FindingIds": []}
|
|
122
|
+
mock_client.list_members.return_value = {"Members": []}
|
|
123
|
+
|
|
124
|
+
# Execute
|
|
125
|
+
result = collector.collect()
|
|
126
|
+
|
|
127
|
+
# Verify - should have both detectors
|
|
128
|
+
assert len(result["Detectors"]) == 2
|
|
129
|
+
|
|
130
|
+
@patch("regscale.integrations.commercial.aws.inventory.resources.guardduty.logger")
|
|
131
|
+
def test_collect_handles_client_error(self, mock_logger):
|
|
132
|
+
"""Test collection handles ClientError."""
|
|
133
|
+
# Setup mock client
|
|
134
|
+
mock_client = MagicMock()
|
|
135
|
+
self.mock_session.client.return_value = mock_client
|
|
136
|
+
|
|
137
|
+
# Simulate ClientError
|
|
138
|
+
error_response = {"Error": {"Code": "InternalError", "Message": "Internal error"}}
|
|
139
|
+
mock_client.list_detectors.side_effect = ClientError(error_response, "list_detectors")
|
|
140
|
+
|
|
141
|
+
# Execute
|
|
142
|
+
result = self.collector.collect()
|
|
143
|
+
|
|
144
|
+
# Verify
|
|
145
|
+
assert result["Detectors"] == []
|
|
146
|
+
|
|
147
|
+
@patch("regscale.integrations.commercial.aws.inventory.resources.guardduty.logger")
|
|
148
|
+
def test_collect_handles_unexpected_error(self, mock_logger):
|
|
149
|
+
"""Test collection handles unexpected errors."""
|
|
150
|
+
# Setup mock client
|
|
151
|
+
mock_client = MagicMock()
|
|
152
|
+
self.mock_session.client.return_value = mock_client
|
|
153
|
+
|
|
154
|
+
# Simulate unexpected error
|
|
155
|
+
mock_client.list_detectors.side_effect = Exception("Unexpected error")
|
|
156
|
+
|
|
157
|
+
# Execute
|
|
158
|
+
result = self.collector.collect()
|
|
159
|
+
|
|
160
|
+
# Verify
|
|
161
|
+
assert result["Detectors"] == []
|
|
162
|
+
mock_logger.error.assert_called()
|
|
163
|
+
|
|
164
|
+
def test_list_detectors_success(self):
|
|
165
|
+
"""Test successful listing of detectors."""
|
|
166
|
+
mock_client = MagicMock()
|
|
167
|
+
mock_client.list_detectors.return_value = {"DetectorIds": ["detector-1", "detector-2"]}
|
|
168
|
+
|
|
169
|
+
result = self.collector._list_detectors(mock_client)
|
|
170
|
+
|
|
171
|
+
assert len(result) == 2
|
|
172
|
+
assert "detector-1" in result
|
|
173
|
+
|
|
174
|
+
def test_list_detectors_access_denied(self):
|
|
175
|
+
"""Test listing detectors with access denied."""
|
|
176
|
+
mock_client = MagicMock()
|
|
177
|
+
error_response = {"Error": {"Code": "AccessDeniedException", "Message": "Access denied"}}
|
|
178
|
+
mock_client.list_detectors.side_effect = ClientError(error_response, "list_detectors")
|
|
179
|
+
|
|
180
|
+
result = self.collector._list_detectors(mock_client)
|
|
181
|
+
|
|
182
|
+
assert result == []
|
|
183
|
+
|
|
184
|
+
def test_get_detector_success(self):
|
|
185
|
+
"""Test successful detector retrieval."""
|
|
186
|
+
mock_client = MagicMock()
|
|
187
|
+
detector_id = "test-detector"
|
|
188
|
+
mock_client.get_detector.return_value = {
|
|
189
|
+
"Status": "ENABLED",
|
|
190
|
+
"ServiceRole": "arn:aws:iam::123456789012:role/service-role",
|
|
191
|
+
"ResponseMetadata": {"RequestId": "test"},
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
result = self.collector._get_detector(mock_client, detector_id)
|
|
195
|
+
|
|
196
|
+
assert result is not None
|
|
197
|
+
assert result["Status"] == "ENABLED"
|
|
198
|
+
assert "ResponseMetadata" not in result
|
|
199
|
+
|
|
200
|
+
def test_get_detector_error(self):
|
|
201
|
+
"""Test detector retrieval with error."""
|
|
202
|
+
mock_client = MagicMock()
|
|
203
|
+
detector_id = "test-detector"
|
|
204
|
+
error_response = {"Error": {"Code": "BadRequestException", "Message": "Bad request"}}
|
|
205
|
+
mock_client.get_detector.side_effect = ClientError(error_response, "get_detector")
|
|
206
|
+
|
|
207
|
+
result = self.collector._get_detector(mock_client, detector_id)
|
|
208
|
+
|
|
209
|
+
assert result is None
|
|
210
|
+
|
|
211
|
+
def test_list_and_get_findings_success(self):
|
|
212
|
+
"""Test successful findings retrieval."""
|
|
213
|
+
mock_client = MagicMock()
|
|
214
|
+
detector_id = "test-detector"
|
|
215
|
+
|
|
216
|
+
mock_client.list_findings.return_value = {"FindingIds": ["finding-1", "finding-2"]}
|
|
217
|
+
mock_client.get_findings.return_value = {
|
|
218
|
+
"Findings": [{"Id": "finding-1", "Severity": 8.0}, {"Id": "finding-2", "Severity": 5.0}]
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
result = self.collector._list_and_get_findings(mock_client, detector_id)
|
|
222
|
+
|
|
223
|
+
assert len(result) == 2
|
|
224
|
+
assert result[0]["Region"] == self.region
|
|
225
|
+
assert result[0]["DetectorId"] == detector_id
|
|
226
|
+
|
|
227
|
+
def test_list_and_get_findings_no_findings(self):
|
|
228
|
+
"""Test findings retrieval with no findings."""
|
|
229
|
+
mock_client = MagicMock()
|
|
230
|
+
detector_id = "test-detector"
|
|
231
|
+
mock_client.list_findings.return_value = {"FindingIds": []}
|
|
232
|
+
|
|
233
|
+
result = self.collector._list_and_get_findings(mock_client, detector_id)
|
|
234
|
+
|
|
235
|
+
assert result == []
|
|
236
|
+
mock_client.get_findings.assert_not_called()
|
|
237
|
+
|
|
238
|
+
def test_list_and_get_findings_access_denied(self):
|
|
239
|
+
"""Test findings retrieval with access denied."""
|
|
240
|
+
mock_client = MagicMock()
|
|
241
|
+
detector_id = "test-detector"
|
|
242
|
+
error_response = {"Error": {"Code": "AccessDeniedException", "Message": "Access denied"}}
|
|
243
|
+
mock_client.list_findings.side_effect = ClientError(error_response, "list_findings")
|
|
244
|
+
|
|
245
|
+
result = self.collector._list_and_get_findings(mock_client, detector_id)
|
|
246
|
+
|
|
247
|
+
assert result == []
|
|
248
|
+
|
|
249
|
+
def test_list_and_get_findings_with_max_limit(self):
|
|
250
|
+
"""Test findings retrieval respects max_findings parameter."""
|
|
251
|
+
mock_client = MagicMock()
|
|
252
|
+
detector_id = "test-detector"
|
|
253
|
+
max_findings = 10
|
|
254
|
+
|
|
255
|
+
mock_client.list_findings.return_value = {"FindingIds": ["finding-1"]}
|
|
256
|
+
mock_client.get_findings.return_value = {"Findings": [{"Id": "finding-1"}]}
|
|
257
|
+
|
|
258
|
+
self.collector._list_and_get_findings(mock_client, detector_id, max_findings=max_findings)
|
|
259
|
+
|
|
260
|
+
# Verify MaxResults was passed
|
|
261
|
+
call_args = mock_client.list_findings.call_args[1]
|
|
262
|
+
assert call_args["MaxResults"] == max_findings
|
|
263
|
+
|
|
264
|
+
def test_list_members_success(self):
|
|
265
|
+
"""Test successful members listing."""
|
|
266
|
+
mock_client = MagicMock()
|
|
267
|
+
detector_id = "test-detector"
|
|
268
|
+
mock_client.list_members.return_value = {
|
|
269
|
+
"Members": [{"AccountId": "111111111111", "Email": "test@example.com"}]
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
result = self.collector._list_members(mock_client, detector_id)
|
|
273
|
+
|
|
274
|
+
assert len(result) == 1
|
|
275
|
+
assert result[0]["Region"] == self.region
|
|
276
|
+
assert result[0]["DetectorId"] == detector_id
|
|
277
|
+
|
|
278
|
+
def test_list_members_access_denied(self):
|
|
279
|
+
"""Test members listing with access denied."""
|
|
280
|
+
mock_client = MagicMock()
|
|
281
|
+
detector_id = "test-detector"
|
|
282
|
+
error_response = {"Error": {"Code": "AccessDeniedException", "Message": "Access denied"}}
|
|
283
|
+
mock_client.list_members.side_effect = ClientError(error_response, "list_members")
|
|
284
|
+
|
|
285
|
+
result = self.collector._list_members(mock_client, detector_id)
|
|
286
|
+
|
|
287
|
+
assert result == []
|
|
288
|
+
|
|
289
|
+
def test_list_members_other_error(self):
|
|
290
|
+
"""Test members listing with other error."""
|
|
291
|
+
mock_client = MagicMock()
|
|
292
|
+
detector_id = "test-detector"
|
|
293
|
+
error_response = {"Error": {"Code": "InternalError", "Message": "Internal error"}}
|
|
294
|
+
mock_client.list_members.side_effect = ClientError(error_response, "list_members")
|
|
295
|
+
|
|
296
|
+
result = self.collector._list_members(mock_client, detector_id)
|
|
297
|
+
|
|
298
|
+
assert result == []
|
|
299
|
+
|
|
300
|
+
def test_matches_account_id_with_matching_id(self):
|
|
301
|
+
"""Test account ID matching with matching ID."""
|
|
302
|
+
assert self.collector._matches_account_id(self.account_id) is True
|
|
303
|
+
|
|
304
|
+
def test_matches_account_id_with_non_matching_id(self):
|
|
305
|
+
"""Test account ID matching with non-matching ID."""
|
|
306
|
+
assert self.collector._matches_account_id("999999999999") is False
|
|
307
|
+
|
|
308
|
+
def test_matches_account_id_without_filter(self):
|
|
309
|
+
"""Test account ID matching without account filter."""
|
|
310
|
+
collector = GuardDutyCollector(self.mock_session, self.region)
|
|
311
|
+
assert collector._matches_account_id("999999999999") is True
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
if __name__ == "__main__":
|
|
315
|
+
pytest.main([__file__, "-v"])
|