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
@@ -8,7 +8,89 @@ from regscale.core.app.utils.app_utils import get_current_datetime
8
8
  from regscale.integrations.scanner_integration import IntegrationFinding, issue_due_date
9
9
  from regscale.models import regscale_models
10
10
 
11
- logger = logging.getLogger(__name__)
11
+ logger = logging.getLogger("regscale")
12
+
13
+ # Constants
14
+ DEFAULT_CCI_REF = "CCI-000366" # Default CCI reference for STIG findings
15
+ UNKNOWN_VULN_NUM = "unknown"
16
+ _SOLUTION_KEYWORD = "Solution:"
17
+ _REFERENCE_INFO_KEYWORD = "Reference Information:"
18
+
19
+ # Status mapping for STIG results
20
+ _RESULT_STATUS_MAP = {
21
+ "PASSED": regscale_models.ChecklistStatus.PASS,
22
+ "FAILED": regscale_models.ChecklistStatus.FAIL,
23
+ "ERROR": regscale_models.ChecklistStatus.FAIL,
24
+ "NOT APPLICABLE": regscale_models.ChecklistStatus.NOT_APPLICABLE,
25
+ "NOT_APPLICABLE": regscale_models.ChecklistStatus.NOT_APPLICABLE,
26
+ }
27
+
28
+ # Severity mapping for STIG findings
29
+ _SEVERITY_MAP = {
30
+ "critical": regscale_models.IssueSeverity.Critical,
31
+ "high": regscale_models.IssueSeverity.High,
32
+ "medium": regscale_models.IssueSeverity.Moderate,
33
+ "low": regscale_models.IssueSeverity.Low,
34
+ }
35
+
36
+
37
+ def _extract_field(pattern: str, text: str, flags: Union[int, re.RegexFlag] = 0, group: int = 1) -> str:
38
+ """
39
+ Extracts a field from a string using a regular expression.
40
+
41
+ :param str pattern: The regular expression pattern to search for
42
+ :param str text: The string to search in
43
+ :param int flags: Optional regular expression flags, defaults to 0
44
+ :param int group: The group number to return from the match, defaults to 1
45
+ :return: The extracted field as a string. Empty string if no match was found
46
+ :rtype: str
47
+ """
48
+ match = re.search(pattern, text, flags)
49
+ return match.group(group).strip() if match else ""
50
+
51
+
52
+ def _extract_reference_dict(output: str) -> dict:
53
+ """
54
+ Extract and parse reference information into dictionary.
55
+
56
+ :param str output: The STIG output string
57
+ :return: Dictionary of reference information
58
+ :rtype: dict
59
+ """
60
+ ref_info = _extract_field(r"Reference Information:\s*(.+)", output, flags=re.DOTALL)
61
+ ref_dict = {}
62
+ for item in ref_info.split(","):
63
+ if "|" in item:
64
+ parts = item.split("|", 1)
65
+ if len(parts) == 2:
66
+ ref_dict[parts[0].strip()] = parts[1].strip()
67
+ return ref_dict
68
+
69
+
70
+ def _map_result_to_status(result: str) -> regscale_models.ChecklistStatus:
71
+ """
72
+ Map result string to ChecklistStatus enum.
73
+
74
+ :param str result: The result string from STIG output
75
+ :return: ChecklistStatus enum value
76
+ :rtype: regscale_models.ChecklistStatus
77
+ """
78
+ result_key = result.upper().replace("_", " ").strip()
79
+ if result_key not in _RESULT_STATUS_MAP:
80
+ logger.warning("Result '%s' not found in status map", result)
81
+ return regscale_models.ChecklistStatus.NOT_REVIEWED
82
+ return _RESULT_STATUS_MAP[result_key]
83
+
84
+
85
+ def _map_severity_to_enum(severity: str) -> regscale_models.IssueSeverity:
86
+ """
87
+ Map severity string to IssueSeverity enum.
88
+
89
+ :param str severity: The severity string
90
+ :return: IssueSeverity enum value
91
+ :rtype: regscale_models.IssueSeverity
92
+ """
93
+ return _SEVERITY_MAP.get(severity.lower(), regscale_models.IssueSeverity.NotAssigned)
12
94
 
13
95
 
14
96
  def parse_stig_output(output: str, finding: IntegrationFinding) -> IntegrationFinding:
@@ -20,43 +102,39 @@ def parse_stig_output(output: str, finding: IntegrationFinding) -> IntegrationFi
20
102
  :return: An IntegrationFinding object containing the parsed finding information.
21
103
  :rtype: IntegrationFinding
22
104
  """
23
-
24
- def _extract_field(pattern: str, text: str, flags: Union[int, re.RegexFlag] = 0, group: int = 1) -> str:
25
- """
26
- Extracts a field from a string using a regular expression
27
-
28
- :param str pattern: The regular expression pattern to search for
29
- :param str text: The string to search in
30
- :param int flags: Optional regular expression flags, defaults to 0
31
- :param int group: The group number to return from the match, defaults to 1
32
- :return: The extracted field as a string. Empty string if no match was found
33
- :rtype: str
34
- """
35
- match = re.search(pattern, text, flags)
36
- return match.group(group).strip() if match else ""
37
-
38
- # Extract fields
39
- check_name_full = _extract_field(r"Check Name:\s*(.*?)\n", output, flags=re.DOTALL | re.MULTILINE)
105
+ # Extract fields using optimized regex patterns (preventing catastrophic backtracking)
106
+ check_name_full = _extract_field(r"Check Name:\s*([^\n]+)", output)
40
107
  check_name_parts = check_name_full.split(":", 1)
41
108
  rule_id = check_name_parts[0].strip()
42
109
  check_description = check_name_parts[1].strip() if len(check_name_parts) > 1 else ""
43
110
 
111
+ # Extract baseline from Target keyword
44
112
  baseline = _extract_field(r"(.*?)\s+Target\s+(.*)", check_description, group=2)
45
- if target_match := _extract_field(r"(.*?)\s+Target\s+(.*)", check_description):
113
+ target_match = _extract_field(r"(.*?)\s+Target\s+(.*)", check_description)
114
+ if target_match:
46
115
  check_description = check_description[: check_description.find(target_match) + len(target_match)].strip()
47
116
 
48
- information = _extract_field(r"Information:\s*(.*?)\n", output, flags=re.DOTALL | re.MULTILINE)
49
- vuln_discuss = _extract_field(r"VulnDiscussion='(.*?)'\s", output, flags=re.DOTALL | re.MULTILINE)
50
- result = _extract_field(r"Result:\s*(.*?)(?:\n|$)", output, flags=re.IGNORECASE | re.DOTALL)
51
- solution = _extract_field(r"Solution:\s*(.*?)\n\nReference Information:", output, flags=re.DOTALL | re.MULTILINE)
117
+ information = _extract_field(r"Information:\s*([^\n]+)", output)
118
+ vuln_discuss = _extract_field(r"VulnDiscussion='([^']+)'\s", output)
119
+ result = _extract_field(r"Result:\s*([^\n]+)", output, flags=re.IGNORECASE)
120
+ # Extract solution using a safe pattern that avoids backtracking
121
+ # Split on reference keyword and take the solution section
122
+ if _SOLUTION_KEYWORD in output and _REFERENCE_INFO_KEYWORD in output:
123
+ solution_start = output.find(_SOLUTION_KEYWORD) + len(_SOLUTION_KEYWORD)
124
+ ref_start = output.find(_REFERENCE_INFO_KEYWORD, solution_start)
125
+ solution = output[solution_start:ref_start].strip()
126
+ elif _SOLUTION_KEYWORD in output:
127
+ solution_start = output.find(_SOLUTION_KEYWORD) + len(_SOLUTION_KEYWORD)
128
+ solution = output[solution_start:].strip()
129
+ else:
130
+ solution = ""
52
131
 
53
- # Extract reference information
54
- ref_info = _extract_field(r"Reference Information:\s*(.*)", output, flags=re.DOTALL | re.MULTILINE)
55
- ref_dict = dict(item.split("|", 1) for item in ref_info.split(",") if "|" in item)
132
+ # Extract reference information using helper function
133
+ ref_dict = _extract_reference_dict(output)
56
134
 
57
135
  # Extract specific references
58
- cci_ref = ref_dict.get("CCI", "CCI-000366")
59
- severity = ref_dict.get("SEVERITY", "").lower()
136
+ cci_ref = ref_dict.get("CCI", DEFAULT_CCI_REF)
137
+ severity_str = ref_dict.get("SEVERITY", "").lower()
60
138
  oval_def = ref_dict.get("OVAL-DEF", "")
61
139
  generated_date = ref_dict.get("GENERATED-DATE", "")
62
140
  updated_date = ref_dict.get("UPDATED-DATE", "")
@@ -64,36 +142,19 @@ def parse_stig_output(output: str, finding: IntegrationFinding) -> IntegrationFi
64
142
  rule_id_full = ref_dict.get("RULE-ID", "")
65
143
  group_id = ref_dict.get("GROUP-ID", "")
66
144
 
145
+ # Extract vulnerability number
67
146
  vuln_num_match = re.search(r"SV-\d+r\d+_rule", rule_id)
68
- vuln_num = vuln_num_match.group(0) if vuln_num_match else "unknown"
147
+ vuln_num = vuln_num_match.group(0) if vuln_num_match else UNKNOWN_VULN_NUM
69
148
 
70
149
  title = f"{vuln_num}: {check_description}"
71
150
  issue_title = title
72
151
 
73
- status_map = {
74
- "PASSED": regscale_models.ChecklistStatus.PASS,
75
- "FAILED": regscale_models.ChecklistStatus.FAIL,
76
- "ERROR": regscale_models.ChecklistStatus.FAIL,
77
- "NOT APPLICABLE": regscale_models.ChecklistStatus.NOT_APPLICABLE,
78
- "NOT_APPLICABLE": regscale_models.ChecklistStatus.NOT_APPLICABLE,
79
- }
152
+ # Map result to status using helper function
153
+ status = _map_result_to_status(result)
80
154
 
81
- result_key = result.upper().replace("_", " ").strip()
82
- if result_key not in status_map:
83
- logger.warning(f"Result '{result}' not found in status map")
84
- status = regscale_models.ChecklistStatus.NOT_REVIEWED
85
- else:
86
- status = status_map[result_key]
87
-
88
- # Map severity to IssueSeverity enum
89
- priority = (severity or "").title()
90
- severity_map = {
91
- "critical": regscale_models.IssueSeverity.Critical,
92
- "high": regscale_models.IssueSeverity.High,
93
- "medium": regscale_models.IssueSeverity.Moderate,
94
- "low": regscale_models.IssueSeverity.Low,
95
- }
96
- severity = severity_map.get(severity.lower(), regscale_models.IssueSeverity.NotAssigned)
155
+ # Map severity to IssueSeverity enum using helper function
156
+ priority = (severity_str or "").title()
157
+ severity = _map_severity_to_enum(severity_str)
97
158
 
98
159
  results = (
99
160
  f"Vulnerability Number: {vuln_num}, Severity: {severity.value}, "
@@ -132,9 +193,4 @@ def parse_stig_output(output: str, finding: IntegrationFinding) -> IntegrationFi
132
193
  finding.rule_id_full = rule_id_full
133
194
  finding.group_id = group_id
134
195
 
135
- # Future values
136
- # finding.comments = ""
137
- # finding.poam_comments = ""
138
- # finding.rule_version = ""
139
-
140
196
  return finding
@@ -5,7 +5,7 @@ from datetime import datetime, timedelta
5
5
  from typing import Optional, Dict, Any, List
6
6
 
7
7
  from regscale.core.app.utils.app_utils import error_and_exit, check_file_path
8
- from regscale.integrations.commercial.wizv2.constants import CONTENT_TYPE
8
+ from regscale.integrations.commercial.wizv2.core.constants import CONTENT_TYPE
9
9
  from regscale.core.app.application import Application
10
10
  from regscale.utils import PaginatedGraphQLClient
11
11
 
@@ -25,7 +25,7 @@ def wiz():
25
25
  @click.option("--client_secret", default=None, hide_input=True, required=False) # type: ignore
26
26
  def authenticate(client_id, client_secret):
27
27
  """Authenticate to Wiz."""
28
- from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
28
+ from regscale.integrations.commercial.wizv2.core.auth import wiz_authenticate
29
29
 
30
30
  wiz_authenticate(client_id, client_secret)
31
31
 
@@ -45,7 +45,7 @@ def authenticate(client_id, client_secret):
45
45
  "--client_id",
46
46
  "-ci",
47
47
  help="Wiz Client ID, or can be set as environment variable wizClientId",
48
- default="",
48
+ default=None,
49
49
  hide_input=False,
50
50
  required=False,
51
51
  )
@@ -53,7 +53,7 @@ def authenticate(client_id, client_secret):
53
53
  "--client_secret",
54
54
  "-cs",
55
55
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
56
- default="",
56
+ default=None,
57
57
  hide_input=False,
58
58
  required=False,
59
59
  )
@@ -77,9 +77,9 @@ def inventory(
77
77
  """Process inventory from Wiz and create assets in RegScale."""
78
78
  from regscale.integrations.commercial.wizv2.scanner import WizVulnerabilityIntegration
79
79
 
80
- if not client_secret:
80
+ if client_secret is None:
81
81
  client_secret = WizVariables.wizClientSecret
82
- if not client_id:
82
+ if client_id is None:
83
83
  client_id = WizVariables.wizClientId
84
84
 
85
85
  scanner = WizVulnerabilityIntegration(plan_id=regscale_ssp_id)
@@ -104,14 +104,14 @@ def inventory(
104
104
  @click.option(
105
105
  "--client_id",
106
106
  help="Wiz Client ID, or can be set as environment variable wizClientId",
107
- default="",
107
+ default=None,
108
108
  hide_input=False,
109
109
  required=False,
110
110
  )
111
111
  @click.option(
112
112
  "--client_secret",
113
113
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
114
- default="",
114
+ default=None,
115
115
  hide_input=True,
116
116
  required=False,
117
117
  )
@@ -136,13 +136,13 @@ def issues(
136
136
  Process Issues from Wiz into RegScale
137
137
  """
138
138
  from regscale.core.app.utils.app_utils import check_license
139
- from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
139
+ from regscale.integrations.commercial.wizv2.core.auth import wiz_authenticate
140
140
  from regscale.integrations.commercial.wizv2.issue import WizIssue
141
141
  import json
142
142
 
143
- if not client_secret:
143
+ if client_secret is None:
144
144
  client_secret = WizVariables.wizClientSecret
145
- if not client_id:
145
+ if client_id is None:
146
146
  client_id = WizVariables.wizClientId
147
147
 
148
148
  check_license()
@@ -154,7 +154,7 @@ def issues(
154
154
  scanner = WizIssue(plan_id=regscale_ssp_id)
155
155
  scanner.sync_findings(
156
156
  plan_id=regscale_ssp_id,
157
- filter_by_override=filter_by_override, # type: ignore
157
+ filter_by_override=filter_by, # Pass the processed dict with project ID
158
158
  client_id=client_id, # type: ignore
159
159
  client_secret=client_secret, # type: ignore
160
160
  wiz_project_id=wiz_project_id,
@@ -166,7 +166,7 @@ def issues(
166
166
  "--client_id",
167
167
  "-ci",
168
168
  help="Wiz Client ID, or can be set as environment variable wizClientId",
169
- default="",
169
+ default=None,
170
170
  hide_input=False,
171
171
  required=False,
172
172
  )
@@ -174,7 +174,7 @@ def issues(
174
174
  "--client_secret",
175
175
  "-cs",
176
176
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
177
- default="",
177
+ default=None,
178
178
  hide_input=True,
179
179
  required=False,
180
180
  )
@@ -191,12 +191,12 @@ def attach_sbom(
191
191
  standard="CycloneDX",
192
192
  ):
193
193
  """Download SBOMs from a Wiz report by ID and add them to the corresponding RegScale assets."""
194
- from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
194
+ from regscale.integrations.commercial.wizv2.core.auth import wiz_authenticate
195
195
  from regscale.integrations.commercial.wizv2.utils import fetch_sbom_report
196
196
 
197
- if not client_secret:
197
+ if client_secret is None:
198
198
  client_secret = WizVariables.wizClientSecret
199
- if not client_id:
199
+ if client_id is None:
200
200
  client_id = WizVariables.wizClientId
201
201
 
202
202
  wiz_authenticate(
@@ -212,15 +212,6 @@ def attach_sbom(
212
212
  )
213
213
 
214
214
 
215
- @wiz.command()
216
- def threats():
217
- """Process threats from Wiz -> Coming soon"""
218
- from regscale.core.app.utils.app_utils import check_license
219
-
220
- check_license()
221
- logger.info("Threats - COMING SOON")
222
-
223
-
224
215
  @wiz.command()
225
216
  @click.option( # type: ignore
226
217
  "--wiz_project_id",
@@ -234,7 +225,7 @@ def threats():
234
225
  "--client_id",
235
226
  "-ci",
236
227
  help="Wiz Client ID, or can be set as environment variable wizClientId",
237
- default="",
228
+ default=None,
238
229
  hide_input=False,
239
230
  required=False,
240
231
  )
@@ -242,7 +233,7 @@ def threats():
242
233
  "--client_secret",
243
234
  "-cs",
244
235
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
245
- default="",
236
+ default=None,
246
237
  hide_input=True,
247
238
  required=False,
248
239
  )
@@ -266,9 +257,9 @@ def vulnerabilities(
266
257
  """Process vulnerabilities from Wiz"""
267
258
  from regscale.integrations.commercial.wizv2.scanner import WizVulnerabilityIntegration
268
259
 
269
- if not client_secret:
260
+ if client_secret is None:
270
261
  client_secret = WizVariables.wizClientSecret
271
- if not client_id:
262
+ if client_id is None:
272
263
  client_id = WizVariables.wizClientId
273
264
 
274
265
  scanner = WizVulnerabilityIntegration(plan_id=regscale_ssp_id)
@@ -286,7 +277,7 @@ def vulnerabilities(
286
277
  "--client_id",
287
278
  "-ci",
288
279
  help="Wiz Client ID, or can be set as environment variable wizClientId",
289
- default="",
280
+ default=None,
290
281
  hide_input=False,
291
282
  required=False,
292
283
  )
@@ -294,14 +285,18 @@ def vulnerabilities(
294
285
  "--client_secret",
295
286
  "-cs",
296
287
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
297
- default="",
288
+ default=None,
298
289
  hide_input=True,
299
290
  required=False,
300
291
  )
301
292
  @click.option("--evidence_id", "-e", help="Wiz Evidence ID", required=True, type=int) # type: ignore
302
293
  @click.option("--report_id", "-r", help="Wiz Report ID", required=True) # type: ignore
303
- @click.option("--report_file_name", "-n", help="Report file name", default="evidence_report", required=False) # type: ignore
304
- @click.option("--report_file_extension", "-e", help="Report file extension", default="csv", required=False) # type: ignore
294
+ @click.option(
295
+ "--report_file_name", "-n", help="Report file name", default="evidence_report", required=False
296
+ ) # type: ignore
297
+ @click.option(
298
+ "--report_file_extension", "-e", help="Report file extension", default="csv", required=False
299
+ ) # type: ignore
305
300
  def add_report_evidence(
306
301
  client_id,
307
302
  client_secret,
@@ -311,12 +306,12 @@ def add_report_evidence(
311
306
  report_file_extension: str = "csv",
312
307
  ):
313
308
  """Download a Wiz report by ID and Attach to Evidence locker"""
314
- from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
309
+ from regscale.integrations.commercial.wizv2.core.auth import wiz_authenticate
315
310
  from regscale.integrations.commercial.wizv2.utils import fetch_report_by_id
316
311
 
317
- if not client_secret:
312
+ if client_secret is None:
318
313
  client_secret = WizVariables.wizClientSecret
319
- if not client_id:
314
+ if client_id is None:
320
315
  client_id = WizVariables.wizClientId
321
316
 
322
317
  wiz_authenticate(
@@ -324,11 +319,18 @@ def add_report_evidence(
324
319
  client_secret=client_secret,
325
320
  )
326
321
  fetch_report_by_id(
327
- report_id, parent_id=evidence_id, report_file_name=report_file_name, report_file_extension=report_file_extension
322
+ report_id,
323
+ parent_id=evidence_id,
324
+ report_file_name=report_file_name,
325
+ report_file_extension=report_file_extension,
328
326
  )
329
327
 
330
328
 
331
- @wiz.command("sync_compliance")
329
+ @wiz.command(
330
+ "sync_compliance",
331
+ deprecated=True,
332
+ help="[BETA] This command shows an experimental feature. Use with caution. Use compliance report instead for Compliance sync from Wiz.",
333
+ )
332
334
  @click.option( # type: ignore
333
335
  "--wiz_project_id",
334
336
  "-p",
@@ -342,7 +344,7 @@ def add_report_evidence(
342
344
  "--client_id",
343
345
  "-i",
344
346
  help="Wiz Client ID, or can be set as environment variable wizClientId",
345
- default="",
347
+ default=None,
346
348
  hide_input=False,
347
349
  required=False,
348
350
  )
@@ -350,14 +352,17 @@ def add_report_evidence(
350
352
  "--client_secret",
351
353
  "-s",
352
354
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
353
- default="",
355
+ default=None,
354
356
  hide_input=True,
355
357
  required=False,
356
358
  )
357
359
  @click.option( # type: ignore
358
360
  "--framework_id",
359
361
  "-f",
360
- help="Wiz framework ID or shorthand (e.g., 'nist', 'aws', 'wf-id-4'). Use --list-frameworks to see options. Default: wf-id-4 (NIST SP 800-53 Rev 5)",
362
+ help=(
363
+ "Wiz framework ID or shorthand (e.g., 'nist', 'aws', 'wf-id-4'). "
364
+ "Use --list-frameworks to see options. Default: wf-id-4 (NIST SP 800-53 Rev 5)"
365
+ ),
361
366
  default="wf-id-4",
362
367
  required=False,
363
368
  )
@@ -416,63 +421,173 @@ def sync_compliance(
416
421
  """
417
422
  Sync policy compliance assessments from Wiz to RegScale.
418
423
 
419
- This command fetches policy assessment data from Wiz and creates:
424
+ This command now uses CSV compliance reports from Wiz instead of GraphQL API.
425
+ It creates:
420
426
  - Control assessments based on policy compliance results
421
427
  - Issues for failed policy assessments (if --create-issues enabled)
422
428
  - Updates to control implementation status (if --update-control-status enabled)
423
- - JSON output file with policy compliance data in artifacts/wiz/ directory
424
- - Cached framework mapping for improved performance
425
429
 
426
- CACHING:
427
- By default, the command will reuse cached policy data if it's newer than the cache
428
- duration (default: 60 minutes). Use --refresh to force fresh data from Wiz.
429
- Use --cache-duration to adjust how long cached data is considered valid.
430
+ NOTE: This command is deprecated. Use 'compliance_report' command instead.
430
431
  """
431
- from regscale.integrations.commercial.wizv2.policy_compliance import (
432
- WizPolicyComplianceIntegration,
433
- list_available_frameworks,
434
- resolve_framework_id,
435
- )
432
+ click.echo("⚠️ sync_compliance is deprecated and now uses compliance_report implementation.")
433
+ click.echo(" Consider using 'regscale wiz compliance_report' directly for future use.\n")
436
434
 
437
- # Handle --list-frameworks flag
435
+ # Handle --list-frameworks flag (no longer supported, inform user)
438
436
  if list_frameworks:
439
- click.echo(list_available_frameworks())
437
+ click.echo("❌ --list-frameworks is no longer supported in the deprecated sync_compliance command.")
438
+ click.echo(" Please use 'regscale wiz compliance_report' instead.\n")
440
439
  return
441
440
 
442
441
  # Use environment variables if not provided
443
- if not client_secret:
442
+ if client_secret is None or client_secret == "":
444
443
  client_secret = WizVariables.wizClientSecret
445
- if not client_id:
444
+ if client_id is None or client_id == "":
446
445
  client_id = WizVariables.wizClientId
447
446
 
448
- # Resolve framework ID using the enhanced framework resolution
449
- try:
450
- resolved_framework_id = resolve_framework_id(framework_id.lower())
451
- if resolved_framework_id != framework_id:
452
- from regscale.integrations.commercial.wizv2.policy_compliance import FRAMEWORK_MAPPINGS
453
-
454
- framework_name = FRAMEWORK_MAPPINGS.get(resolved_framework_id, resolved_framework_id)
455
- click.echo(f"🔍 Resolved '{framework_id}' to '{framework_name}' ({resolved_framework_id})")
456
- except ValueError as e:
457
- click.echo(f"❌ {str(e)}")
458
- click.echo("\nUse --list-frameworks to see all available options.")
459
- return
447
+ # Import compliance_report processor instead of GraphQL-based integration
448
+ from regscale.integrations.commercial.wizv2.compliance_report import WizComplianceReportProcessor
460
449
 
461
- # Create and run the policy compliance integration
462
- policy_integration = WizPolicyComplianceIntegration(
450
+ # Create processor with similar options to compliance_report command
451
+ processor = WizComplianceReportProcessor(
463
452
  plan_id=regscale_id,
464
453
  wiz_project_id=wiz_project_id,
465
454
  client_id=client_id,
466
455
  client_secret=client_secret,
467
- framework_id=resolved_framework_id,
468
456
  regscale_module=regscale_module,
469
457
  create_poams=create_poams,
470
- cache_duration_minutes=cache_duration,
471
- force_refresh=refresh,
458
+ create_issues=create_issues,
459
+ update_control_status=update_control_status,
460
+ report_file_path=None, # Will create new report
461
+ force_fresh_report=refresh, # Map --refresh to force_fresh_report
462
+ reuse_existing_reports=not refresh, # Inverse of refresh
463
+ bypass_control_filtering=True, # Enable for performance
472
464
  )
473
465
 
474
- # Run the policy compliance sync
475
- policy_integration.sync_policy_compliance(
466
+ # Process the compliance report using new ComplianceIntegration pattern
467
+ processor.process_compliance_sync()
468
+
469
+
470
+ @wiz.command(name="compliance_report")
471
+ @click.option(
472
+ "--wiz_project_id",
473
+ "-p",
474
+ prompt="Enter the Wiz project ID",
475
+ help="Enter the Wiz Project ID for compliance report processing.",
476
+ required=True,
477
+ )
478
+ @regscale_id(help="RegScale will create and update control assessments as children of this record.")
479
+ @regscale_module(required=True, default="securityplans", prompt=False)
480
+ @click.option(
481
+ "--client_id",
482
+ "-i",
483
+ help="Wiz Client ID, or can be set as environment variable wizClientId",
484
+ default=None,
485
+ hide_input=False,
486
+ required=False,
487
+ )
488
+ @click.option(
489
+ "--client_secret",
490
+ "-s",
491
+ help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
492
+ default=None,
493
+ hide_input=True,
494
+ required=False,
495
+ )
496
+ @click.option(
497
+ "--report_file_path",
498
+ "-f",
499
+ help="Path to existing CSV compliance report file (optional - will create new report if not provided)",
500
+ default=None,
501
+ required=False,
502
+ )
503
+ @click.option(
504
+ "--create-issues/--no-create-issues",
505
+ "-ci/-ni",
506
+ default=True,
507
+ help="Create issues for failed compliance assessments (default: enabled)",
508
+ )
509
+ @click.option(
510
+ "--update-control-status/--no-update-control-status",
511
+ "-ucs/-nucs",
512
+ default=True,
513
+ help="Update control implementation status based on assessment results (default: enabled)",
514
+ )
515
+ @click.option(
516
+ "--create-poams/--no-create-poams",
517
+ "-cp/-ncp",
518
+ default=False,
519
+ help="Mark created issues as POAMs (default: disabled)",
520
+ )
521
+ @click.option(
522
+ "--reuse-existing-reports/--no-reuse-existing-reports",
523
+ "-rer/-nrer",
524
+ default=True,
525
+ help="Reuse existing Wiz compliance reports instead of creating new ones (default: enabled)",
526
+ )
527
+ @click.option(
528
+ "--force-fresh-report/--no-force-fresh-report",
529
+ "-ffr/-nffr",
530
+ default=False,
531
+ help="Force creation of a fresh compliance report, ignoring existing reports (default: disabled)",
532
+ )
533
+ def compliance_report(
534
+ wiz_project_id,
535
+ regscale_id,
536
+ regscale_module,
537
+ client_id,
538
+ client_secret,
539
+ report_file_path,
540
+ create_issues,
541
+ update_control_status,
542
+ create_poams,
543
+ reuse_existing_reports,
544
+ force_fresh_report,
545
+ ):
546
+ """
547
+ Process Wiz compliance reports and create assessments in RegScale.
548
+
549
+ This command can either:
550
+ 1. Create a new compliance report from Wiz and process it
551
+ 2. Process an existing compliance report CSV file
552
+
553
+ The command will:
554
+ - Parse compliance assessment data from CSV format
555
+ - Create control assessments based on compliance results
556
+ - Create issues for failed compliance assessments (if --create-issues enabled)
557
+ - Update control implementation status (if --update-control-status enabled)
558
+ - Support POAM creation for compliance issues
559
+
560
+ REPORT MANAGEMENT:
561
+ By default, the command will look for existing compliance reports in Wiz for the
562
+ specified project and rerun them instead of creating new ones. This prevents the
563
+ accumulation of duplicate reports in Wiz. Use --no-reuse-existing-reports to
564
+ always create new reports, or --force-fresh-report to force a new report even
565
+ when reuse is enabled.
566
+ """
567
+ from regscale.integrations.commercial.wizv2.compliance_report import WizComplianceReportProcessor
568
+
569
+ # Use environment variables if not provided or empty
570
+ if not client_secret:
571
+ client_secret = WizVariables.wizClientSecret
572
+ if not client_id:
573
+ client_id = WizVariables.wizClientId
574
+
575
+ # Create and run the compliance report processor
576
+ # Enable bypass_control_filtering by default for performance with large control sets
577
+ processor = WizComplianceReportProcessor(
578
+ plan_id=regscale_id,
579
+ wiz_project_id=wiz_project_id,
580
+ client_id=client_id,
581
+ client_secret=client_secret,
582
+ regscale_module=regscale_module,
583
+ create_poams=create_poams,
476
584
  create_issues=create_issues,
477
585
  update_control_status=update_control_status,
586
+ report_file_path=report_file_path,
587
+ bypass_control_filtering=True, # Bypass filtering for performance with large control sets
588
+ reuse_existing_reports=reuse_existing_reports,
589
+ force_fresh_report=force_fresh_report,
478
590
  )
591
+
592
+ # Process the compliance report using new ComplianceIntegration pattern
593
+ processor.process_compliance_sync()