regscale-cli 6.16.0.0__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.

Potentially problematic release.


This version of regscale-cli might be problematic. Click here for more details.

Files changed (481) hide show
  1. regscale/__init__.py +1 -0
  2. regscale/airflow/__init__.py +9 -0
  3. regscale/airflow/azure/__init__.py +9 -0
  4. regscale/airflow/azure/cli.py +89 -0
  5. regscale/airflow/azure/upload_dags.py +116 -0
  6. regscale/airflow/click_dags.py +127 -0
  7. regscale/airflow/click_mixins.py +82 -0
  8. regscale/airflow/config.py +25 -0
  9. regscale/airflow/factories/__init__.py +0 -0
  10. regscale/airflow/factories/connections.py +58 -0
  11. regscale/airflow/factories/workflows.py +78 -0
  12. regscale/airflow/hierarchy.py +88 -0
  13. regscale/airflow/operators/__init__.py +0 -0
  14. regscale/airflow/operators/click.py +36 -0
  15. regscale/airflow/sensors/__init__.py +0 -0
  16. regscale/airflow/sensors/sql.py +107 -0
  17. regscale/airflow/sessions/__init__.py +0 -0
  18. regscale/airflow/sessions/sql/__init__.py +3 -0
  19. regscale/airflow/sessions/sql/queries.py +64 -0
  20. regscale/airflow/sessions/sql/sql_server_queries.py +248 -0
  21. regscale/airflow/tasks/__init__.py +0 -0
  22. regscale/airflow/tasks/branches.py +22 -0
  23. regscale/airflow/tasks/cli.py +116 -0
  24. regscale/airflow/tasks/click.py +73 -0
  25. regscale/airflow/tasks/debugging.py +9 -0
  26. regscale/airflow/tasks/groups.py +116 -0
  27. regscale/airflow/tasks/init.py +60 -0
  28. regscale/airflow/tasks/states.py +47 -0
  29. regscale/airflow/tasks/workflows.py +36 -0
  30. regscale/ansible/__init__.py +9 -0
  31. regscale/core/__init__.py +0 -0
  32. regscale/core/app/__init__.py +3 -0
  33. regscale/core/app/api.py +571 -0
  34. regscale/core/app/application.py +665 -0
  35. regscale/core/app/internal/__init__.py +136 -0
  36. regscale/core/app/internal/admin_actions.py +230 -0
  37. regscale/core/app/internal/assessments_editor.py +873 -0
  38. regscale/core/app/internal/catalog.py +316 -0
  39. regscale/core/app/internal/comparison.py +459 -0
  40. regscale/core/app/internal/control_editor.py +571 -0
  41. regscale/core/app/internal/encrypt.py +79 -0
  42. regscale/core/app/internal/evidence.py +1240 -0
  43. regscale/core/app/internal/file_uploads.py +151 -0
  44. regscale/core/app/internal/healthcheck.py +66 -0
  45. regscale/core/app/internal/login.py +305 -0
  46. regscale/core/app/internal/migrations.py +240 -0
  47. regscale/core/app/internal/model_editor.py +1701 -0
  48. regscale/core/app/internal/poam_editor.py +632 -0
  49. regscale/core/app/internal/workflow.py +105 -0
  50. regscale/core/app/logz.py +74 -0
  51. regscale/core/app/utils/XMLIR.py +258 -0
  52. regscale/core/app/utils/__init__.py +0 -0
  53. regscale/core/app/utils/api_handler.py +358 -0
  54. regscale/core/app/utils/app_utils.py +1110 -0
  55. regscale/core/app/utils/catalog_utils/__init__.py +0 -0
  56. regscale/core/app/utils/catalog_utils/common.py +91 -0
  57. regscale/core/app/utils/catalog_utils/compare_catalog.py +193 -0
  58. regscale/core/app/utils/catalog_utils/diagnostic_catalog.py +97 -0
  59. regscale/core/app/utils/catalog_utils/download_catalog.py +103 -0
  60. regscale/core/app/utils/catalog_utils/update_catalog.py +718 -0
  61. regscale/core/app/utils/catalog_utils/update_catalog_v2.py +1378 -0
  62. regscale/core/app/utils/catalog_utils/update_catalog_v3.py +1272 -0
  63. regscale/core/app/utils/catalog_utils/update_plans.py +334 -0
  64. regscale/core/app/utils/file_utils.py +238 -0
  65. regscale/core/app/utils/parser_utils.py +81 -0
  66. regscale/core/app/utils/pickle_file_handler.py +57 -0
  67. regscale/core/app/utils/regscale_utils.py +319 -0
  68. regscale/core/app/utils/report_utils.py +119 -0
  69. regscale/core/app/utils/variables.py +226 -0
  70. regscale/core/decorators.py +31 -0
  71. regscale/core/lazy_group.py +65 -0
  72. regscale/core/login.py +63 -0
  73. regscale/core/server/__init__.py +0 -0
  74. regscale/core/server/flask_api.py +473 -0
  75. regscale/core/server/helpers.py +373 -0
  76. regscale/core/server/rest.py +64 -0
  77. regscale/core/server/static/css/bootstrap.css +6030 -0
  78. regscale/core/server/static/css/bootstrap.min.css +6 -0
  79. regscale/core/server/static/css/main.css +176 -0
  80. regscale/core/server/static/images/regscale-cli.svg +49 -0
  81. regscale/core/server/static/images/regscale.svg +38 -0
  82. regscale/core/server/templates/base.html +74 -0
  83. regscale/core/server/templates/index.html +43 -0
  84. regscale/core/server/templates/login.html +28 -0
  85. regscale/core/server/templates/make_base64.html +22 -0
  86. regscale/core/server/templates/upload_STIG.html +109 -0
  87. regscale/core/server/templates/upload_STIG_result.html +26 -0
  88. regscale/core/server/templates/upload_ssp.html +144 -0
  89. regscale/core/server/templates/upload_ssp_result.html +128 -0
  90. regscale/core/static/__init__.py +0 -0
  91. regscale/core/static/regex.py +14 -0
  92. regscale/core/utils/__init__.py +117 -0
  93. regscale/core/utils/click_utils.py +13 -0
  94. regscale/core/utils/date.py +238 -0
  95. regscale/core/utils/graphql.py +254 -0
  96. regscale/core/utils/urls.py +23 -0
  97. regscale/dev/__init__.py +6 -0
  98. regscale/dev/analysis.py +454 -0
  99. regscale/dev/cli.py +235 -0
  100. regscale/dev/code_gen.py +492 -0
  101. regscale/dev/dirs.py +69 -0
  102. regscale/dev/docs.py +384 -0
  103. regscale/dev/monitoring.py +26 -0
  104. regscale/dev/profiling.py +216 -0
  105. regscale/exceptions/__init__.py +4 -0
  106. regscale/exceptions/license_exception.py +7 -0
  107. regscale/exceptions/validation_exception.py +9 -0
  108. regscale/integrations/__init__.py +1 -0
  109. regscale/integrations/commercial/__init__.py +486 -0
  110. regscale/integrations/commercial/ad.py +433 -0
  111. regscale/integrations/commercial/amazon/__init__.py +0 -0
  112. regscale/integrations/commercial/amazon/common.py +106 -0
  113. regscale/integrations/commercial/aqua/__init__.py +0 -0
  114. regscale/integrations/commercial/aqua/aqua.py +91 -0
  115. regscale/integrations/commercial/aws/__init__.py +6 -0
  116. regscale/integrations/commercial/aws/cli.py +322 -0
  117. regscale/integrations/commercial/aws/inventory/__init__.py +110 -0
  118. regscale/integrations/commercial/aws/inventory/base.py +64 -0
  119. regscale/integrations/commercial/aws/inventory/resources/__init__.py +19 -0
  120. regscale/integrations/commercial/aws/inventory/resources/compute.py +234 -0
  121. regscale/integrations/commercial/aws/inventory/resources/containers.py +113 -0
  122. regscale/integrations/commercial/aws/inventory/resources/database.py +101 -0
  123. regscale/integrations/commercial/aws/inventory/resources/integration.py +237 -0
  124. regscale/integrations/commercial/aws/inventory/resources/networking.py +253 -0
  125. regscale/integrations/commercial/aws/inventory/resources/security.py +240 -0
  126. regscale/integrations/commercial/aws/inventory/resources/storage.py +91 -0
  127. regscale/integrations/commercial/aws/scanner.py +823 -0
  128. regscale/integrations/commercial/azure/__init__.py +0 -0
  129. regscale/integrations/commercial/azure/common.py +32 -0
  130. regscale/integrations/commercial/azure/intune.py +488 -0
  131. regscale/integrations/commercial/azure/scanner.py +49 -0
  132. regscale/integrations/commercial/burp.py +78 -0
  133. regscale/integrations/commercial/cpe.py +144 -0
  134. regscale/integrations/commercial/crowdstrike.py +1117 -0
  135. regscale/integrations/commercial/defender.py +1511 -0
  136. regscale/integrations/commercial/dependabot.py +210 -0
  137. regscale/integrations/commercial/durosuite/__init__.py +0 -0
  138. regscale/integrations/commercial/durosuite/api.py +1546 -0
  139. regscale/integrations/commercial/durosuite/process_devices.py +101 -0
  140. regscale/integrations/commercial/durosuite/scanner.py +637 -0
  141. regscale/integrations/commercial/durosuite/variables.py +21 -0
  142. regscale/integrations/commercial/ecr.py +90 -0
  143. regscale/integrations/commercial/gcp/__init__.py +237 -0
  144. regscale/integrations/commercial/gcp/auth.py +96 -0
  145. regscale/integrations/commercial/gcp/control_tests.py +238 -0
  146. regscale/integrations/commercial/gcp/variables.py +18 -0
  147. regscale/integrations/commercial/gitlab.py +332 -0
  148. regscale/integrations/commercial/grype.py +165 -0
  149. regscale/integrations/commercial/ibm.py +90 -0
  150. regscale/integrations/commercial/import_all/__init__.py +0 -0
  151. regscale/integrations/commercial/import_all/import_all_cmd.py +467 -0
  152. regscale/integrations/commercial/import_all/scan_file_fingerprints.json +27 -0
  153. regscale/integrations/commercial/jira.py +1046 -0
  154. regscale/integrations/commercial/mappings/__init__.py +0 -0
  155. regscale/integrations/commercial/mappings/csf_controls.json +713 -0
  156. regscale/integrations/commercial/mappings/nist_800_53_r5_controls.json +1516 -0
  157. regscale/integrations/commercial/nessus/__init__.py +0 -0
  158. regscale/integrations/commercial/nessus/nessus_utils.py +429 -0
  159. regscale/integrations/commercial/nessus/scanner.py +416 -0
  160. regscale/integrations/commercial/nexpose.py +90 -0
  161. regscale/integrations/commercial/okta.py +798 -0
  162. regscale/integrations/commercial/opentext/__init__.py +0 -0
  163. regscale/integrations/commercial/opentext/click.py +99 -0
  164. regscale/integrations/commercial/opentext/scanner.py +143 -0
  165. regscale/integrations/commercial/prisma.py +91 -0
  166. regscale/integrations/commercial/qualys.py +1462 -0
  167. regscale/integrations/commercial/salesforce.py +980 -0
  168. regscale/integrations/commercial/sap/__init__.py +0 -0
  169. regscale/integrations/commercial/sap/click.py +31 -0
  170. regscale/integrations/commercial/sap/sysdig/__init__.py +0 -0
  171. regscale/integrations/commercial/sap/sysdig/click.py +57 -0
  172. regscale/integrations/commercial/sap/sysdig/sysdig_scanner.py +190 -0
  173. regscale/integrations/commercial/sap/tenable/__init__.py +0 -0
  174. regscale/integrations/commercial/sap/tenable/click.py +49 -0
  175. regscale/integrations/commercial/sap/tenable/scanner.py +196 -0
  176. regscale/integrations/commercial/servicenow.py +1756 -0
  177. regscale/integrations/commercial/sicura/__init__.py +0 -0
  178. regscale/integrations/commercial/sicura/api.py +855 -0
  179. regscale/integrations/commercial/sicura/commands.py +73 -0
  180. regscale/integrations/commercial/sicura/scanner.py +481 -0
  181. regscale/integrations/commercial/sicura/variables.py +16 -0
  182. regscale/integrations/commercial/snyk.py +90 -0
  183. regscale/integrations/commercial/sonarcloud.py +260 -0
  184. regscale/integrations/commercial/sqlserver.py +369 -0
  185. regscale/integrations/commercial/stig_mapper_integration/__init__.py +0 -0
  186. regscale/integrations/commercial/stig_mapper_integration/click_commands.py +38 -0
  187. regscale/integrations/commercial/stig_mapper_integration/mapping_engine.py +353 -0
  188. regscale/integrations/commercial/stigv2/__init__.py +0 -0
  189. regscale/integrations/commercial/stigv2/ckl_parser.py +349 -0
  190. regscale/integrations/commercial/stigv2/click_commands.py +95 -0
  191. regscale/integrations/commercial/stigv2/stig_integration.py +202 -0
  192. regscale/integrations/commercial/synqly/__init__.py +0 -0
  193. regscale/integrations/commercial/synqly/assets.py +46 -0
  194. regscale/integrations/commercial/synqly/ticketing.py +132 -0
  195. regscale/integrations/commercial/synqly/vulnerabilities.py +223 -0
  196. regscale/integrations/commercial/synqly_jira.py +840 -0
  197. regscale/integrations/commercial/tenablev2/__init__.py +0 -0
  198. regscale/integrations/commercial/tenablev2/authenticate.py +31 -0
  199. regscale/integrations/commercial/tenablev2/click.py +1584 -0
  200. regscale/integrations/commercial/tenablev2/scanner.py +504 -0
  201. regscale/integrations/commercial/tenablev2/stig_parsers.py +140 -0
  202. regscale/integrations/commercial/tenablev2/utils.py +78 -0
  203. regscale/integrations/commercial/tenablev2/variables.py +17 -0
  204. regscale/integrations/commercial/trivy.py +162 -0
  205. regscale/integrations/commercial/veracode.py +96 -0
  206. regscale/integrations/commercial/wizv2/WizDataMixin.py +97 -0
  207. regscale/integrations/commercial/wizv2/__init__.py +0 -0
  208. regscale/integrations/commercial/wizv2/click.py +429 -0
  209. regscale/integrations/commercial/wizv2/constants.py +1001 -0
  210. regscale/integrations/commercial/wizv2/issue.py +361 -0
  211. regscale/integrations/commercial/wizv2/models.py +112 -0
  212. regscale/integrations/commercial/wizv2/parsers.py +339 -0
  213. regscale/integrations/commercial/wizv2/sbom.py +115 -0
  214. regscale/integrations/commercial/wizv2/scanner.py +416 -0
  215. regscale/integrations/commercial/wizv2/utils.py +796 -0
  216. regscale/integrations/commercial/wizv2/variables.py +39 -0
  217. regscale/integrations/commercial/wizv2/wiz_auth.py +159 -0
  218. regscale/integrations/commercial/xray.py +91 -0
  219. regscale/integrations/integration/__init__.py +2 -0
  220. regscale/integrations/integration/integration.py +26 -0
  221. regscale/integrations/integration/inventory.py +17 -0
  222. regscale/integrations/integration/issue.py +100 -0
  223. regscale/integrations/integration_override.py +149 -0
  224. regscale/integrations/public/__init__.py +103 -0
  225. regscale/integrations/public/cisa.py +641 -0
  226. regscale/integrations/public/criticality_updater.py +70 -0
  227. regscale/integrations/public/emass.py +411 -0
  228. regscale/integrations/public/emass_slcm_import.py +697 -0
  229. regscale/integrations/public/fedramp/__init__.py +0 -0
  230. regscale/integrations/public/fedramp/appendix_parser.py +548 -0
  231. regscale/integrations/public/fedramp/click.py +479 -0
  232. regscale/integrations/public/fedramp/components.py +714 -0
  233. regscale/integrations/public/fedramp/docx_parser.py +259 -0
  234. regscale/integrations/public/fedramp/fedramp_cis_crm.py +1124 -0
  235. regscale/integrations/public/fedramp/fedramp_common.py +3181 -0
  236. regscale/integrations/public/fedramp/fedramp_docx.py +388 -0
  237. regscale/integrations/public/fedramp/fedramp_five.py +2343 -0
  238. regscale/integrations/public/fedramp/fedramp_traversal.py +138 -0
  239. regscale/integrations/public/fedramp/import_fedramp_r4_ssp.py +279 -0
  240. regscale/integrations/public/fedramp/import_workbook.py +495 -0
  241. regscale/integrations/public/fedramp/inventory_items.py +244 -0
  242. regscale/integrations/public/fedramp/mappings/__init__.py +0 -0
  243. regscale/integrations/public/fedramp/mappings/fedramp_r4_parts.json +7388 -0
  244. regscale/integrations/public/fedramp/mappings/fedramp_r5_params.json +8636 -0
  245. regscale/integrations/public/fedramp/mappings/fedramp_r5_parts.json +9605 -0
  246. regscale/integrations/public/fedramp/mappings/system_roles.py +34 -0
  247. regscale/integrations/public/fedramp/mappings/user.py +175 -0
  248. regscale/integrations/public/fedramp/mappings/values.py +141 -0
  249. regscale/integrations/public/fedramp/markdown_parser.py +150 -0
  250. regscale/integrations/public/fedramp/metadata.py +689 -0
  251. regscale/integrations/public/fedramp/models/__init__.py +59 -0
  252. regscale/integrations/public/fedramp/models/leveraged_auth_new.py +168 -0
  253. regscale/integrations/public/fedramp/models/poam_importer.py +522 -0
  254. regscale/integrations/public/fedramp/parts_mapper.py +107 -0
  255. regscale/integrations/public/fedramp/poam/__init__.py +0 -0
  256. regscale/integrations/public/fedramp/poam/scanner.py +851 -0
  257. regscale/integrations/public/fedramp/properties.py +201 -0
  258. regscale/integrations/public/fedramp/reporting.py +84 -0
  259. regscale/integrations/public/fedramp/resources.py +496 -0
  260. regscale/integrations/public/fedramp/rosetta.py +110 -0
  261. regscale/integrations/public/fedramp/ssp_logger.py +87 -0
  262. regscale/integrations/public/fedramp/system_characteristics.py +922 -0
  263. regscale/integrations/public/fedramp/system_control_implementations.py +582 -0
  264. regscale/integrations/public/fedramp/system_implementation.py +190 -0
  265. regscale/integrations/public/fedramp/xml_utils.py +87 -0
  266. regscale/integrations/public/nist_catalog.py +275 -0
  267. regscale/integrations/public/oscal.py +1946 -0
  268. regscale/integrations/public/otx.py +169 -0
  269. regscale/integrations/scanner_integration.py +2692 -0
  270. regscale/integrations/variables.py +25 -0
  271. regscale/models/__init__.py +7 -0
  272. regscale/models/app_models/__init__.py +5 -0
  273. regscale/models/app_models/catalog_compare.py +213 -0
  274. regscale/models/app_models/click.py +252 -0
  275. regscale/models/app_models/datetime_encoder.py +21 -0
  276. regscale/models/app_models/import_validater.py +321 -0
  277. regscale/models/app_models/mapping.py +260 -0
  278. regscale/models/app_models/pipeline.py +37 -0
  279. regscale/models/click_models.py +413 -0
  280. regscale/models/config.py +154 -0
  281. regscale/models/email_style.css +67 -0
  282. regscale/models/hierarchy.py +8 -0
  283. regscale/models/inspect_models.py +79 -0
  284. regscale/models/integration_models/__init__.py +0 -0
  285. regscale/models/integration_models/amazon_models/__init__.py +0 -0
  286. regscale/models/integration_models/amazon_models/inspector.py +262 -0
  287. regscale/models/integration_models/amazon_models/inspector_scan.py +206 -0
  288. regscale/models/integration_models/aqua.py +247 -0
  289. regscale/models/integration_models/azure_alerts.py +255 -0
  290. regscale/models/integration_models/base64.py +23 -0
  291. regscale/models/integration_models/burp.py +433 -0
  292. regscale/models/integration_models/burp_models.py +128 -0
  293. regscale/models/integration_models/cisa_kev_data.json +19333 -0
  294. regscale/models/integration_models/defender_data.py +93 -0
  295. regscale/models/integration_models/defenderimport.py +143 -0
  296. regscale/models/integration_models/drf.py +443 -0
  297. regscale/models/integration_models/ecr_models/__init__.py +0 -0
  298. regscale/models/integration_models/ecr_models/data.py +69 -0
  299. regscale/models/integration_models/ecr_models/ecr.py +239 -0
  300. regscale/models/integration_models/flat_file_importer.py +1079 -0
  301. regscale/models/integration_models/grype_import.py +247 -0
  302. regscale/models/integration_models/ibm.py +126 -0
  303. regscale/models/integration_models/implementation_results.py +85 -0
  304. regscale/models/integration_models/nexpose.py +140 -0
  305. regscale/models/integration_models/prisma.py +202 -0
  306. regscale/models/integration_models/qualys.py +720 -0
  307. regscale/models/integration_models/qualys_scanner.py +160 -0
  308. regscale/models/integration_models/sbom/__init__.py +0 -0
  309. regscale/models/integration_models/sbom/cyclone_dx.py +139 -0
  310. regscale/models/integration_models/send_reminders.py +620 -0
  311. regscale/models/integration_models/snyk.py +155 -0
  312. regscale/models/integration_models/synqly_models/__init__.py +0 -0
  313. regscale/models/integration_models/synqly_models/capabilities.json +1 -0
  314. regscale/models/integration_models/synqly_models/connector_types.py +22 -0
  315. regscale/models/integration_models/synqly_models/connectors/__init__.py +7 -0
  316. regscale/models/integration_models/synqly_models/connectors/assets.py +97 -0
  317. regscale/models/integration_models/synqly_models/connectors/ticketing.py +583 -0
  318. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +169 -0
  319. regscale/models/integration_models/synqly_models/ocsf_mapper.py +331 -0
  320. regscale/models/integration_models/synqly_models/param.py +72 -0
  321. regscale/models/integration_models/synqly_models/synqly_model.py +733 -0
  322. regscale/models/integration_models/synqly_models/tenants.py +39 -0
  323. regscale/models/integration_models/tenable_models/__init__.py +0 -0
  324. regscale/models/integration_models/tenable_models/integration.py +187 -0
  325. regscale/models/integration_models/tenable_models/models.py +513 -0
  326. regscale/models/integration_models/trivy_import.py +231 -0
  327. regscale/models/integration_models/veracode.py +217 -0
  328. regscale/models/integration_models/xray.py +135 -0
  329. regscale/models/locking.py +100 -0
  330. regscale/models/platform.py +110 -0
  331. regscale/models/regscale_models/__init__.py +67 -0
  332. regscale/models/regscale_models/assessment.py +570 -0
  333. regscale/models/regscale_models/assessment_plan.py +52 -0
  334. regscale/models/regscale_models/asset.py +567 -0
  335. regscale/models/regscale_models/asset_mapping.py +190 -0
  336. regscale/models/regscale_models/case.py +42 -0
  337. regscale/models/regscale_models/catalog.py +261 -0
  338. regscale/models/regscale_models/cci.py +46 -0
  339. regscale/models/regscale_models/change.py +167 -0
  340. regscale/models/regscale_models/checklist.py +372 -0
  341. regscale/models/regscale_models/comment.py +49 -0
  342. regscale/models/regscale_models/compliance_settings.py +112 -0
  343. regscale/models/regscale_models/component.py +412 -0
  344. regscale/models/regscale_models/component_mapping.py +65 -0
  345. regscale/models/regscale_models/control.py +38 -0
  346. regscale/models/regscale_models/control_implementation.py +1128 -0
  347. regscale/models/regscale_models/control_objective.py +261 -0
  348. regscale/models/regscale_models/control_parameter.py +100 -0
  349. regscale/models/regscale_models/control_test.py +34 -0
  350. regscale/models/regscale_models/control_test_plan.py +75 -0
  351. regscale/models/regscale_models/control_test_result.py +52 -0
  352. regscale/models/regscale_models/custom_field.py +245 -0
  353. regscale/models/regscale_models/data.py +109 -0
  354. regscale/models/regscale_models/data_center.py +40 -0
  355. regscale/models/regscale_models/deviation.py +203 -0
  356. regscale/models/regscale_models/email.py +97 -0
  357. regscale/models/regscale_models/evidence.py +47 -0
  358. regscale/models/regscale_models/evidence_mapping.py +40 -0
  359. regscale/models/regscale_models/facility.py +59 -0
  360. regscale/models/regscale_models/file.py +382 -0
  361. regscale/models/regscale_models/filetag.py +37 -0
  362. regscale/models/regscale_models/form_field_value.py +94 -0
  363. regscale/models/regscale_models/group.py +169 -0
  364. regscale/models/regscale_models/implementation_objective.py +335 -0
  365. regscale/models/regscale_models/implementation_option.py +275 -0
  366. regscale/models/regscale_models/implementation_role.py +33 -0
  367. regscale/models/regscale_models/incident.py +177 -0
  368. regscale/models/regscale_models/interconnection.py +43 -0
  369. regscale/models/regscale_models/issue.py +1176 -0
  370. regscale/models/regscale_models/leveraged_authorization.py +125 -0
  371. regscale/models/regscale_models/line_of_inquiry.py +52 -0
  372. regscale/models/regscale_models/link.py +205 -0
  373. regscale/models/regscale_models/meta_data.py +64 -0
  374. regscale/models/regscale_models/mixins/__init__.py +0 -0
  375. regscale/models/regscale_models/mixins/parent_cache.py +124 -0
  376. regscale/models/regscale_models/module.py +224 -0
  377. regscale/models/regscale_models/modules.py +191 -0
  378. regscale/models/regscale_models/objective.py +14 -0
  379. regscale/models/regscale_models/parameter.py +87 -0
  380. regscale/models/regscale_models/ports_protocol.py +81 -0
  381. regscale/models/regscale_models/privacy.py +89 -0
  382. regscale/models/regscale_models/profile.py +50 -0
  383. regscale/models/regscale_models/profile_link.py +68 -0
  384. regscale/models/regscale_models/profile_mapping.py +124 -0
  385. regscale/models/regscale_models/project.py +63 -0
  386. regscale/models/regscale_models/property.py +278 -0
  387. regscale/models/regscale_models/question.py +85 -0
  388. regscale/models/regscale_models/questionnaire.py +87 -0
  389. regscale/models/regscale_models/questionnaire_instance.py +177 -0
  390. regscale/models/regscale_models/rbac.py +132 -0
  391. regscale/models/regscale_models/reference.py +86 -0
  392. regscale/models/regscale_models/regscale_model.py +1643 -0
  393. regscale/models/regscale_models/requirement.py +29 -0
  394. regscale/models/regscale_models/risk.py +274 -0
  395. regscale/models/regscale_models/sbom.py +54 -0
  396. regscale/models/regscale_models/scan_history.py +436 -0
  397. regscale/models/regscale_models/search.py +53 -0
  398. regscale/models/regscale_models/security_control.py +132 -0
  399. regscale/models/regscale_models/security_plan.py +204 -0
  400. regscale/models/regscale_models/software_inventory.py +159 -0
  401. regscale/models/regscale_models/stake_holder.py +64 -0
  402. regscale/models/regscale_models/stig.py +647 -0
  403. regscale/models/regscale_models/supply_chain.py +152 -0
  404. regscale/models/regscale_models/system_role.py +188 -0
  405. regscale/models/regscale_models/system_role_external_assignment.py +40 -0
  406. regscale/models/regscale_models/tag.py +37 -0
  407. regscale/models/regscale_models/tag_mapping.py +19 -0
  408. regscale/models/regscale_models/task.py +133 -0
  409. regscale/models/regscale_models/threat.py +196 -0
  410. regscale/models/regscale_models/user.py +175 -0
  411. regscale/models/regscale_models/user_group.py +55 -0
  412. regscale/models/regscale_models/vulnerability.py +242 -0
  413. regscale/models/regscale_models/vulnerability_mapping.py +162 -0
  414. regscale/models/regscale_models/workflow.py +55 -0
  415. regscale/models/regscale_models/workflow_action.py +34 -0
  416. regscale/models/regscale_models/workflow_instance.py +269 -0
  417. regscale/models/regscale_models/workflow_instance_step.py +114 -0
  418. regscale/models/regscale_models/workflow_template.py +58 -0
  419. regscale/models/regscale_models/workflow_template_step.py +45 -0
  420. regscale/regscale.py +815 -0
  421. regscale/utils/__init__.py +7 -0
  422. regscale/utils/b64conversion.py +14 -0
  423. regscale/utils/click_utils.py +118 -0
  424. regscale/utils/decorators.py +48 -0
  425. regscale/utils/dict_utils.py +59 -0
  426. regscale/utils/files.py +79 -0
  427. regscale/utils/fxns.py +30 -0
  428. regscale/utils/graphql_client.py +113 -0
  429. regscale/utils/lists.py +16 -0
  430. regscale/utils/numbers.py +12 -0
  431. regscale/utils/shell.py +148 -0
  432. regscale/utils/string.py +121 -0
  433. regscale/utils/synqly_utils.py +165 -0
  434. regscale/utils/threading/__init__.py +8 -0
  435. regscale/utils/threading/threadhandler.py +131 -0
  436. regscale/utils/threading/threadsafe_counter.py +47 -0
  437. regscale/utils/threading/threadsafe_dict.py +242 -0
  438. regscale/utils/threading/threadsafe_list.py +83 -0
  439. regscale/utils/version.py +104 -0
  440. regscale/validation/__init__.py +0 -0
  441. regscale/validation/address.py +37 -0
  442. regscale/validation/record.py +48 -0
  443. regscale/visualization/__init__.py +5 -0
  444. regscale/visualization/click.py +34 -0
  445. regscale_cli-6.16.0.0.dist-info/LICENSE +21 -0
  446. regscale_cli-6.16.0.0.dist-info/METADATA +659 -0
  447. regscale_cli-6.16.0.0.dist-info/RECORD +481 -0
  448. regscale_cli-6.16.0.0.dist-info/WHEEL +5 -0
  449. regscale_cli-6.16.0.0.dist-info/entry_points.txt +6 -0
  450. regscale_cli-6.16.0.0.dist-info/top_level.txt +2 -0
  451. tests/fixtures/__init__.py +2 -0
  452. tests/fixtures/api.py +87 -0
  453. tests/fixtures/models.py +91 -0
  454. tests/fixtures/test_fixture.py +144 -0
  455. tests/mocks/__init__.py +0 -0
  456. tests/mocks/objects.py +3 -0
  457. tests/mocks/response.py +32 -0
  458. tests/mocks/xml.py +13 -0
  459. tests/regscale/__init__.py +0 -0
  460. tests/regscale/core/__init__.py +0 -0
  461. tests/regscale/core/test_api.py +232 -0
  462. tests/regscale/core/test_app.py +406 -0
  463. tests/regscale/core/test_login.py +37 -0
  464. tests/regscale/core/test_logz.py +66 -0
  465. tests/regscale/core/test_sbom_generator.py +87 -0
  466. tests/regscale/core/test_validation_utils.py +163 -0
  467. tests/regscale/core/test_version.py +78 -0
  468. tests/regscale/models/__init__.py +0 -0
  469. tests/regscale/models/test_asset.py +71 -0
  470. tests/regscale/models/test_config.py +26 -0
  471. tests/regscale/models/test_control_implementation.py +27 -0
  472. tests/regscale/models/test_import.py +97 -0
  473. tests/regscale/models/test_issue.py +36 -0
  474. tests/regscale/models/test_mapping.py +52 -0
  475. tests/regscale/models/test_platform.py +31 -0
  476. tests/regscale/models/test_regscale_model.py +346 -0
  477. tests/regscale/models/test_report.py +32 -0
  478. tests/regscale/models/test_tenable_integrations.py +118 -0
  479. tests/regscale/models/test_user_model.py +121 -0
  480. tests/regscale/test_about.py +19 -0
  481. tests/regscale/test_authorization.py +65 -0
@@ -0,0 +1,796 @@
1
+ import codecs
2
+ import csv
3
+ import datetime
4
+ import json
5
+ import logging
6
+ import time
7
+ import traceback
8
+ from contextlib import closing
9
+ from typing import Dict, List, Any, Optional
10
+ from zipfile import ZipFile
11
+
12
+ import cachetools
13
+ import requests
14
+ from pydantic import ValidationError
15
+
16
+ from regscale.core.app.api import Api
17
+ from regscale.core.app.utils.app_utils import (
18
+ error_and_exit,
19
+ check_file_path,
20
+ get_current_datetime,
21
+ format_dict_to_html,
22
+ create_progress_object,
23
+ )
24
+ from regscale.core.utils.date import datetime_obj
25
+ from regscale.integrations.commercial.wizv2.constants import (
26
+ DOWNLOAD_QUERY,
27
+ BEARER,
28
+ REPORTS_QUERY,
29
+ CONTENT_TYPE,
30
+ RATE_LIMIT_MSG,
31
+ CREATE_REPORT_QUERY,
32
+ MAX_RETRIES,
33
+ CHECK_INTERVAL_FOR_DOWNLOAD_REPORT,
34
+ )
35
+ from regscale.integrations.commercial.wizv2.variables import WizVariables
36
+ from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
37
+ from regscale.models import File, Sbom, SecurityPlan, Catalog, ControlImplementation, Assessment, regscale_models
38
+ from regscale.integrations.commercial.wizv2.models import ComplianceReport, ComplianceCheckStatus
39
+ from regscale.utils import PaginatedGraphQLClient
40
+ from regscale.utils.decorators import deprecated
41
+
42
+ logger = logging.getLogger("regscale")
43
+ compliance_job_progress = create_progress_object()
44
+
45
+
46
+ def get_notes_from_wiz_props(wiz_entity_properties: Dict, external_id: str) -> str:
47
+ """
48
+ Get notes from wiz properties
49
+ :param Dict wiz_entity_properties: Wiz entity properties
50
+ :param str external_id: External ID
51
+ :return: Notes
52
+ :rtype: str
53
+ """
54
+ notes = []
55
+ notes.append(f"External ID: {external_id}") if external_id else None
56
+ (
57
+ notes.append(f"Cloud Platform: {wiz_entity_properties.get('cloudPlatform')}")
58
+ if wiz_entity_properties.get("cloudPlatform")
59
+ else None
60
+ )
61
+ (
62
+ notes.append(f"Provider Unique ID: {wiz_entity_properties.get('providerUniqueId')}")
63
+ if wiz_entity_properties.get("providerUniqueId")
64
+ else None
65
+ )
66
+ (
67
+ notes.append(
68
+ f"""cloudProviderURL:<a href="{wiz_entity_properties.get("cloudProviderURL")}"
69
+ target="_blank">{wiz_entity_properties.get("cloudProviderURL")}</a>"""
70
+ )
71
+ if wiz_entity_properties.get("cloudProviderURL")
72
+ else None
73
+ )
74
+ (
75
+ notes.append(f"Vertex ID: {wiz_entity_properties.get('_vertexID')}")
76
+ if wiz_entity_properties.get("_vertexID")
77
+ else None
78
+ )
79
+ (
80
+ notes.append(f"Severity Name: {wiz_entity_properties.get('severity_name')}")
81
+ if wiz_entity_properties.get("severity_name")
82
+ else None
83
+ )
84
+ (
85
+ notes.append(f"Severity Description: {wiz_entity_properties.get('severity_description')}")
86
+ if wiz_entity_properties.get("severity_description")
87
+ else None
88
+ )
89
+ return "<br>".join(notes)
90
+
91
+
92
+ def handle_management_type(wiz_entity_properties: Dict) -> str:
93
+ """
94
+ Handle management type
95
+ :param Dict wiz_entity_properties: Wiz entity properties
96
+ :return: Management type
97
+ :rtype: str
98
+ """
99
+ return "External/Third Party Managed" if wiz_entity_properties.get("isManaged") else "Internally Managed"
100
+
101
+
102
+ @cachetools.cached(cachetools.TTLCache(maxsize=1024, ttl=3600))
103
+ def create_asset_type(asset_type: str) -> str:
104
+ """
105
+ Create asset type if it does not exist and reformat the string to Title Case ie
106
+ ( "ASSET_TYPE" or "asset_type" -> "Asset Type")
107
+ :param asset_type str Asset_type
108
+ :return: Asset type
109
+ :rtype: str
110
+ """
111
+ #
112
+ asset_type = asset_type.title().replace("_", " ")
113
+ meta_data_list = regscale_models.Metadata.get_metadata_by_module_field(module="assets", field="Asset Type")
114
+ if not any(meta_data.value == asset_type for meta_data in meta_data_list):
115
+ regscale_models.Metadata(
116
+ field="Asset Type",
117
+ module="assets",
118
+ value=asset_type,
119
+ ).create()
120
+ return asset_type
121
+
122
+
123
+ def map_category(asset_string: str) -> regscale_models.AssetCategory:
124
+ """
125
+ category mapper
126
+
127
+ :param str asset_string:
128
+ :return: Category
129
+ :rtype: regscale_models.AssetCategory
130
+ """
131
+ try:
132
+ if asset_string in ["CONTAINER_IMAGE"]:
133
+ return regscale_models.AssetCategory.Software
134
+ return getattr(regscale_models.AssetCategory, asset_string)
135
+ except (KeyError, AttributeError) as ex:
136
+ # why map AssetCategory of everything is software?
137
+ logger.debug("Unable to find %s in AssetType enum \n", ex)
138
+ return regscale_models.AssetCategory.Hardware
139
+
140
+
141
+ def convert_first_seen_to_days(first_seen: str) -> int:
142
+ """
143
+ Converts the first seen date to days
144
+ :param str first_seen: First seen date
145
+ :returns: Days
146
+ :rtype: int
147
+ """
148
+ first_seen_date = datetime_obj(first_seen)
149
+ if not first_seen_date:
150
+ return 0
151
+ first_seen_date_naive = first_seen_date.replace(tzinfo=None)
152
+ return (datetime.datetime.now() - first_seen_date_naive).days
153
+
154
+
155
+ def fetch_report_by_id(
156
+ report_id: str, parent_id: int, report_file_name: str = "evidence_report", report_file_extension: str = "csv"
157
+ ):
158
+ """
159
+ Fetch report by id and add it to evidence
160
+
161
+ :param str report_id: Wiz report ID
162
+ :param int parent_id: RegScale Parent ID
163
+ :param str report_file_name: Report file name, defaults to "evidence_report"
164
+ :param str report_file_extension: Report file extension, defaults to "csv"
165
+ :rtype: None
166
+ """
167
+
168
+ current_datetime = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
169
+ report_file_path = f"artifacts/{report_file_name}_{current_datetime}.{report_file_extension}"
170
+ variables = {"reportId": report_id}
171
+ api_endpoint_url = WizVariables.wizUrl
172
+ token = WizVariables.wizAccessToken
173
+ if not token:
174
+ error_and_exit("Wiz Access Token is missing. Authenticate with Wiz first.")
175
+ client = PaginatedGraphQLClient(
176
+ endpoint=api_endpoint_url,
177
+ query=DOWNLOAD_QUERY,
178
+ headers={
179
+ "Content-Type": "application/json",
180
+ "Authorization": BEARER + token,
181
+ },
182
+ )
183
+ downloaded_report = client.fetch_results(variables=variables)
184
+ logger.debug(f"Download Report result: {downloaded_report}")
185
+ if "errors" in downloaded_report:
186
+ logger.error(f"Error fetching report: {downloaded_report['errors']}")
187
+ logger.error(f"Raw Response Data: {downloaded_report}")
188
+ return
189
+
190
+ if download_url := downloaded_report.get("report", {}).get("lastRun", {}).get("url"):
191
+ logger.info(f"Download URL: {download_url}")
192
+ download_file(url=download_url, local_filename=report_file_path)
193
+ api = Api()
194
+ _ = File.upload_file_to_regscale(
195
+ file_name=str(report_file_path),
196
+ parent_id=parent_id,
197
+ parent_module="evidence",
198
+ api=api,
199
+ )
200
+ logger.info("File uploaded successfully")
201
+ else:
202
+ logger.error("Could not retrieve the download URL.")
203
+
204
+
205
+ def download_file(url, local_filename="artifacts/test_report.csv"):
206
+ """
207
+ Download a file from a URL and save it to the local file system.
208
+
209
+ :param url: The URL of the file to download.
210
+ :param local_filename: The local path where the file should be saved.
211
+ :return: None
212
+ """
213
+
214
+ check_file_path("artifacts")
215
+ with requests.get(url, stream=True) as response:
216
+ response.raise_for_status() # Check if the request was successful
217
+ with open(local_filename, "wb") as file:
218
+ for chunk in response.iter_content(chunk_size=8192): # Download in chunks
219
+ file.write(chunk)
220
+ logger.info(f"File downloaded successfully and saved to {local_filename}")
221
+
222
+
223
+ def fetch_sbom_report(
224
+ report_id: str,
225
+ parent_id: str,
226
+ report_file_name: str = "sbom_report",
227
+ report_file_extension: str = "zip",
228
+ standard="CycloneDX",
229
+ ):
230
+ """
231
+ Fetch report by id and add it to evidence
232
+
233
+ :param str report_id: Wiz report ID
234
+ :param str parent_id: RegScale Parent ID
235
+ :param str report_file_name: Report file name, defaults to "evidence_report"
236
+ :param str report_file_extension: Report file extension, defaults to "zip"
237
+ :param str standard: SBOM standard, defaults to "CycloneDX"
238
+ :rtype: None
239
+ """
240
+
241
+ current_datetime = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
242
+ report_file_path = f"artifacts/{report_file_name}_{current_datetime}.{report_file_extension}"
243
+ variables = {"reportId": report_id}
244
+ api_endpoint_url = WizVariables.wizUrl
245
+ token = WizVariables.wizAccessToken
246
+ if not token:
247
+ error_and_exit("Wiz Access Token is missing. Authenticate with Wiz first.")
248
+ client = PaginatedGraphQLClient(
249
+ endpoint=api_endpoint_url,
250
+ query=DOWNLOAD_QUERY,
251
+ headers={
252
+ "Content-Type": "application/json",
253
+ "Authorization": BEARER + token,
254
+ },
255
+ )
256
+ download_report = client.fetch_results(variables=variables)
257
+ logger.debug(f"Download Report result: {download_report}")
258
+ if "errors" in download_report:
259
+ logger.error(f"Error fetching report: {download_report['errors']}")
260
+ logger.error(f"Raw Response Data: {download_report}")
261
+ return
262
+ report_data = None
263
+ if download_url := download_report.get("report", {}).get("lastRun", {}).get("url"):
264
+ logger.info(f"Download URL: {download_url}")
265
+ download_file(url=download_url, local_filename=report_file_path)
266
+ with ZipFile(report_file_path, "r") as zObject:
267
+ for filename in zObject.namelist():
268
+ with zObject.open(filename) as json_f:
269
+ file_name = ".".join(filename.split(".")[:-1])
270
+ report_data = json.load(json_f)
271
+ sbom_standard = report_data.get("bomFormat", standard)
272
+ standard_version = report_data.get("specVersion", 1.5)
273
+ Sbom(
274
+ name=file_name,
275
+ tool="Wiz",
276
+ parentId=int(parent_id),
277
+ parentModule=SecurityPlan.get_module_slug(),
278
+ results=json.dumps(report_data),
279
+ standardVersion=standard_version,
280
+ sbomStandard=sbom_standard,
281
+ ).create_or_update(
282
+ bulk_update=True
283
+ ) # need put in for this endpoint to update SBOMS
284
+
285
+ logger.info("SBOM attached successfully!")
286
+ else:
287
+ logger.error("Could not retrieve the download URL.")
288
+
289
+
290
+ @deprecated("Use the 'fetch_report_by_id' command instead.")
291
+ def fetch_report_id(query: str, variables: Dict, url: str) -> str:
292
+ """
293
+ Fetch report ID from Wiz
294
+
295
+ :param str query: Query string
296
+ :param Dict variables: Variables
297
+ :param str url: Wiz URL
298
+ :return str: Wiz ID
299
+ :rtype str: str
300
+ """
301
+ try:
302
+ resp = send_request(
303
+ query=query,
304
+ variables=variables,
305
+ api_endpoint_url=url,
306
+ )
307
+ if "error" in resp.json().keys():
308
+ error_and_exit(f'Wiz Error: {resp.json()["error"]}')
309
+ return resp.json()["data"]["createReport"]["report"]["id"]
310
+ except (requests.RequestException, AttributeError, TypeError) as rex:
311
+ logger.error("Unable to pull report id from requests object\n%s", rex)
312
+ return ""
313
+
314
+
315
+ def get_framework_names(wiz_frameworks: List) -> List:
316
+ """
317
+ Get the names of frameworks and replace spaces with underscores.
318
+
319
+ :param List wiz_frameworks: List of Wiz frameworks.
320
+ :return List: List of framework names.
321
+ :rtype List: list
322
+ """
323
+ return [framework["name"].replace(" ", "_") for framework in wiz_frameworks]
324
+
325
+
326
+ def check_reports_for_frameworks(reports: List, frames: List) -> bool:
327
+ """
328
+ Check if any reports contain the given frameworks.
329
+
330
+ :param List reports: List of reports.
331
+ :param List frames: List of framework names.
332
+ :return bool: Boolean indicating if any report contains a framework.
333
+ :rtype bool: bool
334
+ """
335
+ return any(frame in item["name"] for item in reports for frame in frames)
336
+
337
+
338
+ def create_report_if_needed(
339
+ wiz_project_id: str, frames: List, wiz_frameworks: List, reports: List, snake_framework: str
340
+ ) -> List:
341
+ """
342
+ Create a report if needed and return report IDs.
343
+
344
+ :param str wiz_project_id: Wiz Project ID.
345
+ :param List frames: List of framework names.
346
+ :param List wiz_frameworks: List of Wiz frameworks.
347
+ :param List reports: List of reports.
348
+ :param str snake_framework: Framework name with spaces replaced by underscores.
349
+ :return List: List of Wiz report IDs.
350
+ :rtype List: list
351
+ """
352
+ if not check_reports_for_frameworks(reports, frames):
353
+ selected_frame = snake_framework
354
+ selected_index = frames.index(selected_frame)
355
+ wiz_framework = wiz_frameworks[selected_index]
356
+ wiz_report_id = create_compliance_report(
357
+ wiz_project_id=wiz_project_id,
358
+ report_name=f"{selected_frame}_project_{wiz_project_id}",
359
+ framework_id=wiz_framework.get("id"),
360
+ )
361
+ logger.info(f"Wiz compliance report created with ID {wiz_report_id}")
362
+ return [wiz_report_id]
363
+
364
+ return [report["id"] for report in reports if any(frame in report["name"] for frame in frames)]
365
+
366
+
367
+ def fetch_and_process_report_data(wiz_report_ids: List) -> List:
368
+ """
369
+ Fetch and process report data from report IDs.
370
+
371
+ :param List wiz_report_ids: List of Wiz report IDs.
372
+ :return List: List of processed report data.
373
+ :rtype List: List
374
+ """
375
+ report_data = []
376
+ for wiz_report in wiz_report_ids:
377
+ download_url = get_report_url_and_status(wiz_report)
378
+ logger.debug(f"Download url: {download_url}")
379
+ with closing(requests.get(url=download_url, stream=True, timeout=10)) as data:
380
+ logger.info("Download URL fetched. Streaming and parsing report")
381
+ reader = csv.DictReader(codecs.iterdecode(data.iter_lines(), encoding="utf-8"), delimiter=",")
382
+ for row in reader:
383
+ report_data.append(row)
384
+ return report_data
385
+
386
+
387
+ def fetch_framework_report(wiz_project_id: str, snake_framework: str) -> List[Any]:
388
+ """
389
+ Fetch Framework Report from Wiz.
390
+
391
+ :param str wiz_project_id: Wiz Project ID.
392
+ :param str snake_framework: Framework name with spaces replaced by underscores.
393
+ :return: List containing the framework report data.
394
+ :rtype: List[Any]
395
+ """
396
+ wiz_frameworks = fetch_frameworks()
397
+ frames = get_framework_names(wiz_frameworks)
398
+ reports = list(query_reports())
399
+
400
+ wiz_report_ids = create_report_if_needed(wiz_project_id, frames, wiz_frameworks, reports, snake_framework)
401
+ return fetch_and_process_report_data(wiz_report_ids)
402
+
403
+
404
+ def fetch_frameworks() -> list:
405
+ """
406
+ Fetch frameworks from Wiz
407
+
408
+ :raises General Error: If error in API response
409
+ :return: List of frameworks
410
+ :rtype: list
411
+ """
412
+ query = """
413
+ query SecurityFrameworkAutosuggestOptions($policyTypes: [SecurityFrameworkPolicyType!],
414
+ $onlyEnabledPolicies: Boolean) {
415
+ securityFrameworks(
416
+ first: 500
417
+ filterBy: {policyTypes: $policyTypes, enabled: $onlyEnabledPolicies}
418
+ ) {
419
+ nodes {
420
+ id
421
+ name
422
+ }
423
+ }
424
+ }
425
+ """
426
+ variables = {
427
+ "policyTypes": "CLOUD",
428
+ "first": 500,
429
+ }
430
+ resp = send_request(
431
+ query=query,
432
+ variables=variables,
433
+ api_endpoint_url=WizVariables.wizUrl,
434
+ )
435
+
436
+ if resp.ok:
437
+ # ["data"]["securityFrameworks"]["nodes"]
438
+ data = resp.json()
439
+ return data.get("data", {}).get("securityFrameworks", {}).get("nodes")
440
+ else:
441
+ error_and_exit(f"Wiz Error: {resp.status_code if resp else None} - {resp.text if resp else 'No response'}")
442
+
443
+
444
+ def query_reports() -> list:
445
+ """
446
+ Query Report table from Wiz
447
+
448
+ :return: list object from an API response from Wiz
449
+ :rtype: list
450
+ """
451
+
452
+ # The variables sent along with the above query
453
+ variables = {"first": 100, "filterBy": {}}
454
+
455
+ res = send_request(
456
+ query=REPORTS_QUERY,
457
+ variables=variables,
458
+ api_endpoint_url=WizVariables.wizUrl,
459
+ )
460
+ result = []
461
+ try:
462
+ if "errors" in res.json().keys():
463
+ error_and_exit(f'Wiz Error: {res.json()["errors"]}')
464
+ json_result = res.json()
465
+ result = json_result.get("data", {}).get("reports", {}).get("nodes")
466
+ except requests.JSONDecodeError:
467
+ error_and_exit(f"Unable to fetch reports from Wiz: {res.status_code}, {res.reason}")
468
+ return result
469
+
470
+
471
+ def send_request(
472
+ query: str,
473
+ variables: Dict,
474
+ api_endpoint_url: Optional[str] = None,
475
+ ) -> requests.Response:
476
+ """
477
+ Send a graphQL request to Wiz.
478
+
479
+ :param str query: Query to use for GraphQL
480
+ :param Dict variables:
481
+ :param Optional[str] api_endpoint_url: Wiz GraphQL URL Default is None
482
+ :raises ValueError: Value Error if the access token is missing from wizAccessToken in init.yaml
483
+ :return requests.Response: response from post call to provided api_endpoint_url
484
+ :rtype requests.Response: requests.Response
485
+ """
486
+ logger.debug("Sending a request to Wiz API")
487
+ api = Api()
488
+ payload = dict({"query": query, "variables": variables})
489
+ if api_endpoint_url is None:
490
+ api_endpoint_url = WizVariables.wizUrl
491
+ if WizVariables.wizAccessToken:
492
+ return api.post(
493
+ url=api_endpoint_url,
494
+ headers={
495
+ "Content-Type": CONTENT_TYPE,
496
+ "Authorization": BEARER + WizVariables.wizAccessToken,
497
+ },
498
+ json=payload,
499
+ )
500
+ raise ValueError("An access token is missing.")
501
+
502
+
503
+ def create_compliance_report(
504
+ report_name: str,
505
+ wiz_project_id: str,
506
+ framework_id: str,
507
+ ) -> str:
508
+ """Create Wiz compliance report
509
+
510
+ :param str report_name: Report name
511
+ :param str wiz_project_id: Wiz Project ID
512
+ :param str framework_id: Wiz Framework ID
513
+ :return str: Compliance Report id
514
+ :rtype str: str
515
+ """
516
+ report_variables = {
517
+ "input": {
518
+ "name": report_name,
519
+ "type": "COMPLIANCE_ASSESSMENTS",
520
+ "csvDelimiter": "US",
521
+ "projectId": wiz_project_id,
522
+ "complianceAssessmentsParams": {"securityFrameworkIds": [framework_id]},
523
+ "emailTargetParams": None,
524
+ "exportDestinations": None,
525
+ }
526
+ }
527
+
528
+ return fetch_report_id(CREATE_REPORT_QUERY, report_variables, url=WizVariables.wizUrl)
529
+
530
+
531
+ def get_report_url_and_status(report_id: str) -> str:
532
+ """
533
+ Generate Report URL from Wiz report
534
+
535
+ :param str report_id: Wiz report ID
536
+ :raises: requests.RequestException if download failed and exceeded max # of retries
537
+ :return: URL of report
538
+ :rtype: str
539
+ """
540
+ for attempt in range(MAX_RETRIES):
541
+ if attempt:
542
+ logger.info(
543
+ "Report %s is still updating, waiting %.2f seconds", report_id, CHECK_INTERVAL_FOR_DOWNLOAD_REPORT
544
+ )
545
+ time.sleep(CHECK_INTERVAL_FOR_DOWNLOAD_REPORT)
546
+
547
+ response = download_report({"reportId": report_id})
548
+ if not response or not response.ok:
549
+ raise requests.RequestException("Failed to download report")
550
+
551
+ response_json = response.json()
552
+ errors = response_json.get("errors")
553
+ if errors:
554
+ message = errors[0]["message"]
555
+ if RATE_LIMIT_MSG in message:
556
+ rate = errors[0]["extensions"]["retryAfter"]
557
+ logger.warning("Sleeping %i seconds due to rate limit", rate)
558
+ time.sleep(rate)
559
+ continue
560
+
561
+ logger.error(errors)
562
+ else:
563
+ status = response_json.get("data", {}).get("report", {}).get("lastRun", {}).get("status")
564
+ if status == "COMPLETED":
565
+ return response_json["data"]["report"]["lastRun"]["url"]
566
+
567
+ raise requests.RequestException("Download failed, exceeding the maximum number of retries")
568
+
569
+
570
+ def download_report(variables: Dict) -> requests.Response:
571
+ """
572
+ Return a download URL for a provided Wiz report id
573
+
574
+ :param Dict variables: Variables for Wiz request
575
+ :return: response from Wiz API
576
+ :rtype: requests.Response
577
+ """
578
+ response = send_request(DOWNLOAD_QUERY, variables=variables)
579
+ return response
580
+
581
+
582
+ def _sync_compliance(
583
+ wiz_project_id: str,
584
+ regscale_id: int,
585
+ regscale_module: str,
586
+ include_not_implemented: bool,
587
+ client_id: str,
588
+ client_secret: str,
589
+ catalog_id: int,
590
+ framework: Optional[str] = "NIST800-53R5",
591
+ ) -> List[ComplianceReport]:
592
+ """
593
+ Sync compliance posture from Wiz to RegScale
594
+
595
+ :param str wiz_project_id: Wiz Project ID
596
+ :param int regscale_id: RegScale ID
597
+ :param str regscale_module: RegScale module
598
+ :param bool include_not_implemented: Include not implemented controls
599
+ :param str client_id: Wiz Client ID
600
+ :param str client_secret: Wiz Client Secret
601
+ :param int catalog_id: Catalog ID, defaults to None
602
+ :param Optional[str] framework: Framework, defaults to NIST800-53R5
603
+ :return: List of ComplianceReport objects
604
+ :rtype: List[ComplianceReport]
605
+ """
606
+
607
+ logger.info("Syncing compliance from Wiz with project ID %s", wiz_project_id)
608
+ wiz_authenticate(
609
+ client_id=client_id,
610
+ client_secret=client_secret,
611
+ )
612
+ report_job = compliance_job_progress.add_task("[#f68d1f]Fetching Wiz compliance report...", total=1)
613
+ fetch_regscale_data_job = compliance_job_progress.add_task(
614
+ "[#f68d1f]Fetching RegScale Catalog info for framework...", total=1
615
+ )
616
+ logger.info("Fetching Wiz compliance report for project ID %s...", wiz_project_id)
617
+ compliance_job_progress.update(report_job, completed=True, advance=1)
618
+
619
+ framework_mapping = {
620
+ "CSF": "NIST CSF v1.1",
621
+ "NIST800-53R5": "NIST SP 800-53 Revision 5",
622
+ "NIST800-53R4": "NIST SP 800-53 Revision 4",
623
+ }
624
+ sync_framework = framework_mapping.get(framework)
625
+ snake_framework = sync_framework.replace(" ", "_")
626
+ logger.info(snake_framework)
627
+ logger.info("Fetching Wiz compliance report for project ID %s", wiz_project_id)
628
+ report_data = fetch_framework_report(wiz_project_id, snake_framework)
629
+ report_models = []
630
+ compliance_job_progress.update(report_job, completed=True, advance=1)
631
+
632
+ catalog = Catalog.get_with_all_details(catalog_id=catalog_id)
633
+ controls = catalog.get("controls") if catalog else []
634
+ passing_controls = dict()
635
+ failing_controls = dict()
636
+ controls_to_reports = dict()
637
+ existing_implementations = ControlImplementation.get_existing_control_implementations(parent_id=regscale_id)
638
+ compliance_job_progress.update(fetch_regscale_data_job, completed=True, advance=1)
639
+ logger.info(f"Analyzing ComplianceReport for framework {sync_framework} from Wiz")
640
+ running_compliance_job = compliance_job_progress.add_task(
641
+ "[#f68d1f]Building compliance posture from wiz report...",
642
+ total=len(report_data),
643
+ )
644
+ for row in report_data:
645
+ try:
646
+ cr = ComplianceReport(**row)
647
+ if cr.framework == sync_framework:
648
+ check_compliance(
649
+ cr,
650
+ controls,
651
+ passing_controls,
652
+ failing_controls,
653
+ controls_to_reports,
654
+ )
655
+ report_models.append(cr)
656
+ compliance_job_progress.update(running_compliance_job, advance=1)
657
+ except ValidationError as e:
658
+ logger.error(f"Error creating ComplianceReport: {e}")
659
+ try:
660
+ saving_regscale_data_job = compliance_job_progress.add_task("[#f68d1f]Saving RegScale data...", total=1)
661
+ ControlImplementation.create_control_implementations(
662
+ controls=controls,
663
+ parent_id=regscale_id,
664
+ parent_module=regscale_module,
665
+ existing_implementation_dict=existing_implementations,
666
+ full_controls=passing_controls,
667
+ partial_controls={},
668
+ failing_controls=failing_controls,
669
+ include_not_implemented=include_not_implemented,
670
+ )
671
+ create_assessment_from_compliance_report(
672
+ controls_to_reports=controls_to_reports,
673
+ regscale_id=regscale_id,
674
+ regscale_module=regscale_module,
675
+ controls=controls,
676
+ )
677
+ compliance_job_progress.update(saving_regscale_data_job, completed=True, advance=1)
678
+
679
+ except Exception as e:
680
+ logger.error(f"Error creating ControlImplementations from compliance report: {e}")
681
+ traceback.print_exc()
682
+ return report_models
683
+
684
+
685
+ def check_compliance(
686
+ cr: ComplianceReport,
687
+ controls: List[Dict],
688
+ passing: Dict,
689
+ failing: Dict,
690
+ controls_to_reports: Dict,
691
+ ) -> None:
692
+ """
693
+ Check compliance report for against controls
694
+
695
+ :param ComplianceReport cr: Compliance Report
696
+ :param List[Dict] controls: Controls List
697
+ :param Dict passing: Passing controls
698
+ :param Dict failing: Failing controls
699
+ :param Dict controls_to_reports: Controls to reports
700
+ :return: None
701
+ :rtype: None
702
+ """
703
+ for control in controls:
704
+ if f"{control.get('controlId').lower()} " in cr.compliance_check.lower():
705
+ _add_controls_to_controls_to_report_dict(control, controls_to_reports, cr)
706
+ if cr.result == ComplianceCheckStatus.PASS.value:
707
+ if control.get("controlId").lower() not in passing:
708
+ passing[control.get("controlId").lower()] = control
709
+ else:
710
+ if control.get("controlId").lower() not in failing:
711
+ failing[control.get("controlId").lower()] = control
712
+ _clean_passing_list(passing, failing)
713
+
714
+
715
+ def _add_controls_to_controls_to_report_dict(control: Dict, controls_to_reports: Dict, cr: ComplianceReport) -> None:
716
+ """
717
+ Add controls to dict to process assessments from later
718
+
719
+ :param Dict control: Control
720
+ :param Dict controls_to_reports: Controls to reports
721
+ :param ComplianceReport cr: Compliance Report
722
+ :return: None
723
+ :rtype: None
724
+ """
725
+ if control.get("controlId").lower() not in controls_to_reports.keys():
726
+ controls_to_reports[control.get("controlId").lower()] = [cr]
727
+ else:
728
+ controls_to_reports[control.get("controlId").lower()].append(cr)
729
+
730
+
731
+ def _clean_passing_list(passing: Dict, failing: Dict) -> None:
732
+ """
733
+ Clean passing list. Ensures that controls that are passing are not also failing
734
+
735
+ :param Dict passing: Passing controls
736
+ :param Dict failing: Failing controls
737
+ :return: None
738
+ :rtype: None
739
+ """
740
+ for control_id in failing:
741
+ if control_id in passing:
742
+ passing.pop(control_id, None)
743
+
744
+
745
+ def create_assessment_from_compliance_report(
746
+ controls_to_reports: Dict, regscale_id: int, regscale_module: str, controls: List
747
+ ) -> None:
748
+ """
749
+ Create assessment from compliance report
750
+
751
+ :param Dict controls_to_reports: Controls to reports
752
+ :param int regscale_id: RegScale ID
753
+ :param str regscale_module: RegScale module
754
+ :param List controls: Controls
755
+ :return: None
756
+ :rtype: None
757
+ """
758
+ implementations = ControlImplementation.get_all_by_parent(parent_module=regscale_module, parent_id=regscale_id)
759
+ for control_id, reports in controls_to_reports.items():
760
+ control_record_id = None
761
+ for control in controls:
762
+ if control.get("controlId").lower() == control_id:
763
+ control_record_id = control.get("id")
764
+ break
765
+ filtered_results = [x for x in implementations if x.controlID == control_record_id]
766
+ create_report_assessment(filtered_results, reports, control_id)
767
+
768
+
769
+ def create_report_assessment(filtered_results: List, reports: List, control_id: str) -> None:
770
+ """
771
+ Create report assessment
772
+
773
+ :param List filtered_results: Filtered results
774
+ :param List reports: Reports
775
+ :param str control_id: Control ID
776
+ :return: None
777
+ :rtype: None
778
+ """
779
+ implementation = filtered_results[0] if len(filtered_results) > 0 else None
780
+ for report in reports:
781
+ html_summary = format_dict_to_html(report.dict())
782
+ if implementation:
783
+ Assessment(
784
+ leadAssessorId=implementation.createdById,
785
+ title=f"Wiz compliance report assessment for {control_id}",
786
+ assessmentType="Control Testing",
787
+ plannedStart=get_current_datetime(),
788
+ plannedFinish=get_current_datetime(),
789
+ actualFinish=get_current_datetime(),
790
+ assessmentResult=report.result,
791
+ assessmentReport=html_summary,
792
+ status="Complete",
793
+ parentId=implementation.id,
794
+ parentModule="controls",
795
+ isPublic=True,
796
+ ).create()