complio 0.1.1__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.
Files changed (79) hide show
  1. CHANGELOG.md +208 -0
  2. README.md +343 -0
  3. complio/__init__.py +48 -0
  4. complio/cli/__init__.py +0 -0
  5. complio/cli/banner.py +87 -0
  6. complio/cli/commands/__init__.py +0 -0
  7. complio/cli/commands/history.py +439 -0
  8. complio/cli/commands/scan.py +700 -0
  9. complio/cli/main.py +115 -0
  10. complio/cli/output.py +338 -0
  11. complio/config/__init__.py +17 -0
  12. complio/config/settings.py +333 -0
  13. complio/connectors/__init__.py +9 -0
  14. complio/connectors/aws/__init__.py +0 -0
  15. complio/connectors/aws/client.py +342 -0
  16. complio/connectors/base.py +135 -0
  17. complio/core/__init__.py +10 -0
  18. complio/core/registry.py +228 -0
  19. complio/core/runner.py +351 -0
  20. complio/py.typed +0 -0
  21. complio/reporters/__init__.py +7 -0
  22. complio/reporters/generator.py +417 -0
  23. complio/tests_library/__init__.py +0 -0
  24. complio/tests_library/base.py +492 -0
  25. complio/tests_library/identity/__init__.py +0 -0
  26. complio/tests_library/identity/access_key_rotation.py +302 -0
  27. complio/tests_library/identity/mfa_enforcement.py +327 -0
  28. complio/tests_library/identity/root_account_protection.py +470 -0
  29. complio/tests_library/infrastructure/__init__.py +0 -0
  30. complio/tests_library/infrastructure/cloudtrail_encryption.py +286 -0
  31. complio/tests_library/infrastructure/cloudtrail_log_validation.py +274 -0
  32. complio/tests_library/infrastructure/cloudtrail_logging.py +400 -0
  33. complio/tests_library/infrastructure/ebs_encryption.py +244 -0
  34. complio/tests_library/infrastructure/ec2_security_groups.py +321 -0
  35. complio/tests_library/infrastructure/iam_password_policy.py +460 -0
  36. complio/tests_library/infrastructure/nacl_security.py +356 -0
  37. complio/tests_library/infrastructure/rds_encryption.py +252 -0
  38. complio/tests_library/infrastructure/s3_encryption.py +301 -0
  39. complio/tests_library/infrastructure/s3_public_access.py +369 -0
  40. complio/tests_library/infrastructure/secrets_manager_encryption.py +248 -0
  41. complio/tests_library/infrastructure/vpc_flow_logs.py +287 -0
  42. complio/tests_library/logging/__init__.py +0 -0
  43. complio/tests_library/logging/cloudwatch_alarms.py +354 -0
  44. complio/tests_library/logging/cloudwatch_logs_encryption.py +281 -0
  45. complio/tests_library/logging/cloudwatch_retention.py +252 -0
  46. complio/tests_library/logging/config_enabled.py +393 -0
  47. complio/tests_library/logging/eventbridge_rules.py +460 -0
  48. complio/tests_library/logging/guardduty_enabled.py +436 -0
  49. complio/tests_library/logging/security_hub_enabled.py +416 -0
  50. complio/tests_library/logging/sns_encryption.py +273 -0
  51. complio/tests_library/network/__init__.py +0 -0
  52. complio/tests_library/network/alb_nlb_security.py +421 -0
  53. complio/tests_library/network/api_gateway_security.py +452 -0
  54. complio/tests_library/network/cloudfront_https.py +332 -0
  55. complio/tests_library/network/direct_connect_security.py +343 -0
  56. complio/tests_library/network/nacl_configuration.py +367 -0
  57. complio/tests_library/network/network_firewall.py +355 -0
  58. complio/tests_library/network/transit_gateway_security.py +318 -0
  59. complio/tests_library/network/vpc_endpoints_security.py +339 -0
  60. complio/tests_library/network/vpn_security.py +333 -0
  61. complio/tests_library/network/waf_configuration.py +428 -0
  62. complio/tests_library/security/__init__.py +0 -0
  63. complio/tests_library/security/kms_key_rotation.py +314 -0
  64. complio/tests_library/storage/__init__.py +0 -0
  65. complio/tests_library/storage/backup_encryption.py +288 -0
  66. complio/tests_library/storage/dynamodb_encryption.py +280 -0
  67. complio/tests_library/storage/efs_encryption.py +257 -0
  68. complio/tests_library/storage/elasticache_encryption.py +370 -0
  69. complio/tests_library/storage/redshift_encryption.py +252 -0
  70. complio/tests_library/storage/s3_versioning.py +264 -0
  71. complio/utils/__init__.py +26 -0
  72. complio/utils/errors.py +179 -0
  73. complio/utils/exceptions.py +151 -0
  74. complio/utils/history.py +243 -0
  75. complio/utils/logger.py +391 -0
  76. complio-0.1.1.dist-info/METADATA +385 -0
  77. complio-0.1.1.dist-info/RECORD +79 -0
  78. complio-0.1.1.dist-info/WHEEL +4 -0
  79. complio-0.1.1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,416 @@
1
+ """
2
+ AWS Security Hub enabled compliance test.
3
+
4
+ Checks that AWS Security Hub is enabled for centralized security monitoring.
5
+
6
+ ISO 27001 Control: A.8.16 - Monitoring activities
7
+ Requirement: Security Hub should be enabled for aggregated security findings
8
+
9
+ Example:
10
+ >>> from complio.connectors.aws.client import AWSConnector
11
+ >>> from complio.tests_library.logging.security_hub_enabled import SecurityHubEnabledTest
12
+ >>>
13
+ >>> connector = AWSConnector("production", "us-east-1")
14
+ >>> connector.connect()
15
+ >>>
16
+ >>> test = SecurityHubEnabledTest(connector)
17
+ >>> result = test.run()
18
+ >>> print(f"Passed: {result.passed}, Score: {result.score}")
19
+ """
20
+
21
+ from typing import Any, Dict, List
22
+
23
+ from botocore.exceptions import ClientError
24
+
25
+ from complio.connectors.aws.client import AWSConnector
26
+ from complio.tests_library.base import (
27
+ ComplianceTest,
28
+ Severity,
29
+ TestResult,
30
+ TestStatus,
31
+ )
32
+
33
+
34
+ class SecurityHubEnabledTest(ComplianceTest):
35
+ """Test for AWS Security Hub enabled compliance.
36
+
37
+ Verifies that AWS Security Hub is properly enabled:
38
+ - Security Hub should be enabled
39
+ - Security standards should be enabled (CIS, AWS Foundational Security)
40
+ - Product integrations should be enabled (GuardDuty, Config, etc.)
41
+ - Findings should be aggregated and actionable
42
+
43
+ Compliance Requirements:
44
+ - Security Hub enabled in region
45
+ - At least one security standard enabled
46
+ - Product integrations enabled
47
+ - Findings aggregation configured
48
+
49
+ Scoring:
50
+ - 100% if Security Hub fully enabled with standards
51
+ - Partial score for basic enablement
52
+ - 0% if Security Hub not enabled
53
+
54
+ Example:
55
+ >>> test = SecurityHubEnabledTest(connector)
56
+ >>> result = test.execute()
57
+ >>> for finding in result.findings:
58
+ ... print(f"{finding.resource_id}: {finding.title}")
59
+ """
60
+
61
+ def __init__(self, connector: AWSConnector) -> None:
62
+ """Initialize Security Hub enabled test.
63
+
64
+ Args:
65
+ connector: AWS connector instance
66
+ """
67
+ super().__init__(
68
+ test_id="security_hub_enabled",
69
+ test_name="AWS Security Hub Enabled Check",
70
+ description="Verify AWS Security Hub is enabled for centralized security monitoring",
71
+ control_id="A.8.16",
72
+ connector=connector,
73
+ scope="regional",
74
+ )
75
+
76
+ def execute(self) -> TestResult:
77
+ """Execute Security Hub enabled compliance test.
78
+
79
+ Returns:
80
+ TestResult with findings if Security Hub is not properly enabled
81
+
82
+ Example:
83
+ >>> test = SecurityHubEnabledTest(connector)
84
+ >>> result = test.execute()
85
+ >>> print(result.score)
86
+ 100.0
87
+ """
88
+ result = TestResult(
89
+ test_id=self.test_id,
90
+ test_name=self.test_name,
91
+ status=TestStatus.PASSED,
92
+ passed=True,
93
+ score=100.0,
94
+ )
95
+
96
+ try:
97
+ # Get Security Hub client
98
+ securityhub_client = self.connector.get_client("securityhub")
99
+
100
+ # Check if Security Hub is enabled
101
+ self.logger.info("checking_security_hub_status")
102
+
103
+ try:
104
+ hub_response = securityhub_client.describe_hub()
105
+ hub_arn = hub_response.get("HubArn", "")
106
+ subscribed_at = hub_response.get("SubscribedAt", "")
107
+ auto_enable_controls = hub_response.get("AutoEnableControls", False)
108
+
109
+ security_hub_enabled = True
110
+ result.resources_scanned += 1
111
+
112
+ except ClientError as e:
113
+ error_code = e.response.get("Error", {}).get("Code")
114
+ if error_code == "InvalidAccessException":
115
+ security_hub_enabled = False
116
+ self.logger.warning("security_hub_not_enabled")
117
+ else:
118
+ raise
119
+
120
+ if not security_hub_enabled:
121
+ # Create finding for Security Hub not enabled
122
+ finding = self.create_finding(
123
+ resource_id="security-hub",
124
+ resource_type="security_hub",
125
+ severity=Severity.HIGH,
126
+ title="AWS Security Hub not enabled",
127
+ description="AWS Security Hub is not enabled in this region. Security Hub provides a "
128
+ "comprehensive view of security alerts and compliance status across AWS accounts. "
129
+ "It aggregates findings from GuardDuty, Inspector, Macie, IAM Access Analyzer, "
130
+ "AWS Firewall Manager, and third-party products. Without Security Hub, security "
131
+ "findings are dispersed and difficult to manage. ISO 27001 A.8.16 requires "
132
+ "centralized monitoring of security events.",
133
+ remediation=(
134
+ "Enable AWS Security Hub in this region:\n\n"
135
+ "1. Enable Security Hub:\n"
136
+ "aws securityhub enable-security-hub\n\n"
137
+ "2. Enable security standards:\n"
138
+ "# AWS Foundational Security Best Practices\n"
139
+ "aws securityhub batch-enable-standards \\\n"
140
+ " --standards-subscription-requests \\\n"
141
+ " StandardsArn=arn:aws:securityhub:REGION::standards/aws-foundational-security-best-practices/v/1.0.0\n\n"
142
+ "# CIS AWS Foundations Benchmark\n"
143
+ "aws securityhub batch-enable-standards \\\n"
144
+ " --standards-subscription-requests \\\n"
145
+ " StandardsArn=arn:aws:securityhub:REGION::standards/cis-aws-foundations-benchmark/v/1.2.0\n\n"
146
+ "# PCI-DSS (if applicable)\n"
147
+ "aws securityhub batch-enable-standards \\\n"
148
+ " --standards-subscription-requests \\\n"
149
+ " StandardsArn=arn:aws:securityhub:REGION::standards/pci-dss/v/3.2.1\n\n"
150
+ "3. Enable product integrations:\n"
151
+ "# GuardDuty\n"
152
+ "aws securityhub enable-import-findings-for-product \\\n"
153
+ " --product-arn arn:aws:securityhub:REGION::product/aws/guardduty\n\n"
154
+ "# AWS Config\n"
155
+ "aws securityhub enable-import-findings-for-product \\\n"
156
+ " --product-arn arn:aws:securityhub:REGION::product/aws/config\n\n"
157
+ "# IAM Access Analyzer\n"
158
+ "aws securityhub enable-import-findings-for-product \\\n"
159
+ " --product-arn arn:aws:securityhub:REGION::product/aws/access-analyzer\n\n"
160
+ "4. Configure findings aggregation (for multi-region):\n"
161
+ "aws securityhub create-finding-aggregator \\\n"
162
+ " --region-linking-mode ALL_REGIONS\n\n"
163
+ "Or use AWS Console:\n"
164
+ "1. Go to AWS Security Hub console\n"
165
+ "2. Click 'Enable Security Hub'\n"
166
+ "3. Select security standards to enable:\n"
167
+ " - AWS Foundational Security Best Practices (recommended)\n"
168
+ " - CIS AWS Foundations Benchmark\n"
169
+ " - PCI-DSS (if applicable)\n"
170
+ "4. Enable product integrations:\n"
171
+ " - Go to Integrations\n"
172
+ " - Enable AWS services (GuardDuty, Config, IAM Access Analyzer)\n"
173
+ " - Enable third-party products if needed\n"
174
+ "5. Configure settings:\n"
175
+ " - Enable auto-enable for new controls\n"
176
+ " - Set up finding aggregation for multi-region\n\n"
177
+ "Best practices:\n"
178
+ "- Enable Security Hub in all regions\n"
179
+ "- Use AWS Organizations for multi-account setup\n"
180
+ "- Enable delegated administrator account\n"
181
+ "- Configure automated remediation with EventBridge + Lambda\n"
182
+ "- Set up custom insights for your environment\n"
183
+ "- Integrate with SIEM tools (Splunk, etc.)\n"
184
+ "- Review findings daily/weekly\n"
185
+ "- Suppress accepted risks with proper documentation\n"
186
+ "- Use Security Hub automation rules\n"
187
+ "- Export findings to S3 for long-term analysis\n\n"
188
+ "Security standards comparison:\n"
189
+ "AWS Foundational Security Best Practices:\n"
190
+ "- 200+ automated checks\n"
191
+ "- AWS best practices\n"
192
+ "- Continuously updated by AWS\n"
193
+ "- Recommended for all accounts\n\n"
194
+ "CIS AWS Foundations Benchmark:\n"
195
+ "- Industry standard\n"
196
+ "- Required for compliance certifications\n"
197
+ "- Periodic updates\n"
198
+ "- Focus on foundational security\n\n"
199
+ "PCI-DSS:\n"
200
+ "- Required for payment card processing\n"
201
+ "- Specific compliance requirements\n"
202
+ "- More restrictive controls\n\n"
203
+ "Key integrations:\n"
204
+ "- GuardDuty: Threat detection\n"
205
+ "- Inspector: Vulnerability scanning\n"
206
+ "- Macie: Data security\n"
207
+ "- Config: Configuration compliance\n"
208
+ "- IAM Access Analyzer: Permissions analysis\n"
209
+ "- Firewall Manager: Network security\n"
210
+ "- Systems Manager: Patch compliance\n\n"
211
+ "Cost considerations:\n"
212
+ "- Free 30-day trial\n"
213
+ "- Pricing:\n"
214
+ " • Security checks: $0.0010 per check\n"
215
+ " • Finding ingestion: $0.00003 per finding\n"
216
+ "- Typical cost: $20-50 per account per month\n"
217
+ "- Consolidated billing with Organizations"
218
+ ),
219
+ evidence=None
220
+ )
221
+ result.add_finding(finding)
222
+ result.score = 0.0
223
+ result.passed = False
224
+ result.status = TestStatus.FAILED
225
+ result.metadata = {
226
+ "security_hub_enabled": False,
227
+ "message": "Security Hub not enabled in region"
228
+ }
229
+ return result
230
+
231
+ # Security Hub is enabled, check configuration
232
+ issues = []
233
+ severity = Severity.MEDIUM
234
+ score_points = 0 # Out of 100
235
+
236
+ # Hub is enabled (40 points)
237
+ score_points += 40
238
+
239
+ # Check enabled standards (30 points)
240
+ self.logger.info("checking_enabled_standards")
241
+ standards_response = securityhub_client.get_enabled_standards()
242
+ enabled_standards = standards_response.get("StandardsSubscriptions", [])
243
+
244
+ active_standards = [
245
+ s for s in enabled_standards
246
+ if s.get("StandardsStatus") == "READY"
247
+ ]
248
+
249
+ if len(active_standards) > 0:
250
+ score_points += 30
251
+ else:
252
+ issues.append("No security standards enabled")
253
+ severity = Severity.HIGH
254
+
255
+ # Check product integrations (20 points)
256
+ self.logger.info("checking_product_integrations")
257
+ try:
258
+ integrations_response = securityhub_client.list_enabled_products_for_import()
259
+ enabled_products = integrations_response.get("ProductSubscriptions", [])
260
+
261
+ if len(enabled_products) > 0:
262
+ score_points += 20
263
+ else:
264
+ issues.append("No product integrations enabled")
265
+ except ClientError:
266
+ issues.append("Could not check product integrations")
267
+
268
+ # Check auto-enable controls (10 points - bonus)
269
+ if auto_enable_controls:
270
+ score_points += 10
271
+
272
+ # Create evidence
273
+ evidence = self.create_evidence(
274
+ resource_id=hub_arn,
275
+ resource_type="security_hub",
276
+ data={
277
+ "hub_arn": hub_arn,
278
+ "subscribed_at": subscribed_at,
279
+ "auto_enable_controls": auto_enable_controls,
280
+ "enabled_standards_count": len(active_standards),
281
+ "enabled_products_count": len(enabled_products) if 'enabled_products' in locals() else 0,
282
+ "has_issues": len(issues) > 0,
283
+ "issues": issues,
284
+ "security_score": score_points,
285
+ }
286
+ )
287
+ result.add_evidence(evidence)
288
+
289
+ if len(issues) > 0:
290
+ # Create finding for Security Hub configuration issues
291
+ finding = self.create_finding(
292
+ resource_id=hub_arn,
293
+ resource_type="security_hub",
294
+ severity=severity,
295
+ title="AWS Security Hub has configuration issues",
296
+ description=f"AWS Security Hub is enabled but has configuration issues: {'; '.join(issues)}. "
297
+ "Security Hub should have security standards enabled and product integrations "
298
+ "configured to provide comprehensive security monitoring. ISO 27001 A.8.16 requires "
299
+ "effective monitoring and correlation of security events.",
300
+ remediation=(
301
+ "Improve Security Hub configuration:\n\n"
302
+ "1. Enable security standards:\n"
303
+ "aws securityhub batch-enable-standards \\\n"
304
+ " --standards-subscription-requests \\\n"
305
+ " StandardsArn=arn:aws:securityhub:REGION::standards/aws-foundational-security-best-practices/v/1.0.0 \\\n"
306
+ " StandardsArn=arn:aws:securityhub:REGION::standards/cis-aws-foundations-benchmark/v/1.2.0\n\n"
307
+ "2. Enable product integrations:\n"
308
+ "aws securityhub enable-import-findings-for-product \\\n"
309
+ " --product-arn arn:aws:securityhub:REGION::product/aws/guardduty\n\n"
310
+ "aws securityhub enable-import-findings-for-product \\\n"
311
+ " --product-arn arn:aws:securityhub:REGION::product/aws/config\n\n"
312
+ "3. Enable auto-enable controls:\n"
313
+ "aws securityhub update-security-hub-configuration \\\n"
314
+ " --auto-enable-controls\n\n"
315
+ "Or use AWS Console:\n"
316
+ "1. Go to Security Hub console\n"
317
+ "2. Click 'Security standards'\n"
318
+ "3. Enable recommended standards\n"
319
+ "4. Go to 'Integrations'\n"
320
+ "5. Enable AWS service integrations\n"
321
+ "6. Go to 'Settings'\n"
322
+ "7. Enable 'Auto-enable new controls'\n\n"
323
+ "Monitor and respond:\n"
324
+ "- Review critical/high findings daily\n"
325
+ "- Create EventBridge rules for automated responses\n"
326
+ "- Use custom insights for trending\n"
327
+ "- Suppress accepted risks with notes\n"
328
+ "- Track compliance scores over time"
329
+ ),
330
+ evidence=evidence
331
+ )
332
+ result.add_finding(finding)
333
+
334
+ result.score = score_points
335
+ result.passed = score_points >= 80 # Require 80% for pass
336
+ result.status = TestStatus.PASSED if result.passed else TestStatus.FAILED
337
+
338
+ self.logger.warning(
339
+ "security_hub_has_issues",
340
+ hub_arn=hub_arn,
341
+ issues=issues,
342
+ score=score_points
343
+ )
344
+ else:
345
+ # Security Hub is properly configured
346
+ result.score = 100.0
347
+ result.passed = True
348
+ result.status = TestStatus.PASSED
349
+
350
+ self.logger.info(
351
+ "security_hub_properly_configured",
352
+ hub_arn=hub_arn
353
+ )
354
+
355
+ # Add metadata
356
+ result.metadata = {
357
+ "security_hub_enabled": True,
358
+ "hub_arn": hub_arn,
359
+ "enabled_standards": len(active_standards),
360
+ "enabled_products": len(enabled_products) if 'enabled_products' in locals() else 0,
361
+ "auto_enable_controls": auto_enable_controls,
362
+ "security_score": score_points,
363
+ "compliance_percentage": result.score,
364
+ }
365
+
366
+ self.logger.info(
367
+ "security_hub_enabled_test_completed",
368
+ hub_arn=hub_arn,
369
+ enabled_standards=len(active_standards),
370
+ score=result.score,
371
+ passed=result.passed
372
+ )
373
+
374
+ except ClientError as e:
375
+ error_code = e.response.get("Error", {}).get("Code")
376
+ self.logger.error("security_hub_enabled_test_error", error_code=error_code, error=str(e))
377
+ result.status = TestStatus.ERROR
378
+ result.passed = False
379
+ result.score = 0.0
380
+ result.error_message = f"AWS API Error: {error_code} - {str(e)}"
381
+
382
+ except Exception as e:
383
+ self.logger.error("security_hub_enabled_test_error", error=str(e))
384
+ result.status = TestStatus.ERROR
385
+ result.passed = False
386
+ result.score = 0.0
387
+ result.error_message = str(e)
388
+
389
+ return result
390
+
391
+
392
+ # ============================================================================
393
+ # CONVENIENCE FUNCTION
394
+ # ============================================================================
395
+
396
+
397
+ def run_security_hub_enabled_test(connector: AWSConnector) -> TestResult:
398
+ """Run AWS Security Hub enabled compliance test.
399
+
400
+ Convenience function for running the test.
401
+
402
+ Args:
403
+ connector: AWS connector
404
+
405
+ Returns:
406
+ TestResult
407
+
408
+ Example:
409
+ >>> from complio.connectors.aws.client import AWSConnector
410
+ >>> connector = AWSConnector("production", "us-east-1")
411
+ >>> connector.connect()
412
+ >>> result = run_security_hub_enabled_test(connector)
413
+ >>> print(f"Score: {result.score}%")
414
+ """
415
+ test = SecurityHubEnabledTest(connector)
416
+ return test.execute()
@@ -0,0 +1,273 @@
1
+ """
2
+ SNS topic encryption compliance test.
3
+
4
+ Checks that all SNS topics use encryption at rest.
5
+
6
+ ISO 27001 Control: A.8.24 - Use of cryptography
7
+ Requirement: SNS topics must be encrypted with KMS keys
8
+
9
+ Example:
10
+ >>> from complio.connectors.aws.client import AWSConnector
11
+ >>> from complio.tests_library.logging.sns_encryption import SNSEncryptionTest
12
+ >>>
13
+ >>> connector = AWSConnector("production", "us-east-1")
14
+ >>> connector.connect()
15
+ >>>
16
+ >>> test = SNSEncryptionTest(connector)
17
+ >>> result = test.run()
18
+ >>> print(f"Passed: {result.passed}, Score: {result.score}")
19
+ """
20
+
21
+ from typing import Any, Dict
22
+
23
+ from botocore.exceptions import ClientError
24
+
25
+ from complio.connectors.aws.client import AWSConnector
26
+ from complio.tests_library.base import (
27
+ ComplianceTest,
28
+ Severity,
29
+ TestResult,
30
+ TestStatus,
31
+ )
32
+
33
+
34
+ class SNSEncryptionTest(ComplianceTest):
35
+ """Test for SNS topic encryption compliance.
36
+
37
+ Verifies that all SNS topics use server-side encryption with KMS keys
38
+ to protect message data at rest.
39
+
40
+ Compliance Requirements:
41
+ - All SNS topics must have KmsMasterKeyId configured
42
+ - Encryption protects sensitive notification data
43
+ - Customer-managed KMS keys recommended for better control
44
+
45
+ Scoring:
46
+ - 100% if all topics are encrypted
47
+ - Proportional score based on compliant/total ratio
48
+ - 100% if no topics exist
49
+
50
+ Example:
51
+ >>> test = SNSEncryptionTest(connector)
52
+ >>> result = test.execute()
53
+ >>> for finding in result.findings:
54
+ ... print(f"{finding.resource_id}: {finding.title}")
55
+ """
56
+
57
+ def __init__(self, connector: AWSConnector) -> None:
58
+ """Initialize SNS encryption test.
59
+
60
+ Args:
61
+ connector: AWS connector instance
62
+ """
63
+ super().__init__(
64
+ test_id="sns_encryption",
65
+ test_name="SNS Topic Encryption Check",
66
+ description="Verify all SNS topics use encryption at rest with KMS",
67
+ control_id="A.8.24",
68
+ connector=connector,
69
+ scope="regional",
70
+ )
71
+
72
+ def execute(self) -> TestResult:
73
+ """Execute SNS topic encryption compliance test.
74
+
75
+ Returns:
76
+ TestResult with findings for unencrypted topics
77
+
78
+ Example:
79
+ >>> test = SNSEncryptionTest(connector)
80
+ >>> result = test.execute()
81
+ >>> print(result.score)
82
+ 100.0
83
+ """
84
+ result = TestResult(
85
+ test_id=self.test_id,
86
+ test_name=self.test_name,
87
+ status=TestStatus.PASSED,
88
+ passed=True,
89
+ score=100.0,
90
+ )
91
+
92
+ try:
93
+ # Get SNS client
94
+ sns_client = self.connector.get_client("sns")
95
+
96
+ # List all SNS topics
97
+ self.logger.info("listing_sns_topics")
98
+ topics = []
99
+
100
+ paginator = sns_client.get_paginator("list_topics")
101
+ for page in paginator.paginate():
102
+ topics.extend(page.get("Topics", []))
103
+
104
+ if not topics:
105
+ self.logger.info("no_sns_topics_found")
106
+ result.metadata["message"] = "No SNS topics found in region"
107
+ return result
108
+
109
+ self.logger.info("sns_topics_found", count=len(topics))
110
+
111
+ # Check encryption for each topic
112
+ encrypted_count = 0
113
+
114
+ for topic in topics:
115
+ topic_arn = topic["TopicArn"]
116
+ result.resources_scanned += 1
117
+
118
+ try:
119
+ # Get topic attributes to check encryption
120
+ attributes_response = sns_client.get_topic_attributes(TopicArn=topic_arn)
121
+ attributes = attributes_response.get("Attributes", {})
122
+
123
+ # Check if KMS master key is configured
124
+ kms_master_key_id = attributes.get("KmsMasterKeyId")
125
+ is_encrypted = kms_master_key_id is not None and kms_master_key_id != ""
126
+
127
+ # Create evidence
128
+ evidence = self.create_evidence(
129
+ resource_id=topic_arn,
130
+ resource_type="sns_topic",
131
+ data={
132
+ "topic_arn": topic_arn,
133
+ "kms_master_key_id": kms_master_key_id,
134
+ "is_encrypted": is_encrypted,
135
+ "display_name": attributes.get("DisplayName"),
136
+ "subscriptions_confirmed": attributes.get("SubscriptionsConfirmed", "0"),
137
+ }
138
+ )
139
+ result.add_evidence(evidence)
140
+
141
+ if is_encrypted:
142
+ encrypted_count += 1
143
+ self.logger.debug(
144
+ "sns_topic_encrypted",
145
+ topic_arn=topic_arn,
146
+ kms_key_id=kms_master_key_id
147
+ )
148
+ else:
149
+ # Create finding for unencrypted topic
150
+ finding = self.create_finding(
151
+ resource_id=topic_arn,
152
+ resource_type="sns_topic",
153
+ severity=Severity.HIGH,
154
+ title="SNS topic encryption not enabled",
155
+ description=f"SNS topic '{topic_arn}' does not have encryption enabled. "
156
+ "Without encryption, messages published to this topic are stored "
157
+ "unencrypted at rest, potentially exposing sensitive notification "
158
+ "data. SNS encryption with AWS KMS protects message data and metadata "
159
+ "at rest. ISO 27001 A.8.24 requires cryptographic controls for "
160
+ "protecting sensitive information.",
161
+ remediation=(
162
+ f"Enable encryption for SNS topic:\n\n"
163
+ "Using AWS CLI:\n"
164
+ f"aws sns set-topic-attributes \\\n"
165
+ f" --topic-arn {topic_arn} \\\n"
166
+ " --attribute-name KmsMasterKeyId \\\n"
167
+ " --attribute-value alias/aws/sns\n\n"
168
+ "Or use a customer-managed KMS key (recommended):\n"
169
+ f"aws sns set-topic-attributes \\\n"
170
+ f" --topic-arn {topic_arn} \\\n"
171
+ " --attribute-name KmsMasterKeyId \\\n"
172
+ " --attribute-value arn:aws:kms:REGION:ACCOUNT:key/KEY-ID\n\n"
173
+ "Or use AWS Console:\n"
174
+ "1. Go to AWS SNS console\n"
175
+ "2. Select the topic\n"
176
+ "3. Click 'Edit'\n"
177
+ "4. Expand 'Encryption'\n"
178
+ "5. Select 'Enable encryption'\n"
179
+ "6. Choose a KMS key (AWS managed or customer managed)\n"
180
+ "7. Click 'Save changes'\n\n"
181
+ "Important notes:\n"
182
+ "- Update KMS key policy to allow SNS to use it\n"
183
+ "- Publishers need kms:GenerateDataKey permission\n"
184
+ "- Subscribers need kms:Decrypt permission\n"
185
+ "- Consider using customer-managed keys for better audit trail"
186
+ ),
187
+ evidence=evidence
188
+ )
189
+ result.add_finding(finding)
190
+
191
+ self.logger.warning(
192
+ "sns_topic_not_encrypted",
193
+ topic_arn=topic_arn
194
+ )
195
+
196
+ except ClientError as e:
197
+ error_code = e.response.get("Error", {}).get("Code")
198
+ if error_code in ["NotFound", "AuthorizationError"]:
199
+ self.logger.warning(
200
+ "sns_topic_access_error",
201
+ topic_arn=topic_arn,
202
+ error_code=error_code
203
+ )
204
+ continue
205
+ else:
206
+ raise
207
+
208
+ # Calculate compliance score
209
+ result.score = (encrypted_count / len(topics)) * 100
210
+
211
+ # Determine pass/fail
212
+ result.passed = encrypted_count == len(topics)
213
+ result.status = TestStatus.PASSED if result.passed else TestStatus.FAILED
214
+
215
+ # Add metadata
216
+ result.metadata = {
217
+ "total_topics": len(topics),
218
+ "encrypted_topics": encrypted_count,
219
+ "unencrypted_topics": len(topics) - encrypted_count,
220
+ "compliance_percentage": result.score,
221
+ }
222
+
223
+ self.logger.info(
224
+ "sns_encryption_test_completed",
225
+ total_topics=len(topics),
226
+ encrypted=encrypted_count,
227
+ score=result.score,
228
+ passed=result.passed
229
+ )
230
+
231
+ except ClientError as e:
232
+ error_code = e.response.get("Error", {}).get("Code")
233
+ self.logger.error("sns_encryption_test_error", error_code=error_code, error=str(e))
234
+ result.status = TestStatus.ERROR
235
+ result.passed = False
236
+ result.score = 0.0
237
+ result.error_message = f"AWS API Error: {error_code} - {str(e)}"
238
+
239
+ except Exception as e:
240
+ self.logger.error("sns_encryption_test_error", error=str(e))
241
+ result.status = TestStatus.ERROR
242
+ result.passed = False
243
+ result.score = 0.0
244
+ result.error_message = str(e)
245
+
246
+ return result
247
+
248
+
249
+ # ============================================================================
250
+ # CONVENIENCE FUNCTION
251
+ # ============================================================================
252
+
253
+
254
+ def run_sns_encryption_test(connector: AWSConnector) -> TestResult:
255
+ """Run SNS topic encryption compliance test.
256
+
257
+ Convenience function for running the test.
258
+
259
+ Args:
260
+ connector: AWS connector
261
+
262
+ Returns:
263
+ TestResult
264
+
265
+ Example:
266
+ >>> from complio.connectors.aws.client import AWSConnector
267
+ >>> connector = AWSConnector("production", "us-east-1")
268
+ >>> connector.connect()
269
+ >>> result = run_sns_encryption_test(connector)
270
+ >>> print(f"Score: {result.score}%")
271
+ """
272
+ test = SNSEncryptionTest(connector)
273
+ return test.execute()