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
tests/core/__init__.py ADDED
File without changes
File without changes
@@ -0,0 +1,472 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Tests for the async GraphQL client."""
4
+
5
+ import asyncio
6
+ from unittest import TestCase
7
+ from unittest.mock import AsyncMock, MagicMock, Mock, patch
8
+
9
+ import anyio
10
+ import pytest
11
+ from gql import Client, gql
12
+ from gql.transport.aiohttp import AIOHTTPTransport
13
+ from gql.transport.exceptions import TransportQueryError
14
+
15
+ from regscale.core.utils.async_graphql_client import (
16
+ AsyncRegScaleGraphQLClient,
17
+ run_async_paginated_query,
18
+ )
19
+
20
+
21
+ class TestAsyncRegScaleGraphQLClient(TestCase):
22
+ """Test cases for AsyncRegScaleGraphQLClient."""
23
+
24
+ def setUp(self):
25
+ """Set up test fixtures."""
26
+ self.endpoint = "https://test.regscale.com/graphql"
27
+ self.headers = {"Authorization": "Bearer test_token"}
28
+ self.client = AsyncRegScaleGraphQLClient(endpoint=self.endpoint, headers=self.headers, max_concurrent=3)
29
+
30
+ def test_init(self):
31
+ """Test client initialization."""
32
+ client = AsyncRegScaleGraphQLClient(
33
+ endpoint=self.endpoint,
34
+ headers=self.headers,
35
+ timeout=45.0,
36
+ max_concurrent=10,
37
+ )
38
+
39
+ self.assertEqual(client.endpoint, self.endpoint)
40
+ self.assertEqual(client.headers, self.headers)
41
+ self.assertEqual(client.timeout, 45.0)
42
+ self.assertEqual(client.max_concurrent, 10)
43
+
44
+ # Verify _create_client method creates a new client with correct params
45
+ with patch("regscale.core.utils.async_graphql_client.AIOHTTPTransport") as mock_transport_class:
46
+ with patch("regscale.core.utils.async_graphql_client.Client") as mock_client_class:
47
+ client._create_client()
48
+
49
+ # Verify transport was created with correct params
50
+ mock_transport_class.assert_called_once()
51
+ call_args = mock_transport_class.call_args
52
+ self.assertEqual(call_args[1]["url"], self.endpoint)
53
+ self.assertEqual(call_args[1]["headers"], self.headers)
54
+ self.assertEqual(call_args[1]["timeout"], 45) # Converted to int
55
+
56
+ # Verify client was created
57
+ mock_client_class.assert_called_once()
58
+
59
+ @pytest.mark.asyncio
60
+ async def test_execute_query_success(self):
61
+ """Test successful query execution."""
62
+ with patch.object(self.client.client, "__aenter__") as mock_aenter:
63
+ mock_session = AsyncMock()
64
+ mock_session.execute = AsyncMock(
65
+ return_value={
66
+ "issues": {
67
+ "items": [{"id": 1, "title": "Test Issue"}],
68
+ "totalCount": 1,
69
+ }
70
+ }
71
+ )
72
+ mock_aenter.return_value = mock_session
73
+
74
+ query = """
75
+ query {
76
+ issues(skip: 0, take: 10) {
77
+ items { id title }
78
+ totalCount
79
+ }
80
+ }
81
+ """
82
+
83
+ result = await self.client.execute_query(query)
84
+
85
+ self.assertIn("issues", result)
86
+ self.assertEqual(result["issues"]["totalCount"], 1)
87
+ mock_session.execute.assert_called_once()
88
+
89
+ @pytest.mark.asyncio
90
+ async def test_execute_query_with_variables(self):
91
+ """Test query execution with variables."""
92
+ with patch.object(self.client.client, "__aenter__") as mock_aenter:
93
+ mock_session = AsyncMock()
94
+ mock_session.execute = AsyncMock(return_value={"data": {"result": "success"}})
95
+ mock_aenter.return_value = mock_session
96
+
97
+ query = "query($id: Int!) { issue(id: $id) { title } }"
98
+ variables = {"id": 123}
99
+
100
+ await self.client.execute_query(query, variables=variables)
101
+
102
+ mock_session.execute.assert_called_once()
103
+ call_args = mock_session.execute.call_args
104
+ self.assertEqual(call_args[1]["variable_values"], variables)
105
+
106
+ @pytest.mark.asyncio
107
+ async def test_execute_query_with_auth_error_and_retry(self):
108
+ """Test query execution with auth error and successful retry."""
109
+ token_refresh_callback = Mock(return_value="new_token")
110
+ client = AsyncRegScaleGraphQLClient(
111
+ endpoint=self.endpoint,
112
+ headers=self.headers,
113
+ token_refresh_callback=token_refresh_callback,
114
+ )
115
+
116
+ with patch.object(client.client, "__aenter__") as mock_aenter:
117
+ mock_session = AsyncMock()
118
+ # First call fails with auth error, second succeeds
119
+ mock_session.execute = AsyncMock(
120
+ side_effect=[
121
+ TransportQueryError("AUTH_NOT_AUTHENTICATED"),
122
+ {"data": {"result": "success"}},
123
+ ]
124
+ )
125
+ mock_aenter.return_value = mock_session
126
+
127
+ # Need to patch transport recreation
128
+ with patch("regscale.core.utils.async_graphql_client.AIOHTTPTransport"):
129
+ with patch("regscale.core.utils.async_graphql_client.Client") as mock_client:
130
+ # Set up the new client
131
+ new_client = MagicMock()
132
+ new_session = AsyncMock()
133
+ new_session.execute = AsyncMock(return_value={"data": {"result": "success"}})
134
+ new_client.__aenter__ = AsyncMock(return_value=new_session)
135
+ mock_client.return_value = new_client
136
+
137
+ query = "query { test }"
138
+ result = await client.execute_query(query)
139
+
140
+ self.assertEqual(result, {"data": {"result": "success"}})
141
+ token_refresh_callback.assert_called_once()
142
+
143
+ @pytest.mark.asyncio
144
+ async def test_execute_query_with_non_auth_error(self):
145
+ """Test query execution with non-auth error."""
146
+ with patch.object(self.client.client, "__aenter__") as mock_aenter:
147
+ mock_session = AsyncMock()
148
+ mock_session.execute = AsyncMock(side_effect=Exception("Network error"))
149
+ mock_aenter.return_value = mock_session
150
+
151
+ query = "query { test }"
152
+
153
+ with self.assertRaises(Exception) as context:
154
+ await self.client.execute_query(query)
155
+
156
+ self.assertIn("Network error", str(context.exception))
157
+
158
+ @pytest.mark.asyncio
159
+ async def test_execute_query_with_progress_callback(self):
160
+ """Test query execution with progress callback."""
161
+ progress_callback = Mock()
162
+
163
+ with patch.object(self.client.client, "__aenter__") as mock_aenter:
164
+ mock_session = AsyncMock()
165
+ mock_session.execute = AsyncMock(return_value={"data": {"test": "data"}})
166
+ mock_aenter.return_value = mock_session
167
+
168
+ query = "query { test }"
169
+ await self.client.execute_query(query, progress_callback=progress_callback, task_name="Test Query")
170
+
171
+ # Verify progress callbacks were made
172
+ progress_callback.assert_any_call("Test Query", "starting")
173
+ progress_callback.assert_any_call("Test Query", "requesting")
174
+ progress_callback.assert_any_call("Test Query", "completed")
175
+
176
+ @pytest.mark.asyncio
177
+ async def test_execute_paginated_query_concurrent(self):
178
+ """Test concurrent paginated query execution."""
179
+ query_builder = Mock(side_effect=lambda skip, take: f"query({skip},{take})")
180
+
181
+ with patch.object(self.client, "execute_query", new_callable=AsyncMock) as mock_execute:
182
+ # Simulate 3 pages of results
183
+ mock_execute.side_effect = [
184
+ {"issues": {"items": [{"id": 1}, {"id": 2}]}},
185
+ {"issues": {"items": [{"id": 3}, {"id": 4}]}},
186
+ {"issues": {"items": [{"id": 5}]}},
187
+ ]
188
+
189
+ result = await self.client.execute_paginated_query_concurrent(
190
+ query_builder=query_builder,
191
+ topic_key="issues",
192
+ total_count=5,
193
+ page_size=2,
194
+ starting_skip=0,
195
+ )
196
+
197
+ self.assertEqual(len(result), 5)
198
+ self.assertEqual(query_builder.call_count, 3)
199
+ self.assertEqual(mock_execute.call_count, 3)
200
+
201
+ @pytest.mark.asyncio
202
+ async def test_fetch_single_page_success(self):
203
+ """Test successful single page fetch."""
204
+ with patch.object(self.client, "execute_query", new_callable=AsyncMock) as mock_execute:
205
+ mock_execute.return_value = {"issues": {"items": [{"id": 1}, {"id": 2}], "totalCount": 2}}
206
+
207
+ result = await self.client._fetch_single_page(
208
+ query="test query",
209
+ variables={},
210
+ topic_key="issues",
211
+ page_num=1,
212
+ )
213
+
214
+ self.assertEqual(len(result), 2)
215
+ self.assertEqual(result[0]["id"], 1)
216
+
217
+ @pytest.mark.asyncio
218
+ async def test_fetch_single_page_with_none_items(self):
219
+ """Test single page fetch when items is None."""
220
+ with patch.object(self.client, "execute_query", new_callable=AsyncMock) as mock_execute:
221
+ mock_execute.return_value = {"issues": {"items": None, "totalCount": 0}}
222
+
223
+ result = await self.client._fetch_single_page(
224
+ query="test query",
225
+ variables={},
226
+ topic_key="issues",
227
+ page_num=1,
228
+ )
229
+
230
+ self.assertEqual(result, [])
231
+
232
+ @pytest.mark.asyncio
233
+ async def test_fetch_single_page_with_error(self):
234
+ """Test single page fetch with error."""
235
+ with patch.object(self.client, "execute_query", new_callable=AsyncMock) as mock_execute:
236
+ mock_execute.side_effect = Exception("Query failed")
237
+
238
+ with self.assertRaises(Exception) as context:
239
+ await self.client._fetch_single_page(
240
+ query="test query",
241
+ variables={},
242
+ topic_key="issues",
243
+ page_num=1,
244
+ )
245
+
246
+ self.assertIn("Query failed", str(context.exception))
247
+
248
+ @pytest.mark.asyncio
249
+ async def test_concurrent_pagination_with_mixed_results(self):
250
+ """Test concurrent pagination with some failures."""
251
+ query_builder = Mock(side_effect=lambda skip, take: f"query({skip},{take})")
252
+
253
+ with patch.object(self.client, "execute_query", new_callable=AsyncMock) as mock_execute:
254
+ # Mix of success and failure
255
+ mock_execute.side_effect = [
256
+ {"issues": {"items": [{"id": 1}, {"id": 2}]}},
257
+ Exception("Page 2 failed"),
258
+ {"issues": {"items": [{"id": 3}]}},
259
+ ]
260
+
261
+ result = await self.client.execute_paginated_query_concurrent(
262
+ query_builder=query_builder,
263
+ topic_key="issues",
264
+ total_count=5,
265
+ page_size=2,
266
+ starting_skip=0,
267
+ )
268
+
269
+ # Should still get results from successful pages
270
+ self.assertEqual(len(result), 3) # Only pages 1 and 3 succeeded
271
+
272
+ def test_run_async_paginated_query(self):
273
+ """Test the synchronous wrapper function."""
274
+ query_builder = Mock(side_effect=lambda skip, take: f"query({skip},{take})")
275
+
276
+ with patch("regscale.core.utils.async_graphql_client.AsyncRegScaleGraphQLClient") as mock_client_class:
277
+ mock_client = Mock()
278
+ mock_client.execute_paginated_query_concurrent = AsyncMock(return_value=[{"id": 1}, {"id": 2}])
279
+ mock_client_class.return_value = mock_client
280
+
281
+ result = run_async_paginated_query(
282
+ endpoint=self.endpoint,
283
+ headers=self.headers,
284
+ query_builder=query_builder,
285
+ topic_key="issues",
286
+ total_count=2,
287
+ page_size=50,
288
+ )
289
+
290
+ self.assertEqual(len(result), 2)
291
+ mock_client_class.assert_called_once()
292
+ mock_client.execute_paginated_query_concurrent.assert_called_once()
293
+
294
+ def test_run_async_paginated_query_with_token_refresh(self):
295
+ """Test synchronous wrapper with token refresh callback."""
296
+ query_builder = Mock(side_effect=lambda skip, take: f"query({skip},{take})")
297
+ token_refresh = Mock(return_value="new_token")
298
+
299
+ with patch("regscale.core.utils.async_graphql_client.AsyncRegScaleGraphQLClient") as mock_client_class:
300
+ mock_client = Mock()
301
+ mock_client.execute_paginated_query_concurrent = AsyncMock(return_value=[{"id": 1}])
302
+ mock_client_class.return_value = mock_client
303
+
304
+ run_async_paginated_query(
305
+ endpoint=self.endpoint,
306
+ headers=self.headers,
307
+ query_builder=query_builder,
308
+ topic_key="issues",
309
+ total_count=1,
310
+ token_refresh_callback=token_refresh,
311
+ )
312
+
313
+ # Verify token refresh callback was passed to client
314
+ call_kwargs = mock_client_class.call_args[1]
315
+ self.assertEqual(call_kwargs["token_refresh_callback"], token_refresh)
316
+
317
+ @pytest.mark.asyncio
318
+ async def test_semaphore_limiting(self):
319
+ """Test that concurrent requests are limited by semaphore."""
320
+ client = AsyncRegScaleGraphQLClient(endpoint=self.endpoint, headers=self.headers, max_concurrent=2)
321
+
322
+ # Track concurrent executions
323
+ concurrent_count = 0
324
+ max_concurrent_seen = 0
325
+
326
+ async def slow_execute(*args, **kwargs):
327
+ nonlocal concurrent_count, max_concurrent_seen
328
+ concurrent_count += 1
329
+ max_concurrent_seen = max(max_concurrent_seen, concurrent_count)
330
+ await asyncio.sleep(0.1)
331
+ concurrent_count -= 1
332
+ return {"data": {"items": []}}
333
+
334
+ with patch.object(client, "execute_query", side_effect=slow_execute):
335
+ query_builder = Mock(side_effect=lambda skip, take: f"query({skip},{take})")
336
+
337
+ # Request 5 pages with max_concurrent=2
338
+ await client.execute_paginated_query_concurrent(
339
+ query_builder=query_builder,
340
+ topic_key="data",
341
+ total_count=10,
342
+ page_size=2,
343
+ )
344
+
345
+ # Should never exceed max_concurrent
346
+ self.assertLessEqual(max_concurrent_seen, 2)
347
+
348
+ @pytest.mark.asyncio
349
+ async def test_execute_query_with_timeout(self):
350
+ """Test query execution respects timeout."""
351
+ client = AsyncRegScaleGraphQLClient(endpoint=self.endpoint, headers=self.headers, timeout=0.001)
352
+
353
+ with patch.object(client.client, "__aenter__") as mock_aenter:
354
+ mock_session = AsyncMock()
355
+
356
+ async def slow_execute(*args, **kwargs):
357
+ await asyncio.sleep(1) # Longer than timeout
358
+ return {"data": {}}
359
+
360
+ mock_session.execute = slow_execute
361
+ mock_aenter.return_value = mock_session
362
+
363
+ # Should timeout
364
+ with self.assertRaises(Exception):
365
+ await client.execute_query("query { test }")
366
+
367
+ @pytest.mark.asyncio
368
+ async def test_ssl_verify_setting(self):
369
+ """Test SSL verification setting from ScannerVariables."""
370
+ with patch("regscale.core.utils.async_graphql_client.ScannerVariables") as mock_vars:
371
+ mock_vars.sslVerify = False
372
+
373
+ with patch("regscale.core.utils.async_graphql_client.AIOHTTPTransport") as mock_transport:
374
+ AsyncRegScaleGraphQLClient(endpoint=self.endpoint, headers=self.headers)
375
+
376
+ # Verify transport was created with ssl parameter
377
+ mock_transport.assert_called()
378
+ call_args = mock_transport.call_args
379
+ # Check that ssl parameter was set (should be ssl context when verify=False)
380
+ self.assertIsNotNone(call_args[1].get("ssl"))
381
+
382
+ @pytest.mark.asyncio
383
+ async def test_auth_error_without_callback(self):
384
+ """Test auth error handling without token refresh callback."""
385
+ client = AsyncRegScaleGraphQLClient(
386
+ endpoint=self.endpoint,
387
+ headers=self.headers,
388
+ token_refresh_callback=None, # No callback
389
+ )
390
+
391
+ with patch.object(client.client, "__aenter__") as mock_aenter:
392
+ mock_session = AsyncMock()
393
+ mock_session.execute = AsyncMock(side_effect=TransportQueryError("AUTH_NOT_AUTHENTICATED"))
394
+ mock_aenter.return_value = mock_session
395
+
396
+ with self.assertRaises(Exception) as context:
397
+ await client.execute_query("query { test }")
398
+
399
+ self.assertIn("AUTH_NOT_AUTHENTICATED", str(context.exception))
400
+
401
+ @pytest.mark.asyncio
402
+ async def test_multiple_auth_retries_exhausted(self):
403
+ """Test that auth retries are exhausted after max attempts."""
404
+ token_refresh = Mock(return_value="new_token")
405
+ client = AsyncRegScaleGraphQLClient(
406
+ endpoint=self.endpoint,
407
+ headers=self.headers,
408
+ token_refresh_callback=token_refresh,
409
+ )
410
+
411
+ with patch.object(client.client, "__aenter__") as mock_aenter:
412
+ mock_session = AsyncMock()
413
+ # Always fail with auth error
414
+ mock_session.execute = AsyncMock(side_effect=TransportQueryError("AUTH_NOT_AUTHENTICATED"))
415
+ mock_aenter.return_value = mock_session
416
+
417
+ with patch("regscale.core.utils.async_graphql_client.AIOHTTPTransport"):
418
+ with patch("regscale.core.utils.async_graphql_client.Client"):
419
+ with self.assertRaises(Exception) as context:
420
+ await client.execute_query("query { test }")
421
+
422
+ # Should have tried to refresh token once
423
+ token_refresh.assert_called_once()
424
+ self.assertIn("Failed to execute query", str(context.exception))
425
+
426
+ @pytest.mark.asyncio
427
+ async def test_progress_callback_on_error(self):
428
+ """Test progress callback is called on error."""
429
+ progress_callback = Mock()
430
+
431
+ with patch.object(self.client.client, "__aenter__") as mock_aenter:
432
+ mock_session = AsyncMock()
433
+ mock_session.execute = AsyncMock(side_effect=Exception("Query error"))
434
+ mock_aenter.return_value = mock_session
435
+
436
+ with self.assertRaises(Exception):
437
+ await self.client.execute_query(
438
+ "query { test }",
439
+ progress_callback=progress_callback,
440
+ task_name="Error Query",
441
+ )
442
+
443
+ # Should have called failed status
444
+ progress_callback.assert_any_call("Error Query", "starting")
445
+ progress_callback.assert_any_call("Error Query", "requesting")
446
+ progress_callback.assert_any_call("Error Query", "failed")
447
+
448
+ @pytest.mark.asyncio
449
+ async def test_concurrent_queries_with_progress(self):
450
+ """Test concurrent queries with progress tracking."""
451
+ progress_callback = Mock()
452
+ query_builder = Mock(side_effect=lambda skip, take: f"query({skip},{take})")
453
+
454
+ with patch.object(self.client, "execute_query", new_callable=AsyncMock) as mock_execute:
455
+ mock_execute.side_effect = [
456
+ {"data": {"items": [{"id": 1}]}},
457
+ {"data": {"items": [{"id": 2}]}},
458
+ ]
459
+
460
+ result = await self.client.execute_paginated_query_concurrent(
461
+ query_builder=query_builder,
462
+ topic_key="data",
463
+ total_count=2,
464
+ page_size=1,
465
+ progress_callback=progress_callback,
466
+ task_name="Test",
467
+ )
468
+
469
+ self.assertEqual(len(result), 2)
470
+ # Verify progress was tracked for each page
471
+ progress_callback.assert_any_call("Test (Page 1/2)", "fetched_1_items")
472
+ progress_callback.assert_any_call("Test (Page 2/2)", "fetched_1_items")
@@ -141,14 +141,19 @@ class CLITestFixture:
141
141
  self.config = self.app.config
142
142
  self.title_prefix = generate_uuid
143
143
  # login with token if available
144
- if token := os.getenv("REGSCALE_TOKEN") or self.config.get("token"):
145
- login(token=token, app=self.app)
146
- elif "REGSCALE_USERNAME" in os.environ and "REGSCALE_PASSWORD" in os.environ:
147
- login(
148
- str_user=os.getenv("REGSCALE_USERNAME"),
149
- str_password=os.getenv("REGSCALE_PASSWORD"),
150
- app=self.app,
151
- )
144
+ try:
145
+ if token := os.getenv("REGSCALE_TOKEN") or self.config.get("token"):
146
+ login(token=token, app=self.app)
147
+ elif "REGSCALE_USERNAME" in os.environ and "REGSCALE_PASSWORD" in os.environ:
148
+ login(
149
+ str_user=os.getenv("REGSCALE_USERNAME"),
150
+ str_password=os.getenv("REGSCALE_PASSWORD"),
151
+ app=self.app,
152
+ )
153
+ except SystemExit:
154
+ # In test environment, don't exit on authentication failure
155
+ logger.warning("Authentication failed in test environment - continuing without authentication")
156
+ pass
152
157
  self.api = Api()
153
158
  self.logger = logger
154
159