regscale-cli 6.27.3.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/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/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 +34 -4
- 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.0.0.dist-info}/METADATA +1 -1
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/RECORD +112 -33
- 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.0.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
"""Unit tests for AWS Scanner evidence integration."""
|
|
2
|
+
|
|
3
|
+
import unittest
|
|
4
|
+
from unittest.mock import MagicMock, patch
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from regscale.integrations.commercial.aws.scanner import AWSInventoryIntegration
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestScannerEvidenceIntegration(unittest.TestCase):
|
|
12
|
+
"""Test cases for scanner evidence integration."""
|
|
13
|
+
|
|
14
|
+
def setUp(self):
|
|
15
|
+
"""Set up test fixtures."""
|
|
16
|
+
self.plan_id = 123
|
|
17
|
+
self.scanner = AWSInventoryIntegration(plan_id=self.plan_id)
|
|
18
|
+
|
|
19
|
+
@patch("regscale.core.app.api.Api")
|
|
20
|
+
@patch("regscale.integrations.commercial.aws.ocsf.mapper.AWSOCSFMapper")
|
|
21
|
+
@patch("regscale.integrations.commercial.aws.evidence_generator.AWSEvidenceGenerator")
|
|
22
|
+
def test_process_findings_with_evidence_native_only(self, mock_evidence_gen, mock_mapper, mock_api):
|
|
23
|
+
"""Test processing findings with native format only."""
|
|
24
|
+
findings = [
|
|
25
|
+
{
|
|
26
|
+
"Id": "finding-1",
|
|
27
|
+
"Title": "Test Finding 1",
|
|
28
|
+
"Severity": {"Label": "HIGH"},
|
|
29
|
+
"Resources": [{"Type": "AwsEc2Instance", "Id": "i-123"}],
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
# Mock parse_finding to return IntegrationFinding objects
|
|
34
|
+
with patch.object(self.scanner, "parse_finding") as mock_parse:
|
|
35
|
+
mock_integration_finding = MagicMock()
|
|
36
|
+
mock_parse.return_value = [mock_integration_finding]
|
|
37
|
+
|
|
38
|
+
result_findings, result_evidence = self.scanner.process_findings_with_evidence(
|
|
39
|
+
findings=findings,
|
|
40
|
+
service_name="SecurityHub",
|
|
41
|
+
generate_evidence=False,
|
|
42
|
+
ssp_id=None,
|
|
43
|
+
control_ids=None,
|
|
44
|
+
ocsf_format=False,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Verify
|
|
48
|
+
assert len(result_findings) == 1
|
|
49
|
+
assert result_evidence is None
|
|
50
|
+
mock_parse.assert_called_once_with(findings[0])
|
|
51
|
+
mock_mapper.assert_not_called()
|
|
52
|
+
mock_evidence_gen.assert_not_called()
|
|
53
|
+
|
|
54
|
+
@patch("regscale.core.app.api.Api")
|
|
55
|
+
@patch("regscale.integrations.commercial.aws.ocsf.mapper.AWSOCSFMapper")
|
|
56
|
+
@patch("regscale.integrations.commercial.aws.evidence_generator.AWSEvidenceGenerator")
|
|
57
|
+
def test_process_findings_with_ocsf_only(self, mock_evidence_gen, mock_mapper, mock_api):
|
|
58
|
+
"""Test processing findings with OCSF format."""
|
|
59
|
+
findings = [
|
|
60
|
+
{
|
|
61
|
+
"Id": "finding-1",
|
|
62
|
+
"Severity": {"Label": "HIGH"},
|
|
63
|
+
"Resources": [{"Type": "AwsEc2Instance", "Id": "i-123"}],
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
# Setup mocks
|
|
68
|
+
mock_mapper_instance = MagicMock()
|
|
69
|
+
mock_mapper.return_value = mock_mapper_instance
|
|
70
|
+
mock_mapper_instance.securityhub_to_ocsf.return_value = {"class_uid": 2001}
|
|
71
|
+
|
|
72
|
+
with patch.object(self.scanner, "parse_finding") as mock_parse:
|
|
73
|
+
mock_integration_finding = MagicMock()
|
|
74
|
+
mock_parse.return_value = [mock_integration_finding]
|
|
75
|
+
|
|
76
|
+
result_findings, result_evidence = self.scanner.process_findings_with_evidence(
|
|
77
|
+
findings=findings,
|
|
78
|
+
service_name="SecurityHub",
|
|
79
|
+
generate_evidence=False,
|
|
80
|
+
ocsf_format=True,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Verify OCSF mapper called
|
|
84
|
+
mock_mapper_instance.securityhub_to_ocsf.assert_called_once_with(findings[0])
|
|
85
|
+
assert result_evidence is None
|
|
86
|
+
|
|
87
|
+
@patch("regscale.core.app.api.Api")
|
|
88
|
+
@patch("regscale.integrations.commercial.aws.ocsf.mapper.AWSOCSFMapper")
|
|
89
|
+
@patch("regscale.integrations.commercial.aws.evidence_generator.AWSEvidenceGenerator")
|
|
90
|
+
def test_process_findings_with_evidence_generation(self, mock_evidence_gen, mock_mapper, mock_api):
|
|
91
|
+
"""Test processing findings with evidence generation."""
|
|
92
|
+
findings = [
|
|
93
|
+
{
|
|
94
|
+
"Id": "finding-1",
|
|
95
|
+
"Severity": {"Label": "HIGH"},
|
|
96
|
+
"Resources": [{"Type": "AwsEc2Instance", "Id": "i-123"}],
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
# Setup mocks
|
|
101
|
+
mock_evidence_instance = MagicMock()
|
|
102
|
+
mock_evidence_instance.id = 12345
|
|
103
|
+
mock_evidence_instance.title = "Test Evidence"
|
|
104
|
+
|
|
105
|
+
mock_gen_instance = MagicMock()
|
|
106
|
+
mock_gen_instance.create_evidence_from_scan.return_value = mock_evidence_instance
|
|
107
|
+
mock_evidence_gen.return_value = mock_gen_instance
|
|
108
|
+
|
|
109
|
+
with patch.object(self.scanner, "parse_finding") as mock_parse:
|
|
110
|
+
mock_integration_finding = MagicMock()
|
|
111
|
+
mock_parse.return_value = [mock_integration_finding]
|
|
112
|
+
|
|
113
|
+
result_findings, result_evidence = self.scanner.process_findings_with_evidence(
|
|
114
|
+
findings=findings,
|
|
115
|
+
service_name="SecurityHub",
|
|
116
|
+
generate_evidence=True,
|
|
117
|
+
ssp_id=456,
|
|
118
|
+
control_ids=[789, 790],
|
|
119
|
+
ocsf_format=False,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Verify evidence generator called with Api instance
|
|
123
|
+
mock_api.assert_called_once()
|
|
124
|
+
mock_evidence_gen.assert_called_once_with(api=mock_api.return_value, ssp_id=456)
|
|
125
|
+
mock_gen_instance.create_evidence_from_scan.assert_called_once_with(
|
|
126
|
+
service_name="SecurityHub",
|
|
127
|
+
findings=findings,
|
|
128
|
+
ocsf_data=None,
|
|
129
|
+
control_ids=[789, 790],
|
|
130
|
+
)
|
|
131
|
+
assert result_evidence == mock_evidence_instance
|
|
132
|
+
|
|
133
|
+
@patch("regscale.core.app.api.Api")
|
|
134
|
+
@patch("regscale.integrations.commercial.aws.ocsf.mapper.AWSOCSFMapper")
|
|
135
|
+
@patch("regscale.integrations.commercial.aws.evidence_generator.AWSEvidenceGenerator")
|
|
136
|
+
def test_process_findings_guardduty_with_ocsf(self, mock_evidence_gen, mock_mapper, mock_api):
|
|
137
|
+
"""Test processing GuardDuty findings with OCSF."""
|
|
138
|
+
findings = [
|
|
139
|
+
{
|
|
140
|
+
"Id": "guardduty-1",
|
|
141
|
+
"Severity": 8.0,
|
|
142
|
+
"Type": "UnauthorizedAccess:IAMUser/MaliciousIPCaller.Custom",
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
# Setup mocks
|
|
147
|
+
mock_mapper_instance = MagicMock()
|
|
148
|
+
mock_mapper.return_value = mock_mapper_instance
|
|
149
|
+
mock_mapper_instance.guardduty_to_ocsf.return_value = {"class_uid": 2004}
|
|
150
|
+
|
|
151
|
+
with patch.object(self.scanner, "parse_finding") as mock_parse:
|
|
152
|
+
mock_integration_finding = MagicMock()
|
|
153
|
+
mock_parse.return_value = [mock_integration_finding]
|
|
154
|
+
|
|
155
|
+
result_findings, result_evidence = self.scanner.process_findings_with_evidence(
|
|
156
|
+
findings=findings,
|
|
157
|
+
service_name="GuardDuty",
|
|
158
|
+
ocsf_format=True,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# Verify GuardDuty mapper called
|
|
162
|
+
mock_mapper_instance.guardduty_to_ocsf.assert_called_once_with(findings[0])
|
|
163
|
+
|
|
164
|
+
@patch("regscale.core.app.api.Api")
|
|
165
|
+
@patch("regscale.integrations.commercial.aws.ocsf.mapper.AWSOCSFMapper")
|
|
166
|
+
@patch("regscale.integrations.commercial.aws.evidence_generator.AWSEvidenceGenerator")
|
|
167
|
+
def test_process_findings_cloudtrail_with_ocsf(self, mock_evidence_gen, mock_mapper, mock_api):
|
|
168
|
+
"""Test processing CloudTrail events with OCSF."""
|
|
169
|
+
findings = [
|
|
170
|
+
{
|
|
171
|
+
"EventName": "DescribeInstances",
|
|
172
|
+
"EventSource": "ec2.amazonaws.com",
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
|
|
176
|
+
# Setup mocks
|
|
177
|
+
mock_mapper_instance = MagicMock()
|
|
178
|
+
mock_mapper.return_value = mock_mapper_instance
|
|
179
|
+
mock_mapper_instance.cloudtrail_event_to_ocsf.return_value = {"class_uid": 3005}
|
|
180
|
+
|
|
181
|
+
with patch.object(self.scanner, "parse_finding") as mock_parse:
|
|
182
|
+
mock_integration_finding = MagicMock()
|
|
183
|
+
mock_parse.return_value = [mock_integration_finding]
|
|
184
|
+
|
|
185
|
+
result_findings, result_evidence = self.scanner.process_findings_with_evidence(
|
|
186
|
+
findings=findings,
|
|
187
|
+
service_name="CloudTrail",
|
|
188
|
+
ocsf_format=True,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Verify CloudTrail mapper called
|
|
192
|
+
mock_mapper_instance.cloudtrail_event_to_ocsf.assert_called_once_with(findings[0])
|
|
193
|
+
|
|
194
|
+
@patch("regscale.core.app.api.Api")
|
|
195
|
+
@patch("regscale.integrations.commercial.aws.ocsf.mapper.AWSOCSFMapper")
|
|
196
|
+
@patch("regscale.integrations.commercial.aws.evidence_generator.AWSEvidenceGenerator")
|
|
197
|
+
def test_process_findings_with_both_formats(self, mock_evidence_gen, mock_mapper, mock_api):
|
|
198
|
+
"""Test processing findings with both native and OCSF formats."""
|
|
199
|
+
findings = [
|
|
200
|
+
{
|
|
201
|
+
"Id": "finding-1",
|
|
202
|
+
"Severity": {"Label": "HIGH"},
|
|
203
|
+
"Resources": [{"Type": "AwsEc2Instance", "Id": "i-123"}],
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
# Setup mocks
|
|
208
|
+
mock_mapper_instance = MagicMock()
|
|
209
|
+
mock_mapper.return_value = mock_mapper_instance
|
|
210
|
+
mock_mapper_instance.securityhub_to_ocsf.return_value = {"class_uid": 2001}
|
|
211
|
+
|
|
212
|
+
mock_evidence_instance = MagicMock()
|
|
213
|
+
mock_gen_instance = MagicMock()
|
|
214
|
+
mock_gen_instance.create_evidence_from_scan.return_value = mock_evidence_instance
|
|
215
|
+
mock_evidence_gen.return_value = mock_gen_instance
|
|
216
|
+
|
|
217
|
+
with patch.object(self.scanner, "parse_finding") as mock_parse:
|
|
218
|
+
mock_integration_finding = MagicMock()
|
|
219
|
+
mock_parse.return_value = [mock_integration_finding]
|
|
220
|
+
|
|
221
|
+
result_findings, result_evidence = self.scanner.process_findings_with_evidence(
|
|
222
|
+
findings=findings,
|
|
223
|
+
service_name="SecurityHub",
|
|
224
|
+
generate_evidence=True,
|
|
225
|
+
ssp_id=456,
|
|
226
|
+
ocsf_format=True,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Verify both OCSF mapper and evidence generator called
|
|
230
|
+
mock_mapper_instance.securityhub_to_ocsf.assert_called_once()
|
|
231
|
+
mock_gen_instance.create_evidence_from_scan.assert_called_once()
|
|
232
|
+
|
|
233
|
+
# Verify OCSF data passed to evidence generator
|
|
234
|
+
call_kwargs = mock_gen_instance.create_evidence_from_scan.call_args[1]
|
|
235
|
+
assert call_kwargs["ocsf_data"] is not None
|
|
236
|
+
assert len(call_kwargs["ocsf_data"]) == 1
|
|
237
|
+
|
|
238
|
+
@patch("regscale.core.app.api.Api")
|
|
239
|
+
@patch("regscale.integrations.commercial.aws.ocsf.mapper.AWSOCSFMapper")
|
|
240
|
+
@patch("regscale.integrations.commercial.aws.evidence_generator.AWSEvidenceGenerator")
|
|
241
|
+
def test_process_findings_multiple_findings(self, mock_evidence_gen, mock_mapper, mock_api):
|
|
242
|
+
"""Test processing multiple findings."""
|
|
243
|
+
findings = [
|
|
244
|
+
{
|
|
245
|
+
"Id": "finding-1",
|
|
246
|
+
"Severity": {"Label": "HIGH"},
|
|
247
|
+
"Resources": [{"Type": "AwsEc2Instance", "Id": "i-123"}],
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"Id": "finding-2",
|
|
251
|
+
"Severity": {"Label": "MEDIUM"},
|
|
252
|
+
"Resources": [{"Type": "AwsS3Bucket", "Id": "bucket-1"}],
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
"Id": "finding-3",
|
|
256
|
+
"Severity": {"Label": "LOW"},
|
|
257
|
+
"Resources": [{"Type": "AwsIamRole", "Id": "role-1"}],
|
|
258
|
+
},
|
|
259
|
+
]
|
|
260
|
+
|
|
261
|
+
with patch.object(self.scanner, "parse_finding") as mock_parse:
|
|
262
|
+
mock_integration_finding = MagicMock()
|
|
263
|
+
# Each parse_finding returns a list with one finding
|
|
264
|
+
mock_parse.return_value = [mock_integration_finding]
|
|
265
|
+
|
|
266
|
+
result_findings, result_evidence = self.scanner.process_findings_with_evidence(
|
|
267
|
+
findings=findings,
|
|
268
|
+
service_name="SecurityHub",
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
# Verify all findings parsed
|
|
272
|
+
assert mock_parse.call_count == 3
|
|
273
|
+
assert len(result_findings) == 3
|
|
274
|
+
|
|
275
|
+
@patch("regscale.core.app.api.Api")
|
|
276
|
+
@patch("regscale.integrations.commercial.aws.ocsf.mapper.AWSOCSFMapper")
|
|
277
|
+
@patch("regscale.integrations.commercial.aws.evidence_generator.AWSEvidenceGenerator")
|
|
278
|
+
def test_process_findings_with_multi_resource_finding(self, mock_evidence_gen, mock_mapper, mock_api):
|
|
279
|
+
"""Test processing finding with multiple resources (yields multiple IntegrationFindings)."""
|
|
280
|
+
findings = [
|
|
281
|
+
{
|
|
282
|
+
"Id": "finding-1",
|
|
283
|
+
"Severity": {"Label": "HIGH"},
|
|
284
|
+
"Resources": [
|
|
285
|
+
{"Type": "AwsEc2Instance", "Id": "i-123"},
|
|
286
|
+
{"Type": "AwsEc2Instance", "Id": "i-456"},
|
|
287
|
+
],
|
|
288
|
+
}
|
|
289
|
+
]
|
|
290
|
+
|
|
291
|
+
with patch.object(self.scanner, "parse_finding") as mock_parse:
|
|
292
|
+
# parse_finding returns multiple IntegrationFindings for multi-resource finding
|
|
293
|
+
mock_finding_1 = MagicMock()
|
|
294
|
+
mock_finding_2 = MagicMock()
|
|
295
|
+
mock_parse.return_value = [mock_finding_1, mock_finding_2]
|
|
296
|
+
|
|
297
|
+
result_findings, result_evidence = self.scanner.process_findings_with_evidence(
|
|
298
|
+
findings=findings,
|
|
299
|
+
service_name="SecurityHub",
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# Verify multiple IntegrationFindings returned
|
|
303
|
+
assert len(result_findings) == 2
|
|
304
|
+
|
|
305
|
+
@patch("regscale.core.app.api.Api")
|
|
306
|
+
@patch("regscale.integrations.commercial.aws.ocsf.mapper.AWSOCSFMapper")
|
|
307
|
+
@patch("regscale.integrations.commercial.aws.evidence_generator.AWSEvidenceGenerator")
|
|
308
|
+
def test_process_findings_empty_list(self, mock_evidence_gen, mock_mapper, mock_api):
|
|
309
|
+
"""Test processing empty findings list."""
|
|
310
|
+
with patch.object(self.scanner, "parse_finding") as mock_parse:
|
|
311
|
+
result_findings, result_evidence = self.scanner.process_findings_with_evidence(
|
|
312
|
+
findings=[],
|
|
313
|
+
service_name="SecurityHub",
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Verify no findings processed
|
|
317
|
+
assert len(result_findings) == 0
|
|
318
|
+
assert result_evidence is None
|
|
319
|
+
mock_parse.assert_not_called()
|
|
320
|
+
|
|
321
|
+
@patch("regscale.core.app.api.Api")
|
|
322
|
+
@patch("regscale.integrations.commercial.aws.ocsf.mapper.AWSOCSFMapper")
|
|
323
|
+
@patch("regscale.integrations.commercial.aws.evidence_generator.AWSEvidenceGenerator")
|
|
324
|
+
def test_process_findings_no_control_ids(self, mock_evidence_gen, mock_mapper, mock_api):
|
|
325
|
+
"""Test processing findings without control IDs."""
|
|
326
|
+
findings = [{"Id": "finding-1", "Severity": {"Label": "HIGH"}, "Resources": [{"Type": "AwsEc2Instance"}]}]
|
|
327
|
+
|
|
328
|
+
mock_evidence_instance = MagicMock()
|
|
329
|
+
mock_gen_instance = MagicMock()
|
|
330
|
+
mock_gen_instance.create_evidence_from_scan.return_value = mock_evidence_instance
|
|
331
|
+
mock_evidence_gen.return_value = mock_gen_instance
|
|
332
|
+
|
|
333
|
+
with patch.object(self.scanner, "parse_finding") as mock_parse:
|
|
334
|
+
mock_parse.return_value = [MagicMock()]
|
|
335
|
+
|
|
336
|
+
result_findings, result_evidence = self.scanner.process_findings_with_evidence(
|
|
337
|
+
findings=findings,
|
|
338
|
+
service_name="SecurityHub",
|
|
339
|
+
generate_evidence=True,
|
|
340
|
+
control_ids=None,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
# Verify control_ids passed as None
|
|
344
|
+
call_kwargs = mock_gen_instance.create_evidence_from_scan.call_args[1]
|
|
345
|
+
assert call_kwargs["control_ids"] is None
|
|
346
|
+
|
|
347
|
+
@patch("regscale.core.app.api.Api")
|
|
348
|
+
@patch("regscale.integrations.commercial.aws.ocsf.mapper.AWSOCSFMapper")
|
|
349
|
+
@patch("regscale.integrations.commercial.aws.evidence_generator.AWSEvidenceGenerator")
|
|
350
|
+
def test_process_findings_no_ssp_id(self, mock_evidence_gen, mock_mapper, mock_api):
|
|
351
|
+
"""Test processing findings without SSP ID."""
|
|
352
|
+
findings = [{"Id": "finding-1", "Severity": {"Label": "HIGH"}, "Resources": [{"Type": "AwsEc2Instance"}]}]
|
|
353
|
+
|
|
354
|
+
mock_gen_instance = MagicMock()
|
|
355
|
+
mock_evidence_gen.return_value = mock_gen_instance
|
|
356
|
+
|
|
357
|
+
with patch.object(self.scanner, "parse_finding") as mock_parse:
|
|
358
|
+
mock_parse.return_value = [MagicMock()]
|
|
359
|
+
|
|
360
|
+
result_findings, result_evidence = self.scanner.process_findings_with_evidence(
|
|
361
|
+
findings=findings,
|
|
362
|
+
service_name="SecurityHub",
|
|
363
|
+
generate_evidence=True,
|
|
364
|
+
ssp_id=None,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
# Verify AWSEvidenceGenerator initialized with None SSP ID
|
|
368
|
+
mock_api.assert_called_once()
|
|
369
|
+
mock_evidence_gen.assert_called_once_with(api=mock_api.return_value, ssp_id=None)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
if __name__ == "__main__":
|
|
373
|
+
pytest.main([__file__, "-v"])
|