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.

Files changed (140) hide show
  1. regscale/_version.py +1 -1
  2. regscale/core/app/application.py +1 -0
  3. regscale/core/app/internal/control_editor.py +73 -21
  4. regscale/core/app/internal/login.py +4 -1
  5. regscale/core/app/internal/model_editor.py +219 -64
  6. regscale/core/app/utils/app_utils.py +11 -2
  7. regscale/core/login.py +21 -4
  8. regscale/core/utils/date.py +77 -1
  9. regscale/dev/cli.py +26 -0
  10. regscale/dev/version.py +72 -0
  11. regscale/integrations/commercial/__init__.py +15 -1
  12. regscale/integrations/commercial/amazon/amazon/__init__.py +0 -0
  13. regscale/integrations/commercial/amazon/amazon/common.py +204 -0
  14. regscale/integrations/commercial/amazon/common.py +48 -58
  15. regscale/integrations/commercial/aws/audit_manager_compliance.py +2671 -0
  16. regscale/integrations/commercial/aws/cli.py +3093 -55
  17. regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
  18. regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
  19. regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
  20. regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
  21. regscale/integrations/commercial/aws/config_compliance.py +914 -0
  22. regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
  23. regscale/integrations/commercial/aws/evidence_generator.py +283 -0
  24. regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
  25. regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
  26. regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
  27. regscale/integrations/commercial/aws/iam_evidence.py +574 -0
  28. regscale/integrations/commercial/aws/inventory/__init__.py +223 -22
  29. regscale/integrations/commercial/aws/inventory/base.py +107 -5
  30. regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
  31. regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
  32. regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
  33. regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
  34. regscale/integrations/commercial/aws/inventory/resources/compute.py +66 -9
  35. regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
  36. regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
  37. regscale/integrations/commercial/aws/inventory/resources/database.py +106 -31
  38. regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
  39. regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
  40. regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
  41. regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
  42. regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
  43. regscale/integrations/commercial/aws/inventory/resources/networking.py +103 -67
  44. regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
  45. regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
  46. regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
  47. regscale/integrations/commercial/aws/inventory/resources/storage.py +53 -29
  48. regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
  49. regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
  50. regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
  51. regscale/integrations/commercial/aws/kms_evidence.py +879 -0
  52. regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
  53. regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
  54. regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
  55. regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
  56. regscale/integrations/commercial/aws/org_evidence.py +666 -0
  57. regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
  58. regscale/integrations/commercial/aws/s3_evidence.py +632 -0
  59. regscale/integrations/commercial/aws/scanner.py +853 -205
  60. regscale/integrations/commercial/aws/security_hub.py +319 -0
  61. regscale/integrations/commercial/aws/session_manager.py +282 -0
  62. regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
  63. regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
  64. regscale/integrations/commercial/synqly/query_builder.py +4 -1
  65. regscale/integrations/compliance_integration.py +308 -38
  66. regscale/integrations/control_matcher.py +78 -23
  67. regscale/integrations/due_date_handler.py +3 -0
  68. regscale/integrations/public/csam/csam.py +572 -763
  69. regscale/integrations/public/csam/csam_agency_defined.py +179 -0
  70. regscale/integrations/public/csam/csam_common.py +154 -0
  71. regscale/integrations/public/csam/csam_controls.py +432 -0
  72. regscale/integrations/public/csam/csam_poam.py +124 -0
  73. regscale/integrations/public/fedramp/click.py +17 -4
  74. regscale/integrations/public/fedramp/fedramp_cis_crm.py +271 -62
  75. regscale/integrations/public/fedramp/poam/scanner.py +74 -7
  76. regscale/integrations/scanner_integration.py +415 -85
  77. regscale/models/integration_models/cisa_kev_data.json +80 -20
  78. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  79. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +44 -3
  80. regscale/models/integration_models/synqly_models/ocsf_mapper.py +41 -12
  81. regscale/models/platform.py +3 -0
  82. regscale/models/regscale_models/__init__.py +5 -0
  83. regscale/models/regscale_models/assessment.py +2 -1
  84. regscale/models/regscale_models/component.py +1 -1
  85. regscale/models/regscale_models/control_implementation.py +55 -24
  86. regscale/models/regscale_models/control_objective.py +74 -5
  87. regscale/models/regscale_models/file.py +2 -0
  88. regscale/models/regscale_models/issue.py +2 -5
  89. regscale/models/regscale_models/organization.py +3 -0
  90. regscale/models/regscale_models/regscale_model.py +17 -5
  91. regscale/models/regscale_models/security_plan.py +1 -0
  92. regscale/regscale.py +11 -1
  93. {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.28.0.0.dist-info}/METADATA +1 -1
  94. {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.28.0.0.dist-info}/RECORD +140 -57
  95. tests/regscale/core/test_login.py +171 -4
  96. tests/regscale/integrations/commercial/aws/__init__.py +0 -0
  97. tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
  98. tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
  99. tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
  100. tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
  101. tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
  102. tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
  103. tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
  104. tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
  105. tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
  106. tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
  107. tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
  108. tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
  109. tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
  110. tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
  111. tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
  112. tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
  113. tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
  114. tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
  115. tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
  116. tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
  117. tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
  118. tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
  119. tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
  120. tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
  121. tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
  122. tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
  123. tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
  124. tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
  125. tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
  126. tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
  127. tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
  128. tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
  129. tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
  130. tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
  131. tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
  132. tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
  133. tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
  134. tests/regscale/integrations/commercial/test_aws.py +55 -56
  135. tests/regscale/integrations/test_control_matcher.py +24 -0
  136. tests/regscale/models/test_control_implementation.py +118 -3
  137. {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.28.0.0.dist-info}/LICENSE +0 -0
  138. {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.28.0.0.dist-info}/WHEEL +0 -0
  139. {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.28.0.0.dist-info}/entry_points.txt +0 -0
  140. {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"