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,473 @@
1
+ """Allow an API Wrapper for the CLI"""
2
+
3
+ import ast
4
+ import base64
5
+ import os
6
+ from os import getenv
7
+ from typing import Union
8
+ from wsgiref import simple_server
9
+
10
+ from click import Command, Group
11
+ from flask import (
12
+ Flask,
13
+ Response,
14
+ redirect,
15
+ render_template,
16
+ render_template_string,
17
+ request,
18
+ url_for,
19
+ )
20
+ from flask_rebar import Rebar
21
+ from flask_restx import Api
22
+ from importlib_resources import files
23
+ from rich.console import Console
24
+ from werkzeug.utils import secure_filename
25
+
26
+ from regscale import __version__
27
+ from regscale.core.app.internal.login import login
28
+ from regscale.core.app.logz import create_logger
29
+ from regscale.core.app.utils.app_utils import walk_directory_for_files
30
+ from regscale.core.app.utils.regscale_utils import update_regscale_config
31
+ from regscale.core.server.helpers import (
32
+ create_view_func,
33
+ delete_all_items_in_directory,
34
+ generate_parameters_model,
35
+ get_site_info,
36
+ get_catgalogues,
37
+ get_profiles,
38
+ get_ssp_name,
39
+ get_ssps,
40
+ generate,
41
+ )
42
+ from regscale.regscale import cli
43
+
44
+ # pylint: disable=line-too-long, logging-fstring-interpolation, invalid-name
45
+
46
+ current_file_size = 0
47
+
48
+ app = Flask(__name__)
49
+
50
+ logger = create_logger()
51
+
52
+
53
+ @app.route("/")
54
+ def index():
55
+ """Index route"""
56
+ return redirect(url_for("home"))
57
+
58
+
59
+ # setup swagger
60
+ api = Api(
61
+ app,
62
+ version=__version__,
63
+ title="RegScale-CLI REST API Wrapper",
64
+ description="A REST API wrapper support GET and POST requests for the RegScale-CLI.",
65
+ doc="/swagger/",
66
+ default="RegScale-CLI REST API",
67
+ default_label="RegScale-CLI REST API Operations",
68
+ template_folder=files("regscale.core.server.templates"),
69
+ debug=True,
70
+ )
71
+ # setup redoc
72
+ rebar = Rebar()
73
+ registry = rebar.create_handler_registry()
74
+
75
+ if not os.path.exists("artifacts"):
76
+ os.makedirs("artifacts")
77
+
78
+
79
+ # recursive function to generate routes from a click group
80
+ def generate_routes(api_instance: Api, group: Union[Group, Command], path: str = ""):
81
+ """Generate routes for the app, recursively
82
+ since group is assumed to be a click.Group, we get the command_name and command for all of those items.
83
+ an endpoint_path is created. if the command is found to be a click.Group it is called on itself until we have a
84
+ click command.
85
+
86
+ :param Api api_instance: the flask_restx.Api instance
87
+ :param Union[Group, Command] group: A Group or Command from click
88
+ :param str path: str a representation of the endpoint path, defaults to ""
89
+ """
90
+ for command_name, command in group.commands.items():
91
+ endpoint_path = f"{path}/{command_name}"
92
+ if command.name in {
93
+ "encrypt",
94
+ "decrypt",
95
+ "change_passkey",
96
+ "catalog",
97
+ "assessments",
98
+ "control_editor",
99
+ }:
100
+ continue
101
+ if isinstance(command, Group) and command.name in {"issues"}:
102
+ continue
103
+ if isinstance(command, Group):
104
+ # Generate routes for nested group
105
+ generate_routes(api_instance, command, endpoint_path)
106
+ else:
107
+ # generate the CommandResource class
108
+ resource_class = create_view_func(command)
109
+ # if command has params, generate the parameters model for that command
110
+ if command.params:
111
+ # generate the parameters model
112
+ parameters_model = generate_parameters_model(api_instance, command)
113
+ resource_class.post = api.expect(parameters_model)(resource_class.post)
114
+ api.add_resource(resource_class, endpoint_path)
115
+
116
+
117
+ # generate all the routes dynamically from the click information
118
+ generate_routes(api, cli)
119
+
120
+
121
+ @app.route("/redoc/")
122
+ def redoc():
123
+ """return render template string"""
124
+ return render_template_string(
125
+ """
126
+ <!DOCTYPE html>
127
+ <html>
128
+ <head>
129
+ <title>ReDoc</title>
130
+ <meta charset="utf-8"/>
131
+ <meta name="viewport" content="width=device-width, initial-scale=1">
132
+ <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
133
+ <style>
134
+ body, html { margin: 0; padding: 0; }
135
+ #redoc { width: 100vw; height: 100vw; position: sticky; }
136
+ </style>
137
+ </head>
138
+ <body>
139
+ <div id="redoc"></div>
140
+ <script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
141
+ <script>
142
+ Redoc.init('/swagger.json', {}, document.getElementById('redoc'))
143
+ </script>
144
+ </body>
145
+ </html>
146
+ """
147
+ )
148
+
149
+
150
+ @app.route("/home")
151
+ def home():
152
+ """Index route"""
153
+ return render_template("index.html", site_info=get_site_info())
154
+
155
+
156
+ @app.route("/auth", methods=["GET"])
157
+ def auth():
158
+ """Auth route"""
159
+ return render_template("login.html", site_info=get_site_info())
160
+
161
+
162
+ @app.route("/login-token", methods=["POST"])
163
+ def login_token():
164
+ domain = request.form["domain"].split(" ")[0] # split on space to avoid injection
165
+ token = request.form["token"]
166
+ if "Bearer" not in token:
167
+ token = f"Bearer {token}"
168
+ if len(token.split(" ")) != 2: # if the length is not 2, then it is not a valid token
169
+ raise ValueError(f"Invalid token: {token}")
170
+ if ";" in token or "&&" in token or "|" in token:
171
+ raise ValueError(f"Invalid token: {token}")
172
+ if not domain.endswith("/"):
173
+ domain += "/"
174
+ # set the domain in init.yaml
175
+ config_response = update_regscale_config(str_param="domain", val=domain)
176
+ if config_response != "Config updated":
177
+ raise ValueError(f"Could not update domain in init.yaml: {config_response}")
178
+ # login to the RegScale instance
179
+ auth_token = login(token=token)
180
+ logger.debug(f"Authenticated with {auth_token}")
181
+ return redirect(url_for("home"))
182
+
183
+
184
+ @app.route("/upload_ssp", methods=["GET"])
185
+ def upload_ssp():
186
+ """Upload a file for processing"""
187
+ regscale_profiles = get_profiles()
188
+ # Get list of catalogs
189
+ regscale_catalogues = get_catgalogues()
190
+ return render_template(
191
+ "upload_ssp.html",
192
+ regscale_catalogues=regscale_catalogues,
193
+ regscale_profiles=regscale_profiles,
194
+ site_info=get_site_info(),
195
+ )
196
+
197
+
198
+ @app.route("/process_file", methods=["POST"])
199
+ def process_file():
200
+ """process uploaded file"""
201
+ logger.debug(f"Request: {request.form}")
202
+ file = request.files["file"]
203
+ version = request.form["fedrampVersion"]
204
+ logger.info(f"Parsed version: {version}")
205
+ catalogue_id = request.form["regscale_catalogue_id"]
206
+ profile_id = request.form["regscale_profile_id"]
207
+ load_missing_controls_str = request.form.get("load_missing_controls")
208
+ load_missing_controls_bool = ast.literal_eval(load_missing_controls_str)
209
+ filename = secure_filename(file.filename)
210
+ default_csv_path = "./artifacts/import-results.csv"
211
+ if os.path.exists(default_csv_path):
212
+ os.remove(default_csv_path)
213
+ artifacts_dir = "artifacts"
214
+ file_path = os.path.join(artifacts_dir, filename)
215
+ create_logger()
216
+ # Check if the file already exists and warn if needed
217
+ if os.path.exists(file_path):
218
+ # Logic to display a warning and handle overwrite (e.g., via user confirmation)
219
+ pass
220
+
221
+ # Save the file to the 'artifacts' directory
222
+ file.save(file_path)
223
+ if version.lower() == "rev4":
224
+ from regscale.integrations.public.fedramp.fedramp_docx import process_fedramp_docx_by_profile_id
225
+
226
+ (
227
+ csv_path,
228
+ result_output,
229
+ implementation_results,
230
+ ) = process_fedramp_docx_by_profile_id(
231
+ file_path=file_path,
232
+ profile_id=int(profile_id),
233
+ load_missing=load_missing_controls_bool,
234
+ )
235
+ return Response(generate(app, file_path, filename, catalogue_id), content_type="text/html")
236
+ elif version.lower() == "rev5docx":
237
+ from regscale.integrations.public.fedramp.fedramp_five import process_fedramp_docx_v5
238
+
239
+ # parse rev 5
240
+ appendix_file = request.files["fileAppendix"]
241
+ appendix_file_name = secure_filename(appendix_file.filename)
242
+ appendix_file_path = os.path.join(artifacts_dir, appendix_file_name)
243
+ appendix_file.save(appendix_file_path)
244
+ ssp_id = process_fedramp_docx_v5(
245
+ file_name=file_path,
246
+ base_fedramp_profile_id=int(profile_id),
247
+ save_data=False,
248
+ add_missing=load_missing_controls_bool,
249
+ appendix_a_file_name=appendix_file_path,
250
+ )
251
+ from urllib.parse import urljoin
252
+
253
+ ssp_url = urljoin(get_site_info()["domain"], f"form/securityplans/{ssp_id}")
254
+ return redirect(ssp_url)
255
+ else:
256
+ return Response(generate(app, file_path, filename, catalogue_id), content_type="text/html")
257
+
258
+
259
+ @app.route("/final_results")
260
+ def final_results():
261
+ """
262
+ Render the final results page with the grouped CSV data and result output
263
+ """
264
+ csv_path = request.args.get("csv_path", "")
265
+ result_output = request.args.get("result_output", "")
266
+ filename = request.args.get("filename", "")
267
+ return load_results(csv_path, result_output, filename)
268
+
269
+
270
+ def load_results(csv_path, result_output_str, filename):
271
+ """
272
+ Load the results from the SSP processing and render the final results page
273
+ """
274
+ import pandas as pd # Optimize import performance
275
+
276
+ # convert result_output to diction
277
+ result_output = ast.literal_eval(result_output_str)
278
+
279
+ # Read and group the CSV data by error level (do this now as it is expensive)
280
+ csv_data = pd.read_csv(csv_path)
281
+ # if the csv file is not empty
282
+ if not csv_data.empty:
283
+ # group the csv data
284
+ csv_data_grouped = (
285
+ # sort the csv data by the model_layer and timestamp columns
286
+ csv_data.sort_values(["model_layer", "timestamp"])
287
+ # group the csv data by the level column
288
+ .groupby("level")
289
+ # convert dataframe to a dictionary and orient by the records
290
+ .apply(lambda x: x.to_dict(orient="records"))
291
+ # convert to a dictionary
292
+ .to_dict()
293
+ )
294
+ value_counts = (
295
+ # group the csv data by the level column
296
+ csv_data.drop("timestamp", axis="columns")
297
+ .drop("model_layer", axis="columns")
298
+ .drop("record_type", axis="columns")
299
+ .drop("event", axis="columns")
300
+ .groupby("level")
301
+ .agg(["count"])
302
+ .to_dict()
303
+ )
304
+ value_counts["Results"] = value_counts.pop(("Unnamed: 0", "count"))
305
+ category_counts = csv_data["record_type"].value_counts().to_dict()
306
+ results = value_counts["Results"]
307
+ # ensure that these exist as numbers
308
+ if "Error" not in results:
309
+ results["Error"] = 0
310
+ if "Info" not in results:
311
+ results["Info"] = 0
312
+ else:
313
+ logger.warning(f"{csv_path} is empty.")
314
+ csv_data_grouped = {}
315
+ results = {}
316
+ category_counts = {}
317
+ # Render the success page with the grouped CSV data and result output
318
+ return render_template(
319
+ "upload_ssp_result.html",
320
+ site_info=get_site_info(),
321
+ csv_data_grouped=csv_data_grouped,
322
+ results=results,
323
+ result_output=result_output,
324
+ category_counts=category_counts,
325
+ filename=filename,
326
+ )
327
+
328
+
329
+ @app.route("/make_base64", methods=["GET"])
330
+ def make_base64():
331
+ """Form to upload a file to convert to base64 string"""
332
+ return render_template("make_base64.html", site_info=get_site_info())
333
+
334
+
335
+ @app.route("/encode_base", methods=["POST"])
336
+ def encode_base64():
337
+ """Return Base64-encoded string as file"""
338
+ if request.method == "POST":
339
+ # Check if the POST request has the file part
340
+ if "file" not in request.files:
341
+ return "No file part"
342
+ file = request.files["file"]
343
+ # If the user does not select a file, the browser submits an empty file without a filename.
344
+ if file.filename == "":
345
+ return "No selected file"
346
+
347
+ if file:
348
+ # Convert the uploaded file to Base64
349
+ file_content = file.read()
350
+ base64_content = base64.b64encode(file_content).decode("utf-8")
351
+
352
+ # Create a filename for downloading
353
+ download_filename = f"{file.filename}.txt"
354
+
355
+ # Set up the response object for downloading
356
+ response = Response(base64_content, content_type="text/plain")
357
+ response.headers["Content-Disposition"] = f"attachment; filename={download_filename}"
358
+
359
+ return response
360
+ else:
361
+ return render_template("make_base64.html", site_info=get_site_info())
362
+
363
+
364
+ @app.route("/upload_STIG", methods=["GET"])
365
+ def create_stig_upload_form() -> str:
366
+ """
367
+ Create a form for uploading STIG files to RegScale
368
+
369
+ :return: The rendered HTML form
370
+ :rtype: str
371
+ """
372
+ """Upload a file for processing"""
373
+ # Get list of SSPs from the RegScale instance
374
+ regscale_ssps = get_ssps()
375
+ regscale_catalogues = get_catgalogues()
376
+ return render_template(
377
+ "upload_STIG.html",
378
+ regscale_ssps=regscale_ssps,
379
+ regscale_catalogues=regscale_catalogues,
380
+ site_info=get_site_info(),
381
+ )
382
+
383
+
384
+ @app.route("/process_stig_file", methods=["POST"])
385
+ async def process_stig_file():
386
+ """process stig file(s) for uploading to RegScale"""
387
+ import time
388
+
389
+ file_directory = "stig_artifacts"
390
+ start_time = time.time()
391
+ ssp_id = request.form["regscale_ssp_id"]
392
+ ckl_file = request.files.get("cklFile")
393
+ zip_file = request.files.get("zip")
394
+ start_msg, warning_msg, result_msg = "", "", ""
395
+ # See if stig artifacts exists, if not create it, else remove all the contents
396
+ if not os.path.exists(file_directory):
397
+ os.mkdir(file_directory)
398
+ else:
399
+ delete_all_items_in_directory(file_directory)
400
+ if ckl_file:
401
+ file_count = 1
402
+ start_msg = f"Processing {ckl_file.filename} STIG file for SSP #{ssp_id}..."
403
+ ckl_file.save(f"{file_directory}{os.sep}{secure_filename(ckl_file.filename)}")
404
+ elif zip_file:
405
+ import zipfile
406
+
407
+ zip_path = f"{file_directory}{os.sep}{secure_filename(zip_file.filename)}"
408
+ zip_file.save(zip_path)
409
+ with zipfile.ZipFile(zip_path, "r") as zip_ref:
410
+ # Extract all the files to a specific directory
411
+ zip_ref.extractall(file_directory)
412
+ # delete the zip file after extracting
413
+ os.remove(zip_path)
414
+ ckl_files, upload_count = walk_directory_for_files(file_directory)
415
+ # delete all the files in the directory except .ckl files
416
+ delete_all_items_in_directory(file_directory, keep_ext=".ckl")
417
+ file_count = len(ckl_files)
418
+ if non_ckl_count := upload_count - file_count:
419
+ warning_msg = (
420
+ f"{non_ckl_count}/{upload_count} " "file(s) will not processed because they are not .ckl files."
421
+ )
422
+ if non_ckl_count == upload_count:
423
+ logger.error("No file(s) were selected for processing because none of them are .ckl files.")
424
+ return redirect(url_for("create_stig_upload_form"))
425
+ start_msg = f"Processing {file_count} STIG file(s) for SSP #{ssp_id}..."
426
+ else:
427
+ logger.error("No file(s) were selected for processing.")
428
+ return redirect(url_for("create_stig_upload_form"))
429
+ # Process the STIG file(s) and upload to RegScale
430
+ from regscale.integrations.commercial.stigv2.stig_integration import StigIntegration
431
+
432
+ StigIntegration.sync_assets(plan_id=int(ssp_id), path="stig_artifacts")
433
+
434
+ StigIntegration.sync_findings(plan_id=int(ssp_id), path="stig_artifacts")
435
+
436
+ time_elapsed = f"{round(time.time() - start_time, 2)} seconds."
437
+ result_msg = f"Processed {file_count} STIG file(s) for SSP #{ssp_id} in {time_elapsed}"
438
+ return render_template(
439
+ "upload_STIG_result.html",
440
+ results={
441
+ "file_count": file_count,
442
+ "start_msg": start_msg,
443
+ "warning_msg": warning_msg,
444
+ "result_msg": result_msg,
445
+ "ssp_id": ssp_id,
446
+ "ssp_name": get_ssp_name(ssp_id),
447
+ "time_elapsed": time_elapsed,
448
+ },
449
+ site_info=get_site_info(),
450
+ )
451
+
452
+
453
+ def run_app(
454
+ port: int = int(getenv("REGSCALE_FLASK_PORT", "5555")),
455
+ debug: bool = getenv("DEBUG_FLASK_APP", "False") == "True",
456
+ ):
457
+ """Run the CLI as a flask app
458
+
459
+ :param int port: The port to serve flask on
460
+ :param bool debug: Whether to run the flask application in debug mode
461
+ """
462
+ Console().print(f"Running on http://127.0.0.1:{port}/")
463
+ Console().print(f"Swagger docs at http://127.0.0.1:{port}/swagger/")
464
+ Console().print("Press CTRL+C to quit")
465
+ try:
466
+ if debug:
467
+ logger.info("Running in debug mode.")
468
+ app.run(debug=True, port=port)
469
+ else:
470
+ server = simple_server.make_server("0.0.0.0", port, app)
471
+ server.serve_forever()
472
+ except KeyboardInterrupt:
473
+ Console().print("Exiting")