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,133 @@
1
+ """Core Wiz integration modules including authentication, client, file operations, and constants."""
2
+
3
+ from regscale.integrations.commercial.wizv2.core.auth import (
4
+ AUTH0_URLS,
5
+ COGNITO_URLS,
6
+ generate_authentication_params,
7
+ get_token,
8
+ wiz_authenticate,
9
+ )
10
+ from regscale.integrations.commercial.wizv2.core.client import (
11
+ AsyncWizGraphQLClient,
12
+ run_async_queries,
13
+ )
14
+ from regscale.integrations.commercial.wizv2.core.file_operations import FileOperations
15
+ from regscale.integrations.commercial.wizv2.core.constants import (
16
+ ASSET_TYPE_MAPPING,
17
+ BEARER,
18
+ CHECK_INTERVAL_FOR_DOWNLOAD_REPORT,
19
+ CLOUD_CONFIG_FINDING_QUERY,
20
+ CLOUD_CONFIG_FINDINGS_FILE_PATH,
21
+ CONTENT_TYPE,
22
+ CPE_PART_TO_CATEGORY_MAPPING,
23
+ CREATE_REPORT_QUERY,
24
+ DATA_FINDING_QUERY,
25
+ DATA_FINDINGS_FILE_PATH,
26
+ DATASOURCE,
27
+ DEFAULT_WIZ_HARDWARE_TYPES,
28
+ DOWNLOAD_QUERY,
29
+ END_OF_LIFE_FILE_PATH,
30
+ END_OF_LIFE_QUERY,
31
+ EXCESSIVE_ACCESS_FILE_PATH,
32
+ EXCESSIVE_ACCESS_QUERY,
33
+ EXTERNAL_ATTACK_SURFACE_FILE_PATH,
34
+ EXTERNAL_ATTACK_SURFACE_QUERY,
35
+ FRAMEWORK_CATEGORIES,
36
+ FRAMEWORK_MAPPINGS,
37
+ FRAMEWORK_SHORTCUTS,
38
+ HOST_VULNERABILITY_FILE_PATH,
39
+ HOST_VULNERABILITY_QUERY,
40
+ INVENTORY_FILE_PATH,
41
+ INVENTORY_QUERY,
42
+ ISSUE_QUERY,
43
+ ISSUES_FILE_PATH,
44
+ MAX_RETRIES,
45
+ NETWORK_EXPOSURE_FILE_PATH,
46
+ NETWORK_EXPOSURE_QUERY,
47
+ PROVIDER,
48
+ RATE_LIMIT_MSG,
49
+ RECOMMENDED_WIZ_INVENTORY_TYPES,
50
+ REPORTS_QUERY,
51
+ RERUN_REPORT_QUERY,
52
+ RESOURCE,
53
+ SBOM_FILE_PATH,
54
+ SBOM_QUERY,
55
+ SECRET_FINDINGS_FILE_PATH,
56
+ SECRET_FINDINGS_QUERY,
57
+ SEVERITY_MAP,
58
+ TECHNOLOGIES_FILE_PATH,
59
+ VULNERABILITY_FILE_PATH,
60
+ VULNERABILITY_QUERY,
61
+ WIZ_FRAMEWORK_QUERY,
62
+ WIZ_POLICY_QUERY,
63
+ WizVulnerabilityType,
64
+ get_compliance_report_variables,
65
+ get_wiz_issue_queries,
66
+ get_wiz_vulnerability_queries,
67
+ )
68
+
69
+ __all__ = [
70
+ # Auth
71
+ "AUTH0_URLS",
72
+ "COGNITO_URLS",
73
+ "generate_authentication_params",
74
+ "get_token",
75
+ "wiz_authenticate",
76
+ # Client
77
+ "AsyncWizGraphQLClient",
78
+ "run_async_queries",
79
+ # File Operations
80
+ "FileOperations",
81
+ # Constants
82
+ "ASSET_TYPE_MAPPING",
83
+ "BEARER",
84
+ "CHECK_INTERVAL_FOR_DOWNLOAD_REPORT",
85
+ "CLOUD_CONFIG_FINDING_QUERY",
86
+ "CLOUD_CONFIG_FINDINGS_FILE_PATH",
87
+ "CONTENT_TYPE",
88
+ "CPE_PART_TO_CATEGORY_MAPPING",
89
+ "CREATE_REPORT_QUERY",
90
+ "DATA_FINDING_QUERY",
91
+ "DATA_FINDINGS_FILE_PATH",
92
+ "DATASOURCE",
93
+ "DEFAULT_WIZ_HARDWARE_TYPES",
94
+ "DOWNLOAD_QUERY",
95
+ "END_OF_LIFE_FILE_PATH",
96
+ "END_OF_LIFE_QUERY",
97
+ "EXCESSIVE_ACCESS_FILE_PATH",
98
+ "EXCESSIVE_ACCESS_QUERY",
99
+ "EXTERNAL_ATTACK_SURFACE_FILE_PATH",
100
+ "EXTERNAL_ATTACK_SURFACE_QUERY",
101
+ "FRAMEWORK_CATEGORIES",
102
+ "FRAMEWORK_MAPPINGS",
103
+ "FRAMEWORK_SHORTCUTS",
104
+ "HOST_VULNERABILITY_FILE_PATH",
105
+ "HOST_VULNERABILITY_QUERY",
106
+ "INVENTORY_FILE_PATH",
107
+ "INVENTORY_QUERY",
108
+ "ISSUE_QUERY",
109
+ "ISSUES_FILE_PATH",
110
+ "MAX_RETRIES",
111
+ "NETWORK_EXPOSURE_FILE_PATH",
112
+ "NETWORK_EXPOSURE_QUERY",
113
+ "PROVIDER",
114
+ "RATE_LIMIT_MSG",
115
+ "RECOMMENDED_WIZ_INVENTORY_TYPES",
116
+ "REPORTS_QUERY",
117
+ "RERUN_REPORT_QUERY",
118
+ "RESOURCE",
119
+ "SBOM_FILE_PATH",
120
+ "SBOM_QUERY",
121
+ "SECRET_FINDINGS_FILE_PATH",
122
+ "SECRET_FINDINGS_QUERY",
123
+ "SEVERITY_MAP",
124
+ "TECHNOLOGIES_FILE_PATH",
125
+ "VULNERABILITY_FILE_PATH",
126
+ "VULNERABILITY_QUERY",
127
+ "WIZ_FRAMEWORK_QUERY",
128
+ "WIZ_POLICY_QUERY",
129
+ "WizVulnerabilityType",
130
+ "get_compliance_report_variables",
131
+ "get_wiz_issue_queries",
132
+ "get_wiz_vulnerability_queries",
133
+ ]
@@ -4,7 +4,7 @@
4
4
 
5
5
  import asyncio
6
6
  import logging
7
- from typing import Any, Dict, List, Optional, Tuple
7
+ from typing import Any, Callable, Dict, List, Optional, Tuple
8
8
 
9
9
  import anyio
10
10
  import httpx
@@ -48,7 +48,7 @@ class AsyncWizGraphQLClient:
48
48
  self,
49
49
  query: str,
50
50
  variables: Optional[Dict[str, Any]] = None,
51
- progress_callback: Optional[callable] = None,
51
+ progress_callback: Optional[Callable] = None,
52
52
  task_name: str = "GraphQL Query",
53
53
  ) -> Dict[str, Any]:
54
54
  """
@@ -118,7 +118,7 @@ class AsyncWizGraphQLClient:
118
118
  query: str,
119
119
  variables: Dict[str, Any],
120
120
  topic_key: str,
121
- progress_callback: Optional[callable] = None,
121
+ progress_callback: Optional[Callable] = None,
122
122
  task_name: str = "Paginated Query",
123
123
  ) -> List[Dict[str, Any]]:
124
124
  """
@@ -156,6 +156,10 @@ class AsyncWizGraphQLClient:
156
156
  nodes = topic_data.get("nodes", [])
157
157
  page_info = topic_data.get("pageInfo", {})
158
158
 
159
+ # Handle case where nodes is explicitly None
160
+ if nodes is None:
161
+ nodes = []
162
+
159
163
  all_nodes.extend(nodes)
160
164
 
161
165
  has_next_page = page_info.get("hasNextPage", False)
@@ -545,6 +545,76 @@ CREATE_REPORT_QUERY = """
545
545
  }
546
546
  }
547
547
  """
548
+
549
+
550
+ def get_compliance_report_variables(
551
+ project_id: str, run_starts_at: Optional[str] = None, framework_ids: Optional[List[str]] = None
552
+ ) -> dict:
553
+ """Get compliance report variables with dynamic projectId and runStartsAt.
554
+
555
+ :param str project_id: The Wiz project ID
556
+ :param Optional[str] run_starts_at: ISO timestamp for when the report should start, defaults to current time
557
+ :param Optional[List[str]] framework_ids: List of framework IDs to include, defaults to NIST SP 800-53 Rev 5
558
+ :return: Variables for compliance report creation
559
+ :rtype: dict
560
+ """
561
+ from datetime import datetime, timezone
562
+
563
+ if not run_starts_at:
564
+ run_starts_at = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z")
565
+
566
+ if not framework_ids:
567
+ # Default to NIST SP 800-53 Revision 5
568
+ framework_ids = ["wf-id-4"]
569
+
570
+ return {
571
+ "input": {
572
+ "name": f"Compliance Report - {project_id}",
573
+ "type": "COMPLIANCE_ASSESSMENTS",
574
+ "compressionMethod": "GZIP",
575
+ "runIntervalHours": 168,
576
+ "runStartsAt": run_starts_at,
577
+ "csvDelimiter": "US",
578
+ "projectId": project_id,
579
+ "complianceAssessmentsParams": {
580
+ "securityFrameworkIds": framework_ids,
581
+ },
582
+ "emailTargetParams": None,
583
+ "exportDestinations": None,
584
+ "columnSelection": [
585
+ "Assessed At",
586
+ "Category",
587
+ "Cloud Provider",
588
+ "Cloud Provider ID",
589
+ "Compliance Check Name (Wiz Subcategory)",
590
+ "Created At",
591
+ "Framework",
592
+ "Ignore Reason",
593
+ "Issue/Finding ID",
594
+ "Native Type",
595
+ "Object Type",
596
+ "Policy Description",
597
+ "Policy ID",
598
+ "Policy Name",
599
+ "Policy Short Name",
600
+ "Policy Type",
601
+ "Projects",
602
+ "Remediation Steps",
603
+ "Resource Cloud Platform",
604
+ "Resource Group Name",
605
+ "Resource ID",
606
+ "Resource Name",
607
+ "Resource Region",
608
+ "Result",
609
+ "Severity",
610
+ "Subscription",
611
+ "Subscription Name",
612
+ "Subscription Provider ID",
613
+ ],
614
+ }
615
+ }
616
+
617
+
548
618
  REPORTS_QUERY = """
549
619
  query ReportsTable($filterBy: ReportFilters, $first: Int, $after: String) {
550
620
  reports(first: $first, after: $after, filterBy: $filterBy) {
@@ -562,22 +632,6 @@ REPORTS_QUERY = """
562
632
  emailTarget {
563
633
  to
564
634
  }
565
- parameters {
566
- query
567
- framework {
568
- name
569
- }
570
- subscriptions {
571
- id
572
- name
573
- type
574
- }
575
- entities {
576
- id
577
- name
578
- type
579
- }
580
- }
581
635
  lastRun {
582
636
  ...LastRunDetails
583
637
  }
@@ -1101,85 +1155,24 @@ fragment SecuritySubCategoryDetails on SecuritySubCategory {
1101
1155
  }
1102
1156
  """
1103
1157
  DATA_FINDING_QUERY = """
1104
- query DataFindingsGroupedByValueTable($groupBy: DataFindingsGroupedByValueField!, $after: String, $first: Int, $filterBy: DataFindingFilters, $orderBy: DataFindingsGroupedByValueOrder) {
1105
- dataFindingsGroupedByValue(
1106
- groupBy: $groupBy
1158
+ query DataFindingsTable($after: String, $first: Int, $filterBy: DataFindingFiltersV2, $orderBy: DataFindingOrder, $fetchTotalCount: Boolean = true) {
1159
+ dataFindingsV2(
1107
1160
  filterBy: $filterBy
1108
1161
  first: $first
1109
1162
  after: $after
1110
1163
  orderBy: $orderBy
1111
1164
  ) {
1112
1165
  nodes {
1113
- categories
1114
- location {
1115
- countryCode
1116
- state
1117
- }
1118
- regionCount
1119
- graphEntityCount
1120
- graphEntity {
1121
- id
1122
- name
1123
- type
1124
- properties
1125
- projects {
1126
- id
1127
- name
1128
- slug
1129
- isFolder
1130
- }
1131
- issues(filterBy: {status: [OPEN, IN_PROGRESS]}) {
1132
- criticalSeverityCount
1133
- highSeverityCount
1134
- mediumSeverityCount
1135
- lowSeverityCount
1136
- informationalSeverityCount
1137
- }
1138
- }
1139
- cloudAccount {
1140
- id
1141
- name
1142
- externalId
1143
- cloudProvider
1144
- }
1145
- dataClassifiers {
1146
- id
1147
- name
1148
- category
1149
- matcherType
1150
- severity
1151
- }
1152
- securitySubCategories {
1153
- id
1154
- title
1155
- externalId
1156
- description
1157
- category {
1158
- id
1159
- name
1160
- description
1161
- framework {
1162
- id
1163
- name
1164
- description
1165
- enabled
1166
- }
1167
- }
1168
- }
1169
- findingsCount
1170
- dataFindings(first: 5) {
1171
- nodes {
1172
- ...DataFindingDetails
1173
- }
1174
- }
1166
+ ...DataFindingDetails
1175
1167
  }
1176
1168
  pageInfo {
1177
1169
  hasNextPage
1178
1170
  endCursor
1179
1171
  }
1180
- totalCount
1172
+ totalCount @include(if: $fetchTotalCount)
1181
1173
  }
1182
1174
  }
1175
+
1183
1176
  fragment DataFindingDetails on DataFinding {
1184
1177
  id
1185
1178
  name
@@ -1187,10 +1180,10 @@ fragment DataFindingDetails on DataFinding {
1187
1180
  id
1188
1181
  name
1189
1182
  category
1183
+ isTenantSpecific
1190
1184
  securitySubCategories {
1191
1185
  id
1192
1186
  title
1193
- externalId
1194
1187
  description
1195
1188
  category {
1196
1189
  id
@@ -1216,8 +1209,12 @@ fragment DataFindingDetails on DataFinding {
1216
1209
  state
1217
1210
  }
1218
1211
  severity
1212
+ status
1219
1213
  totalMatchCount
1220
1214
  uniqueMatchCount
1215
+ maxUniqueMatchesReached
1216
+ uniqueLocationsCount
1217
+ isEntityPublic
1221
1218
  graphEntity {
1222
1219
  id
1223
1220
  name
@@ -1231,6 +1228,12 @@ fragment DataFindingDetails on DataFinding {
1231
1228
  }
1232
1229
  }
1233
1230
  externalSource
1231
+ details {
1232
+ applicationServices {
1233
+ id
1234
+ displayName
1235
+ }
1236
+ }
1234
1237
  }
1235
1238
  """
1236
1239
 
@@ -1295,8 +1298,8 @@ def get_wiz_vulnerability_queries(project_id: str, filter_by: Optional[dict] = N
1295
1298
  "first": 200,
1296
1299
  "quick": True,
1297
1300
  "filterBy": {
1298
- "rule": {},
1299
- "resource": {"projectId": [project_id]},
1301
+ "resource": {"projectId": project_id},
1302
+ "status": ["OPEN", "IN_PROGRESS"],
1300
1303
  },
1301
1304
  },
1302
1305
  },
@@ -1317,14 +1320,14 @@ def get_wiz_vulnerability_queries(project_id: str, filter_by: Optional[dict] = N
1317
1320
  {
1318
1321
  "type": WizVulnerabilityType.DATA_FINDING,
1319
1322
  "query": DATA_FINDING_QUERY,
1320
- "topic_key": "dataFindingsGroupedByValue",
1323
+ "topic_key": "dataFindingsV2",
1321
1324
  "file_path": DATA_FINDINGS_FILE_PATH,
1322
- "asset_lookup": "resource",
1325
+ "asset_lookup": "graphEntity",
1323
1326
  "variables": {
1324
1327
  "first": 200,
1328
+ "fetchTotalCount": True,
1325
1329
  "filterBy": {"projectId": [project_id]},
1326
- "orderBy": {"field": "FINDING_COUNT", "direction": "DESC"},
1327
- "groupBy": "GRAPH_ENTITY",
1330
+ "orderBy": {"field": "TOTAL_MATCHES", "direction": "DESC"},
1328
1331
  },
1329
1332
  },
1330
1333
  {
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """File operations module for Wiz integration - handles caching and file I/O."""
4
+
5
+ import datetime
6
+ import json
7
+ import logging
8
+ import os
9
+ from typing import Any, Callable, Dict, List, Optional, Tuple
10
+
11
+ from regscale.core.app.utils.app_utils import check_file_path
12
+
13
+ logger = logging.getLogger("regscale")
14
+
15
+
16
+ class FileOperations:
17
+ """Handles file operations for Wiz integration including caching and data persistence."""
18
+
19
+ @staticmethod
20
+ def load_json_file(file_path: str) -> Optional[Any]:
21
+ """
22
+ Load data from a JSON file.
23
+
24
+ :param str file_path: Path to JSON file
25
+ :return: Loaded data or None if file doesn't exist or is invalid
26
+ :rtype: Optional[Any]
27
+ """
28
+ if not os.path.exists(file_path):
29
+ logger.debug(f"File does not exist: {file_path}")
30
+ return None
31
+
32
+ try:
33
+ with open(file_path, encoding="utf-8") as f:
34
+ return json.load(f)
35
+ except (OSError, json.JSONDecodeError) as e:
36
+ logger.error(f"Error reading JSON file {file_path}: {e}")
37
+ return None
38
+
39
+ @staticmethod
40
+ def save_json_file(data: Any, file_path: str, create_dir: bool = True) -> bool:
41
+ """
42
+ Save data to a JSON file.
43
+
44
+ :param Any data: Data to save
45
+ :param str file_path: Path to save file
46
+ :param bool create_dir: Whether to create parent directory if needed
47
+ :return: True if successful, False otherwise
48
+ :rtype: bool
49
+ """
50
+ try:
51
+ if create_dir:
52
+ check_file_path(os.path.dirname(file_path))
53
+
54
+ with open(file_path, "w", encoding="utf-8") as f:
55
+ json.dump(data, f)
56
+
57
+ logger.debug(f"Saved data to {file_path}")
58
+ return True
59
+
60
+ except Exception as e:
61
+ logger.warning(f"Failed to save data to {file_path}: {e}")
62
+ return False
63
+
64
+ @staticmethod
65
+ def get_file_age(file_path: str) -> Optional[datetime.timedelta]:
66
+ """
67
+ Get the age of a file as a timedelta.
68
+
69
+ :param str file_path: Path to file
70
+ :return: File age or None if file doesn't exist
71
+ :rtype: Optional[datetime.timedelta]
72
+ """
73
+ if not os.path.exists(file_path):
74
+ return None
75
+
76
+ try:
77
+ file_mod_time = datetime.datetime.fromtimestamp(os.path.getmtime(file_path))
78
+ current_time = datetime.datetime.now()
79
+ return current_time - file_mod_time
80
+ except OSError as e:
81
+ logger.warning(f"Error getting file age for {file_path}: {e}")
82
+ return None
83
+
84
+ @staticmethod
85
+ def is_cache_valid(file_path: str, max_age_hours: float = 8) -> bool:
86
+ """
87
+ Check if a cache file is valid (exists and not too old).
88
+
89
+ :param str file_path: Path to cache file
90
+ :param float max_age_hours: Maximum age in hours before cache is invalid
91
+ :return: True if cache is valid, False otherwise
92
+ :rtype: bool
93
+ """
94
+ file_age = FileOperations.get_file_age(file_path)
95
+ if file_age is None:
96
+ return False
97
+
98
+ max_age = datetime.timedelta(hours=max_age_hours)
99
+ return file_age < max_age
100
+
101
+ @staticmethod
102
+ def load_cache_or_fetch(
103
+ file_path: str,
104
+ fetch_fn: Callable[[], Any],
105
+ max_age_hours: float = 8,
106
+ save_cache: bool = True,
107
+ ) -> Any:
108
+ """
109
+ Load data from cache if valid, otherwise fetch and optionally cache.
110
+
111
+ :param str file_path: Path to cache file
112
+ :param Callable fetch_fn: Function to call to fetch fresh data
113
+ :param float max_age_hours: Maximum cache age in hours
114
+ :param bool save_cache: Whether to save fetched data to cache
115
+ :return: Data from cache or freshly fetched
116
+ :rtype: Any
117
+ """
118
+ # Try to load from cache if valid
119
+ if FileOperations.is_cache_valid(file_path, max_age_hours):
120
+ logger.info(f"Using cached data from {file_path} (newer than {max_age_hours} hours)")
121
+ cached_data = FileOperations.load_json_file(file_path)
122
+ if cached_data is not None:
123
+ return cached_data
124
+
125
+ # Cache invalid or doesn't exist - fetch fresh data
126
+ file_age = FileOperations.get_file_age(file_path)
127
+ if file_age:
128
+ logger.info(
129
+ f"Cache file {file_path} is {file_age.total_seconds() / 3600:.1f} hours old - fetching new data"
130
+ )
131
+ else:
132
+ logger.info(f"Cache file {file_path} does not exist - fetching new data")
133
+
134
+ data = fetch_fn()
135
+
136
+ # Save to cache if requested
137
+ if save_cache:
138
+ FileOperations.save_json_file(data, file_path)
139
+
140
+ return data
141
+
142
+ @staticmethod
143
+ def search_json_files(
144
+ identifier: str,
145
+ file_paths: List[str],
146
+ match_fn: Callable[[Dict, str], bool],
147
+ ) -> Tuple[Optional[Dict], Optional[str]]:
148
+ """
149
+ Search for an item across multiple JSON files.
150
+
151
+ :param str identifier: Identifier to search for
152
+ :param List[str] file_paths: List of file paths to search
153
+ :param Callable match_fn: Function to determine if an item matches (takes item and identifier)
154
+ :return: Tuple of (found_item, source_file) or (None, None)
155
+ :rtype: Tuple[Optional[Dict], Optional[str]]
156
+ """
157
+ for file_path in file_paths:
158
+ if not os.path.exists(file_path):
159
+ continue
160
+
161
+ try:
162
+ result = FileOperations.search_single_json_file(identifier, file_path, match_fn)
163
+ if result:
164
+ return result, file_path
165
+ except Exception as e:
166
+ logger.debug(f"Error searching {file_path}: {e}")
167
+ continue
168
+
169
+ return None, None
170
+
171
+ @staticmethod
172
+ def search_single_json_file(
173
+ identifier: str,
174
+ file_path: str,
175
+ match_fn: Callable[[Dict, str], bool],
176
+ ) -> Optional[Dict]:
177
+ """
178
+ Search for an item in a single JSON file.
179
+
180
+ :param str identifier: Identifier to search for
181
+ :param str file_path: Path to JSON file
182
+ :param Callable match_fn: Function to determine if an item matches
183
+ :return: Matched item or None
184
+ :rtype: Optional[Dict]
185
+ """
186
+ logger.debug(f"Searching for {identifier} in {file_path}")
187
+
188
+ data = FileOperations.load_json_file(file_path)
189
+ if not isinstance(data, list):
190
+ return None
191
+
192
+ # Use generator for memory efficiency
193
+ return next((item for item in data if match_fn(item, identifier)), None)
194
+
195
+ @staticmethod
196
+ def load_cached_findings(
197
+ query_configs: List[Dict[str, Any]],
198
+ progress_callback: Optional[Callable] = None,
199
+ ) -> List[Tuple[str, List[Dict], Optional[Exception]]]:
200
+ """
201
+ Load cached findings from multiple files.
202
+
203
+ :param List[Dict[str, Any]] query_configs: Query configurations with file paths
204
+ :param Optional[Callable] progress_callback: Optional progress callback
205
+ :return: List of (query_type, nodes, error) tuples
206
+ :rtype: List[Tuple[str, List[Dict], Optional[Exception]]]
207
+ """
208
+ results = []
209
+
210
+ for config in query_configs:
211
+ query_type = config["type"].value
212
+ file_path = config.get("file_path")
213
+
214
+ if progress_callback:
215
+ progress_callback(query_type, "loading")
216
+
217
+ try:
218
+ if file_path and os.path.exists(file_path):
219
+ nodes = FileOperations.load_json_file(file_path)
220
+ if nodes is not None:
221
+ logger.info(f"Loaded {len(nodes)} cached {query_type} findings from {file_path}")
222
+ results.append((query_type, nodes, None))
223
+ else:
224
+ logger.warning(f"Failed to load cached data for {query_type}")
225
+ results.append((query_type, [], Exception(f"Failed to load {file_path}")))
226
+ else:
227
+ logger.warning(f"No cached data found for {query_type} at {file_path}")
228
+ results.append((query_type, [], None))
229
+
230
+ except Exception as e:
231
+ logger.error(f"Error loading cached data for {query_type}: {e}")
232
+ results.append((query_type, [], e))
233
+
234
+ if progress_callback:
235
+ progress_callback(query_type, "loaded")
236
+
237
+ return results
@@ -0,0 +1,11 @@
1
+ """Fetchers for Wiz integration - handles data retrieval and caching."""
2
+
3
+ from regscale.integrations.commercial.wizv2.fetchers.policy_assessment import (
4
+ PolicyAssessmentFetcher,
5
+ WizDataCache,
6
+ )
7
+
8
+ __all__ = [
9
+ "PolicyAssessmentFetcher",
10
+ "WizDataCache",
11
+ ]