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,539 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tests for AWS Security Hub config filtering functionality."""
|
|
4
|
+
|
|
5
|
+
from unittest.mock import MagicMock, patch
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from regscale.integrations.commercial.aws.scanner import AWSInventoryIntegration
|
|
10
|
+
from regscale.integrations.commercial.aws.security_hub import SecurityHubPuller
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestSecurityHubSeverityFiltering:
|
|
14
|
+
"""Test suite for SecurityHubPuller severity filtering functionality."""
|
|
15
|
+
|
|
16
|
+
def test_get_severity_filters_from_minimum_critical(self):
|
|
17
|
+
"""Test severity filter generation with CRITICAL minimum."""
|
|
18
|
+
result = SecurityHubPuller.get_severity_filters_from_minimum("CRITICAL")
|
|
19
|
+
assert result == ["CRITICAL"]
|
|
20
|
+
|
|
21
|
+
def test_get_severity_filters_from_minimum_high(self):
|
|
22
|
+
"""Test severity filter generation with HIGH minimum."""
|
|
23
|
+
result = SecurityHubPuller.get_severity_filters_from_minimum("HIGH")
|
|
24
|
+
assert result == ["HIGH", "CRITICAL"]
|
|
25
|
+
|
|
26
|
+
def test_get_severity_filters_from_minimum_medium(self):
|
|
27
|
+
"""Test severity filter generation with MEDIUM minimum."""
|
|
28
|
+
result = SecurityHubPuller.get_severity_filters_from_minimum("MEDIUM")
|
|
29
|
+
# MODERATE is excluded as it's an alias
|
|
30
|
+
assert result == ["MEDIUM", "HIGH", "CRITICAL"]
|
|
31
|
+
|
|
32
|
+
def test_get_severity_filters_from_minimum_moderate_alias(self):
|
|
33
|
+
"""Test severity filter generation with MODERATE minimum (alias for MEDIUM)."""
|
|
34
|
+
result = SecurityHubPuller.get_severity_filters_from_minimum("MODERATE")
|
|
35
|
+
# Should convert MODERATE to MEDIUM and exclude MODERATE from results
|
|
36
|
+
assert result == ["MEDIUM", "HIGH", "CRITICAL"]
|
|
37
|
+
assert "MODERATE" not in result
|
|
38
|
+
|
|
39
|
+
def test_get_severity_filters_from_minimum_low(self):
|
|
40
|
+
"""Test severity filter generation with LOW minimum."""
|
|
41
|
+
result = SecurityHubPuller.get_severity_filters_from_minimum("LOW")
|
|
42
|
+
# MODERATE is excluded as it's an alias
|
|
43
|
+
assert result == ["LOW", "MEDIUM", "HIGH", "CRITICAL"]
|
|
44
|
+
assert "MODERATE" not in result
|
|
45
|
+
|
|
46
|
+
def test_get_severity_filters_from_minimum_informational(self):
|
|
47
|
+
"""Test severity filter generation with INFORMATIONAL minimum."""
|
|
48
|
+
result = SecurityHubPuller.get_severity_filters_from_minimum("INFORMATIONAL")
|
|
49
|
+
# Should return all severities except MODERATE (alias)
|
|
50
|
+
assert result == ["INFORMATIONAL", "LOW", "MEDIUM", "HIGH", "CRITICAL"]
|
|
51
|
+
assert "MODERATE" not in result
|
|
52
|
+
|
|
53
|
+
def test_get_severity_filters_from_minimum_case_insensitive(self):
|
|
54
|
+
"""Test severity filter generation is case insensitive."""
|
|
55
|
+
result_lower = SecurityHubPuller.get_severity_filters_from_minimum("high")
|
|
56
|
+
result_upper = SecurityHubPuller.get_severity_filters_from_minimum("HIGH")
|
|
57
|
+
result_mixed = SecurityHubPuller.get_severity_filters_from_minimum("HiGh")
|
|
58
|
+
|
|
59
|
+
assert result_lower == result_upper == result_mixed
|
|
60
|
+
assert result_lower == ["HIGH", "CRITICAL"]
|
|
61
|
+
|
|
62
|
+
def test_get_severity_filters_from_minimum_unknown_defaults_to_low(self):
|
|
63
|
+
"""Test severity filter generation with unknown severity defaults to LOW."""
|
|
64
|
+
result = SecurityHubPuller.get_severity_filters_from_minimum("UNKNOWN")
|
|
65
|
+
# Should default to LOW
|
|
66
|
+
assert result == ["LOW", "MEDIUM", "HIGH", "CRITICAL"]
|
|
67
|
+
|
|
68
|
+
def test_get_severity_filters_from_minimum_empty_string_defaults_to_low(self):
|
|
69
|
+
"""Test severity filter generation with empty string defaults to LOW."""
|
|
70
|
+
result = SecurityHubPuller.get_severity_filters_from_minimum("")
|
|
71
|
+
# Should default to LOW
|
|
72
|
+
assert result == ["LOW", "MEDIUM", "HIGH", "CRITICAL"]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TestAWSInventoryIntegrationServiceFiltering:
|
|
76
|
+
"""Test suite for AWSInventoryIntegration enabled_services filtering."""
|
|
77
|
+
|
|
78
|
+
@pytest.fixture
|
|
79
|
+
def mock_app_all_services_enabled(self):
|
|
80
|
+
"""Create mock app with all services enabled."""
|
|
81
|
+
app = MagicMock()
|
|
82
|
+
app.config = {
|
|
83
|
+
"aws": {
|
|
84
|
+
"inventory": {
|
|
85
|
+
"enabled_services": {
|
|
86
|
+
"compute": {"enabled": True, "services": {"ec2": True, "lambda": True}},
|
|
87
|
+
"storage": {"enabled": True, "services": {"s3": True}},
|
|
88
|
+
"database": {"enabled": True, "services": {"rds": True, "dynamodb": True}},
|
|
89
|
+
"networking": {"enabled": True, "services": {"vpc": True}},
|
|
90
|
+
"security": {
|
|
91
|
+
"enabled": True,
|
|
92
|
+
"services": {
|
|
93
|
+
"iam": True,
|
|
94
|
+
"kms": True,
|
|
95
|
+
"secrets_manager": True,
|
|
96
|
+
"securityhub": True,
|
|
97
|
+
"cloudtrail": True,
|
|
98
|
+
"config": True,
|
|
99
|
+
"guardduty": True,
|
|
100
|
+
"inspector": True,
|
|
101
|
+
"audit_manager": True,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
"containers": {"enabled": True, "services": {"ecr": True}},
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return app
|
|
110
|
+
|
|
111
|
+
@pytest.fixture
|
|
112
|
+
def mock_app_partial_services_enabled(self):
|
|
113
|
+
"""Create mock app with only some services enabled."""
|
|
114
|
+
app = MagicMock()
|
|
115
|
+
app.config = {
|
|
116
|
+
"aws": {
|
|
117
|
+
"inventory": {
|
|
118
|
+
"enabled_services": {
|
|
119
|
+
"compute": {"enabled": True, "services": {"ec2": True, "lambda": False}},
|
|
120
|
+
"storage": {"enabled": True, "services": {"s3": True}},
|
|
121
|
+
"database": {"enabled": True, "services": {"rds": False, "dynamodb": True}},
|
|
122
|
+
"security": {"enabled": True, "services": {"iam": True, "kms": False, "securityhub": True}},
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return app
|
|
128
|
+
|
|
129
|
+
@pytest.fixture
|
|
130
|
+
def mock_app_no_config(self):
|
|
131
|
+
"""Create mock app with no enabled_services config."""
|
|
132
|
+
app = MagicMock()
|
|
133
|
+
app.config = {}
|
|
134
|
+
return app
|
|
135
|
+
|
|
136
|
+
@pytest.fixture
|
|
137
|
+
def scanner_all_enabled(self, mock_app_all_services_enabled):
|
|
138
|
+
"""Create scanner with all services enabled."""
|
|
139
|
+
scanner = AWSInventoryIntegration(plan_id=36)
|
|
140
|
+
scanner.app = mock_app_all_services_enabled
|
|
141
|
+
return scanner
|
|
142
|
+
|
|
143
|
+
@pytest.fixture
|
|
144
|
+
def scanner_partial_enabled(self, mock_app_partial_services_enabled):
|
|
145
|
+
"""Create scanner with partial services enabled."""
|
|
146
|
+
scanner = AWSInventoryIntegration(plan_id=36)
|
|
147
|
+
scanner.app = mock_app_partial_services_enabled
|
|
148
|
+
return scanner
|
|
149
|
+
|
|
150
|
+
@pytest.fixture
|
|
151
|
+
def scanner_no_config(self, mock_app_no_config):
|
|
152
|
+
"""Create scanner with no config."""
|
|
153
|
+
scanner = AWSInventoryIntegration(plan_id=36)
|
|
154
|
+
scanner.app = mock_app_no_config
|
|
155
|
+
return scanner
|
|
156
|
+
|
|
157
|
+
def test_is_service_enabled_for_resource_ec2_enabled(self, scanner_all_enabled):
|
|
158
|
+
"""Test EC2 resource when EC2 service is enabled."""
|
|
159
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsEc2Instance") is True
|
|
160
|
+
|
|
161
|
+
def test_is_service_enabled_for_resource_ec2_disabled(self, scanner_partial_enabled):
|
|
162
|
+
"""Test EC2 resource when EC2 service is enabled (partial config)."""
|
|
163
|
+
# EC2 is enabled in partial config
|
|
164
|
+
assert scanner_partial_enabled.is_service_enabled_for_resource("AwsEc2Instance") is True
|
|
165
|
+
|
|
166
|
+
def test_is_service_enabled_for_resource_lambda_enabled(self, scanner_all_enabled):
|
|
167
|
+
"""Test Lambda resource when Lambda service is enabled."""
|
|
168
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsLambdaFunction") is True
|
|
169
|
+
|
|
170
|
+
def test_is_service_enabled_for_resource_lambda_disabled(self, scanner_partial_enabled):
|
|
171
|
+
"""Test Lambda resource when Lambda service is disabled."""
|
|
172
|
+
assert scanner_partial_enabled.is_service_enabled_for_resource("AwsLambdaFunction") is False
|
|
173
|
+
|
|
174
|
+
def test_is_service_enabled_for_resource_s3_enabled(self, scanner_all_enabled):
|
|
175
|
+
"""Test S3 resource when S3 service is enabled."""
|
|
176
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsS3Bucket") is True
|
|
177
|
+
|
|
178
|
+
def test_is_service_enabled_for_resource_s3_enabled_partial(self, scanner_partial_enabled):
|
|
179
|
+
"""Test S3 resource when S3 service is enabled (partial config)."""
|
|
180
|
+
assert scanner_partial_enabled.is_service_enabled_for_resource("AwsS3Bucket") is True
|
|
181
|
+
|
|
182
|
+
def test_is_service_enabled_for_resource_rds_enabled(self, scanner_all_enabled):
|
|
183
|
+
"""Test RDS resource when RDS service is enabled."""
|
|
184
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsRdsDbInstance") is True
|
|
185
|
+
|
|
186
|
+
def test_is_service_enabled_for_resource_rds_disabled(self, scanner_partial_enabled):
|
|
187
|
+
"""Test RDS resource when RDS service is disabled."""
|
|
188
|
+
assert scanner_partial_enabled.is_service_enabled_for_resource("AwsRdsDbInstance") is False
|
|
189
|
+
|
|
190
|
+
def test_is_service_enabled_for_resource_dynamodb_enabled(self, scanner_all_enabled):
|
|
191
|
+
"""Test DynamoDB resource when DynamoDB service is enabled."""
|
|
192
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsDynamoDbTable") is True
|
|
193
|
+
|
|
194
|
+
def test_is_service_enabled_for_resource_dynamodb_enabled_partial(self, scanner_partial_enabled):
|
|
195
|
+
"""Test DynamoDB resource when DynamoDB service is enabled (partial config)."""
|
|
196
|
+
assert scanner_partial_enabled.is_service_enabled_for_resource("AwsDynamoDbTable") is True
|
|
197
|
+
|
|
198
|
+
def test_is_service_enabled_for_resource_iam_enabled(self, scanner_all_enabled):
|
|
199
|
+
"""Test IAM User resource when IAM service is enabled."""
|
|
200
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsIamUser") is True
|
|
201
|
+
|
|
202
|
+
def test_is_service_enabled_for_resource_iam_role_enabled(self, scanner_all_enabled):
|
|
203
|
+
"""Test IAM Role resource when IAM service is enabled."""
|
|
204
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsIamRole") is True
|
|
205
|
+
|
|
206
|
+
def test_is_service_enabled_for_resource_iam_enabled_partial(self, scanner_partial_enabled):
|
|
207
|
+
"""Test IAM resources when IAM service is enabled (partial config)."""
|
|
208
|
+
assert scanner_partial_enabled.is_service_enabled_for_resource("AwsIamUser") is True
|
|
209
|
+
assert scanner_partial_enabled.is_service_enabled_for_resource("AwsIamRole") is True
|
|
210
|
+
|
|
211
|
+
def test_is_service_enabled_for_resource_kms_enabled(self, scanner_all_enabled):
|
|
212
|
+
"""Test KMS resource when KMS service is enabled."""
|
|
213
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsKmsKey") is True
|
|
214
|
+
|
|
215
|
+
def test_is_service_enabled_for_resource_kms_disabled(self, scanner_partial_enabled):
|
|
216
|
+
"""Test KMS resource when KMS service is disabled."""
|
|
217
|
+
assert scanner_partial_enabled.is_service_enabled_for_resource("AwsKmsKey") is False
|
|
218
|
+
|
|
219
|
+
def test_is_service_enabled_for_resource_secrets_manager_enabled(self, scanner_all_enabled):
|
|
220
|
+
"""Test Secrets Manager resource when service is enabled."""
|
|
221
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsSecretsManagerSecret") is True
|
|
222
|
+
|
|
223
|
+
def test_is_service_enabled_for_resource_security_group_enabled(self, scanner_all_enabled):
|
|
224
|
+
"""Test Security Group resource when SecurityHub is enabled."""
|
|
225
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsEc2SecurityGroup") is True
|
|
226
|
+
|
|
227
|
+
def test_is_service_enabled_for_resource_security_group_enabled_partial(self, scanner_partial_enabled):
|
|
228
|
+
"""Test Security Group resource when SecurityHub is enabled (partial config)."""
|
|
229
|
+
assert scanner_partial_enabled.is_service_enabled_for_resource("AwsEc2SecurityGroup") is True
|
|
230
|
+
|
|
231
|
+
def test_is_service_enabled_for_resource_subnet_enabled(self, scanner_all_enabled):
|
|
232
|
+
"""Test Subnet resource when VPC service is enabled."""
|
|
233
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsEc2Subnet") is True
|
|
234
|
+
|
|
235
|
+
def test_is_service_enabled_for_resource_ecr_enabled(self, scanner_all_enabled):
|
|
236
|
+
"""Test ECR resource when ECR service is enabled."""
|
|
237
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsEcrRepository") is True
|
|
238
|
+
|
|
239
|
+
def test_is_service_enabled_for_resource_cloudtrail_enabled(self, scanner_all_enabled):
|
|
240
|
+
"""Test CloudTrail resource when CloudTrail service is enabled."""
|
|
241
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsCloudTrailTrail") is True
|
|
242
|
+
|
|
243
|
+
def test_is_service_enabled_for_resource_config_enabled(self, scanner_all_enabled):
|
|
244
|
+
"""Test Config resource when Config service is enabled."""
|
|
245
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsConfigConfigurationRecorder") is True
|
|
246
|
+
|
|
247
|
+
def test_is_service_enabled_for_resource_guardduty_enabled(self, scanner_all_enabled):
|
|
248
|
+
"""Test GuardDuty resource when GuardDuty service is enabled."""
|
|
249
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsGuardDutyDetector") is True
|
|
250
|
+
|
|
251
|
+
def test_is_service_enabled_for_resource_inspector_enabled(self, scanner_all_enabled):
|
|
252
|
+
"""Test Inspector resource when Inspector service is enabled."""
|
|
253
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsInspector2") is True
|
|
254
|
+
|
|
255
|
+
def test_is_service_enabled_for_resource_audit_manager_enabled(self, scanner_all_enabled):
|
|
256
|
+
"""Test Audit Manager resource when Audit Manager service is enabled."""
|
|
257
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsAuditManagerAssessment") is True
|
|
258
|
+
|
|
259
|
+
def test_is_service_enabled_for_resource_unknown_type_defaults_true(self, scanner_all_enabled):
|
|
260
|
+
"""Test unknown resource type defaults to enabled (fail-safe)."""
|
|
261
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("AwsUnknownResource") is True
|
|
262
|
+
|
|
263
|
+
def test_is_service_enabled_for_resource_no_config_defaults_true(self, scanner_no_config):
|
|
264
|
+
"""Test resource with no config defaults to enabled (fail-safe)."""
|
|
265
|
+
assert scanner_no_config.is_service_enabled_for_resource("AwsEc2Instance") is True
|
|
266
|
+
assert scanner_no_config.is_service_enabled_for_resource("AwsS3Bucket") is True
|
|
267
|
+
assert scanner_no_config.is_service_enabled_for_resource("AwsIamUser") is True
|
|
268
|
+
|
|
269
|
+
def test_is_service_enabled_for_resource_empty_string_defaults_true(self, scanner_all_enabled):
|
|
270
|
+
"""Test empty resource type defaults to enabled (fail-safe)."""
|
|
271
|
+
assert scanner_all_enabled.is_service_enabled_for_resource("") is True
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class TestAWSInventoryIntegrationConfigReading:
|
|
275
|
+
"""Test suite for AWSInventoryIntegration config reading in fetch_findings."""
|
|
276
|
+
|
|
277
|
+
@pytest.fixture
|
|
278
|
+
def mock_app_with_severity_config(self):
|
|
279
|
+
"""Create mock app with minimumSeverity config."""
|
|
280
|
+
app = MagicMock()
|
|
281
|
+
app.config = {
|
|
282
|
+
"issues": {
|
|
283
|
+
"amazon": {
|
|
284
|
+
"status": "Open",
|
|
285
|
+
"minimumSeverity": "HIGH",
|
|
286
|
+
"low": 30,
|
|
287
|
+
"moderate": 15,
|
|
288
|
+
"high": 7,
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
"aws": {"inventory": {"enabled_services": {"compute": {"ec2": True}}}},
|
|
292
|
+
}
|
|
293
|
+
return app
|
|
294
|
+
|
|
295
|
+
@pytest.fixture
|
|
296
|
+
def scanner_with_config(self, mock_app_with_severity_config):
|
|
297
|
+
"""Create scanner with severity config."""
|
|
298
|
+
scanner = AWSInventoryIntegration(plan_id=36)
|
|
299
|
+
scanner.app = mock_app_with_severity_config
|
|
300
|
+
return scanner
|
|
301
|
+
|
|
302
|
+
def test_fetch_findings_reads_minimum_severity_from_config(self, scanner_with_config):
|
|
303
|
+
"""Test that fetch_findings reads minimumSeverity from config."""
|
|
304
|
+
mock_session = MagicMock()
|
|
305
|
+
mock_client = MagicMock()
|
|
306
|
+
mock_session.client.return_value = mock_client
|
|
307
|
+
mock_session.meta.region_name = "us-east-1"
|
|
308
|
+
|
|
309
|
+
with patch("boto3.Session", return_value=mock_session):
|
|
310
|
+
with patch("regscale.integrations.commercial.amazon.common.fetch_aws_findings") as mock_fetch:
|
|
311
|
+
mock_fetch.return_value = []
|
|
312
|
+
|
|
313
|
+
# Call fetch_findings
|
|
314
|
+
list(
|
|
315
|
+
scanner_with_config.fetch_findings(
|
|
316
|
+
region="us-east-1",
|
|
317
|
+
profile="test",
|
|
318
|
+
aws_access_key_id=None,
|
|
319
|
+
aws_secret_access_key=None,
|
|
320
|
+
aws_session_token=None,
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# Verify fetch_aws_findings was called with minimum_severity
|
|
325
|
+
mock_fetch.assert_called_once()
|
|
326
|
+
call_args = mock_fetch.call_args
|
|
327
|
+
assert call_args[1]["minimum_severity"] == "HIGH"
|
|
328
|
+
|
|
329
|
+
def test_fetch_findings_passes_none_when_no_severity_config(self):
|
|
330
|
+
"""Test that fetch_findings passes None when no minimumSeverity config."""
|
|
331
|
+
app = MagicMock()
|
|
332
|
+
app.config = {"issues": {"amazon": {"status": "Open"}}}
|
|
333
|
+
scanner = AWSInventoryIntegration(plan_id=36)
|
|
334
|
+
scanner.app = app
|
|
335
|
+
|
|
336
|
+
mock_session = MagicMock()
|
|
337
|
+
mock_client = MagicMock()
|
|
338
|
+
mock_session.client.return_value = mock_client
|
|
339
|
+
mock_session.meta.region_name = "us-east-1"
|
|
340
|
+
|
|
341
|
+
with patch("boto3.Session", return_value=mock_session):
|
|
342
|
+
with patch("regscale.integrations.commercial.amazon.common.fetch_aws_findings") as mock_fetch:
|
|
343
|
+
mock_fetch.return_value = []
|
|
344
|
+
|
|
345
|
+
# Call fetch_findings
|
|
346
|
+
list(
|
|
347
|
+
scanner.fetch_findings(
|
|
348
|
+
region="us-east-1",
|
|
349
|
+
profile="test",
|
|
350
|
+
aws_access_key_id=None,
|
|
351
|
+
aws_secret_access_key=None,
|
|
352
|
+
aws_session_token=None,
|
|
353
|
+
)
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
# Verify fetch_aws_findings was called with None
|
|
357
|
+
mock_fetch.assert_called_once()
|
|
358
|
+
call_args = mock_fetch.call_args
|
|
359
|
+
assert call_args[1]["minimum_severity"] is None
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class TestParseFindingServiceFiltering:
|
|
363
|
+
"""Test suite for parse_finding service filtering logic."""
|
|
364
|
+
|
|
365
|
+
@pytest.fixture
|
|
366
|
+
def mock_app_ec2_disabled(self):
|
|
367
|
+
"""Create mock app with EC2 service disabled."""
|
|
368
|
+
app = MagicMock()
|
|
369
|
+
app.config = {
|
|
370
|
+
"issues": {"amazon": {"status": "Open", "minimumSeverity": "LOW"}},
|
|
371
|
+
"aws": {"inventory": {"enabled_services": {"compute": {"enabled": True, "services": {"ec2": False}}}}},
|
|
372
|
+
}
|
|
373
|
+
return app
|
|
374
|
+
|
|
375
|
+
@pytest.fixture
|
|
376
|
+
def mock_app_s3_disabled(self):
|
|
377
|
+
"""Create mock app with S3 service disabled."""
|
|
378
|
+
app = MagicMock()
|
|
379
|
+
app.config = {
|
|
380
|
+
"issues": {"amazon": {"status": "Open", "minimumSeverity": "LOW"}},
|
|
381
|
+
"aws": {"inventory": {"enabled_services": {"storage": {"enabled": True, "services": {"s3": False}}}}},
|
|
382
|
+
}
|
|
383
|
+
return app
|
|
384
|
+
|
|
385
|
+
@pytest.fixture
|
|
386
|
+
def scanner_ec2_disabled(self, mock_app_ec2_disabled):
|
|
387
|
+
"""Create scanner with EC2 disabled."""
|
|
388
|
+
scanner = AWSInventoryIntegration(plan_id=36)
|
|
389
|
+
scanner.app = mock_app_ec2_disabled
|
|
390
|
+
return scanner
|
|
391
|
+
|
|
392
|
+
@pytest.fixture
|
|
393
|
+
def scanner_s3_disabled(self, mock_app_s3_disabled):
|
|
394
|
+
"""Create scanner with S3 disabled."""
|
|
395
|
+
scanner = AWSInventoryIntegration(plan_id=36)
|
|
396
|
+
scanner.app = mock_app_s3_disabled
|
|
397
|
+
return scanner
|
|
398
|
+
|
|
399
|
+
def test_parse_finding_filters_disabled_ec2_resource(self, scanner_ec2_disabled):
|
|
400
|
+
"""Test that parse_finding filters out findings for disabled EC2 service."""
|
|
401
|
+
finding = {
|
|
402
|
+
"Id": "test-finding-id",
|
|
403
|
+
"Title": "Test EC2 Finding",
|
|
404
|
+
"Description": "Test description",
|
|
405
|
+
"Severity": {"Label": "HIGH"},
|
|
406
|
+
"CreatedAt": "2024-01-15T10:30:00.000Z",
|
|
407
|
+
"Remediation": {"Recommendation": {"Text": "Fix this", "Url": "https://example.com"}},
|
|
408
|
+
"Types": ["Software and Configuration Checks"],
|
|
409
|
+
"Resources": [
|
|
410
|
+
{
|
|
411
|
+
"Type": "AwsEc2Instance",
|
|
412
|
+
"Id": "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0",
|
|
413
|
+
"Region": "us-east-1",
|
|
414
|
+
"Details": {"AwsEc2Instance": {"Type": "t3.medium"}},
|
|
415
|
+
}
|
|
416
|
+
],
|
|
417
|
+
"Compliance": {"Status": "FAILED"},
|
|
418
|
+
"FindingProviderFields": {"Severity": {"Label": "HIGH"}},
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
findings = scanner_ec2_disabled.parse_finding(finding)
|
|
422
|
+
|
|
423
|
+
# Should return empty list because EC2 is disabled
|
|
424
|
+
assert len(findings) == 0
|
|
425
|
+
|
|
426
|
+
def test_parse_finding_filters_disabled_s3_resource(self, scanner_s3_disabled):
|
|
427
|
+
"""Test that parse_finding filters out findings for disabled S3 service."""
|
|
428
|
+
finding = {
|
|
429
|
+
"Id": "test-finding-id",
|
|
430
|
+
"Title": "Test S3 Finding",
|
|
431
|
+
"Description": "Test description",
|
|
432
|
+
"Severity": {"Label": "HIGH"},
|
|
433
|
+
"CreatedAt": "2024-01-15T10:30:00.000Z",
|
|
434
|
+
"Remediation": {"Recommendation": {"Text": "Fix this", "Url": "https://example.com"}},
|
|
435
|
+
"Types": ["Software and Configuration Checks"],
|
|
436
|
+
"Resources": [
|
|
437
|
+
{
|
|
438
|
+
"Type": "AwsS3Bucket",
|
|
439
|
+
"Id": "arn:aws:s3:::test-bucket",
|
|
440
|
+
"Region": "us-east-1",
|
|
441
|
+
"Details": {"AwsS3Bucket": {"Name": "test-bucket"}},
|
|
442
|
+
}
|
|
443
|
+
],
|
|
444
|
+
"Compliance": {"Status": "FAILED"},
|
|
445
|
+
"FindingProviderFields": {"Severity": {"Label": "HIGH"}},
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
findings = scanner_s3_disabled.parse_finding(finding)
|
|
449
|
+
|
|
450
|
+
# Should return empty list because S3 is disabled
|
|
451
|
+
assert len(findings) == 0
|
|
452
|
+
|
|
453
|
+
def test_parse_finding_allows_enabled_service(self):
|
|
454
|
+
"""Test that parse_finding allows findings for enabled services."""
|
|
455
|
+
app = MagicMock()
|
|
456
|
+
app.config = {
|
|
457
|
+
"issues": {"amazon": {"status": "Open", "minimumSeverity": "LOW"}},
|
|
458
|
+
"aws": {"inventory": {"enabled_services": {"compute": {"enabled": True, "services": {"ec2": True}}}}},
|
|
459
|
+
}
|
|
460
|
+
scanner = AWSInventoryIntegration(plan_id=36)
|
|
461
|
+
scanner.app = app
|
|
462
|
+
|
|
463
|
+
finding = {
|
|
464
|
+
"Id": "test-finding-id",
|
|
465
|
+
"Title": "Test EC2 Finding",
|
|
466
|
+
"Description": "Test description",
|
|
467
|
+
"Severity": {"Label": "HIGH"},
|
|
468
|
+
"CreatedAt": "2024-01-15T10:30:00.000Z",
|
|
469
|
+
"Remediation": {"Recommendation": {"Text": "Fix this", "Url": "https://example.com"}},
|
|
470
|
+
"Types": ["Software and Configuration Checks"],
|
|
471
|
+
"Resources": [
|
|
472
|
+
{
|
|
473
|
+
"Type": "AwsEc2Instance",
|
|
474
|
+
"Id": "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0",
|
|
475
|
+
"Region": "us-east-1",
|
|
476
|
+
"Tags": {"Name": "test-instance"},
|
|
477
|
+
"Details": {"AwsEc2Instance": {"Type": "t3.medium"}},
|
|
478
|
+
}
|
|
479
|
+
],
|
|
480
|
+
"Compliance": {"Status": "FAILED"},
|
|
481
|
+
"FindingProviderFields": {"Severity": {"Label": "HIGH"}},
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
findings = scanner.parse_finding(finding)
|
|
485
|
+
|
|
486
|
+
# Should return the finding because EC2 is enabled
|
|
487
|
+
assert len(findings) == 1
|
|
488
|
+
assert findings[0].title == "Test EC2 Finding"
|
|
489
|
+
|
|
490
|
+
def test_parse_finding_with_multiple_resources_mixed_enabled(self):
|
|
491
|
+
"""Test parse_finding with multiple resources where some services are disabled."""
|
|
492
|
+
app = MagicMock()
|
|
493
|
+
app.config = {
|
|
494
|
+
"issues": {"amazon": {"status": "Open", "minimumSeverity": "LOW"}},
|
|
495
|
+
"aws": {
|
|
496
|
+
"inventory": {
|
|
497
|
+
"enabled_services": {
|
|
498
|
+
"compute": {"enabled": True, "services": {"ec2": True}},
|
|
499
|
+
"storage": {"enabled": True, "services": {"s3": False}},
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
}
|
|
504
|
+
scanner = AWSInventoryIntegration(plan_id=36)
|
|
505
|
+
scanner.app = app
|
|
506
|
+
|
|
507
|
+
finding = {
|
|
508
|
+
"Id": "test-finding-id",
|
|
509
|
+
"Title": "Test Multi-Resource Finding",
|
|
510
|
+
"Description": "Test description",
|
|
511
|
+
"Severity": {"Label": "HIGH"},
|
|
512
|
+
"CreatedAt": "2024-01-15T10:30:00.000Z",
|
|
513
|
+
"Remediation": {"Recommendation": {"Text": "Fix this", "Url": "https://example.com"}},
|
|
514
|
+
"Types": ["Software and Configuration Checks"],
|
|
515
|
+
"Resources": [
|
|
516
|
+
{
|
|
517
|
+
"Type": "AwsEc2Instance",
|
|
518
|
+
"Id": "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0",
|
|
519
|
+
"Region": "us-east-1",
|
|
520
|
+
"Tags": {"Name": "test-instance"},
|
|
521
|
+
"Details": {"AwsEc2Instance": {"Type": "t3.medium"}},
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
"Type": "AwsS3Bucket",
|
|
525
|
+
"Id": "arn:aws:s3:::test-bucket",
|
|
526
|
+
"Region": "us-east-1",
|
|
527
|
+
"Details": {"AwsS3Bucket": {"Name": "test-bucket"}},
|
|
528
|
+
},
|
|
529
|
+
],
|
|
530
|
+
"Compliance": {"Status": "FAILED"},
|
|
531
|
+
"FindingProviderFields": {"Severity": {"Label": "HIGH"}},
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
findings = scanner.parse_finding(finding)
|
|
535
|
+
|
|
536
|
+
# Should return only 1 finding for EC2 (S3 is filtered out)
|
|
537
|
+
assert len(findings) == 1
|
|
538
|
+
# The asset_identifier is the full resource ID (ARN) from the finding
|
|
539
|
+
assert findings[0].asset_identifier == "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0"
|