regscale-cli 6.27.3.0__py3-none-any.whl → 6.28.1.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 (113) 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/commercial/synqly/ticketing.py +27 -0
  59. regscale/integrations/compliance_integration.py +308 -38
  60. regscale/integrations/due_date_handler.py +3 -0
  61. regscale/integrations/scanner_integration.py +399 -84
  62. regscale/models/integration_models/cisa_kev_data.json +65 -5
  63. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  64. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +17 -9
  65. regscale/models/regscale_models/assessment.py +2 -1
  66. regscale/models/regscale_models/control_objective.py +74 -5
  67. regscale/models/regscale_models/file.py +2 -0
  68. regscale/models/regscale_models/issue.py +2 -5
  69. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/METADATA +1 -1
  70. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/RECORD +113 -34
  71. tests/regscale/integrations/commercial/aws/__init__.py +0 -0
  72. tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
  73. tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
  74. tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
  75. tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
  76. tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
  77. tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
  78. tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
  79. tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
  80. tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
  81. tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
  82. tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
  83. tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
  84. tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
  85. tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
  86. tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
  87. tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
  88. tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
  89. tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
  90. tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
  91. tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
  92. tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
  93. tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
  94. tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
  95. tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
  96. tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
  97. tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
  98. tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
  99. tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
  100. tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
  101. tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
  102. tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
  103. tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
  104. tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
  105. tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
  106. tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
  107. tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
  108. tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
  109. tests/regscale/integrations/commercial/test_aws.py +55 -56
  110. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/LICENSE +0 -0
  111. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/WHEEL +0 -0
  112. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/entry_points.txt +0 -0
  113. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,368 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """AWS IAM 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 IAM
11
+ IAM_CONTROL_MAPPINGS = {
12
+ "AC-2": {
13
+ "name": "Account Management",
14
+ "description": "Manage system accounts including creation, enabling, modification, and removal",
15
+ "checks": {
16
+ "user_mfa": {
17
+ "weight": 100,
18
+ "pass_criteria": "All IAM users have MFA enabled",
19
+ "fail_criteria": "IAM users without MFA enabled",
20
+ },
21
+ "inactive_users": {
22
+ "weight": 90,
23
+ "pass_criteria": "No inactive users (password age > 90 days)",
24
+ "fail_criteria": "Users with credentials unused for extended period",
25
+ },
26
+ "root_account_usage": {
27
+ "weight": 100,
28
+ "pass_criteria": "Root account has MFA and no access keys",
29
+ "fail_criteria": "Root account without MFA or with active access keys",
30
+ },
31
+ },
32
+ },
33
+ "AC-6": {
34
+ "name": "Least Privilege",
35
+ "description": "Employ the principle of least privilege",
36
+ "checks": {
37
+ "admin_policies": {
38
+ "weight": 100,
39
+ "pass_criteria": "No users with direct AdministratorAccess policy attachment",
40
+ "fail_criteria": "Users with full admin (*:*) permissions",
41
+ },
42
+ "inline_policies": {
43
+ "weight": 80,
44
+ "pass_criteria": "Minimal use of inline policies, prefer managed policies",
45
+ "fail_criteria": "Excessive inline policies indicating poor policy management",
46
+ },
47
+ },
48
+ },
49
+ "IA-2": {
50
+ "name": "Identification and Authentication (Organizational Users)",
51
+ "description": "Uniquely identify and authenticate organizational users",
52
+ "checks": {
53
+ "password_policy": {
54
+ "weight": 100,
55
+ "pass_criteria": "Strong password policy enforced (length, complexity, rotation)",
56
+ "fail_criteria": "Weak or no password policy",
57
+ },
58
+ "mfa_enforcement": {
59
+ "weight": 100,
60
+ "pass_criteria": "MFA required for all users",
61
+ "fail_criteria": "Users without MFA",
62
+ },
63
+ },
64
+ },
65
+ "IA-5": {
66
+ "name": "Authenticator Management",
67
+ "description": "Manage system authenticators",
68
+ "checks": {
69
+ "access_key_rotation": {
70
+ "weight": 100,
71
+ "pass_criteria": "Access keys rotated within 90 days",
72
+ "fail_criteria": "Access keys older than 90 days",
73
+ },
74
+ "unused_credentials": {
75
+ "weight": 90,
76
+ "pass_criteria": "No unused credentials (password or access keys)",
77
+ "fail_criteria": "Credentials not used in 90+ days",
78
+ },
79
+ },
80
+ },
81
+ "AC-3": {
82
+ "name": "Access Enforcement",
83
+ "description": "Enforce approved authorizations for logical access",
84
+ "checks": {
85
+ "role_trust_policies": {
86
+ "weight": 100,
87
+ "pass_criteria": "IAM roles have restrictive trust policies",
88
+ "fail_criteria": "Roles with overly permissive trust relationships",
89
+ },
90
+ "group_based_access": {
91
+ "weight": 80,
92
+ "pass_criteria": "Users assigned to groups rather than direct policy attachments",
93
+ "fail_criteria": "Users with direct policy attachments instead of group membership",
94
+ },
95
+ },
96
+ },
97
+ }
98
+
99
+ # ISO 27001 Control Mappings
100
+ ISO_27001_MAPPINGS = {
101
+ "A.9.2.1": {
102
+ "name": "User registration and de-registration",
103
+ "iam_attributes": ["users", "user_status", "last_activity"],
104
+ },
105
+ "A.9.2.2": {
106
+ "name": "User access provisioning",
107
+ "iam_attributes": ["policies", "groups", "roles"],
108
+ },
109
+ "A.9.4.3": {
110
+ "name": "Password management system",
111
+ "iam_attributes": ["password_policy", "mfa_status"],
112
+ },
113
+ }
114
+
115
+ # Password policy requirements (strong policy)
116
+ STRONG_PASSWORD_POLICY = {
117
+ "MinimumPasswordLength": 14,
118
+ "RequireSymbols": True,
119
+ "RequireNumbers": True,
120
+ "RequireUppercaseCharacters": True,
121
+ "RequireLowercaseCharacters": True,
122
+ "MaxPasswordAge": 90,
123
+ "PasswordReusePrevention": 24,
124
+ }
125
+
126
+ # Age thresholds (in days)
127
+ ACCESS_KEY_MAX_AGE_DAYS = 90
128
+ PASSWORD_MAX_AGE_DAYS = 90
129
+ CREDENTIAL_UNUSED_DAYS = 90
130
+
131
+
132
+ class IAMControlMapper:
133
+ """Map AWS IAM resources to compliance control status."""
134
+
135
+ def __init__(self, framework: str = "NIST800-53R5"):
136
+ """
137
+ Initialize IAM control mapper.
138
+
139
+ :param str framework: Compliance framework (NIST800-53R5 or ISO27001)
140
+ """
141
+ self.framework = framework
142
+ self.mappings = IAM_CONTROL_MAPPINGS if framework == "NIST800-53R5" else ISO_27001_MAPPINGS
143
+
144
+ def assess_iam_compliance(self, iam_data: Dict) -> Dict[str, str]:
145
+ """
146
+ Assess AWS IAM compliance against all mapped controls.
147
+
148
+ :param Dict iam_data: IAM resources and configuration
149
+ :return: Dictionary mapping control IDs to compliance results (PASS/FAIL)
150
+ :rtype: Dict[str, str]
151
+ """
152
+ results = {}
153
+
154
+ if self.framework == "NIST800-53R5":
155
+ results["AC-2"] = self._assess_ac2(iam_data)
156
+ results["AC-6"] = self._assess_ac6(iam_data)
157
+ results["IA-2"] = self._assess_ia2(iam_data)
158
+ results["IA-5"] = self._assess_ia5(iam_data)
159
+ results["AC-3"] = self._assess_ac3(iam_data)
160
+
161
+ return results
162
+
163
+ def _assess_ac2(self, iam_data: Dict) -> str:
164
+ """
165
+ Assess AC-2 (Account Management) compliance.
166
+
167
+ :param Dict iam_data: IAM data
168
+ :return: Compliance result (PASS/FAIL)
169
+ :rtype: str
170
+ """
171
+ users = iam_data.get("users", [])
172
+ account_summary = iam_data.get("account_summary", {})
173
+
174
+ # Check MFA for users
175
+ users_without_mfa = [u for u in users if not u.get("MfaEnabled", False)]
176
+ if users_without_mfa:
177
+ logger.debug(f"IAM FAILS AC-2: {len(users_without_mfa)} users without MFA")
178
+ return "FAIL"
179
+
180
+ # Check root account
181
+ root_mfa = account_summary.get("AccountMFAEnabled", False)
182
+ if not root_mfa:
183
+ logger.debug("IAM FAILS AC-2: Root account does not have MFA enabled")
184
+ return "FAIL"
185
+
186
+ # Check for root access keys
187
+ root_access_keys = account_summary.get("AccountAccessKeysPresent", 0)
188
+ if root_access_keys > 0:
189
+ logger.debug("IAM FAILS AC-2: Root account has active access keys")
190
+ return "FAIL"
191
+
192
+ logger.debug("IAM PASSES AC-2: All users have MFA, root account secured")
193
+ return "PASS"
194
+
195
+ def _assess_ac6(self, iam_data: Dict) -> str:
196
+ """
197
+ Assess AC-6 (Least Privilege) compliance.
198
+
199
+ :param Dict iam_data: IAM data
200
+ :return: Compliance result (PASS/FAIL)
201
+ :rtype: str
202
+ """
203
+ users = iam_data.get("users", [])
204
+
205
+ # Check for users with AdministratorAccess
206
+ users_with_admin = []
207
+ for user in users:
208
+ attached_policies = user.get("AttachedPolicies", [])
209
+ inline_policies = user.get("InlinePolicies", [])
210
+
211
+ # Check attached policies
212
+ if any("AdministratorAccess" in p.get("PolicyName", "") for p in attached_policies):
213
+ users_with_admin.append(user.get("UserName"))
214
+
215
+ # Check inline policies for broad permissions
216
+ for policy in inline_policies:
217
+ policy_doc = policy.get("PolicyDocument", {})
218
+ if self._has_full_admin_permissions(policy_doc):
219
+ users_with_admin.append(user.get("UserName"))
220
+
221
+ if users_with_admin:
222
+ logger.debug(f"IAM FAILS AC-6: {len(users_with_admin)} users with administrator access")
223
+ return "FAIL"
224
+
225
+ logger.debug("IAM PASSES AC-6: No users with direct administrator access")
226
+ return "PASS"
227
+
228
+ def _assess_ia2(self, iam_data: Dict) -> str:
229
+ """
230
+ Assess IA-2 (Identification and Authentication) compliance.
231
+
232
+ :param Dict iam_data: IAM data
233
+ :return: Compliance result (PASS/FAIL)
234
+ :rtype: str
235
+ """
236
+ users = iam_data.get("users", [])
237
+ password_policy = iam_data.get("password_policy", {})
238
+
239
+ # Check password policy strength
240
+ if not self._is_strong_password_policy(password_policy):
241
+ logger.debug("IAM FAILS IA-2: Weak password policy")
242
+ return "FAIL"
243
+
244
+ # Check MFA enforcement
245
+ users_without_mfa = [u for u in users if not u.get("MfaEnabled", False)]
246
+ if users_without_mfa:
247
+ logger.debug(f"IAM FAILS IA-2: {len(users_without_mfa)} users without MFA")
248
+ return "FAIL"
249
+
250
+ logger.debug("IAM PASSES IA-2: Strong password policy and MFA enforced")
251
+ return "PASS"
252
+
253
+ def _assess_ia5(self, iam_data: Dict) -> str:
254
+ """
255
+ Assess IA-5 (Authenticator Management) compliance.
256
+
257
+ :param Dict iam_data: IAM data
258
+ :return: Compliance result (PASS/FAIL)
259
+ :rtype: str
260
+ """
261
+ users = iam_data.get("users", [])
262
+
263
+ # Check access key age
264
+ old_access_keys = []
265
+ unused_credentials = []
266
+
267
+ for user in users:
268
+ access_keys = user.get("AccessKeys", [])
269
+ for key in access_keys:
270
+ age_days = key.get("AgeDays", 0)
271
+ if age_days > ACCESS_KEY_MAX_AGE_DAYS:
272
+ old_access_keys.append(key.get("AccessKeyId"))
273
+
274
+ # Check credential usage
275
+ password_last_used = user.get("PasswordLastUsed")
276
+ if password_last_used and password_last_used.get("DaysSinceUsed", 0) > CREDENTIAL_UNUSED_DAYS:
277
+ unused_credentials.append(user.get("UserName"))
278
+
279
+ if old_access_keys:
280
+ logger.debug(
281
+ f"IAM FAILS IA-5: {len(old_access_keys)} access keys older than {ACCESS_KEY_MAX_AGE_DAYS} days"
282
+ )
283
+ return "FAIL"
284
+
285
+ if unused_credentials:
286
+ logger.debug(f"IAM FAILS IA-5: {len(unused_credentials)} users with unused credentials")
287
+ return "FAIL"
288
+
289
+ logger.debug("IAM PASSES IA-5: All access keys rotated, no unused credentials")
290
+ return "PASS"
291
+
292
+ def _assess_ac3(self, iam_data: Dict) -> str:
293
+ """
294
+ Assess AC-3 (Access Enforcement) compliance.
295
+
296
+ :param Dict iam_data: IAM data
297
+ :return: Compliance result (PASS/FAIL)
298
+ :rtype: str
299
+ """
300
+ roles = iam_data.get("roles", [])
301
+
302
+ # Check for overly permissive role trust policies
303
+ permissive_roles = []
304
+ for role in roles:
305
+ trust_policy = role.get("AssumeRolePolicyDocument", {})
306
+ if self._has_permissive_trust_policy(trust_policy):
307
+ permissive_roles.append(role.get("RoleName"))
308
+
309
+ if permissive_roles:
310
+ logger.debug(f"IAM FAILS AC-3: {len(permissive_roles)} roles with overly permissive trust policies")
311
+ return "FAIL"
312
+
313
+ logger.debug("IAM PASSES AC-3: All roles have restrictive trust policies")
314
+ return "PASS"
315
+
316
+ def _has_full_admin_permissions(self, policy_doc: Dict) -> bool:
317
+ """Check if policy grants full admin permissions."""
318
+ statements = policy_doc.get("Statement", [])
319
+ for statement in statements:
320
+ if statement.get("Effect") == "Allow":
321
+ actions = statement.get("Action", [])
322
+ resources = statement.get("Resource", [])
323
+ if ("*" in actions or "*:*" in actions) and ("*" in resources):
324
+ return True
325
+ return False
326
+
327
+ def _has_permissive_trust_policy(self, trust_policy: Dict) -> bool:
328
+ """Check if trust policy is overly permissive."""
329
+ statements = trust_policy.get("Statement", [])
330
+ for statement in statements:
331
+ principal = statement.get("Principal", {})
332
+ if principal == "*" or (isinstance(principal, dict) and principal.get("AWS") == "*"):
333
+ return True
334
+ return False
335
+
336
+ def _is_strong_password_policy(self, policy: Dict) -> bool:
337
+ """Check if password policy meets strong requirements."""
338
+ if not policy:
339
+ return False
340
+
341
+ checks = [
342
+ policy.get("MinimumPasswordLength", 0) >= STRONG_PASSWORD_POLICY["MinimumPasswordLength"],
343
+ policy.get("RequireSymbols", False),
344
+ policy.get("RequireNumbers", False),
345
+ policy.get("RequireUppercaseCharacters", False),
346
+ policy.get("RequireLowercaseCharacters", False),
347
+ policy.get("MaxPasswordAge", 0) <= STRONG_PASSWORD_POLICY["MaxPasswordAge"],
348
+ ]
349
+
350
+ return all(checks)
351
+
352
+ def get_control_description(self, control_id: str) -> Optional[str]:
353
+ """Get human-readable description for a control."""
354
+ control_data = self.mappings.get(control_id)
355
+ if control_data:
356
+ return f"{control_data.get('name')}: {control_data.get('description', '')}"
357
+ return None
358
+
359
+ def get_mapped_controls(self) -> List[str]:
360
+ """Get list of all control IDs mapped for this framework."""
361
+ return list(self.mappings.keys())
362
+
363
+ def get_check_details(self, control_id: str) -> Optional[Dict]:
364
+ """Get detailed check criteria for a control."""
365
+ control_data = self.mappings.get(control_id)
366
+ if control_data:
367
+ return control_data.get("checks", {})
368
+ return None