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,432 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Control Matcher - A utility class for identifying and matching control implementations
5
+ across different RegScale entities based on control ID strings.
6
+ """
7
+
8
+ import logging
9
+ import re
10
+ from typing import Dict, List, Optional, Tuple
11
+
12
+ from regscale.core.app.api import Api
13
+ from regscale.core.app.application import Application
14
+ from regscale.models.regscale_models.control_implementation import ControlImplementation
15
+ from regscale.models.regscale_models.security_control import SecurityControl
16
+
17
+ logger = logging.getLogger("regscale")
18
+
19
+
20
+ class ControlMatcher:
21
+ """
22
+ A class to identify control IDs and match them across different RegScale entities.
23
+
24
+ This class provides control matching capabilities:
25
+ - Parse control ID strings to extract NIST control identifiers
26
+ - Match controls from catalogs to security plans
27
+ - Find control implementations based on control IDs
28
+ - Support multiple control ID formats (e.g., AC-1, AC-1(1), AC-1.1)
29
+
30
+ Note: This class is focused on finding and matching existing controls only.
31
+ Control creation/modification should be handled by calling code.
32
+ """
33
+
34
+ def __init__(self, app: Optional[Application] = None):
35
+ """
36
+ Initialize the ControlMatcher.
37
+
38
+ :param Optional[Application] app: RegScale Application instance
39
+ """
40
+ self.app = app or Application()
41
+ self.api = Api()
42
+ self._catalog_cache: Dict[int, List[SecurityControl]] = {}
43
+ self._control_impl_cache: Dict[Tuple[int, str], Dict[str, ControlImplementation]] = {}
44
+
45
+ @staticmethod
46
+ def parse_control_id(control_string: str) -> Optional[str]:
47
+ """
48
+ Parse a control ID string and extract the standardized control identifier.
49
+
50
+ Handles various formats:
51
+ - NIST format: AC-1, AC-1(1), AC-1.1, AC-1(a), AC-1.a
52
+ - With leading zeros: AC-01, AC-17(02)
53
+ - With spaces: AC-1 (1), AC-02 (04), AC-1 (a)
54
+ - With text: "Access Control AC-1"
55
+ - Multiple controls: "AC-1, AC-2"
56
+
57
+ :param str control_string: Raw control ID string
58
+ :return: Standardized control ID or None if not found
59
+ :rtype: Optional[str]
60
+ """
61
+ if not control_string:
62
+ return None
63
+
64
+ # Clean the string
65
+ control_string = control_string.strip().upper()
66
+
67
+ # Common NIST control ID pattern
68
+ # Matches: AC-1, AC-01, AC-1(1), AC-1(01), AC-1 (1), AC-1.1, AC-1.01, AC-1(a), AC-1.a, etc.
69
+ # Allows optional whitespace before and inside parentheses
70
+ # Now includes letter parts (a, b, c) in addition to numeric parts
71
+ pattern = r"([A-Z]{2,3}-\d+(?:\s*\(\s*(?:\d+|[A-Z])\s*\)|\.(?:\d+|[A-Z]))?)"
72
+
73
+ if matches := re.findall(pattern, control_string):
74
+ # Normalize parentheses to dots for consistency and remove spaces
75
+ control_id = matches[0]
76
+ control_id = control_id.replace(" ", "") # Remove all spaces
77
+ control_id = control_id.replace("(", ".").replace(")", "")
78
+
79
+ # Normalize leading zeros (e.g., AC-01.02 -> AC-1.2)
80
+ parts = control_id.split("-")
81
+ if len(parts) == 2:
82
+ family = parts[0]
83
+ number_part = parts[1]
84
+
85
+ if "." in number_part:
86
+ main_num, enhancement = number_part.split(".", 1)
87
+ main_num = str(int(main_num))
88
+ # Only normalize if enhancement is numeric, preserve letters as-is
89
+ if enhancement.isdigit():
90
+ enhancement = str(int(enhancement))
91
+ control_id = f"{family}-{main_num}.{enhancement}"
92
+ else:
93
+ main_num = str(int(number_part))
94
+ control_id = f"{family}-{main_num}"
95
+
96
+ return control_id
97
+
98
+ return None
99
+
100
+ def find_control_in_catalog(self, control_id: str, catalog_id: int) -> Optional[SecurityControl]:
101
+ """
102
+ Find a security control in a specific catalog by control ID.
103
+
104
+ :param str control_id: The control ID to search for
105
+ :param int catalog_id: The catalog ID to search in
106
+ :return: SecurityControl object if found, None otherwise
107
+ :rtype: Optional[SecurityControl]
108
+ """
109
+ controls = self._get_catalog_controls(catalog_id)
110
+
111
+ # Generate all possible variations of the control ID
112
+ search_ids = self._get_control_id_variations(control_id)
113
+
114
+ # Try exact match with any variation
115
+ for control in controls:
116
+ if control.controlId in search_ids:
117
+ return control
118
+
119
+ # Try matching control variations against search variations
120
+ for control in controls:
121
+ control_variations = self._get_control_id_variations(control.controlId)
122
+ if control_variations & search_ids: # Set intersection
123
+ return control
124
+
125
+ return None
126
+
127
+ def find_control_implementation(
128
+ self, control_id: str, parent_id: int, parent_module: str = "securityplans", catalog_id: Optional[int] = None
129
+ ) -> Optional[ControlImplementation]:
130
+ """
131
+ Find a control implementation based on control ID and parent context.
132
+
133
+ :param str control_id: The control ID to match
134
+ :param int parent_id: Parent entity ID (e.g., security plan ID)
135
+ :param str parent_module: Parent module type (default: securityplans)
136
+ :param Optional[int] catalog_id: Optional catalog ID for better matching
137
+ :return: ControlImplementation if found, None otherwise
138
+ :rtype: Optional[ControlImplementation]
139
+ """
140
+ # Get control implementations for the parent
141
+ implementations = self._get_control_implementations(parent_id, parent_module)
142
+
143
+ # Get all variations of the control ID for matching
144
+ search_variations = self._get_control_id_variations(control_id)
145
+ if not search_variations:
146
+ logger.warning(f"Could not parse control ID: {control_id}")
147
+ return None
148
+
149
+ # Try to find matching implementation with variation matching
150
+ for impl_key, impl in implementations.items():
151
+ impl_variations = self._get_control_id_variations(impl_key)
152
+ if impl_variations & search_variations: # Set intersection
153
+ return impl
154
+
155
+ # If catalog ID provided, try to find via security control
156
+ if catalog_id:
157
+ control = self.find_control_in_catalog(control_id, catalog_id)
158
+ if control:
159
+ # Search implementations by control ID
160
+ for impl in implementations.values():
161
+ if impl.controlID == control.id:
162
+ return impl
163
+
164
+ return None
165
+
166
+ def match_controls_to_implementations(
167
+ self,
168
+ control_ids: List[str],
169
+ parent_id: int,
170
+ parent_module: str = "securityplans",
171
+ catalog_id: Optional[int] = None,
172
+ ) -> Dict[str, Optional[ControlImplementation]]:
173
+ """
174
+ Match multiple control IDs to their implementations.
175
+
176
+ :param List[str] control_ids: List of control ID strings
177
+ :param int parent_id: Parent entity ID
178
+ :param str parent_module: Parent module type
179
+ :param Optional[int] catalog_id: Optional catalog ID
180
+ :return: Dictionary mapping control IDs to implementations
181
+ :rtype: Dict[str, Optional[ControlImplementation]]
182
+ """
183
+ results = {}
184
+
185
+ for control_id in control_ids:
186
+ impl = self.find_control_implementation(control_id, parent_id, parent_module, catalog_id)
187
+ results[control_id] = impl
188
+
189
+ return results
190
+
191
+ def get_security_plan_controls(self, security_plan_id: int) -> Dict[str, ControlImplementation]:
192
+ """
193
+ Get all control implementations for a security plan.
194
+
195
+ :param int security_plan_id: The security plan ID
196
+ :return: Dictionary of control implementations keyed by control ID
197
+ :rtype: Dict[str, ControlImplementation]
198
+ """
199
+ return self._get_control_implementations(security_plan_id, "securityplans")
200
+
201
+ def find_controls_by_pattern(self, pattern: str, catalog_id: int) -> List[SecurityControl]:
202
+ """
203
+ Find all controls in a catalog matching a pattern.
204
+
205
+ :param str pattern: Regex pattern or substring to match
206
+ :param int catalog_id: Catalog ID to search in
207
+ :return: List of matching SecurityControl objects
208
+ :rtype: List[SecurityControl]
209
+ """
210
+ controls = self._get_catalog_controls(catalog_id)
211
+ matched = []
212
+
213
+ for control in controls:
214
+ if (re.search(pattern, control.controlId, re.IGNORECASE)) or (
215
+ control.title and re.search(pattern, control.title, re.IGNORECASE)
216
+ ):
217
+ matched.append(control)
218
+
219
+ return matched
220
+
221
+ def bulk_match_controls(
222
+ self,
223
+ control_mappings: Dict[str, str],
224
+ parent_id: int,
225
+ parent_module: str = "securityplans",
226
+ catalog_id: Optional[int] = None,
227
+ ) -> Dict[str, Optional[ControlImplementation]]:
228
+ """
229
+ Bulk match control IDs to their implementations.
230
+
231
+ :param Dict[str, str] control_mappings: Dict of {external_id: control_id}
232
+ :param int parent_id: Parent entity ID
233
+ :param str parent_module: Parent module type
234
+ :param Optional[int] catalog_id: Catalog ID for controls
235
+ :return: Dictionary mapping external IDs to ControlImplementations (None if not found)
236
+ :rtype: Dict[str, Optional[ControlImplementation]]
237
+ """
238
+ results = {}
239
+
240
+ for external_id, control_id in control_mappings.items():
241
+ impl = self.find_control_implementation(control_id, parent_id, parent_module, catalog_id)
242
+ results[external_id] = impl
243
+
244
+ return results
245
+
246
+ def _get_catalog_controls(self, catalog_id: int) -> List[SecurityControl]:
247
+ """
248
+ Get all controls for a catalog (with caching).
249
+
250
+ :param int catalog_id: Catalog ID
251
+ :return: List of SecurityControl objects
252
+ :rtype: List[SecurityControl]
253
+ """
254
+ if catalog_id not in self._catalog_cache:
255
+ try:
256
+ controls = SecurityControl.get_list_by_catalog(catalog_id)
257
+ self._catalog_cache[catalog_id] = controls
258
+ except Exception as e:
259
+ logger.error(f"Failed to get controls for catalog {catalog_id}: {e}")
260
+ return []
261
+
262
+ return self._catalog_cache.get(catalog_id, [])
263
+
264
+ def _normalize_control_id(self, control_id: str) -> Optional[str]:
265
+ """
266
+ Normalize a control ID by removing leading zeros from all numeric parts.
267
+
268
+ Examples:
269
+ - AC-01 -> AC-1
270
+ - AC-17(02) -> AC-17.2
271
+ - AC-1.01 -> AC-1.1
272
+
273
+ :param str control_id: The control ID to normalize
274
+ :return: Normalized control ID or None if invalid
275
+ :rtype: Optional[str]
276
+ """
277
+ parsed = self.parse_control_id(control_id)
278
+ if not parsed:
279
+ return None
280
+
281
+ # Split by '-' to get family and number parts
282
+ parts = parsed.split("-")
283
+ if len(parts) != 2:
284
+ return None
285
+
286
+ family = parts[0]
287
+ number_part = parts[1]
288
+
289
+ # Handle enhancement notation (both . and parentheses are normalized to .)
290
+ if "." in number_part:
291
+ main_num, enhancement = number_part.split(".", 1)
292
+ # Remove leading zeros from both parts
293
+ main_num = str(int(main_num))
294
+ enhancement = str(int(enhancement))
295
+ return f"{family}-{main_num}.{enhancement}"
296
+ else:
297
+ # Just main control number
298
+ main_num = str(int(number_part))
299
+ return f"{family}-{main_num}"
300
+
301
+ @staticmethod
302
+ def _generate_simple_variations(family: str, main_num: str) -> set:
303
+ """
304
+ Generate variations for simple control IDs without enhancements.
305
+
306
+ :param str family: Control family (e.g., AC, SI)
307
+ :param str main_num: Main control number
308
+ :return: Set of variations
309
+ :rtype: set
310
+ """
311
+ main_int = int(main_num)
312
+ return {
313
+ f"{family}-{main_int}",
314
+ f"{family}-{main_int:02d}",
315
+ }
316
+
317
+ @staticmethod
318
+ def _generate_letter_enhancement_variations(family: str, main_num: str, enhancement: str) -> set:
319
+ """
320
+ Generate variations for control IDs with letter-based enhancements.
321
+
322
+ :param str family: Control family (e.g., AC, SI)
323
+ :param str main_num: Main control number
324
+ :param str enhancement: Letter enhancement (e.g., a, b)
325
+ :return: Set of variations
326
+ :rtype: set
327
+ """
328
+ main_int = int(main_num)
329
+ variations = set()
330
+
331
+ for main_fmt in [str(main_int), f"{main_int:02d}"]:
332
+ variations.add(f"{family}-{main_fmt}.{enhancement}")
333
+ variations.add(f"{family}-{main_fmt}({enhancement})")
334
+
335
+ return variations
336
+
337
+ @staticmethod
338
+ def _generate_numeric_enhancement_variations(family: str, main_num: str, enhancement: str) -> set:
339
+ """
340
+ Generate variations for control IDs with numeric enhancements.
341
+
342
+ :param str family: Control family (e.g., AC, SI)
343
+ :param str main_num: Main control number
344
+ :param str enhancement: Numeric enhancement
345
+ :return: Set of variations
346
+ :rtype: set
347
+ """
348
+ main_int = int(main_num)
349
+ enh_int = int(enhancement)
350
+ variations = set()
351
+
352
+ for main_fmt in [str(main_int), f"{main_int:02d}"]:
353
+ for enh_fmt in [str(enh_int), f"{enh_int:02d}"]:
354
+ variations.add(f"{family}-{main_fmt}.{enh_fmt}")
355
+ variations.add(f"{family}-{main_fmt}({enh_fmt})")
356
+
357
+ return variations
358
+
359
+ def _get_control_id_variations(self, control_id: str) -> set:
360
+ """
361
+ Generate all valid variations of a control ID (with and without leading zeros).
362
+
363
+ Examples:
364
+ - AC-1 -> {AC-1, AC-01}
365
+ - AC-17.2 -> {AC-17.2, AC-17.02, AC-17(2), AC-17(02)}
366
+ - AC-1.a -> {AC-1.a, AC-01.a, AC-1(a), AC-01(a)}
367
+
368
+ :param str control_id: The control ID to generate variations for
369
+ :return: Set of all valid variations
370
+ :rtype: set
371
+ """
372
+ parsed = self.parse_control_id(control_id)
373
+ if not parsed:
374
+ return set()
375
+
376
+ # Split by '-' to get family and number parts
377
+ parts = parsed.split("-")
378
+ if len(parts) != 2:
379
+ return set()
380
+
381
+ family = parts[0]
382
+ number_part = parts[1]
383
+
384
+ # Handle enhancement notation
385
+ if "." in number_part:
386
+ main_num, enhancement = number_part.split(".", 1)
387
+
388
+ # Check if enhancement is a letter (a, b, c, etc.) or a number
389
+ if enhancement.isalpha():
390
+ variations = self._generate_letter_enhancement_variations(family, main_num, enhancement)
391
+ else:
392
+ variations = self._generate_numeric_enhancement_variations(family, main_num, enhancement)
393
+ else:
394
+ variations = self._generate_simple_variations(family, number_part)
395
+
396
+ # Add uppercase versions to ensure consistency
397
+ return {v.upper() for v in variations}
398
+
399
+ def _get_control_implementations(self, parent_id: int, parent_module: str) -> Dict[str, ControlImplementation]:
400
+ """
401
+ Get control implementations for a parent (with caching).
402
+
403
+ :param int parent_id: Parent ID
404
+ :param str parent_module: Parent module
405
+ :return: Dict of implementations keyed by control ID
406
+ :rtype: Dict[str, ControlImplementation]
407
+ """
408
+ cache_key = (parent_id, parent_module)
409
+
410
+ if cache_key not in self._control_impl_cache:
411
+ try:
412
+ # Get the label map which maps control IDs to implementation IDs
413
+ label_map = ControlImplementation.get_control_label_map_by_parent(parent_id, parent_module)
414
+
415
+ implementations = {}
416
+ for control_label, impl_id in label_map.items():
417
+ impl = ControlImplementation.get_object(impl_id)
418
+ if impl:
419
+ implementations[control_label] = impl
420
+
421
+ self._control_impl_cache[cache_key] = implementations
422
+ except Exception as e:
423
+ logger.error(f"Failed to get implementations for {parent_module}/{parent_id}: {e}")
424
+ return {}
425
+
426
+ return self._control_impl_cache.get(cache_key, {})
427
+
428
+ def clear_cache(self):
429
+ """Clear all cached data."""
430
+ self._catalog_cache.clear()
431
+ self._control_impl_cache.clear()
432
+ logger.info("Cleared control matcher cache")