regscale-cli 6.21.2.0__py3-none-any.whl → 6.28.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. regscale/_version.py +1 -1
  2. regscale/airflow/hierarchy.py +2 -2
  3. regscale/core/app/api.py +5 -2
  4. regscale/core/app/application.py +36 -6
  5. regscale/core/app/internal/control_editor.py +73 -21
  6. regscale/core/app/internal/evidence.py +727 -204
  7. regscale/core/app/internal/login.py +4 -2
  8. regscale/core/app/internal/model_editor.py +219 -64
  9. regscale/core/app/utils/app_utils.py +86 -12
  10. regscale/core/app/utils/catalog_utils/common.py +1 -1
  11. regscale/core/login.py +21 -4
  12. regscale/core/utils/async_graphql_client.py +363 -0
  13. regscale/core/utils/date.py +77 -1
  14. regscale/dev/cli.py +26 -0
  15. regscale/dev/code_gen.py +109 -24
  16. regscale/dev/version.py +72 -0
  17. regscale/integrations/commercial/__init__.py +30 -2
  18. regscale/integrations/commercial/aws/audit_manager_compliance.py +3908 -0
  19. regscale/integrations/commercial/aws/cli.py +3107 -54
  20. regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
  21. regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
  22. regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
  23. regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
  24. regscale/integrations/commercial/{amazon → aws}/common.py +71 -19
  25. regscale/integrations/commercial/aws/config_compliance.py +914 -0
  26. regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
  27. regscale/integrations/commercial/aws/control_compliance_analyzer.py +439 -0
  28. regscale/integrations/commercial/aws/evidence_generator.py +283 -0
  29. regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
  30. regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
  31. regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
  32. regscale/integrations/commercial/aws/iam_evidence.py +574 -0
  33. regscale/integrations/commercial/aws/inventory/__init__.py +338 -22
  34. regscale/integrations/commercial/aws/inventory/base.py +107 -5
  35. regscale/integrations/commercial/aws/inventory/resources/analytics.py +390 -0
  36. regscale/integrations/commercial/aws/inventory/resources/applications.py +234 -0
  37. regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
  38. regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
  39. regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
  40. regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
  41. regscale/integrations/commercial/aws/inventory/resources/compute.py +328 -9
  42. regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
  43. regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
  44. regscale/integrations/commercial/aws/inventory/resources/database.py +481 -31
  45. regscale/integrations/commercial/aws/inventory/resources/developer_tools.py +253 -0
  46. regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
  47. regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
  48. regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
  49. regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
  50. regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
  51. regscale/integrations/commercial/aws/inventory/resources/machine_learning.py +358 -0
  52. regscale/integrations/commercial/aws/inventory/resources/networking.py +390 -67
  53. regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
  54. regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
  55. regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
  56. regscale/integrations/commercial/aws/inventory/resources/storage.py +288 -29
  57. regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
  58. regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
  59. regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
  60. regscale/integrations/commercial/aws/kms_evidence.py +879 -0
  61. regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
  62. regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
  63. regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
  64. regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
  65. regscale/integrations/commercial/aws/org_evidence.py +666 -0
  66. regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
  67. regscale/integrations/commercial/aws/s3_evidence.py +632 -0
  68. regscale/integrations/commercial/aws/scanner.py +1072 -205
  69. regscale/integrations/commercial/aws/security_hub.py +319 -0
  70. regscale/integrations/commercial/aws/session_manager.py +282 -0
  71. regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
  72. regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
  73. regscale/integrations/commercial/jira.py +489 -153
  74. regscale/integrations/commercial/microsoft_defender/defender.py +326 -5
  75. regscale/integrations/commercial/microsoft_defender/defender_api.py +348 -14
  76. regscale/integrations/commercial/microsoft_defender/defender_constants.py +157 -0
  77. regscale/integrations/commercial/qualys/__init__.py +167 -68
  78. regscale/integrations/commercial/qualys/scanner.py +305 -39
  79. regscale/integrations/commercial/sarif/sairf_importer.py +432 -0
  80. regscale/integrations/commercial/sarif/sarif_converter.py +67 -0
  81. regscale/integrations/commercial/sicura/api.py +79 -42
  82. regscale/integrations/commercial/sicura/commands.py +8 -2
  83. regscale/integrations/commercial/sicura/scanner.py +83 -44
  84. regscale/integrations/commercial/stigv2/ckl_parser.py +5 -5
  85. regscale/integrations/commercial/synqly/assets.py +133 -16
  86. regscale/integrations/commercial/synqly/edr.py +2 -8
  87. regscale/integrations/commercial/synqly/query_builder.py +536 -0
  88. regscale/integrations/commercial/synqly/ticketing.py +27 -0
  89. regscale/integrations/commercial/synqly/vulnerabilities.py +165 -28
  90. regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
  91. regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
  92. regscale/integrations/commercial/tenablev2/commands.py +146 -5
  93. regscale/integrations/commercial/tenablev2/scanner.py +1 -3
  94. regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
  95. regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
  96. regscale/integrations/commercial/wizv2/click.py +191 -76
  97. regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
  98. regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
  99. regscale/integrations/commercial/wizv2/compliance_report.py +1592 -0
  100. regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
  101. regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +7 -3
  102. regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +92 -89
  103. regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
  104. regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
  105. regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +66 -9
  106. regscale/integrations/commercial/wizv2/file_cleanup.py +104 -0
  107. regscale/integrations/commercial/wizv2/issue.py +776 -28
  108. regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
  109. regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
  110. regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
  111. regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
  112. regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
  113. regscale/integrations/commercial/wizv2/reports.py +243 -0
  114. regscale/integrations/commercial/wizv2/sbom.py +1 -1
  115. regscale/integrations/commercial/wizv2/scanner.py +1031 -441
  116. regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
  117. regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
  118. regscale/integrations/commercial/wizv2/variables.py +89 -3
  119. regscale/integrations/compliance_integration.py +1036 -151
  120. regscale/integrations/control_matcher.py +432 -0
  121. regscale/integrations/due_date_handler.py +333 -0
  122. regscale/integrations/milestone_manager.py +291 -0
  123. regscale/integrations/public/__init__.py +14 -0
  124. regscale/integrations/public/cci_importer.py +834 -0
  125. regscale/integrations/public/csam/__init__.py +0 -0
  126. regscale/integrations/public/csam/csam.py +938 -0
  127. regscale/integrations/public/csam/csam_agency_defined.py +179 -0
  128. regscale/integrations/public/csam/csam_common.py +154 -0
  129. regscale/integrations/public/csam/csam_controls.py +432 -0
  130. regscale/integrations/public/csam/csam_poam.py +124 -0
  131. regscale/integrations/public/fedramp/click.py +77 -6
  132. regscale/integrations/public/fedramp/docx_parser.py +10 -1
  133. regscale/integrations/public/fedramp/fedramp_cis_crm.py +675 -289
  134. regscale/integrations/public/fedramp/fedramp_five.py +1 -1
  135. regscale/integrations/public/fedramp/poam/scanner.py +75 -7
  136. regscale/integrations/public/fedramp/poam_export_v5.py +888 -0
  137. regscale/integrations/scanner_integration.py +1961 -430
  138. regscale/models/integration_models/CCI_List.xml +1 -0
  139. regscale/models/integration_models/aqua.py +2 -2
  140. regscale/models/integration_models/cisa_kev_data.json +805 -11
  141. regscale/models/integration_models/flat_file_importer/__init__.py +5 -8
  142. regscale/models/integration_models/nexpose.py +36 -10
  143. regscale/models/integration_models/qualys.py +3 -4
  144. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  145. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +87 -18
  146. regscale/models/integration_models/synqly_models/filter_parser.py +332 -0
  147. regscale/models/integration_models/synqly_models/ocsf_mapper.py +124 -25
  148. regscale/models/integration_models/synqly_models/synqly_model.py +89 -16
  149. regscale/models/locking.py +12 -8
  150. regscale/models/platform.py +4 -2
  151. regscale/models/regscale_models/__init__.py +7 -0
  152. regscale/models/regscale_models/assessment.py +2 -1
  153. regscale/models/regscale_models/catalog.py +1 -1
  154. regscale/models/regscale_models/compliance_settings.py +251 -1
  155. regscale/models/regscale_models/component.py +1 -0
  156. regscale/models/regscale_models/control_implementation.py +236 -41
  157. regscale/models/regscale_models/control_objective.py +74 -5
  158. regscale/models/regscale_models/file.py +2 -0
  159. regscale/models/regscale_models/form_field_value.py +5 -3
  160. regscale/models/regscale_models/inheritance.py +44 -0
  161. regscale/models/regscale_models/issue.py +301 -102
  162. regscale/models/regscale_models/milestone.py +33 -14
  163. regscale/models/regscale_models/organization.py +3 -0
  164. regscale/models/regscale_models/regscale_model.py +310 -73
  165. regscale/models/regscale_models/security_plan.py +4 -2
  166. regscale/models/regscale_models/vulnerability.py +3 -3
  167. regscale/regscale.py +25 -4
  168. regscale/templates/__init__.py +0 -0
  169. regscale/utils/threading/threadhandler.py +20 -15
  170. regscale/validation/record.py +23 -1
  171. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/METADATA +17 -33
  172. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/RECORD +310 -111
  173. tests/core/__init__.py +0 -0
  174. tests/core/utils/__init__.py +0 -0
  175. tests/core/utils/test_async_graphql_client.py +472 -0
  176. tests/fixtures/test_fixture.py +13 -8
  177. tests/regscale/core/test_login.py +171 -4
  178. tests/regscale/integrations/commercial/__init__.py +0 -0
  179. tests/regscale/integrations/commercial/aws/__init__.py +0 -0
  180. tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
  181. tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
  182. tests/regscale/integrations/commercial/aws/test_aws_analytics_collector.py +260 -0
  183. tests/regscale/integrations/commercial/aws/test_aws_applications_collector.py +242 -0
  184. tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
  185. tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
  186. tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
  187. tests/regscale/integrations/commercial/aws/test_aws_developer_tools_collector.py +203 -0
  188. tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
  189. tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
  190. tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
  191. tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
  192. tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
  193. tests/regscale/integrations/commercial/aws/test_aws_machine_learning_collector.py +237 -0
  194. tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
  195. tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
  196. tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
  197. tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
  198. tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
  199. tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
  200. tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
  201. tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
  202. tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
  203. tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
  204. tests/regscale/integrations/commercial/aws/test_control_compliance_analyzer.py +375 -0
  205. tests/regscale/integrations/commercial/aws/test_datetime_parsing.py +223 -0
  206. tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
  207. tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
  208. tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
  209. tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
  210. tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
  211. tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
  212. tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
  213. tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
  214. tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
  215. tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
  216. tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
  217. tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
  218. tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
  219. tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
  220. tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
  221. tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
  222. tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
  223. tests/regscale/integrations/commercial/conftest.py +28 -0
  224. tests/regscale/integrations/commercial/microsoft_defender/__init__.py +1 -0
  225. tests/regscale/integrations/commercial/microsoft_defender/test_defender.py +1517 -0
  226. tests/regscale/integrations/commercial/microsoft_defender/test_defender_api.py +1748 -0
  227. tests/regscale/integrations/commercial/microsoft_defender/test_defender_constants.py +327 -0
  228. tests/regscale/integrations/commercial/microsoft_defender/test_defender_scanner.py +487 -0
  229. tests/regscale/integrations/commercial/test_aws.py +3742 -0
  230. tests/regscale/integrations/commercial/test_burp.py +48 -0
  231. tests/regscale/integrations/commercial/test_crowdstrike.py +49 -0
  232. tests/regscale/integrations/commercial/test_dependabot.py +341 -0
  233. tests/regscale/integrations/commercial/test_gcp.py +1543 -0
  234. tests/regscale/integrations/commercial/test_gitlab.py +549 -0
  235. tests/regscale/integrations/commercial/test_ip_mac_address_length.py +84 -0
  236. tests/regscale/integrations/commercial/test_jira.py +2204 -0
  237. tests/regscale/integrations/commercial/test_npm_audit.py +42 -0
  238. tests/regscale/integrations/commercial/test_okta.py +1228 -0
  239. tests/regscale/integrations/commercial/test_sarif_converter.py +251 -0
  240. tests/regscale/integrations/commercial/test_sicura.py +349 -0
  241. tests/regscale/integrations/commercial/test_snow.py +423 -0
  242. tests/regscale/integrations/commercial/test_sonarcloud.py +394 -0
  243. tests/regscale/integrations/commercial/test_sqlserver.py +186 -0
  244. tests/regscale/integrations/commercial/test_stig.py +33 -0
  245. tests/regscale/integrations/commercial/test_stig_mapper.py +153 -0
  246. tests/regscale/integrations/commercial/test_stigv2.py +406 -0
  247. tests/regscale/integrations/commercial/test_wiz.py +1365 -0
  248. tests/regscale/integrations/commercial/test_wiz_inventory.py +256 -0
  249. tests/regscale/integrations/commercial/wizv2/__init__.py +339 -0
  250. tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
  251. tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
  252. tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
  253. tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
  254. tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
  255. tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
  256. tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
  257. tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
  258. tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
  259. tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
  260. tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
  261. tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
  262. tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
  263. tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
  264. tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
  265. tests/regscale/integrations/commercial/wizv2/test_compliance_report_normalization.py +138 -0
  266. tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
  267. tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
  268. tests/regscale/integrations/commercial/wizv2/test_issue.py +343 -0
  269. tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
  270. tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
  271. tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
  272. tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
  273. tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +165 -0
  274. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +1394 -0
  275. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_unit.py +341 -0
  276. tests/regscale/integrations/commercial/wizv2/test_wiz_control_normalization.py +138 -0
  277. tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
  278. tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
  279. tests/regscale/integrations/commercial/wizv2/test_wiz_status_mapping.py +149 -0
  280. tests/regscale/integrations/commercial/wizv2/test_wizv2.py +1218 -0
  281. tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +519 -0
  282. tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
  283. tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
  284. tests/regscale/integrations/public/__init__.py +0 -0
  285. tests/regscale/integrations/public/fedramp/__init__.py +1 -0
  286. tests/regscale/integrations/public/fedramp/test_gen_asset_list.py +150 -0
  287. tests/regscale/integrations/public/fedramp/test_poam_export_v5.py +1293 -0
  288. tests/regscale/integrations/public/test_alienvault.py +220 -0
  289. tests/regscale/integrations/public/test_cci.py +1053 -0
  290. tests/regscale/integrations/public/test_cisa.py +1021 -0
  291. tests/regscale/integrations/public/test_emass.py +518 -0
  292. tests/regscale/integrations/public/test_fedramp.py +1152 -0
  293. tests/regscale/integrations/public/test_fedramp_cis_crm.py +3661 -0
  294. tests/regscale/integrations/public/test_file_uploads.py +506 -0
  295. tests/regscale/integrations/public/test_oscal.py +453 -0
  296. tests/regscale/integrations/test_compliance_status_mapping.py +406 -0
  297. tests/regscale/integrations/test_control_matcher.py +1421 -0
  298. tests/regscale/integrations/test_control_matching.py +155 -0
  299. tests/regscale/integrations/test_milestone_manager.py +408 -0
  300. tests/regscale/models/test_control_implementation.py +118 -3
  301. tests/regscale/models/test_form_field_value_integration.py +304 -0
  302. tests/regscale/models/test_issue.py +378 -1
  303. tests/regscale/models/test_module_integration.py +582 -0
  304. tests/regscale/models/test_tenable_integrations.py +811 -105
  305. regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3057
  306. regscale/integrations/public/fedramp/mappings/fedramp_r4_parts.json +0 -7388
  307. regscale/integrations/public/fedramp/mappings/fedramp_r5_parts.json +0 -9605
  308. regscale/integrations/public/fedramp/parts_mapper.py +0 -107
  309. /regscale/integrations/commercial/{amazon → sarif}/__init__.py +0 -0
  310. /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
  311. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/LICENSE +0 -0
  312. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/WHEEL +0 -0
  313. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/entry_points.txt +0 -0
  314. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,432 @@
1
+ """
2
+ Module to import and convert SARIF files to OCSF format using an API converter, and creating Vulnerability,
3
+ VulnerabilityMapping, and ScanHistory objects in RegScale.
4
+ """
5
+
6
+ import datetime
7
+ import json
8
+ import os
9
+ from logging import getLogger
10
+ from typing import Any, Dict, Optional, Union
11
+
12
+ import requests
13
+ from pathlib import Path
14
+ from requests.exceptions import RequestException
15
+ from rich.progress import Progress
16
+ from synqly.engine.resources.ocsf.resources.v_1_3_0.resources.vulnerabilityfinding import Vulnerability as OcsfVuln
17
+ from synqly.engine.resources.ocsf.resources.v_1_5_0.resources.applicationsecurityposturefinding import (
18
+ ApplicationSecurityPostureFinding,
19
+ )
20
+
21
+ from regscale.core.app.application import Application
22
+ from regscale.core.app.utils.app_utils import (
23
+ create_progress_object,
24
+ check_file_path,
25
+ check_license,
26
+ error_and_exit,
27
+ save_data_to,
28
+ get_current_datetime,
29
+ )
30
+ from regscale.models import Asset, IssueStatus, Issue, ScanHistory, SecurityPlan, Vulnerability, VulnerabilityMapping
31
+ from regscale.validation.record import validate_regscale_object
32
+
33
+ logger = getLogger("regscale")
34
+
35
+
36
+ class SarifImporter:
37
+ """
38
+ Class to handle importing and converting SARIF files to OCSF format using an API converter. It then creates
39
+ Vulnerability, VulnerabilityMapping, ScanHistory objects in RegScale.
40
+
41
+ :param Path file_path: Path to the SARIF file or directory of files
42
+ :param int asset_id: The RegScale Asset ID to import the findings to
43
+ :param Union[datetime.datetime, str, None] scan_date: The scan date of the file (defaults to current date if None)
44
+ :rtype: None
45
+ """
46
+
47
+ def __init__(self, file_path: Path, asset_id: int, scan_date: Union[datetime.datetime, str, None] = None) -> None:
48
+ self.file_path: Path = file_path
49
+ self.app: Application = check_license()
50
+ if not scan_date:
51
+ scan_date = get_current_datetime()
52
+ if isinstance(scan_date, datetime.datetime):
53
+ scan_date = scan_date.strftime("%Y-%m-%d")
54
+ self.scan_date: str = scan_date
55
+ self.token: Optional[str] = os.getenv("synqlyAccessToken") or self.app.config.get("synqlyAccessToken")
56
+ if not self.token:
57
+ error_and_exit("synqlyAccessToken environment variable or init.yaml value is required")
58
+ if not validate_regscale_object(asset_id, Asset.get_module_string()):
59
+ error_and_exit(f"Asset ID {asset_id} does not exist in RegScale.")
60
+ self.asset: Asset = Asset.get_object(asset_id)
61
+ self.parent_id = asset_id
62
+ self.parent_module = Asset.get_module_slug()
63
+ self.scan_history: Optional[ScanHistory] = None
64
+ self.progress: Progress = create_progress_object()
65
+ self.process_sarif_files()
66
+
67
+ @staticmethod
68
+ def load_sarif_file(file_path: Path) -> Dict[str, Any]:
69
+ """
70
+ Load and parse a SARIF file from disk
71
+
72
+ :param Path file_path: Path to the SARIF file
73
+ :return: Parsed SARIF data as dictionary
74
+ :rtype: Dict[str, Any]
75
+ :raises: SystemExit if file cannot be loaded or parsed
76
+ """
77
+ try:
78
+ with open(file_path, "r", encoding="utf-8") as file: # type: ignore
79
+ sarif_data = json.load(file)
80
+
81
+ # Basic validation that it's a SARIF file
82
+ if not isinstance(sarif_data, dict) or "$schema" not in sarif_data:
83
+ logger.warning("File may not be a valid SARIF file (missing $schema)")
84
+ elif "sarif" not in sarif_data.get("$schema", "").lower():
85
+ logger.warning("File may not be a valid SARIF file (schema doesn't contain 'sarif')")
86
+
87
+ logger.debug("Successfully loaded SARIF file with %d runs", len(sarif_data.get("runs", [])))
88
+ return sarif_data
89
+
90
+ except FileNotFoundError:
91
+ error_and_exit(f"SARIF file not found: {file_path}")
92
+ except json.JSONDecodeError as e:
93
+ error_and_exit(f"Failed to parse SARIF file as JSON: {e}")
94
+ except Exception as e:
95
+ error_and_exit(f"Failed to load SARIF file: {e}")
96
+
97
+ def convert_sarif_to_ocsf(self, sarif_data: Dict[str, Any]) -> Optional[Any]:
98
+ """
99
+ Convert SARIF data to OCSF format using Synqly API
100
+
101
+ :param Dict[str, Any] sarif_data: The SARIF data to convert
102
+ :raises: SystemExit if API call fails
103
+ :return: Converted data from Synqly API
104
+ :rtype: Optional[Any]
105
+ """
106
+ api_url = "https://api.synqly.com/v1/mappings/apply"
107
+
108
+ headers = {"content-type": "application/json", "authorization": f"Bearer {self.token}"}
109
+
110
+ payload = {"mappings": ["synqly-default.sarif:1.5.0.0"], "data": sarif_data}
111
+
112
+ try:
113
+ logger.debug("Making API request to convert the data: %s", api_url)
114
+ response = requests.post(api_url, headers=headers, json=payload, timeout=60) # 60 second timeout
115
+
116
+ if response.status_code != 200:
117
+ logger.error("API request failed with status %d: %s", response.status_code, response.text)
118
+
119
+ converted_data = response.json()
120
+ logger.debug("Successfully converted SARIF data using API.")
121
+ logger.debug("Response status: %d, Content length: %d bytes", response.status_code, len(response.content))
122
+
123
+ # get the results from the nested dictionary
124
+ if mapping := converted_data.get("result", {}).get("mapping", {}):
125
+ return mapping
126
+ logger.error("Failed to convert SARIF data using API.")
127
+
128
+ except RequestException as e:
129
+ if hasattr(e, "response") and e.response is not None:
130
+ try:
131
+ error_detail = e.response.json()
132
+ error_and_exit(f"API error ({e.response.status_code}): {error_detail}")
133
+ except json.JSONDecodeError:
134
+ error_and_exit(f"API error ({e.response.status_code}): {e.response.text}")
135
+ else:
136
+ error_and_exit(f"Failed to connect to API: {e}")
137
+ except json.JSONDecodeError:
138
+ error_and_exit("Failed to parse API response as JSON")
139
+ except Exception as e:
140
+ error_and_exit(f"Unexpected error during SARIF conversion: {e}")
141
+
142
+ def _process_single_sarif_file(
143
+ self,
144
+ sarif_file: Path,
145
+ output_directory: Path,
146
+ ) -> Dict[str, Any]:
147
+ """
148
+ Process a single SARIF file and return result information
149
+
150
+ :param Path sarif_file: Path to the SARIF file to process
151
+ :param Path output_directory: Output directory for converted files
152
+ :return: Processing result information
153
+ :rtype: Dict[str, Any]
154
+ """
155
+ try:
156
+ sarif_data = self.load_sarif_file(sarif_file)
157
+ logger.debug(f"Converting {sarif_file} data to OCSF data via API...")
158
+
159
+ if "runs" in sarif_data and isinstance(sarif_data["runs"], list) and len(sarif_data["runs"]) > 0:
160
+ # Process each run separately and collect results
161
+ converted_data = [self.convert_sarif_to_ocsf(run) for run in sarif_data["runs"]]
162
+ else:
163
+ # Process the entire SARIF data as a single unit
164
+ converted_data = self.convert_sarif_to_ocsf(sarif_data)
165
+
166
+ # Save converted data
167
+ output_file = output_directory / f"{sarif_file.stem}-converted.json"
168
+ logger.debug("Saving converted data to: %s", output_file)
169
+
170
+ save_data_to(
171
+ file=output_file,
172
+ data=converted_data, # Only log save details in single file mode
173
+ )
174
+
175
+ created_count, updated_count = self._map_sarif_to_integration_findings(converted_data)
176
+
177
+ return {
178
+ "file": sarif_file.absolute(),
179
+ "output": output_file.absolute(),
180
+ "status": "success",
181
+ "error": None,
182
+ "created": created_count,
183
+ "updated": updated_count,
184
+ }
185
+
186
+ except Exception as e:
187
+ return {"file": sarif_file.absolute(), "output": None, "status": "failed", "error": str(e)}
188
+
189
+ def process_sarif_files(
190
+ self,
191
+ ) -> None:
192
+ """
193
+ Unified function to process SARIF files (single or multiple) and convert them using an API converter
194
+
195
+ :rtype: None
196
+ """
197
+ if self.file_path.is_file():
198
+ sarif_files = [self.file_path]
199
+ else:
200
+ sarif_files = list(self.file_path.glob("*.sarif"))
201
+
202
+ # Ensure output directory exists
203
+ output_directory = self.file_path.parent / "converted"
204
+ check_file_path(output_directory)
205
+
206
+ # Initialize processing statistics
207
+ successful_conversions = 0
208
+ failed_conversions = 0
209
+ processed_files = []
210
+
211
+ if total_files := len(sarif_files):
212
+ self.create_scan_history()
213
+ else:
214
+ error_and_exit(f"No SARIF files found in directory: {self.file_path}")
215
+ if batch_mode := total_files > 1:
216
+ logger.info("Found %d SARIF files to convert", total_files)
217
+
218
+ # Process each SARIF file
219
+ for idx, sarif_file in enumerate(sarif_files, 1):
220
+ # Log progress for batch mode
221
+ if batch_mode:
222
+ logger.info("Processing file %d/%d: %s", idx, total_files, sarif_file.name)
223
+ else:
224
+ logger.info("Loading SARIF file: %s", sarif_file)
225
+
226
+ # Process single file
227
+ result = self._process_single_sarif_file(sarif_file, output_directory)
228
+ processed_files.append(result)
229
+
230
+ successful_conversions, failed_conversions = self._log_result_summary(
231
+ result=result,
232
+ successful_conversions=successful_conversions,
233
+ failed_conversions=failed_conversions,
234
+ batch_mode=batch_mode,
235
+ )
236
+
237
+ self.scan_history.save()
238
+ if batch_mode:
239
+ logger.info(
240
+ "Batch conversion completed: %d successful, %d failed", successful_conversions, failed_conversions
241
+ )
242
+ logger.info("Converted files saved to: %s", output_directory.absolute())
243
+ else:
244
+ if successful_conversions == 1:
245
+ logger.info(
246
+ "Conversion completed successfully. Converted file saved to: %s", output_directory.absolute()
247
+ )
248
+ else:
249
+ error_and_exit("SARIF file conversion failed.")
250
+
251
+ def create_scan_history(self) -> None:
252
+ """
253
+ Create a ScanHistory record for the SARIF import
254
+
255
+ :rtype: None
256
+ """
257
+ if getattr(self.asset, "parentModule") == SecurityPlan.get_module_string():
258
+ parent_id = self.asset.parentId
259
+ parent_module: str = SecurityPlan.get_module_string()
260
+ else:
261
+ parent_id = self.asset.id
262
+ parent_module = self.asset.parentModule
263
+ self.scan_history = ScanHistory(
264
+ parentId=parent_id,
265
+ parentModule=parent_module,
266
+ scanningTool="Sarif Scanner",
267
+ scanDate=self.scan_date,
268
+ tenantsId=Asset.get_tenant_id(),
269
+ vLow=0,
270
+ vMedium=0,
271
+ vHigh=0,
272
+ vCritical=0,
273
+ ).create()
274
+
275
+ @staticmethod
276
+ def _log_result_summary(
277
+ result: Dict[str, Any],
278
+ successful_conversions: int,
279
+ failed_conversions: int,
280
+ batch_mode: bool,
281
+ ) -> tuple[int, int]:
282
+ """
283
+ Log the result of a single SARIF file conversion and update statistics
284
+
285
+ :param Dict[str, Any] result: The result of the conversion
286
+ :param int successful_conversions: The number of successful conversions
287
+ :param int failed_conversions: The number of failed conversions
288
+ :param bool batch_mode: Whether running in batch mode (multiple files)
289
+ :return: Updated counts of successful and failed conversions
290
+ :rtype: tuple[int, int]
291
+ """
292
+ if result["status"] == "success":
293
+ successful_conversions += 1
294
+ logger.debug("Successfully converted: %s -> %s", result["file"], result["output"])
295
+ logger.info(
296
+ "Successfully converted: %s -> %s, %s created vulnerabilities and %s updated vulnerabilities",
297
+ result["file"],
298
+ result["output"],
299
+ result["created"],
300
+ result["updated"],
301
+ )
302
+ else:
303
+ failed_conversions += 1
304
+ if batch_mode:
305
+ logger.error("Failed to convert %s: %s", result["file"], result["error"])
306
+ else:
307
+ error_and_exit(f"Failed to convert SARIF file: {result['error']}")
308
+ return successful_conversions, failed_conversions
309
+
310
+ def _map_sarif_to_integration_findings(
311
+ self,
312
+ converted_data: list[dict[str, Any]],
313
+ ) -> tuple[int, int]:
314
+ """
315
+ Map converted SARIF data to a list of IntegrationFinding objects
316
+
317
+ :param Any converted_data: The converted SARIF data from the API
318
+ :return: Number of created and updated vulnerabilities
319
+ :rtype: tuple[int, int]
320
+ """
321
+ create_vuln_count = 0
322
+ updated_vuln_count = 0
323
+ # Convert the nested dictionary to a list of ApplicationSecurityPostureFinding objects for easier parsing
324
+ with self.progress as progress:
325
+ task = progress.add_task("Mapping Sarif data to RegScale vulnerabilities...", total=len(converted_data))
326
+ vulneribility_task = progress.add_task("Creating vulnerabilities and mappings...", total=0)
327
+ for result in converted_data:
328
+ name = self._parse_name(result)
329
+ findings = ApplicationSecurityPostureFinding(**result).vulnerabilities
330
+ progress.update(vulneribility_task, total=len(findings))
331
+ for finding in findings:
332
+ created, vuln = Vulnerability(
333
+ title=finding.title,
334
+ severity=Issue.assign_severity(finding.severity), # type: ignore
335
+ description=self._build_description(finding),
336
+ status=IssueStatus.Open,
337
+ firstSeen=self.scan_date,
338
+ lastSeen=self.scan_date,
339
+ plugInName=name,
340
+ plugInId=self._parse_plugin_id(finding),
341
+ parentId=self.parent_id,
342
+ parentModule=self.parent_module,
343
+ ).create_or_update_with_status()
344
+ self.update_scan_history_count(vuln.severity)
345
+ if created:
346
+ create_vuln_count += 1
347
+ else:
348
+ updated_vuln_count += 1
349
+ # Map vulnerability to asset
350
+ VulnerabilityMapping(
351
+ assetId=self.asset.id,
352
+ vulnerabilityId=vuln.id,
353
+ scanId=self.scan_history.id,
354
+ securityPlanId=self.asset.parentId if self.asset.parentModule == "securityplans" else None,
355
+ status=vuln.status,
356
+ firstSeen=vuln.firstSeen,
357
+ lastSeen=vuln.lastSeen,
358
+ dateCreated=self.scan_date,
359
+ ).create_or_update()
360
+ progress.update(vulneribility_task, advance=1)
361
+ progress.update(task, advance=1)
362
+ return create_vuln_count, updated_vuln_count
363
+
364
+ @staticmethod
365
+ def _parse_name(result: Dict[str, Any]) -> str:
366
+ """
367
+ Parse the name and version from the result metadata
368
+
369
+ :param Dict[str, Any] result: The result to parse the name and version from
370
+ :return: The name and version string
371
+ :rtype: str
372
+ """
373
+ if metadata := result["metadata"]:
374
+ name = metadata.get("product", {}).get("name", "Static Scanner")
375
+ version = metadata.get("version")
376
+ else:
377
+ name = "Static Scanner"
378
+ version = None
379
+ name = f"{name} {version}" if version else name
380
+ return name
381
+
382
+ @staticmethod
383
+ def _parse_plugin_id(finding: OcsfVuln) -> str:
384
+ """
385
+ Parse the plugin ID from the finding
386
+
387
+ :param ocsf_vuln finding: The finding to parse the plugin ID from
388
+ :return: The plugin ID string
389
+ :rtype: str
390
+ """
391
+ if cwe := getattr(finding, "cwe"):
392
+ return cwe.uid
393
+ elif cve := getattr(finding, "cve"):
394
+ return cve.uid
395
+ return finding.title
396
+
397
+ def update_scan_history_count(self, severity: str) -> None:
398
+ """
399
+ Update the scan history count for a given severity
400
+
401
+ :param str severity: The severity of the vulnerability
402
+ :rtype: None
403
+ """
404
+ if "Low" in severity:
405
+ self.scan_history.vLow += 1
406
+ elif "Moderate" in severity:
407
+ self.scan_history.vMedium += 1
408
+ elif "High" in severity:
409
+ self.scan_history.vHigh += 1
410
+ elif "Critical" in severity:
411
+ self.scan_history.vCritical += 1
412
+ else:
413
+ self.scan_history.vInfo += 1
414
+
415
+ @staticmethod
416
+ def _build_description(finding: OcsfVuln) -> str:
417
+ """
418
+ Build a detailed description for a vulnerability finding
419
+
420
+ :param ocsf_vuln finding: The finding to build the description for
421
+ :return: Detailed description string
422
+ :rtype: str
423
+ """
424
+ description = f"{finding.desc}\n"
425
+ for affected_code in getattr(finding, "affected_code", []):
426
+
427
+ for key, value in affected_code.dict().items():
428
+ if isinstance(value, dict):
429
+ for sub_key, sub_value in value.items():
430
+ description += f"file {sub_key}: {sub_value}\n"
431
+ description += f"{key}: {value}\n"
432
+ return description
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """SARIF Converter integration for RegScale CLI"""
4
+
5
+ import datetime
6
+ import logging
7
+ from typing import Optional
8
+
9
+ import click
10
+ from pathlib import Path
11
+
12
+ from regscale.core.app.utils.app_utils import (
13
+ get_current_datetime,
14
+ )
15
+
16
+ logger = logging.getLogger("regscale")
17
+
18
+
19
+ @click.group()
20
+ def sarif():
21
+ """Convert SARIF files to OCSF data using an API converter."""
22
+
23
+
24
+ @sarif.command(name="import")
25
+ @click.option(
26
+ "--file_path",
27
+ "-f",
28
+ type=click.Path(exists=True, file_okay=True, dir_okay=True, path_type=Path),
29
+ help="Path to the SARIF file or a directory of files to convert",
30
+ prompt="Enter the path",
31
+ required=True,
32
+ )
33
+ @click.option(
34
+ "--asset_id",
35
+ "-id",
36
+ type=click.INT,
37
+ help="The RegScale Asset ID # to import the findings to.",
38
+ prompt="RegScale Asset ID",
39
+ required=True,
40
+ )
41
+ @click.option(
42
+ "--scan_date",
43
+ "-sd",
44
+ type=click.DateTime(formats=["%Y-%m-%d"]),
45
+ help="The scan date of the file.",
46
+ required=False,
47
+ default=get_current_datetime("%Y-%m-%d"),
48
+ )
49
+ def import_sarif(file_path: Path, asset_id: int, scan_date: Optional[datetime.datetime] = None) -> None:
50
+ """Convert a SARIF file(s) to OCSF format using an API converter."""
51
+ process_sarif_files(file_path, asset_id, scan_date)
52
+
53
+
54
+ def process_sarif_files(file_path: Path, asset_id: int, scan_date: Optional[datetime.datetime]) -> None:
55
+ """
56
+ Process SARIF files for import.
57
+
58
+ :param Path file_path: Path to the SARIF file or directory of files
59
+ :param int asset_id: The RegScale Asset ID to import the findings to
60
+ :param Optional[datetime.datetime] scan_date: The scan date of the file
61
+ :return: None
62
+ """
63
+ from regscale.integrations.commercial.sarif.sairf_importer import SarifImporter
64
+
65
+ if not scan_date:
66
+ scan_date = get_current_datetime()
67
+ SarifImporter(file_path, asset_id, scan_date=scan_date)