regscale-cli 6.21.2.0__py3-none-any.whl → 6.28.2.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 (314) hide show
  1. regscale/_version.py +1 -1
  2. regscale/airflow/hierarchy.py +2 -2
  3. regscale/core/app/api.py +5 -2
  4. regscale/core/app/application.py +36 -6
  5. regscale/core/app/internal/control_editor.py +73 -21
  6. regscale/core/app/internal/evidence.py +727 -204
  7. regscale/core/app/internal/login.py +4 -2
  8. regscale/core/app/internal/model_editor.py +219 -64
  9. regscale/core/app/utils/app_utils.py +86 -12
  10. regscale/core/app/utils/catalog_utils/common.py +1 -1
  11. regscale/core/login.py +21 -4
  12. regscale/core/utils/async_graphql_client.py +363 -0
  13. regscale/core/utils/date.py +77 -1
  14. regscale/dev/cli.py +26 -0
  15. regscale/dev/code_gen.py +109 -24
  16. regscale/dev/version.py +72 -0
  17. regscale/integrations/commercial/__init__.py +30 -2
  18. regscale/integrations/commercial/aws/audit_manager_compliance.py +3908 -0
  19. regscale/integrations/commercial/aws/cli.py +3107 -54
  20. regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
  21. regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
  22. regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
  23. regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
  24. regscale/integrations/commercial/{amazon → aws}/common.py +71 -19
  25. regscale/integrations/commercial/aws/config_compliance.py +914 -0
  26. regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
  27. regscale/integrations/commercial/aws/control_compliance_analyzer.py +439 -0
  28. regscale/integrations/commercial/aws/evidence_generator.py +283 -0
  29. regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
  30. regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
  31. regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
  32. regscale/integrations/commercial/aws/iam_evidence.py +574 -0
  33. regscale/integrations/commercial/aws/inventory/__init__.py +338 -22
  34. regscale/integrations/commercial/aws/inventory/base.py +107 -5
  35. regscale/integrations/commercial/aws/inventory/resources/analytics.py +390 -0
  36. regscale/integrations/commercial/aws/inventory/resources/applications.py +234 -0
  37. regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
  38. regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
  39. regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
  40. regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
  41. regscale/integrations/commercial/aws/inventory/resources/compute.py +328 -9
  42. regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
  43. regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
  44. regscale/integrations/commercial/aws/inventory/resources/database.py +481 -31
  45. regscale/integrations/commercial/aws/inventory/resources/developer_tools.py +253 -0
  46. regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
  47. regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
  48. regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
  49. regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
  50. regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
  51. regscale/integrations/commercial/aws/inventory/resources/machine_learning.py +358 -0
  52. regscale/integrations/commercial/aws/inventory/resources/networking.py +390 -67
  53. regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
  54. regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
  55. regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
  56. regscale/integrations/commercial/aws/inventory/resources/storage.py +288 -29
  57. regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
  58. regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
  59. regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
  60. regscale/integrations/commercial/aws/kms_evidence.py +879 -0
  61. regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
  62. regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
  63. regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
  64. regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
  65. regscale/integrations/commercial/aws/org_evidence.py +666 -0
  66. regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
  67. regscale/integrations/commercial/aws/s3_evidence.py +632 -0
  68. regscale/integrations/commercial/aws/scanner.py +1072 -205
  69. regscale/integrations/commercial/aws/security_hub.py +319 -0
  70. regscale/integrations/commercial/aws/session_manager.py +282 -0
  71. regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
  72. regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
  73. regscale/integrations/commercial/jira.py +489 -153
  74. regscale/integrations/commercial/microsoft_defender/defender.py +326 -5
  75. regscale/integrations/commercial/microsoft_defender/defender_api.py +348 -14
  76. regscale/integrations/commercial/microsoft_defender/defender_constants.py +157 -0
  77. regscale/integrations/commercial/qualys/__init__.py +167 -68
  78. regscale/integrations/commercial/qualys/scanner.py +305 -39
  79. regscale/integrations/commercial/sarif/sairf_importer.py +432 -0
  80. regscale/integrations/commercial/sarif/sarif_converter.py +67 -0
  81. regscale/integrations/commercial/sicura/api.py +79 -42
  82. regscale/integrations/commercial/sicura/commands.py +8 -2
  83. regscale/integrations/commercial/sicura/scanner.py +83 -44
  84. regscale/integrations/commercial/stigv2/ckl_parser.py +5 -5
  85. regscale/integrations/commercial/synqly/assets.py +133 -16
  86. regscale/integrations/commercial/synqly/edr.py +2 -8
  87. regscale/integrations/commercial/synqly/query_builder.py +536 -0
  88. regscale/integrations/commercial/synqly/ticketing.py +27 -0
  89. regscale/integrations/commercial/synqly/vulnerabilities.py +165 -28
  90. regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
  91. regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
  92. regscale/integrations/commercial/tenablev2/commands.py +146 -5
  93. regscale/integrations/commercial/tenablev2/scanner.py +1 -3
  94. regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
  95. regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
  96. regscale/integrations/commercial/wizv2/click.py +191 -76
  97. regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
  98. regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
  99. regscale/integrations/commercial/wizv2/compliance_report.py +1592 -0
  100. regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
  101. regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +7 -3
  102. regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +92 -89
  103. regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
  104. regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
  105. regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +66 -9
  106. regscale/integrations/commercial/wizv2/file_cleanup.py +104 -0
  107. regscale/integrations/commercial/wizv2/issue.py +776 -28
  108. regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
  109. regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
  110. regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
  111. regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
  112. regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
  113. regscale/integrations/commercial/wizv2/reports.py +243 -0
  114. regscale/integrations/commercial/wizv2/sbom.py +1 -1
  115. regscale/integrations/commercial/wizv2/scanner.py +1031 -441
  116. regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
  117. regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
  118. regscale/integrations/commercial/wizv2/variables.py +89 -3
  119. regscale/integrations/compliance_integration.py +1036 -151
  120. regscale/integrations/control_matcher.py +432 -0
  121. regscale/integrations/due_date_handler.py +333 -0
  122. regscale/integrations/milestone_manager.py +291 -0
  123. regscale/integrations/public/__init__.py +14 -0
  124. regscale/integrations/public/cci_importer.py +834 -0
  125. regscale/integrations/public/csam/__init__.py +0 -0
  126. regscale/integrations/public/csam/csam.py +938 -0
  127. regscale/integrations/public/csam/csam_agency_defined.py +179 -0
  128. regscale/integrations/public/csam/csam_common.py +154 -0
  129. regscale/integrations/public/csam/csam_controls.py +432 -0
  130. regscale/integrations/public/csam/csam_poam.py +124 -0
  131. regscale/integrations/public/fedramp/click.py +77 -6
  132. regscale/integrations/public/fedramp/docx_parser.py +10 -1
  133. regscale/integrations/public/fedramp/fedramp_cis_crm.py +675 -289
  134. regscale/integrations/public/fedramp/fedramp_five.py +1 -1
  135. regscale/integrations/public/fedramp/poam/scanner.py +75 -7
  136. regscale/integrations/public/fedramp/poam_export_v5.py +888 -0
  137. regscale/integrations/scanner_integration.py +1961 -430
  138. regscale/models/integration_models/CCI_List.xml +1 -0
  139. regscale/models/integration_models/aqua.py +2 -2
  140. regscale/models/integration_models/cisa_kev_data.json +805 -11
  141. regscale/models/integration_models/flat_file_importer/__init__.py +5 -8
  142. regscale/models/integration_models/nexpose.py +36 -10
  143. regscale/models/integration_models/qualys.py +3 -4
  144. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  145. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +87 -18
  146. regscale/models/integration_models/synqly_models/filter_parser.py +332 -0
  147. regscale/models/integration_models/synqly_models/ocsf_mapper.py +124 -25
  148. regscale/models/integration_models/synqly_models/synqly_model.py +89 -16
  149. regscale/models/locking.py +12 -8
  150. regscale/models/platform.py +4 -2
  151. regscale/models/regscale_models/__init__.py +7 -0
  152. regscale/models/regscale_models/assessment.py +2 -1
  153. regscale/models/regscale_models/catalog.py +1 -1
  154. regscale/models/regscale_models/compliance_settings.py +251 -1
  155. regscale/models/regscale_models/component.py +1 -0
  156. regscale/models/regscale_models/control_implementation.py +236 -41
  157. regscale/models/regscale_models/control_objective.py +74 -5
  158. regscale/models/regscale_models/file.py +2 -0
  159. regscale/models/regscale_models/form_field_value.py +5 -3
  160. regscale/models/regscale_models/inheritance.py +44 -0
  161. regscale/models/regscale_models/issue.py +301 -102
  162. regscale/models/regscale_models/milestone.py +33 -14
  163. regscale/models/regscale_models/organization.py +3 -0
  164. regscale/models/regscale_models/regscale_model.py +310 -73
  165. regscale/models/regscale_models/security_plan.py +4 -2
  166. regscale/models/regscale_models/vulnerability.py +3 -3
  167. regscale/regscale.py +25 -4
  168. regscale/templates/__init__.py +0 -0
  169. regscale/utils/threading/threadhandler.py +20 -15
  170. regscale/validation/record.py +23 -1
  171. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/METADATA +17 -33
  172. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/RECORD +310 -111
  173. tests/core/__init__.py +0 -0
  174. tests/core/utils/__init__.py +0 -0
  175. tests/core/utils/test_async_graphql_client.py +472 -0
  176. tests/fixtures/test_fixture.py +13 -8
  177. tests/regscale/core/test_login.py +171 -4
  178. tests/regscale/integrations/commercial/__init__.py +0 -0
  179. tests/regscale/integrations/commercial/aws/__init__.py +0 -0
  180. tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
  181. tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
  182. tests/regscale/integrations/commercial/aws/test_aws_analytics_collector.py +260 -0
  183. tests/regscale/integrations/commercial/aws/test_aws_applications_collector.py +242 -0
  184. tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
  185. tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
  186. tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
  187. tests/regscale/integrations/commercial/aws/test_aws_developer_tools_collector.py +203 -0
  188. tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
  189. tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
  190. tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
  191. tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
  192. tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
  193. tests/regscale/integrations/commercial/aws/test_aws_machine_learning_collector.py +237 -0
  194. tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
  195. tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
  196. tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
  197. tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
  198. tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
  199. tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
  200. tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
  201. tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
  202. tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
  203. tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
  204. tests/regscale/integrations/commercial/aws/test_control_compliance_analyzer.py +375 -0
  205. tests/regscale/integrations/commercial/aws/test_datetime_parsing.py +223 -0
  206. tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
  207. tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
  208. tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
  209. tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
  210. tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
  211. tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
  212. tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
  213. tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
  214. tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
  215. tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
  216. tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
  217. tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
  218. tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
  219. tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
  220. tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
  221. tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
  222. tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
  223. tests/regscale/integrations/commercial/conftest.py +28 -0
  224. tests/regscale/integrations/commercial/microsoft_defender/__init__.py +1 -0
  225. tests/regscale/integrations/commercial/microsoft_defender/test_defender.py +1517 -0
  226. tests/regscale/integrations/commercial/microsoft_defender/test_defender_api.py +1748 -0
  227. tests/regscale/integrations/commercial/microsoft_defender/test_defender_constants.py +327 -0
  228. tests/regscale/integrations/commercial/microsoft_defender/test_defender_scanner.py +487 -0
  229. tests/regscale/integrations/commercial/test_aws.py +3742 -0
  230. tests/regscale/integrations/commercial/test_burp.py +48 -0
  231. tests/regscale/integrations/commercial/test_crowdstrike.py +49 -0
  232. tests/regscale/integrations/commercial/test_dependabot.py +341 -0
  233. tests/regscale/integrations/commercial/test_gcp.py +1543 -0
  234. tests/regscale/integrations/commercial/test_gitlab.py +549 -0
  235. tests/regscale/integrations/commercial/test_ip_mac_address_length.py +84 -0
  236. tests/regscale/integrations/commercial/test_jira.py +2204 -0
  237. tests/regscale/integrations/commercial/test_npm_audit.py +42 -0
  238. tests/regscale/integrations/commercial/test_okta.py +1228 -0
  239. tests/regscale/integrations/commercial/test_sarif_converter.py +251 -0
  240. tests/regscale/integrations/commercial/test_sicura.py +349 -0
  241. tests/regscale/integrations/commercial/test_snow.py +423 -0
  242. tests/regscale/integrations/commercial/test_sonarcloud.py +394 -0
  243. tests/regscale/integrations/commercial/test_sqlserver.py +186 -0
  244. tests/regscale/integrations/commercial/test_stig.py +33 -0
  245. tests/regscale/integrations/commercial/test_stig_mapper.py +153 -0
  246. tests/regscale/integrations/commercial/test_stigv2.py +406 -0
  247. tests/regscale/integrations/commercial/test_wiz.py +1365 -0
  248. tests/regscale/integrations/commercial/test_wiz_inventory.py +256 -0
  249. tests/regscale/integrations/commercial/wizv2/__init__.py +339 -0
  250. tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
  251. tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
  252. tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
  253. tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
  254. tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
  255. tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
  256. tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
  257. tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
  258. tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
  259. tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
  260. tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
  261. tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
  262. tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
  263. tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
  264. tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
  265. tests/regscale/integrations/commercial/wizv2/test_compliance_report_normalization.py +138 -0
  266. tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
  267. tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
  268. tests/regscale/integrations/commercial/wizv2/test_issue.py +343 -0
  269. tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
  270. tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
  271. tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
  272. tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
  273. tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +165 -0
  274. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +1394 -0
  275. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_unit.py +341 -0
  276. tests/regscale/integrations/commercial/wizv2/test_wiz_control_normalization.py +138 -0
  277. tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
  278. tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
  279. tests/regscale/integrations/commercial/wizv2/test_wiz_status_mapping.py +149 -0
  280. tests/regscale/integrations/commercial/wizv2/test_wizv2.py +1218 -0
  281. tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +519 -0
  282. tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
  283. tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
  284. tests/regscale/integrations/public/__init__.py +0 -0
  285. tests/regscale/integrations/public/fedramp/__init__.py +1 -0
  286. tests/regscale/integrations/public/fedramp/test_gen_asset_list.py +150 -0
  287. tests/regscale/integrations/public/fedramp/test_poam_export_v5.py +1293 -0
  288. tests/regscale/integrations/public/test_alienvault.py +220 -0
  289. tests/regscale/integrations/public/test_cci.py +1053 -0
  290. tests/regscale/integrations/public/test_cisa.py +1021 -0
  291. tests/regscale/integrations/public/test_emass.py +518 -0
  292. tests/regscale/integrations/public/test_fedramp.py +1152 -0
  293. tests/regscale/integrations/public/test_fedramp_cis_crm.py +3661 -0
  294. tests/regscale/integrations/public/test_file_uploads.py +506 -0
  295. tests/regscale/integrations/public/test_oscal.py +453 -0
  296. tests/regscale/integrations/test_compliance_status_mapping.py +406 -0
  297. tests/regscale/integrations/test_control_matcher.py +1421 -0
  298. tests/regscale/integrations/test_control_matching.py +155 -0
  299. tests/regscale/integrations/test_milestone_manager.py +408 -0
  300. tests/regscale/models/test_control_implementation.py +118 -3
  301. tests/regscale/models/test_form_field_value_integration.py +304 -0
  302. tests/regscale/models/test_issue.py +378 -1
  303. tests/regscale/models/test_module_integration.py +582 -0
  304. tests/regscale/models/test_tenable_integrations.py +811 -105
  305. regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3057
  306. regscale/integrations/public/fedramp/mappings/fedramp_r4_parts.json +0 -7388
  307. regscale/integrations/public/fedramp/mappings/fedramp_r5_parts.json +0 -9605
  308. regscale/integrations/public/fedramp/parts_mapper.py +0 -107
  309. /regscale/integrations/commercial/{amazon → sarif}/__init__.py +0 -0
  310. /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
  311. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/LICENSE +0 -0
  312. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/WHEEL +0 -0
  313. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/entry_points.txt +0 -0
  314. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,283 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """AWS Evidence Generator for SSP compliance documentation"""
4
+
5
+ import gzip
6
+ import json
7
+ import logging
8
+ from datetime import datetime, timedelta
9
+ from io import BytesIO
10
+ from typing import List, Optional
11
+
12
+ from regscale.core.app.api import Api
13
+ from regscale.models.regscale_models.evidence import Evidence
14
+ from regscale.models.regscale_models.evidence_mapping import EvidenceMapping
15
+ from regscale.models.regscale_models.file import File
16
+
17
+ logger = logging.getLogger("regscale")
18
+
19
+
20
+ class AWSEvidenceGenerator:
21
+ """Generate compliance evidence from AWS security findings"""
22
+
23
+ def __init__(self, api: Api, ssp_id: Optional[int] = None):
24
+ """
25
+ Initialize evidence generator
26
+
27
+ :param Api api: RegScale API instance
28
+ :param Optional[int] ssp_id: Security Plan ID to link evidence
29
+ """
30
+ self.api = api
31
+ self.ssp_id = ssp_id
32
+
33
+ def create_evidence_from_scan(
34
+ self,
35
+ service_name: str,
36
+ findings: List[dict],
37
+ ocsf_data: Optional[List[dict]] = None,
38
+ update_frequency: int = 30,
39
+ control_ids: Optional[List[int]] = None,
40
+ ) -> Optional[Evidence]:
41
+ """
42
+ Create evidence record from AWS service scan
43
+
44
+ :param str service_name: AWS service name (GuardDuty, SecurityHub, etc.)
45
+ :param List[dict] findings: List of AWS findings (native format)
46
+ :param Optional[List[dict]] ocsf_data: OCSF-formatted findings
47
+ :param int update_frequency: Evidence update frequency in days (default: 30)
48
+ :param Optional[List[int]] control_ids: Control IDs to link evidence
49
+ :return: Created Evidence object or None
50
+ :rtype: Optional[Evidence]
51
+ """
52
+ if not findings:
53
+ logger.warning("No findings provided, skipping evidence creation")
54
+ return None
55
+
56
+ # Generate evidence title with timestamp
57
+ scan_date = datetime.now().strftime("%Y-%m-%d")
58
+ title = f"{service_name} Findings Scan - {scan_date}"
59
+
60
+ # Create description with finding summary
61
+ total_findings = len(findings)
62
+ severity_counts = self._count_severities(findings, service_name)
63
+ description = self._build_evidence_description(service_name, total_findings, severity_counts, ocsf_data)
64
+
65
+ # Calculate due date based on update frequency
66
+ due_date = (datetime.now() + timedelta(days=update_frequency)).isoformat()
67
+
68
+ try:
69
+ # Create evidence record
70
+ evidence = Evidence(
71
+ title=title,
72
+ description=description,
73
+ status="Collected",
74
+ updateFrequency=update_frequency,
75
+ dueDate=due_date,
76
+ )
77
+
78
+ # Create evidence in RegScale
79
+ created_evidence = evidence.create()
80
+ if not created_evidence or not created_evidence.id:
81
+ logger.error("Failed to create evidence record")
82
+ return None
83
+
84
+ logger.info("Created evidence record %s: %s", created_evidence.id, title)
85
+
86
+ # Upload findings as file attachments
87
+ self._attach_findings_files(
88
+ created_evidence.id,
89
+ findings,
90
+ ocsf_data,
91
+ service_name,
92
+ )
93
+
94
+ # Link evidence to SSP if provided
95
+ if self.ssp_id:
96
+ self._link_to_ssp(created_evidence.id)
97
+
98
+ # Link evidence to specific controls if provided
99
+ if control_ids:
100
+ self._link_to_controls(created_evidence.id, control_ids)
101
+
102
+ return created_evidence
103
+
104
+ except Exception as ex:
105
+ logger.error("Failed to create evidence: %s", ex)
106
+ return None
107
+
108
+ def _count_severities(self, findings: List[dict], service_name: str) -> dict:
109
+ """
110
+ Count findings by severity level
111
+
112
+ :param List[dict] findings: AWS findings
113
+ :param str service_name: AWS service name for severity field mapping
114
+ :return: Dictionary with severity counts
115
+ :rtype: dict
116
+ """
117
+ severity_counts = {"CRITICAL": 0, "HIGH": 0, "MEDIUM": 0, "LOW": 0, "INFO": 0}
118
+
119
+ for finding in findings:
120
+ # Map service-specific severity fields
121
+ if service_name == "GuardDuty":
122
+ severity = finding.get("Severity", 0)
123
+ # GuardDuty uses numeric severity (1.0-8.9)
124
+ if severity >= 7.0:
125
+ severity_counts["HIGH"] += 1
126
+ elif severity >= 4.0:
127
+ severity_counts["MEDIUM"] += 1
128
+ else:
129
+ severity_counts["LOW"] += 1
130
+
131
+ elif service_name == "SecurityHub":
132
+ # Security Hub uses normalized severity
133
+ severity_label = finding.get("Severity", {}).get("Label", "INFO")
134
+ severity_counts[severity_label] = severity_counts.get(severity_label, 0) + 1
135
+
136
+ elif service_name == "CloudTrail":
137
+ # CloudTrail events don't have severity, count as INFO
138
+ severity_counts["INFO"] += 1
139
+
140
+ return severity_counts
141
+
142
+ def _build_evidence_description(
143
+ self,
144
+ service_name: str,
145
+ total_findings: int,
146
+ severity_counts: dict,
147
+ ocsf_data: Optional[List[dict]],
148
+ ) -> str:
149
+ """
150
+ Build evidence description with finding summary
151
+
152
+ :param str service_name: AWS service name
153
+ :param int total_findings: Total number of findings
154
+ :param dict severity_counts: Severity breakdown
155
+ :param Optional[List[dict]] ocsf_data: OCSF-formatted findings
156
+ :return: Evidence description text
157
+ :rtype: str
158
+ """
159
+ description_parts = [
160
+ f"Automated evidence collection from AWS {service_name}.",
161
+ f"Total findings: {total_findings}",
162
+ "",
163
+ "Severity Breakdown:",
164
+ ]
165
+
166
+ for severity, count in severity_counts.items():
167
+ if count > 0:
168
+ description_parts.append(f" - {severity}: {count}")
169
+
170
+ description_parts.extend(["", "Files attached:"])
171
+ description_parts.append(f" - {service_name.lower()}_findings_native.jsonl.gz (AWS native format, compressed)")
172
+
173
+ if ocsf_data:
174
+ description_parts.append(
175
+ f" - {service_name.lower()}_findings_ocsf.jsonl.gz (OCSF normalized format, compressed)"
176
+ )
177
+
178
+ return "\n".join(description_parts)
179
+
180
+ def _attach_findings_files(
181
+ self,
182
+ evidence_id: int,
183
+ findings: List[dict],
184
+ ocsf_data: Optional[List[dict]],
185
+ service_name: str,
186
+ ) -> None:
187
+ """
188
+ Upload findings as file attachments to evidence
189
+
190
+ :param int evidence_id: Evidence record ID
191
+ :param List[dict] findings: Native AWS findings
192
+ :param Optional[List[dict]] ocsf_data: OCSF-formatted findings
193
+ :param str service_name: AWS service name
194
+ """
195
+ # Upload native findings as compressed JSONL
196
+ native_jsonl = "\n".join([json.dumps(f) for f in findings])
197
+
198
+ # Compress the JSONL data
199
+ compressed_buffer = BytesIO()
200
+ with gzip.open(compressed_buffer, "wt", encoding="utf-8", compresslevel=9) as gz_file:
201
+ gz_file.write(native_jsonl)
202
+
203
+ compressed_data = compressed_buffer.getvalue()
204
+
205
+ success = File.upload_file_to_regscale(
206
+ file_name=f"{service_name.lower()}_findings_native.jsonl.gz",
207
+ parent_id=evidence_id,
208
+ parent_module="evidence",
209
+ api=self.api,
210
+ file_data=compressed_data,
211
+ tags=f"aws,{service_name.lower()},native,compressed",
212
+ )
213
+
214
+ if success:
215
+ logger.info("Uploaded compressed native findings file for evidence %s", evidence_id)
216
+ else:
217
+ logger.warning("Failed to upload compressed native findings file for evidence %s", evidence_id)
218
+
219
+ # Upload OCSF findings if available
220
+ if ocsf_data:
221
+ ocsf_jsonl = "\n".join([json.dumps(f) for f in ocsf_data])
222
+
223
+ # Compress the OCSF JSONL data
224
+ compressed_buffer = BytesIO()
225
+ with gzip.open(compressed_buffer, "wt", encoding="utf-8", compresslevel=9) as gz_file:
226
+ gz_file.write(ocsf_jsonl)
227
+
228
+ compressed_data = compressed_buffer.getvalue()
229
+
230
+ success = File.upload_file_to_regscale(
231
+ file_name=f"{service_name.lower()}_findings_ocsf.jsonl.gz",
232
+ parent_id=evidence_id,
233
+ parent_module="evidence",
234
+ api=self.api,
235
+ file_data=compressed_data,
236
+ tags=f"aws,{service_name.lower()},ocsf,compressed",
237
+ )
238
+
239
+ if success:
240
+ logger.info("Uploaded compressed OCSF findings file for evidence %s", evidence_id)
241
+ else:
242
+ logger.warning("Failed to upload compressed OCSF findings file for evidence %s", evidence_id)
243
+
244
+ def _link_to_ssp(self, evidence_id: int) -> None:
245
+ """
246
+ Link evidence to Security Plan
247
+
248
+ :param int evidence_id: Evidence record ID
249
+ """
250
+ if not self.ssp_id:
251
+ return
252
+
253
+ mapping = EvidenceMapping(
254
+ evidenceID=evidence_id,
255
+ mappedID=self.ssp_id,
256
+ mappingType="securityplans",
257
+ )
258
+
259
+ try:
260
+ mapping.create()
261
+ logger.info("Linked evidence %s to SSP %s", evidence_id, self.ssp_id)
262
+ except Exception as ex:
263
+ logger.warning("Failed to link evidence to SSP: %s", ex)
264
+
265
+ def _link_to_controls(self, evidence_id: int, control_ids: List[int]) -> None:
266
+ """
267
+ Link evidence to specific security controls
268
+
269
+ :param int evidence_id: Evidence record ID
270
+ :param List[int] control_ids: List of control IDs
271
+ """
272
+ for control_id in control_ids:
273
+ mapping = EvidenceMapping(
274
+ evidenceID=evidence_id,
275
+ mappedID=control_id,
276
+ mappingType="securityControlAssessments",
277
+ )
278
+
279
+ try:
280
+ mapping.create()
281
+ logger.info("Linked evidence %s to control %s", evidence_id, control_id)
282
+ except Exception as ex:
283
+ logger.warning("Failed to link evidence to control %s: %s", control_id, ex)
@@ -0,0 +1,340 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """AWS GuardDuty 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 GuardDuty
11
+ GUARDDUTY_CONTROL_MAPPINGS = {
12
+ "SI-4": {
13
+ "name": "System Monitoring",
14
+ "description": "Monitor the system to detect attacks and indicators of potential attacks",
15
+ "checks": {
16
+ "detector_enabled": {
17
+ "weight": 100,
18
+ "pass_criteria": "GuardDuty detector is enabled and actively monitoring",
19
+ "fail_criteria": "GuardDuty detector is disabled or suspended",
20
+ },
21
+ "findings_processed": {
22
+ "weight": 90,
23
+ "pass_criteria": "Active findings are reviewed and remediated",
24
+ "fail_criteria": "High severity findings remain unaddressed",
25
+ },
26
+ },
27
+ },
28
+ "IR-4": {
29
+ "name": "Incident Handling",
30
+ "description": "Implement incident handling capability for security incidents",
31
+ "checks": {
32
+ "high_severity_findings": {
33
+ "weight": 100,
34
+ "pass_criteria": "No unaddressed high severity findings",
35
+ "fail_criteria": "High severity findings detected and not remediated",
36
+ },
37
+ "incident_response": {
38
+ "weight": 90,
39
+ "pass_criteria": "Findings integrated with incident response workflow",
40
+ "fail_criteria": "Findings not tracked in incident management system",
41
+ },
42
+ },
43
+ },
44
+ "IR-5": {
45
+ "name": "Incident Monitoring",
46
+ "description": "Track and document system security incidents",
47
+ "checks": {
48
+ "finding_tracking": {
49
+ "weight": 100,
50
+ "pass_criteria": "All findings tracked and documented",
51
+ "fail_criteria": "Findings not systematically tracked",
52
+ },
53
+ },
54
+ },
55
+ "SI-3": {
56
+ "name": "Malicious Code Protection",
57
+ "description": "Implement malicious code protection mechanisms",
58
+ "checks": {
59
+ "malware_detection": {
60
+ "weight": 100,
61
+ "pass_criteria": "GuardDuty detecting and alerting on malware activities",
62
+ "fail_criteria": "Malware protection findings not enabled or monitored",
63
+ },
64
+ },
65
+ },
66
+ "RA-5": {
67
+ "name": "Vulnerability Monitoring and Scanning",
68
+ "description": "Monitor and scan for vulnerabilities in the system",
69
+ "checks": {
70
+ "threat_intelligence": {
71
+ "weight": 100,
72
+ "pass_criteria": "GuardDuty threat intelligence enabled and current",
73
+ "fail_criteria": "Threat intelligence feeds not enabled or outdated",
74
+ },
75
+ },
76
+ },
77
+ }
78
+
79
+ # Severity mapping (GuardDuty uses 0-10 scale)
80
+ SEVERITY_MAPPING = {
81
+ "LOW": (0.1, 3.9),
82
+ "MEDIUM": (4.0, 6.9),
83
+ "HIGH": (7.0, 8.9),
84
+ "CRITICAL": (9.0, 10.0),
85
+ }
86
+
87
+ # Finding types that may contain CVEs
88
+ CVE_RELATED_FINDING_TYPES = [
89
+ "UnauthorizedAccess:EC2/MaliciousIPCaller",
90
+ "CryptoCurrency:EC2/BitcoinTool",
91
+ "Trojan:EC2/",
92
+ "Backdoor:EC2/",
93
+ "Behavior:EC2/NetworkPortUnusual",
94
+ "Recon:EC2/",
95
+ ]
96
+
97
+
98
+ class GuardDutyControlMapper:
99
+ """Map AWS GuardDuty findings to compliance control status."""
100
+
101
+ def __init__(self, framework: str = "NIST800-53R5"):
102
+ """
103
+ Initialize GuardDuty control mapper.
104
+
105
+ :param str framework: Compliance framework
106
+ """
107
+ self.framework = framework
108
+ self.mappings = GUARDDUTY_CONTROL_MAPPINGS
109
+
110
+ def assess_guardduty_compliance(self, guardduty_data: Dict) -> Dict[str, str]:
111
+ """
112
+ Assess GuardDuty compliance against all mapped controls.
113
+
114
+ :param Dict guardduty_data: GuardDuty detectors and findings
115
+ :return: Dictionary mapping control IDs to compliance results (PASS/FAIL)
116
+ :rtype: Dict[str, str]
117
+ """
118
+ results = {}
119
+
120
+ if self.framework == "NIST800-53R5":
121
+ results["SI-4"] = self._assess_si4(guardduty_data)
122
+ results["IR-4"] = self._assess_ir4(guardduty_data)
123
+ results["IR-5"] = self._assess_ir5(guardduty_data)
124
+ results["SI-3"] = self._assess_si3(guardduty_data)
125
+ results["RA-5"] = self._assess_ra5(guardduty_data)
126
+
127
+ return results
128
+
129
+ def _assess_si4(self, guardduty_data: Dict) -> str:
130
+ """
131
+ Assess SI-4 (System Monitoring) compliance.
132
+
133
+ :param Dict guardduty_data: GuardDuty data
134
+ :return: Compliance result (PASS/FAIL)
135
+ :rtype: str
136
+ """
137
+ detectors = guardduty_data.get("Detectors", [])
138
+
139
+ if not detectors:
140
+ logger.debug("GuardDuty FAILS SI-4: No detectors configured")
141
+ return "FAIL"
142
+
143
+ # Check if any detector is disabled
144
+ disabled_detectors = [d for d in detectors if d.get("Status") != "ENABLED"]
145
+ if disabled_detectors:
146
+ logger.debug(f"GuardDuty FAILS SI-4: {len(disabled_detectors)} detector(s) disabled")
147
+ return "FAIL"
148
+
149
+ logger.debug(f"GuardDuty PASSES SI-4: {len(detectors)} detector(s) enabled and monitoring")
150
+ return "PASS"
151
+
152
+ def _assess_ir4(self, guardduty_data: Dict) -> str:
153
+ """
154
+ Assess IR-4 (Incident Handling) compliance.
155
+
156
+ :param Dict guardduty_data: GuardDuty data
157
+ :return: Compliance result (PASS/FAIL)
158
+ :rtype: str
159
+ """
160
+ findings = guardduty_data.get("Findings", [])
161
+
162
+ # Check for high/critical severity unaddressed findings
163
+ high_severity_findings = [
164
+ f for f in findings if self._get_severity_level(f.get("Severity", 0)) in ["HIGH", "CRITICAL"]
165
+ ]
166
+
167
+ if high_severity_findings:
168
+ logger.debug(
169
+ f"GuardDuty FAILS IR-4: {len(high_severity_findings)} high/critical severity "
170
+ "findings requiring incident response"
171
+ )
172
+ return "FAIL"
173
+
174
+ logger.debug("GuardDuty PASSES IR-4: No high severity findings requiring immediate attention")
175
+ return "PASS"
176
+
177
+ def _assess_ir5(self, guardduty_data: Dict) -> str:
178
+ """
179
+ Assess IR-5 (Incident Monitoring) compliance.
180
+
181
+ GuardDuty's active monitoring satisfies IR-5 requirements by providing continuous
182
+ security monitoring and incident detection capabilities. The presence of GuardDuty
183
+ itself, along with this integration's tracking of findings, fulfills the control.
184
+
185
+ :param Dict guardduty_data: GuardDuty data
186
+ :return: Compliance result (always PASS when GuardDuty is enabled)
187
+ :rtype: str
188
+ """
189
+ findings = guardduty_data.get("Findings", [])
190
+
191
+ # GuardDuty being active satisfies IR-5 (Incident Monitoring)
192
+ if findings:
193
+ logger.debug(f"GuardDuty PASSES IR-5: {len(findings)} findings tracked for incident monitoring")
194
+ else:
195
+ logger.debug("GuardDuty PASSES IR-5: GuardDuty monitoring active, no incidents detected")
196
+
197
+ return "PASS"
198
+
199
+ def _assess_si3(self, guardduty_data: Dict) -> str:
200
+ """
201
+ Assess SI-3 (Malicious Code Protection) compliance.
202
+
203
+ :param Dict guardduty_data: GuardDuty data
204
+ :return: Compliance result (PASS/FAIL)
205
+ :rtype: str
206
+ """
207
+ detectors = guardduty_data.get("Detectors", [])
208
+ findings = guardduty_data.get("Findings", [])
209
+
210
+ # Check if GuardDuty is enabled (provides malware detection)
211
+ if not detectors or all(d.get("Status") != "ENABLED" for d in detectors):
212
+ logger.debug("GuardDuty FAILS SI-3: No enabled detectors for malware protection")
213
+ return "FAIL"
214
+
215
+ # Check for malware-related findings
216
+ malware_findings = [f for f in findings if self._is_malware_finding(f)]
217
+
218
+ if malware_findings:
219
+ logger.warning(
220
+ f"GuardDuty malware findings detected: {len(malware_findings)} "
221
+ "potential malware activities (requires remediation)"
222
+ )
223
+
224
+ logger.debug("GuardDuty PASSES SI-3: Malware protection monitoring enabled")
225
+ return "PASS"
226
+
227
+ def _assess_ra5(self, guardduty_data: Dict) -> str:
228
+ """
229
+ Assess RA-5 (Vulnerability Monitoring) compliance.
230
+
231
+ :param Dict guardduty_data: GuardDuty data
232
+ :return: Compliance result (PASS/FAIL)
233
+ :rtype: str
234
+ """
235
+ detectors = guardduty_data.get("Detectors", [])
236
+
237
+ # Check if threat intelligence is enabled
238
+ threat_intel_enabled = any(
239
+ d.get("DataSources", {}).get("S3Logs", {}).get("Status") == "ENABLED" for d in detectors
240
+ )
241
+
242
+ if not threat_intel_enabled:
243
+ logger.debug("GuardDuty FAILS RA-5: Threat intelligence data sources not fully enabled")
244
+ return "FAIL"
245
+
246
+ logger.debug("GuardDuty PASSES RA-5: Threat intelligence and vulnerability monitoring active")
247
+ return "PASS"
248
+
249
+ def _get_severity_level(self, severity_score: float) -> str:
250
+ """
251
+ Map GuardDuty severity score to severity level.
252
+
253
+ :param float severity_score: Severity score (0-10)
254
+ :return: Severity level (LOW, MEDIUM, HIGH, CRITICAL)
255
+ :rtype: str
256
+ """
257
+ for level, (min_score, max_score) in SEVERITY_MAPPING.items():
258
+ if min_score <= severity_score <= max_score:
259
+ return level
260
+ return "LOW"
261
+
262
+ def _is_malware_finding(self, finding: Dict) -> bool:
263
+ """
264
+ Check if finding is related to malware.
265
+
266
+ :param Dict finding: GuardDuty finding
267
+ :return: True if malware-related
268
+ :rtype: bool
269
+ """
270
+ finding_type = finding.get("Type", "")
271
+ malware_indicators = [
272
+ "Trojan",
273
+ "Backdoor",
274
+ "CryptoCurrency",
275
+ "Malware",
276
+ "Bitcoin",
277
+ "CryptoCoin",
278
+ ]
279
+ return any(indicator in finding_type for indicator in malware_indicators)
280
+
281
+ def has_cve_reference(self, finding: Dict) -> bool:
282
+ """
283
+ Check if GuardDuty finding references a CVE.
284
+
285
+ :param Dict finding: GuardDuty finding
286
+ :return: True if CVE referenced
287
+ :rtype: bool
288
+ """
289
+ # Check finding description and title for CVE patterns
290
+ description = finding.get("Description", "")
291
+ title = finding.get("Title", "")
292
+ service = finding.get("Service", {})
293
+ additional_info = service.get("AdditionalInfo", {})
294
+
295
+ # CVE pattern: CVE-YYYY-NNNNN
296
+ text_to_search = f"{description} {title} {str(additional_info)}"
297
+ return "CVE-" in text_to_search.upper()
298
+
299
+ def extract_cves_from_finding(self, finding: Dict) -> List[str]:
300
+ """
301
+ Extract CVE IDs from GuardDuty finding.
302
+
303
+ :param Dict finding: GuardDuty finding
304
+ :return: List of CVE IDs
305
+ :rtype: List[str]
306
+ """
307
+ import re
308
+
309
+ cves = []
310
+ description = finding.get("Description", "")
311
+ title = finding.get("Title", "")
312
+ service = finding.get("Service", {})
313
+ additional_info = str(service.get("AdditionalInfo", {}))
314
+
315
+ text_to_search = f"{description} {title} {additional_info}"
316
+
317
+ # CVE pattern: CVE-YYYY-NNNNN
318
+ cve_pattern = r"CVE-\d{4}-\d{4,7}"
319
+ matches = re.findall(cve_pattern, text_to_search, re.IGNORECASE)
320
+
321
+ cves = list({match.upper() for match in matches})
322
+ return cves
323
+
324
+ def get_control_description(self, control_id: str) -> Optional[str]:
325
+ """Get human-readable description for a control."""
326
+ control_data = self.mappings.get(control_id)
327
+ if control_data:
328
+ return f"{control_data.get('name')}: {control_data.get('description', '')}"
329
+ return None
330
+
331
+ def get_mapped_controls(self) -> List[str]:
332
+ """Get list of all control IDs mapped for this framework."""
333
+ return list(self.mappings.keys())
334
+
335
+ def get_check_details(self, control_id: str) -> Optional[Dict]:
336
+ """Get detailed check criteria for a control."""
337
+ control_data = self.mappings.get(control_id)
338
+ if control_data:
339
+ return control_data.get("checks", {})
340
+ return None