regscale-cli 6.27.3.0__py3-none-any.whl → 6.28.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of regscale-cli might be problematic. Click here for more details.

Files changed (112) hide show
  1. regscale/_version.py +1 -1
  2. regscale/core/app/utils/app_utils.py +11 -2
  3. regscale/dev/cli.py +26 -0
  4. regscale/dev/version.py +72 -0
  5. regscale/integrations/commercial/__init__.py +15 -1
  6. regscale/integrations/commercial/amazon/amazon/__init__.py +0 -0
  7. regscale/integrations/commercial/amazon/amazon/common.py +204 -0
  8. regscale/integrations/commercial/amazon/common.py +48 -58
  9. regscale/integrations/commercial/aws/audit_manager_compliance.py +2671 -0
  10. regscale/integrations/commercial/aws/cli.py +3093 -55
  11. regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
  12. regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
  13. regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
  14. regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
  15. regscale/integrations/commercial/aws/config_compliance.py +914 -0
  16. regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
  17. regscale/integrations/commercial/aws/evidence_generator.py +283 -0
  18. regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
  19. regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
  20. regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
  21. regscale/integrations/commercial/aws/iam_evidence.py +574 -0
  22. regscale/integrations/commercial/aws/inventory/__init__.py +223 -22
  23. regscale/integrations/commercial/aws/inventory/base.py +107 -5
  24. regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
  25. regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
  26. regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
  27. regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
  28. regscale/integrations/commercial/aws/inventory/resources/compute.py +66 -9
  29. regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
  30. regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
  31. regscale/integrations/commercial/aws/inventory/resources/database.py +106 -31
  32. regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
  33. regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
  34. regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
  35. regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
  36. regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
  37. regscale/integrations/commercial/aws/inventory/resources/networking.py +103 -67
  38. regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
  39. regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
  40. regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
  41. regscale/integrations/commercial/aws/inventory/resources/storage.py +53 -29
  42. regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
  43. regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
  44. regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
  45. regscale/integrations/commercial/aws/kms_evidence.py +879 -0
  46. regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
  47. regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
  48. regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
  49. regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
  50. regscale/integrations/commercial/aws/org_evidence.py +666 -0
  51. regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
  52. regscale/integrations/commercial/aws/s3_evidence.py +632 -0
  53. regscale/integrations/commercial/aws/scanner.py +851 -206
  54. regscale/integrations/commercial/aws/security_hub.py +319 -0
  55. regscale/integrations/commercial/aws/session_manager.py +282 -0
  56. regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
  57. regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
  58. regscale/integrations/compliance_integration.py +308 -38
  59. regscale/integrations/due_date_handler.py +3 -0
  60. regscale/integrations/scanner_integration.py +399 -84
  61. regscale/models/integration_models/cisa_kev_data.json +34 -4
  62. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  63. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +17 -9
  64. regscale/models/regscale_models/assessment.py +2 -1
  65. regscale/models/regscale_models/control_objective.py +74 -5
  66. regscale/models/regscale_models/file.py +2 -0
  67. regscale/models/regscale_models/issue.py +2 -5
  68. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/METADATA +1 -1
  69. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/RECORD +112 -33
  70. tests/regscale/integrations/commercial/aws/__init__.py +0 -0
  71. tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
  72. tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
  73. tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
  74. tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
  75. tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
  76. tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
  77. tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
  78. tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
  79. tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
  80. tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
  81. tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
  82. tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
  83. tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
  84. tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
  85. tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
  86. tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
  87. tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
  88. tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
  89. tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
  90. tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
  91. tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
  92. tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
  93. tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
  94. tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
  95. tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
  96. tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
  97. tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
  98. tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
  99. tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
  100. tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
  101. tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
  102. tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
  103. tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
  104. tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
  105. tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
  106. tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
  107. tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
  108. tests/regscale/integrations/commercial/test_aws.py +55 -56
  109. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/LICENSE +0 -0
  110. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/WHEEL +0 -0
  111. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/entry_points.txt +0 -0
  112. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,357 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """AWS CloudWatch Logs Control Mappings for RegScale Compliance Integration."""
4
+
5
+ import logging
6
+ from typing import Dict, List, Optional
7
+
8
+ logger = logging.getLogger("regscale")
9
+
10
+ # NIST 800-53 R5 Control Mappings for AWS CloudWatch Logs
11
+ CLOUDWATCH_CONTROL_MAPPINGS = {
12
+ "AU-2": {
13
+ "name": "Event Logging",
14
+ "description": "Identify the types of events that the system is capable of logging",
15
+ "checks": {
16
+ "log_groups_exist": {
17
+ "weight": 100,
18
+ "pass_criteria": "CloudWatch log groups are configured and active",
19
+ "fail_criteria": "No CloudWatch log groups configured",
20
+ },
21
+ "metric_filters": {
22
+ "weight": 90,
23
+ "pass_criteria": "Metric filters configured to monitor security events",
24
+ "fail_criteria": "No metric filters configured",
25
+ },
26
+ },
27
+ },
28
+ "AU-3": {
29
+ "name": "Content of Audit Records",
30
+ "description": "Ensure audit records contain information that establishes what type of event occurred",
31
+ "checks": {
32
+ "log_group_configured": {
33
+ "weight": 100,
34
+ "pass_criteria": "Log groups capture comprehensive event data",
35
+ "fail_criteria": "Log groups not properly configured",
36
+ },
37
+ },
38
+ },
39
+ "AU-6": {
40
+ "name": "Audit Record Review, Analysis, and Reporting",
41
+ "description": "Review and analyze system audit records for indications of inappropriate activity",
42
+ "checks": {
43
+ "subscription_filters": {
44
+ "weight": 100,
45
+ "pass_criteria": "Subscription filters configured for log analysis and alerting",
46
+ "fail_criteria": "No subscription filters configured",
47
+ },
48
+ "metric_alarms": {
49
+ "weight": 95,
50
+ "pass_criteria": "Metric filters with alarms for security monitoring",
51
+ "fail_criteria": "No metric-based alarms configured",
52
+ },
53
+ },
54
+ },
55
+ "AU-9": {
56
+ "name": "Protection of Audit Information",
57
+ "description": "Protect audit information and audit logging tools from unauthorized access",
58
+ "checks": {
59
+ "encryption_enabled": {
60
+ "weight": 100,
61
+ "pass_criteria": "CloudWatch log groups have encryption enabled with KMS",
62
+ "fail_criteria": "Log groups not encrypted",
63
+ },
64
+ },
65
+ },
66
+ "AU-11": {
67
+ "name": "Audit Record Retention",
68
+ "description": "Retain audit records for defined time period to support after-the-fact investigations",
69
+ "checks": {
70
+ "retention_policy": {
71
+ "weight": 100,
72
+ "pass_criteria": "Log groups have retention policies configured (minimum 90 days)",
73
+ "fail_criteria": "No retention policy or insufficient retention period",
74
+ },
75
+ },
76
+ },
77
+ "AU-12": {
78
+ "name": "Audit Record Generation",
79
+ "description": "Provide audit record generation capability for events",
80
+ "checks": {
81
+ "active_log_groups": {
82
+ "weight": 100,
83
+ "pass_criteria": "Log groups are actively receiving logs",
84
+ "fail_criteria": "Log groups not actively receiving data",
85
+ },
86
+ "comprehensive_logging": {
87
+ "weight": 90,
88
+ "pass_criteria": "Multiple log groups covering different services and applications",
89
+ "fail_criteria": "Insufficient log group coverage",
90
+ },
91
+ },
92
+ },
93
+ "SI-4": {
94
+ "name": "System Monitoring",
95
+ "description": "Monitor the system to detect attacks and indicators of potential attacks",
96
+ "checks": {
97
+ "real_time_monitoring": {
98
+ "weight": 100,
99
+ "pass_criteria": "Subscription filters and metric filters enable real-time monitoring",
100
+ "fail_criteria": "No real-time monitoring configured",
101
+ },
102
+ },
103
+ },
104
+ }
105
+
106
+
107
+ class CloudWatchControlMapper:
108
+ """Map AWS CloudWatch Logs configurations to compliance control status."""
109
+
110
+ def __init__(self, framework: str = "NIST800-53R5"):
111
+ """
112
+ Initialize CloudWatch control mapper.
113
+
114
+ :param str framework: Compliance framework
115
+ """
116
+ self.framework = framework
117
+ self.mappings = CLOUDWATCH_CONTROL_MAPPINGS
118
+ self.minimum_retention_days = 90
119
+
120
+ def assess_cloudwatch_compliance(self, cloudwatch_data: Dict) -> Dict[str, str]:
121
+ """
122
+ Assess CloudWatch Logs compliance against all mapped controls.
123
+
124
+ :param Dict cloudwatch_data: CloudWatch Logs configuration data
125
+ :return: Dictionary mapping control IDs to compliance results (PASS/FAIL)
126
+ :rtype: Dict[str, str]
127
+ """
128
+ results = {}
129
+
130
+ if self.framework == "NIST800-53R5":
131
+ results["AU-2"] = self._assess_au2(cloudwatch_data)
132
+ results["AU-3"] = self._assess_au3(cloudwatch_data)
133
+ results["AU-6"] = self._assess_au6(cloudwatch_data)
134
+ results["AU-9"] = self._assess_au9(cloudwatch_data)
135
+ results["AU-11"] = self._assess_au11(cloudwatch_data)
136
+ results["AU-12"] = self._assess_au12(cloudwatch_data)
137
+ results["SI-4"] = self._assess_si4(cloudwatch_data)
138
+
139
+ return results
140
+
141
+ def _assess_au2(self, cloudwatch_data: Dict) -> str:
142
+ """
143
+ Assess AU-2 (Event Logging) compliance.
144
+
145
+ :param Dict cloudwatch_data: CloudWatch configuration data
146
+ :return: Compliance result (PASS/FAIL)
147
+ :rtype: str
148
+ """
149
+ log_groups = cloudwatch_data.get("LogGroups", [])
150
+
151
+ if not log_groups:
152
+ logger.debug("CloudWatch FAILS AU-2: No log groups configured")
153
+ return "FAIL"
154
+
155
+ # Check for metric filters
156
+ total_metric_filters = sum(len(lg.get("MetricFilters", [])) for lg in log_groups)
157
+
158
+ if total_metric_filters == 0:
159
+ logger.debug("CloudWatch PARTIALLY PASSES AU-2: Log groups exist but no metric filters configured")
160
+ return "PASS"
161
+
162
+ logger.debug(f"CloudWatch PASSES AU-2: {len(log_groups)} log groups with {total_metric_filters} metric filters")
163
+ return "PASS"
164
+
165
+ def _assess_au3(self, cloudwatch_data: Dict) -> str:
166
+ """
167
+ Assess AU-3 (Content of Audit Records) compliance.
168
+
169
+ :param Dict cloudwatch_data: CloudWatch configuration data
170
+ :return: Compliance result (PASS/FAIL)
171
+ :rtype: str
172
+ """
173
+ log_groups = cloudwatch_data.get("LogGroups", [])
174
+
175
+ if not log_groups:
176
+ logger.debug("CloudWatch FAILS AU-3: No log groups configured")
177
+ return "FAIL"
178
+
179
+ logger.debug(f"CloudWatch PASSES AU-3: {len(log_groups)} log groups capturing audit data")
180
+ return "PASS"
181
+
182
+ def _assess_au6(self, cloudwatch_data: Dict) -> str:
183
+ """
184
+ Assess AU-6 (Audit Record Review, Analysis, and Reporting) compliance.
185
+
186
+ :param Dict cloudwatch_data: CloudWatch configuration data
187
+ :return: Compliance result (PASS/FAIL)
188
+ :rtype: str
189
+ """
190
+ log_groups = cloudwatch_data.get("LogGroups", [])
191
+
192
+ if not log_groups:
193
+ logger.debug("CloudWatch FAILS AU-6: No log groups configured")
194
+ return "FAIL"
195
+
196
+ # Check for subscription filters or metric filters
197
+ total_subscription_filters = sum(len(lg.get("SubscriptionFilters", [])) for lg in log_groups)
198
+ total_metric_filters = sum(len(lg.get("MetricFilters", [])) for lg in log_groups)
199
+
200
+ if total_subscription_filters == 0 and total_metric_filters == 0:
201
+ logger.debug("CloudWatch FAILS AU-6: No subscription or metric filters for log analysis")
202
+ return "FAIL"
203
+
204
+ logger.debug(
205
+ f"CloudWatch PASSES AU-6: {total_subscription_filters} subscription filters, "
206
+ f"{total_metric_filters} metric filters configured"
207
+ )
208
+ return "PASS"
209
+
210
+ def _assess_au9(self, cloudwatch_data: Dict) -> str:
211
+ """
212
+ Assess AU-9 (Protection of Audit Information) compliance.
213
+
214
+ :param Dict cloudwatch_data: CloudWatch configuration data
215
+ :return: Compliance result (PASS/FAIL)
216
+ :rtype: str
217
+ """
218
+ log_groups = cloudwatch_data.get("LogGroups", [])
219
+
220
+ if not log_groups:
221
+ logger.debug("CloudWatch FAILS AU-9: No log groups configured")
222
+ return "FAIL"
223
+
224
+ # Check for KMS encryption
225
+ encrypted_count = sum(1 for lg in log_groups if lg.get("kmsKeyId"))
226
+ total_count = len(log_groups)
227
+
228
+ if encrypted_count == 0:
229
+ logger.debug("CloudWatch FAILS AU-9: No log groups have KMS encryption enabled")
230
+ return "FAIL"
231
+
232
+ if encrypted_count < total_count:
233
+ logger.debug(f"CloudWatch PARTIALLY PASSES AU-9: {encrypted_count}/{total_count} log groups encrypted")
234
+ return "PASS"
235
+
236
+ logger.debug(f"CloudWatch PASSES AU-9: All {total_count} log groups have KMS encryption enabled")
237
+ return "PASS"
238
+
239
+ def _assess_au11(self, cloudwatch_data: Dict) -> str:
240
+ """
241
+ Assess AU-11 (Audit Record Retention) compliance.
242
+
243
+ :param Dict cloudwatch_data: CloudWatch configuration data
244
+ :return: Compliance result (PASS/FAIL)
245
+ :rtype: str
246
+ """
247
+ log_groups = cloudwatch_data.get("LogGroups", [])
248
+
249
+ if not log_groups:
250
+ logger.debug("CloudWatch FAILS AU-11: No log groups configured")
251
+ return "FAIL"
252
+
253
+ # Check retention policies
254
+ log_groups_with_retention = []
255
+ log_groups_without_retention = []
256
+ insufficient_retention = []
257
+
258
+ for lg in log_groups:
259
+ log_group_name = lg.get("logGroupName", "unknown")
260
+ retention_days = lg.get("retentionInDays")
261
+
262
+ if retention_days is None:
263
+ log_groups_without_retention.append(log_group_name)
264
+ elif retention_days < self.minimum_retention_days:
265
+ insufficient_retention.append((log_group_name, retention_days))
266
+ else:
267
+ log_groups_with_retention.append((log_group_name, retention_days))
268
+
269
+ # Fail if no log groups have retention policies
270
+ if len(log_groups_with_retention) == 0 and len(log_groups) > 0:
271
+ logger.debug(
272
+ f"CloudWatch FAILS AU-11: {len(log_groups_without_retention)} log groups without retention policy"
273
+ )
274
+ return "FAIL"
275
+
276
+ # Warn if some log groups have insufficient retention
277
+ if insufficient_retention:
278
+ logger.debug(
279
+ f"CloudWatch PARTIALLY PASSES AU-11: {len(insufficient_retention)} log groups have "
280
+ f"retention < {self.minimum_retention_days} days"
281
+ )
282
+
283
+ logger.debug(f"CloudWatch PASSES AU-11: {len(log_groups_with_retention)} log groups with adequate retention")
284
+ return "PASS"
285
+
286
+ def _assess_au12(self, cloudwatch_data: Dict) -> str:
287
+ """
288
+ Assess AU-12 (Audit Record Generation) compliance.
289
+
290
+ :param Dict cloudwatch_data: CloudWatch configuration data
291
+ :return: Compliance result (PASS/FAIL)
292
+ :rtype: str
293
+ """
294
+ log_groups = cloudwatch_data.get("LogGroups", [])
295
+
296
+ if not log_groups:
297
+ logger.debug("CloudWatch FAILS AU-12: No log groups configured")
298
+ return "FAIL"
299
+
300
+ # Check for active log groups (have stored bytes)
301
+ log_group_metrics = cloudwatch_data.get("LogGroupMetrics", {})
302
+ active_log_groups = sum(
303
+ 1 for lg_name, metrics in log_group_metrics.items() if metrics.get("StoredBytes", 0) > 0
304
+ )
305
+
306
+ if active_log_groups == 0:
307
+ logger.debug("CloudWatch FAILS AU-12: No log groups are actively receiving data")
308
+ return "FAIL"
309
+
310
+ logger.debug(f"CloudWatch PASSES AU-12: {active_log_groups}/{len(log_groups)} log groups are active")
311
+ return "PASS"
312
+
313
+ def _assess_si4(self, cloudwatch_data: Dict) -> str:
314
+ """
315
+ Assess SI-4 (System Monitoring) compliance.
316
+
317
+ :param Dict cloudwatch_data: CloudWatch configuration data
318
+ :return: Compliance result (PASS/FAIL)
319
+ :rtype: str
320
+ """
321
+ log_groups = cloudwatch_data.get("LogGroups", [])
322
+
323
+ if not log_groups:
324
+ logger.debug("CloudWatch FAILS SI-4: No log groups configured")
325
+ return "FAIL"
326
+
327
+ # Check for subscription filters and metric filters
328
+ total_subscription_filters = sum(len(lg.get("SubscriptionFilters", [])) for lg in log_groups)
329
+ total_metric_filters = sum(len(lg.get("MetricFilters", [])) for lg in log_groups)
330
+
331
+ if total_subscription_filters == 0 and total_metric_filters == 0:
332
+ logger.debug("CloudWatch FAILS SI-4: No real-time monitoring configured")
333
+ return "FAIL"
334
+
335
+ logger.debug(
336
+ f"CloudWatch PASSES SI-4: Real-time monitoring enabled with {total_subscription_filters} "
337
+ f"subscription filters and {total_metric_filters} metric filters"
338
+ )
339
+ return "PASS"
340
+
341
+ def get_control_description(self, control_id: str) -> Optional[str]:
342
+ """Get human-readable description for a control."""
343
+ control_data = self.mappings.get(control_id)
344
+ if control_data:
345
+ return f"{control_data.get('name')}: {control_data.get('description', '')}"
346
+ return None
347
+
348
+ def get_mapped_controls(self) -> List[str]:
349
+ """Get list of all control IDs mapped for this framework."""
350
+ return list(self.mappings.keys())
351
+
352
+ def get_check_details(self, control_id: str) -> Optional[Dict]:
353
+ """Get detailed check criteria for a control."""
354
+ control_data = self.mappings.get(control_id)
355
+ if control_data:
356
+ return control_data.get("checks", {})
357
+ return None