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,151 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Upload files to RegScale."""
4
+ import logging
5
+ import xml.etree.ElementTree as ET
6
+ from typing import Optional, Union
7
+
8
+ from pathlib import Path
9
+
10
+ from regscale.core.app.api import Api
11
+ from regscale.models.regscale_models.file import File
12
+ from regscale.utils.b64conversion import decode_base64_to_bytesio, encode_file_to_base64
13
+
14
+ logger = logging.getLogger("regscale")
15
+
16
+
17
+ def file_upload(
18
+ regscale_id: int,
19
+ regscale_module: str,
20
+ file_path: str,
21
+ file_name: Optional[str] = None,
22
+ **kwargs: dict,
23
+ ) -> Optional[Union[list, dict]]:
24
+ """Upload files to RegScale
25
+
26
+ :param int regscale_id: RegScale ID
27
+ :param str regscale_module: RegScale module
28
+ :param str file_path: Path to file
29
+ :param Optional[str] file_name: Optional name of file to upload
30
+ :param dict **kwargs: Optional kwargs to pass to upload_file
31
+ :return: Results of upload_file
32
+ :rtype: Optional[Union[list, dict]]
33
+ """
34
+ file = Path(file_path)
35
+ if file_name is None:
36
+ file_name = file.name
37
+ if not file.exists():
38
+ logger.error(f"File {file_path} does not exist.")
39
+ return
40
+ if file.suffix == ".xml":
41
+ return process_base64_in_xml(regscale_id, regscale_module, file_path, **kwargs)
42
+ filestring = encode_file_to_base64(file_path)
43
+ return upload_file(
44
+ ssp_id=regscale_id,
45
+ parent_module=regscale_module,
46
+ file_path=file_path,
47
+ filestring=filestring,
48
+ filename=file_name,
49
+ **kwargs,
50
+ )
51
+
52
+
53
+ def upload_file(
54
+ ssp_id: int,
55
+ parent_module: str,
56
+ file_path: str,
57
+ filestring: str,
58
+ filename: str,
59
+ **kwargs: dict,
60
+ ) -> Union[dict, bool]:
61
+ """Upload file to RegScale
62
+
63
+ :param int ssp_id: RegScale ID
64
+ :param str parent_module: RegScale module
65
+ :param str file_path: Path to file
66
+ :param str filestring: Base64-encoded file
67
+ :param str filename: Name of file to upload
68
+ :param dict **kwargs: Optional kwargs to pass to upload_file
69
+ :return: Results of upload_file, or False if upload fails
70
+ :rtype: Union[dict, bool]
71
+ """
72
+ logger.info(f"Uploading {filename} to RegScale {parent_module} with ID {ssp_id}.")
73
+ if "api" not in kwargs:
74
+ api = Api()
75
+ kwargs["api"] = api
76
+ decoded_file = decode_base64_to_bytesio(filestring)
77
+ try:
78
+ results = File.upload_file_to_regscale(
79
+ file_name=filename or file_path,
80
+ parent_id=ssp_id,
81
+ parent_module=parent_module,
82
+ file_data=decoded_file.getvalue(),
83
+ **kwargs,
84
+ )
85
+ except Exception as e:
86
+ logger.error(f"Error uploading file: {e}")
87
+ return False
88
+ logger.info(f"File {filename} uploaded successfully.")
89
+ return results
90
+
91
+
92
+ def process_base64_in_xml(
93
+ regscale_id: int,
94
+ regscale_module: str,
95
+ file_path: str,
96
+ file_name: str = None,
97
+ **kwargs: dict,
98
+ ) -> list:
99
+ """Process base64 in XML file
100
+
101
+ :param int regscale_id: RegScale ID
102
+ :param str regscale_module: RegScale module
103
+ :param str file_path: Path to XML file
104
+ :param str file_name: Optional name of file to upload
105
+ :param dict **kwargs: Optional kwargs to pass to upload_file
106
+ :return: Results of upload_file
107
+ :rtype: list
108
+ """
109
+ results = []
110
+ base64_tags = process_base64_tags_in_xml(file_path)
111
+ for base64_tag in base64_tags:
112
+ filename = base64_tag["filename"]
113
+ filestring = base64_tag["base64"]
114
+ logger.info(f"Uploading base64-tagged file {filename} to RegScale {regscale_module} with ID {regscale_id}.")
115
+ result = upload_file(
116
+ ssp_id=regscale_id,
117
+ parent_module=regscale_module,
118
+ file_path=filename,
119
+ filestring=filestring,
120
+ filename=filename,
121
+ **kwargs,
122
+ )
123
+ results.append(result)
124
+ logger.info(f"Uploading XML file {file_path} to RegScale {regscale_module} with ID {regscale_id}.")
125
+ result = upload_file(
126
+ ssp_id=regscale_id,
127
+ parent_module=regscale_module,
128
+ file_path=file_name or file_path,
129
+ filestring=encode_file_to_base64(file_path),
130
+ filename=file_name or file_path,
131
+ **kwargs,
132
+ )
133
+ results.append(result)
134
+ return results
135
+
136
+
137
+ def process_base64_tags_in_xml(xml_file: str) -> list[dict]:
138
+ """Process base64 tags in XML file
139
+
140
+ :param str xml_file: Path to XML file
141
+ :return: List of dicts containing filename and base64 string
142
+ :rtype: list[dict]
143
+ """
144
+ tree = ET.parse(xml_file)
145
+ root = tree.getroot()
146
+ results = []
147
+ for base64_tag in root.iter("base64"):
148
+ filename = base64_tag.attrib["filename"]
149
+ filestring = base64_tag.text.replace("\n", "")
150
+ results.append({"filename": filename, "base64": filestring})
151
+ return results
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """RegScale Healthcheck Status"""
4
+
5
+ # standard python imports
6
+ import sys
7
+ from json import JSONDecodeError
8
+ from urllib.parse import urljoin
9
+
10
+ from regscale.core.app.logz import create_logger
11
+
12
+ logger = create_logger()
13
+
14
+
15
+ def status() -> None:
16
+ """
17
+ Get Status of Client Application via RegScale API
18
+
19
+ :raises: FileNotFoundError if init.yaml was unable to be loaded
20
+ :raises: ValueError if domain in init.yaml is missing or null
21
+ :raises: General Error if unable to retrieve health check from RegScale API
22
+ :rtype: None
23
+ """
24
+ from regscale.core.app.api import Api
25
+ from regscale.core.app.application import Application
26
+
27
+ app = Application()
28
+ api = Api()
29
+ config = app.config
30
+
31
+ if "domain" not in config or config["domain"] == "":
32
+ raise ValueError("No domain set in the initialization file.")
33
+ if "token" not in config or config["token"] == "":
34
+ raise ValueError("The token has not been set in the initialization file.")
35
+
36
+ url_login = urljoin(config["domain"], "health")
37
+ response = api.get(url=url_login)
38
+
39
+ if not response or not response.ok:
40
+ logger.error("Unable to retrieve health check data from RegScale. Please check your domain value in init.yaml.")
41
+ sys.exit(1)
42
+
43
+ try:
44
+ health_data = response.json()
45
+ except JSONDecodeError:
46
+ logger.error("Unable to decode health check data from RegScale.")
47
+ sys.exit(1)
48
+
49
+ status_loggers = {
50
+ "Healthy": logger.info,
51
+ "Degraded": logger.warning,
52
+ "Unhealthy": logger.error,
53
+ }
54
+
55
+ reg_status = health_data.get("status", "Unknown")
56
+ status_loggers.get(reg_status, logger.info)(f"System Status: {reg_status}")
57
+
58
+ if "entries" not in health_data:
59
+ logger.error("No data returned from system health check.")
60
+ sys.exit(1)
61
+
62
+ checks = health_data["entries"]
63
+ for chk in checks:
64
+ logger.info(f"System: {chk}, Status: " + checks[chk]["status"])
65
+
66
+ return health_data
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Module to allow user to login to RegScale"""
4
+
5
+ # standard python imports
6
+ import base64
7
+ import contextlib
8
+ import json
9
+ import logging
10
+ import os
11
+ import sys
12
+ import uuid
13
+ from datetime import datetime
14
+ from ssl import SSLCertVerificationError
15
+ from typing import Optional, TYPE_CHECKING
16
+ from urllib.parse import urljoin
17
+
18
+ if TYPE_CHECKING:
19
+ from regscale.core.app.application import Application
20
+ from regscale.core.app.api import Api
21
+
22
+ import requests
23
+
24
+ logger = logging.getLogger("regscale")
25
+
26
+
27
+ def login(
28
+ str_user: Optional[str] = None,
29
+ str_password: Optional[str] = None,
30
+ host: Optional[str] = None,
31
+ app: Optional["Application"] = None,
32
+ token: Optional[str] = None,
33
+ mfa_token: Optional[str] = "",
34
+ ) -> str:
35
+ """
36
+ Wrapper for Login to RegScale
37
+
38
+ :param Optional[str] str_user: username to log in, defaults to None
39
+ :param Optional[str] str_password: password of provided user, defaults to None
40
+ :param Optional[str] host: host to log into, defaults to None
41
+ :param Optional[Application] app: Application object, defaults to None
42
+ :param Optional[str] token: a valid JWT token to pass, defaults to None
43
+ :param Optional[str] mfa_token: a valid MFA token to pass, defaults to ""
44
+ :raises: ValueError if no domain value found in init.yaml
45
+ :raises: TypeError if token or user id doesn't match expected data type
46
+ :raises: SSLCertVerificationError if unable to validate SSL certificate
47
+ :return: JWT Token after authentication
48
+ :rtype: str
49
+ """
50
+ from regscale.models.platform import (
51
+ RegScaleAuth,
52
+ ) # Adding the import here, to avoid a circular import with RegScaleAuth.get_token.
53
+
54
+ running_in_airflow = os.getenv("REGSCALE_AIRFLOW") == "true"
55
+ from regscale.core.app.application import Application
56
+
57
+ if not app and running_in_airflow:
58
+ app = Application(
59
+ config={
60
+ "domain": host,
61
+ "token": token,
62
+ }
63
+ )
64
+ elif not app:
65
+ app = Application()
66
+ config = app.config
67
+ # check to see if we are running in airflow
68
+ if config and running_in_airflow:
69
+ token = token or os.getenv("REGSCALE_TOKEN") or config.get("token")
70
+ config["domain"] = (host if host and host != "None" else config.get("REGSCALE_DOMAIN")) or os.getenv(
71
+ "REGSCALE_DOMAIN"
72
+ )
73
+ app.logger.info("Running in Airflow, logging in with token: %s", token)
74
+ else:
75
+ config["domain"] = host or config["domain"]
76
+ token = token or os.getenv("REGSCALE_TOKEN")
77
+ if config and token:
78
+ if verify_token(app, token):
79
+ config["userId"] = parse_user_id_from_jwt(app, token)
80
+ config["token"] = token if token.startswith("Bearer ") else f"Bearer {token}"
81
+ if not running_in_airflow:
82
+ logger.info("RegScale Token and userId has been saved in init.yaml")
83
+ app.save_config(conf=config)
84
+ return token
85
+ else:
86
+ logger.error("Invalid token provided.")
87
+ sys.exit(1)
88
+ from regscale.core.app.api import Api
89
+
90
+ if str_user and str_password:
91
+ if config and "REGSCALE_DOMAIN" not in os.environ and host is None:
92
+ host = config["domain"]
93
+ regscale_auth = RegScaleAuth.authenticate(
94
+ api=Api(),
95
+ username=str_user,
96
+ password=str_password,
97
+ domain=host,
98
+ mfa_token=mfa_token,
99
+ )
100
+ else:
101
+ regscale_auth = RegScaleAuth.authenticate(Api(), mfa_token=mfa_token)
102
+ if config and config["domain"] is None:
103
+ raise ValueError("No domain set in the init.yaml configuration file.")
104
+ if config and config["domain"] == "":
105
+ raise ValueError("The domain is blank in the init.yaml configuration file.")
106
+
107
+ # create object to authenticate
108
+ auth = {
109
+ "userName": regscale_auth.username,
110
+ "password": regscale_auth.password.get_secret_value(),
111
+ "oldPassword": "",
112
+ "mfaToken": mfa_token,
113
+ }
114
+ if auth["password"]:
115
+ try:
116
+ # update init file from login
117
+ if config:
118
+ config["token"] = regscale_auth.token
119
+ config["userId"] = regscale_auth.user_id
120
+ config["domain"] = regscale_auth.domain
121
+ # write the changes back to file
122
+ app.save_config(config)
123
+ # set variables
124
+ logger.info("User ID: %s", regscale_auth.user_id)
125
+ logger.info("New RegScale Token has been updated and saved in init.yaml")
126
+ # Truncate token for logging purposes
127
+ logger.debug("Token: %s", regscale_auth.token[:20])
128
+ except TypeError as ex:
129
+ logger.error("TypeError: %s", ex)
130
+ except SSLCertVerificationError as sslex:
131
+ logger.error("SSLError, python requests requires a valid ssl certificate.\n%s", sslex)
132
+ sys.exit(1)
133
+ config["domain"] = host
134
+ app.save_config(config)
135
+ return regscale_auth.token
136
+
137
+
138
+ def is_valid(host: Optional[str] = None, app: Optional["Application"] = None) -> bool:
139
+ """
140
+ Quick endpoint to check login status
141
+
142
+ :param Optional[str] host: host to verify login, defaults to None
143
+ :param Optional[Application] app: Application object, defaults to None
144
+ :return: Boolean if user is logged in or not
145
+ :rtype: bool
146
+ """
147
+ from regscale.core.app.api import Api
148
+
149
+ if not app:
150
+ from regscale.core.app.application import Application
151
+
152
+ app = Application()
153
+ config = app.config
154
+ login_status = False
155
+ api = Api()
156
+ token_body = {"accessToken": ""}
157
+ try:
158
+ # Make sure url isn't default
159
+ # login with token
160
+ token_body["accessToken"] = config["token"]
161
+ app.logger.debug("config: %s", config)
162
+ url_login = urljoin(host or config["domain"], "/api/authentication/validateToken")
163
+ app.logger.debug("is_valid url: %s", url_login)
164
+ app.logger.debug("token_body: %s", token_body)
165
+ if response := api.post(url=url_login, headers={}, json=token_body):
166
+ app.logger.debug("is_valid response: %s", response.status_code)
167
+ login_status = response.status_code == 200
168
+ except KeyError as ex:
169
+ if str(ex).replace("'", "") == "token":
170
+ app.logger.debug("Token is missing, we will generate this")
171
+ except ConnectionError:
172
+ app.logger.error("ConnectionError: Unable to login user to RegScale, check the server domain.")
173
+ except json.JSONDecodeError as decode_ex:
174
+ app.logger.error(
175
+ "Login Error: Unable to login user to RegScale instance: %s.\n%s",
176
+ config["domain"],
177
+ decode_ex,
178
+ )
179
+ finally:
180
+ app.logger.debug("login status: %s", login_status)
181
+ return login_status
182
+
183
+
184
+ def is_licensed(app: "Application") -> bool:
185
+ """
186
+ Verify if the application is licensed
187
+
188
+ :param Application app: Application object
189
+ :return: License status
190
+ :rtype: bool
191
+ """
192
+ from regscale.core.app.api import Api
193
+
194
+ api = Api()
195
+ try:
196
+ with contextlib.suppress(requests.RequestException):
197
+ if res := app.get_regscale_license(api=api):
198
+ lic = res.json()
199
+ else:
200
+ return False
201
+ license_date = parse_date(lic["expirationDate"])
202
+ if lic["licenseType"] == "Enterprise" and license_date > datetime.now():
203
+ return True
204
+ except (KeyError, ValueError, TypeError, AttributeError):
205
+ return False
206
+ return False
207
+
208
+
209
+ def verify_token(app: "Application", token: str) -> bool:
210
+ """
211
+ Function to verify if the provided JWT for RegScale is valid
212
+
213
+ :param Application app: Application object
214
+ :param str token: the JWT to verify
215
+ :return: Boolean if the token is valid or not
216
+ :rtype: bool
217
+ """
218
+ from regscale.core.app.api import Api
219
+
220
+ api = Api()
221
+ response = api.post(url=f"{app.config['domain']}/api/authentication/validateToken", json={"accessToken": token})
222
+ return response.status_code == 200
223
+
224
+
225
+ def parse_user_id_from_jwt(app: "Application", jwt_token: str) -> str:
226
+ """
227
+ Decode JWT from RegScale to get the user id from the payload
228
+
229
+ :param Application app: Application object
230
+ :param str jwt_token: the JWT to decode
231
+ :raises ValueError: if the JWT token is invalid
232
+ :return: the user id
233
+ :rtype: str
234
+ """
235
+ parts = jwt_token.split(".")
236
+ if len(parts) != 3:
237
+ raise ValueError("Invalid JWT token format. Provided token is not a valid JWT token. Token: %s", jwt_token)
238
+
239
+ payload = json.loads(_decode_base64(parts[1]).decode("utf-8"))
240
+ if "id" not in payload:
241
+ # iterate the payload to find the user id by using any uuid found and validating it
242
+ for value in payload.values():
243
+ try:
244
+ if uuid.UUID(value) and validate_user_id(app, value):
245
+ return value
246
+ except (ValueError, AttributeError):
247
+ continue
248
+ else:
249
+ return payload["id"]
250
+ app.logger.warning("No user id found in JWT token, please update userId manually in init.yaml.")
251
+ return ""
252
+
253
+
254
+ def validate_user_id(app: "Application", user_id: str) -> bool:
255
+ """
256
+ Validate the user id provided by the user in RegScale
257
+
258
+ :param Application app: Application object
259
+ :param str user_id: User id to validate
260
+ :return: Whether the provided user id is valid or not
261
+ :rtype: bool
262
+ """
263
+ from regscale.core.app.api import Api
264
+
265
+ api = Api()
266
+ response = api.get(
267
+ url=f"{app.config['domain']}/api/accounts/find/{user_id}",
268
+ )
269
+ return response.status_code == 200
270
+
271
+
272
+ def _decode_base64(data: str) -> bytes:
273
+ """
274
+ Decode base64, padding being optional
275
+
276
+ :param str data: the data to decode
277
+ :return: the decoded data
278
+ :rtype: bytes
279
+ """
280
+ missing_padding = len(data) % 4
281
+ if missing_padding:
282
+ data += "=" * (4 - missing_padding)
283
+ return base64.urlsafe_b64decode(data)
284
+
285
+
286
+ def parse_date(date_str: str) -> datetime:
287
+ """
288
+ Parse a date string in one of the supported formats
289
+
290
+ :param str date_str: the date string to parse
291
+ :raises ValueError: if unable to parse the provided date_str
292
+ :return: the parsed date
293
+ :rtype: datetime
294
+ """
295
+ formats = ["%Y-%m-%d", "%d-%m-%Y", "%m-%d-%Y", "%Y/%m/%d", "%d/%m/%Y", "%m/%d/%Y"]
296
+ for fmt in formats:
297
+ try:
298
+ parsed_date = datetime.strptime(date_str, fmt)
299
+ return parsed_date # Return the parsed date as soon as parsing is successful
300
+ except ValueError:
301
+ continue # Continue to the next format if parsing fails
302
+
303
+ raise ValueError(
304
+ f"Could not parse the date string {date_str} in any of the provided formats: {', '.join(formats)}."
305
+ )