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,47 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Class for evidence model in RegScale platform"""
4
+
5
+ from typing import Optional
6
+ from pydantic import ConfigDict, Field
7
+
8
+ from regscale.models.regscale_models.regscale_model import RegScaleModel
9
+
10
+
11
+ class Evidence(RegScaleModel):
12
+ """Evidence Model"""
13
+
14
+ _module_slug = "evidence"
15
+ _unique_fields: list[str] = [["title"]]
16
+
17
+ id: int = 0
18
+ isPublic: Optional[bool] = True
19
+ uuid: Optional[str] = None
20
+ title: Optional[str] = None
21
+ description: Optional[str] = None
22
+ evidenceOwnerId: Optional[str] = Field(default_factory=RegScaleModel.get_user_id)
23
+ updateFrequency: Optional[int] = 365
24
+ lastEvidenceUpdate: Optional[str] = None # YY-mm-dd
25
+ dueDate: Optional[str] = None # YY-mm-dd
26
+
27
+ @staticmethod
28
+ def _get_additional_endpoints() -> ConfigDict:
29
+ """
30
+ Get additional endpoints for the Evidence model
31
+
32
+ :return: A dictionary of additional endpoints
33
+ :rtype: ConfigDict
34
+ """
35
+ return ConfigDict(
36
+ get_count="/api/{model_slug}/getCount",
37
+ graph="/api/{model_slug}/graph",
38
+ graph_by_date="/api/{model_slug}/graphByDate/{strGroupBy}/{year}",
39
+ get_evidence_by_date="/api/{model_slug}/getEvidenceByDate/{intDays}",
40
+ get_controls_by_evidence="/api/{model_slug}/getControlsByEvidence/{intEvidenceId}",
41
+ get_evidence_by_control="/api/{model_slug}/getEvidenceByControl/{intControl}",
42
+ get_evidence_by_security_plan="/api/{model_slug}/getEvidenceBySecurityPlan/{intId}",
43
+ filter_evidence="/api/{model_slug}/filterEvidence",
44
+ query_by_custom_field="/api/{model_slug}/queryByCustomField/{strFieldName}/{strValue}",
45
+ mega_api="/api/{model_slug}/megaApi/{intId}",
46
+ get_my_evidence_due_soon="/api/{model_slug}/getMyEvidenceDueSoon/{intDays}/{intPage}/{intPageSize}",
47
+ )
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Class for evidence mapping model in RegScale platform"""
4
+
5
+ from typing import Optional
6
+ from pydantic import ConfigDict, Field
7
+
8
+ from regscale.core.app.utils.app_utils import get_current_datetime
9
+ from regscale.models.regscale_models.regscale_model import RegScaleModel
10
+
11
+
12
+ class EvidenceMapping(RegScaleModel):
13
+ """Evidence mapping Model"""
14
+
15
+ _module_slug = "evidenceMapping"
16
+ _module_string = "evidenceMapping"
17
+
18
+ id: int = 0
19
+ createdById: Optional[str] = Field(default_factory=RegScaleModel.get_user_id)
20
+ dateCreated: Optional[str] = Field(default_factory=get_current_datetime)
21
+ lastUpdatedById: Optional[str] = Field(default_factory=RegScaleModel.get_user_id)
22
+ dateLastUpdated: Optional[str] = Field(default_factory=get_current_datetime)
23
+ isPublic: Optional[bool] = True
24
+ evidenceID: Optional[int] = 0
25
+ mappedID: Optional[int] = 0
26
+ mappingType: Optional[str] = None
27
+
28
+ @staticmethod
29
+ def _get_additional_endpoints() -> ConfigDict:
30
+ """
31
+ Get additional endpoints for the Evidence model
32
+
33
+ :return: A dictionary of additional endpoints
34
+ :rtype: ConfigDict
35
+ """
36
+ return ConfigDict(
37
+ get_all_by_evidence="/api/{model_slug}/getAllByEvudence/{intID}",
38
+ get_summary_by_evidence="/api/{model_slug}/getSummaryByEvidence/{intID}",
39
+ batch_create="/api/{model_slug}/batchCreate",
40
+ )
@@ -0,0 +1,59 @@
1
+ """Facility model for RegScale."""
2
+
3
+ import warnings
4
+ from typing import Optional
5
+ from urllib.parse import urljoin
6
+
7
+ from regscale.core.app.api import Api
8
+ from regscale.core.app.application import Application
9
+ from regscale.models.regscale_models.regscale_model import RegScaleModel
10
+
11
+
12
+ class Facility(RegScaleModel):
13
+ """Data Model for Facilities"""
14
+
15
+ _module_slug = "facilities"
16
+
17
+ id: int = 0
18
+ name: str = ""
19
+ description: str = ""
20
+ address: str = ""
21
+ city: str = ""
22
+ state: str = ""
23
+ zipCode: str = ""
24
+ status: str = ""
25
+ latitude: float = 0
26
+ longitude: float = 0
27
+ createdBy: Optional[str] = None
28
+ createdById: Optional[str] = None
29
+ dateCreated: Optional[str] = None
30
+ lastUpdatedBy: Optional[str] = None
31
+ lastUpdatedById: Optional[str] = None
32
+ isPublic: bool = True
33
+ dateLastUpdated: Optional[str] = None
34
+
35
+ def post(self, app: Application) -> Optional[dict]:
36
+ """Post a Facility to RegScale
37
+
38
+ :param Application app: The application instance
39
+ :return: The response from the API or None
40
+ :rtype: Optional[dict]
41
+ """
42
+ warnings.warn(
43
+ "The 'post' method is deprecated, use 'create' method instead",
44
+ DeprecationWarning,
45
+ )
46
+ api = Api()
47
+ url = urljoin(app.config.get("domain", ""), "/api/facilities")
48
+ data = self.dict()
49
+ response = api.post(url, json=data)
50
+ return response.json() if response.ok else None
51
+
52
+
53
+ class Facilities(Facility):
54
+ def __init__(self, *args, **kwargs):
55
+ warnings.warn(
56
+ "The 'Facilities' class is deprecated, use 'Facility' instead",
57
+ DeprecationWarning,
58
+ )
59
+ super().__init__(*args, **kwargs)
@@ -0,0 +1,382 @@
1
+ # !/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Model for a RegScale File"""
4
+
5
+ # flake8: noqa
6
+ # standard python imports
7
+ import gzip
8
+ import logging
9
+ import mimetypes
10
+ import os
11
+ import sys
12
+ from io import BytesIO
13
+ from pathlib import Path
14
+ from typing import Optional, Union, Tuple
15
+
16
+ from pydantic import BaseModel, ConfigDict, Field
17
+ from requests import JSONDecodeError
18
+
19
+ from regscale.core.app.api import Api
20
+ from regscale.core.app.application import Application
21
+ from regscale.core.app.utils.app_utils import (
22
+ compute_hash,
23
+ error_and_exit,
24
+ get_current_datetime,
25
+ get_file_name,
26
+ get_file_type,
27
+ )
28
+ from regscale.models.regscale_models.tag import Tag
29
+ from regscale.models.regscale_models.tag_mapping import TagMapping
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ class File(BaseModel):
35
+ """File Model"""
36
+
37
+ model_config = ConfigDict(populate_by_name=True)
38
+
39
+ # fileHash and shaHash are not required because this is due
40
+ # fileHash is the old method and shaHash is the new method in RegScale
41
+ # We are not sure which one will be used/returned
42
+ id: str
43
+ trustedDisplayName: str
44
+ trustedStorageName: str
45
+ size: int
46
+ fullPath: str
47
+ mimeType: Optional[str] = ""
48
+ partType: Optional[str] = ""
49
+ partId: Optional[str] = ""
50
+ folder: Optional[str] = ""
51
+ uploadedById: Optional[str] = None
52
+ uploadDate: str = Field(default_factory=get_current_datetime, alias="uploadedDate")
53
+ fileName: Optional[str] = Field("", alias="file_name")
54
+ fileHash: Optional[str] = None
55
+ shaHash: Optional[str] = None
56
+ parentId: int = 0
57
+ parentModule: str = ""
58
+ tags: Optional[str] = ""
59
+
60
+ @staticmethod
61
+ def download_file_from_regscale_to_memory(
62
+ api: Api, record_id: int, module: str, stored_name: str, file_hash: str
63
+ ) -> bytes:
64
+ """
65
+ Download a file from RegScale
66
+
67
+ :param Api api: API object
68
+ :param int record_id: RegScale record ID
69
+ :param str module: RegScale module
70
+ :param str stored_name: RegScale stored name
71
+ :param str file_hash: RegScale file hash
72
+ :return: Bytes of file as BytesIO object
73
+ :rtype: bytes
74
+ """
75
+ response = api.get(
76
+ url=f'{api.config["domain"]}/api/files/downloadFile/{record_id}/{module}/{stored_name}/{file_hash}'
77
+ )
78
+ return response.content
79
+
80
+ @staticmethod
81
+ def get_files_for_parent_from_regscale(
82
+ parent_id: int, parent_module: str, api: Optional[Api] = None
83
+ ) -> list["File"]:
84
+ """
85
+ Function to download all files from RegScale for the provided parent ID and module
86
+
87
+ :param int parent_id: RegScale parent ID
88
+ :param str parent_module: RegScale module
89
+ :param Optional[Api] api: API object
90
+ :return: List of File objects
91
+ :rtype: list[File]
92
+ """
93
+ if not api:
94
+ api = Api()
95
+ # get the files from RegScale
96
+ files = []
97
+ try:
98
+ file_response = api.get(url=f"{api.config['domain']}/api/files/{parent_id}/{parent_module}").json()
99
+ files = [File(**file) for file in file_response]
100
+ except JSONDecodeError:
101
+ api.logger.error("Unable to retrieve files from RegScale for the provided ID & module.")
102
+ except Exception as ex:
103
+ error_and_exit(f"Unable to retrieve files from RegScale.\n{ex}")
104
+ return files
105
+
106
+ @staticmethod
107
+ def delete_file(app: Application, file: "File") -> bool:
108
+ """
109
+ Delete a file from RegScale
110
+
111
+ :param Application app: Application Instance
112
+ :param File file: File to delete in RegScale
113
+ :return: Whether the file was deleted
114
+ :rtype: bool
115
+ """
116
+ api = Api()
117
+
118
+ response = api.delete(app.config["domain"] + f"/api/files/{file.id}")
119
+ return response.ok
120
+
121
+ @staticmethod
122
+ def upload_file_to_regscale(
123
+ file_name: str,
124
+ parent_id: int,
125
+ parent_module: str,
126
+ api: Api,
127
+ file_data: Optional[Union[bytes, dict, bool]] = None,
128
+ return_object: Optional[bool] = False,
129
+ tags: Optional[str] = None,
130
+ ) -> Optional[Union[bytes, dict, bool]]:
131
+ """
132
+ Function that will create and upload a file to RegScale to the provided parent_module and parent_id
133
+ returns whether the file upload was successful or not
134
+
135
+ :param str file_name: Path to the file to upload
136
+ :param int parent_id: RegScale parent ID
137
+ :param str parent_module: RegScale module
138
+ :param Api api: API object
139
+ :param Optional[Union[bytes, dict, bool]] file_data: File data to upload, defaults to None
140
+ :param Optional[bool] return_object: Whether to return the file object from RegScale, defaults to False
141
+ :param Optional[str] tags: Tags to add to the file, defaults to None
142
+ :return: Boolean indicating whether the file upload was successful or not
143
+ :rtype: Optional[bytes, dict, bool]
144
+ """
145
+
146
+ if regscale_file := File._create_regscale_file(
147
+ file_path=file_name,
148
+ parent_id=parent_id,
149
+ parent_module=parent_module,
150
+ api=api,
151
+ file_data=file_data,
152
+ ):
153
+ # set up headers for file upload
154
+ file_headers = {
155
+ "Authorization": api.config["token"],
156
+ "accept": "application/json, text/plain, */*",
157
+ }
158
+
159
+ if "id" not in regscale_file:
160
+ ext = Path(file_name).suffix
161
+ api.logger.error(
162
+ "Unable to upload file to RegScale. Please check to see if %s "
163
+ + "extension is enabled in RegScale admin files configuration.",
164
+ ext,
165
+ )
166
+ return False
167
+
168
+ mime_type = File.determine_mime_type(Path(file_name).suffix)
169
+ if not mime_type:
170
+ return False
171
+ # set up file_data payload with the regscale_file dictionary
172
+ new_file = File(
173
+ parentId=parent_id,
174
+ tags=tags,
175
+ parentModule=parent_module,
176
+ uploadedById=api.config["userId"],
177
+ mimeType=mime_type,
178
+ id=regscale_file["id"],
179
+ fullPath=regscale_file["fullPath"],
180
+ trustedDisplayName=regscale_file["trustedDisplayName"],
181
+ trustedStorageName=regscale_file["trustedStorageName"],
182
+ uploadDate=regscale_file["uploadDate"],
183
+ fileHash=regscale_file["fileHash"],
184
+ shaHash=regscale_file["shaHash"],
185
+ size=(sys.getsizeof(file_data) if file_data else regscale_file["size"]),
186
+ )
187
+ # post the regscale_file data via RegScale API
188
+ file_res = api.post(
189
+ url=f"{api.config['domain']}/api/files",
190
+ headers=file_headers,
191
+ json=new_file.dict(),
192
+ )
193
+ if file_res.ok and return_object:
194
+ return File(**file_res.json()).model_dump()
195
+ else:
196
+ return False
197
+ return file_res.ok
198
+
199
+ @staticmethod
200
+ def get_existing_tags_dict() -> dict:
201
+ """Fetch existing tags and return a dictionary mapping tag titles to their IDs.
202
+ :return: Dictionary mapping tag titles to their IDs
203
+ :rtype: dict
204
+ """
205
+ existing_tag_list = Tag.get_list()
206
+ return {tag.title: tag.id for tag in existing_tag_list}
207
+
208
+ @staticmethod
209
+ def process_tag(tag: str, file_id: str, tags_dict: dict):
210
+ """Process a single tag and create a TagMapping if the tag exists.
211
+ :param str tag: The tag to process
212
+ :param str file_id: The file uuid
213
+ :param dict tags_dict: The dictionary mapping tag titles to their IDs
214
+ """
215
+ tag_id = tags_dict.get(tag)
216
+ if tag_id:
217
+ tag_mapping = TagMapping(parentId=file_id, parentModule="files", tagId=tag_id)
218
+ tag_mapping.create()
219
+
220
+ @staticmethod
221
+ def create_tag_mappings(file_res: any):
222
+ """Create tag mappings for the given file response.
223
+ :param any file_res: The file response from RegScale
224
+ """
225
+ if file_id := file_res.json().get("id"):
226
+ tags_dict = File.get_existing_tags_dict()
227
+ tags = file_res.json().get("tags", "")
228
+ for tag in tags.replace(" ", "").split(","):
229
+ File.process_tag(tag, file_id, tags_dict)
230
+
231
+ @staticmethod
232
+ def _check_compression(
233
+ size_limit_mb: int = 25, file_path: str = None, file_data: Optional[bytes] = None
234
+ ) -> Tuple[str, float]:
235
+ """
236
+ Function to check if the file should be auto compressed.
237
+
238
+ :param int size_limit_mb: The size limit in MB, defaults to 25
239
+ :param str file_path: The file path, defaults to None
240
+ :param Optional[bytes] file_data: File data to calculate size from, defaults to None
241
+ :return: The file path
242
+ :rtype: Tuple[str, float]
243
+ """
244
+ app = Application()
245
+ try:
246
+ size = os.path.getsize(file_path) # kb
247
+ except FileNotFoundError:
248
+ # get size from file_data
249
+ size = sys.getsizeof(file_data) # bytes
250
+ # convert bytes to kb
251
+ return file_path, size / 1024
252
+ app.logger.debug("File size: %i MB", size_limit_mb)
253
+ if size > size_limit_mb * 1024 * 1024:
254
+ app.logger.warning("The file is over %i MB. It will be compressed.", size_limit_mb)
255
+ # compress the file
256
+ output_path = Path(file_path).parent / Path(file_path).with_suffix(".gz").name
257
+ file_path = File._compress_file(file_path, output_path)
258
+ size = os.path.getsize(file_path)
259
+ return file_path, size
260
+
261
+ @staticmethod
262
+ def _compress_file(input_file_path: Union[Path, str], output_file_path: Union[Path, str]) -> str:
263
+ """
264
+ Function to compress a file using gzip
265
+
266
+ :param Union[Path, str] input_file_path: The input file path
267
+ :param Union[Path, str] output_file_path: The output file path
268
+ :return: The output file path
269
+ :rtype: str
270
+ """
271
+ with open(input_file_path, "rb") as f_in:
272
+ # Max compression
273
+ with gzip.GzipFile(filename=output_file_path, mode="wb", compresslevel=9) as f_out:
274
+ f_out.write(f_in.read())
275
+ return str(output_file_path)
276
+
277
+ @staticmethod
278
+ def _create_regscale_file(
279
+ file_path: str,
280
+ parent_id: int,
281
+ parent_module: str,
282
+ api: Api,
283
+ file_data: Optional[bytes] = None,
284
+ ) -> Optional[dict]:
285
+ """
286
+ Function to create a file within RegScale via API
287
+ :param str file_path: Path to the file
288
+ :param int parent_id: RegScale parent ID
289
+ :param str parent_module: RegScale module
290
+ :param Api api: API object
291
+ :param Optional[bytes] file_data: File data to upload, defaults to None
292
+ :raises: General error if unacceptable file type was provided
293
+ :return: Dictionary of the file object from RegScale or None
294
+ :rtype: Optional[dict]
295
+ """
296
+
297
+ # check if the file should be auto compressed
298
+ file_path, file_size = File._check_compression(file_path=file_path, size_limit_mb=100, file_data=file_data)
299
+
300
+ # get the file type of the provided file_path
301
+ file_type = get_file_type(file_path)
302
+
303
+ # get the file name from the provided file_path
304
+ file_name = get_file_name(file_path)
305
+
306
+ if file_size == 0:
307
+ raise ValueError("File size is 0 bytes")
308
+
309
+ if file_size > 104857600:
310
+ mb_size = file_size / 1024 / 1024
311
+ limit_size = 104857600 / 1024 / 1024 # 100 MB
312
+ raise ValueError(f"File size is {mb_size} MB. This is over the max file size of {limit_size} MB")
313
+
314
+ # set up file headers
315
+ file_headers = {
316
+ "Authorization": api.config["token"],
317
+ "Accept": "application/json",
318
+ }
319
+
320
+ # get the file type header
321
+ file_type_header = File.determine_mime_type(file_type)
322
+
323
+ # set the files up for the RegScale API Call
324
+ files = [
325
+ (
326
+ "file",
327
+ (
328
+ file_name,
329
+ file_data or open(file_path, "rb").read(),
330
+ file_type_header,
331
+ ),
332
+ )
333
+ ]
334
+
335
+ file_like_object = BytesIO(file_data) if file_data else open(file_path, "rb")
336
+
337
+ with file_like_object:
338
+ data = {
339
+ "id": parent_id,
340
+ "module": parent_module,
341
+ "shaHash": compute_hash(file_like_object),
342
+ }
343
+
344
+ # make the api call
345
+ file_response = api.post(
346
+ url=f"{api.config['domain']}/api/files/file",
347
+ headers=file_headers,
348
+ data=data,
349
+ files=files,
350
+ )
351
+ if not file_response.ok:
352
+ api.logger.warning(f"{file_response.status_code} - {file_response.text}")
353
+ return None
354
+ if file_response.ok:
355
+ data = file_response.json()
356
+ data["size"] = file_size
357
+ return data
358
+
359
+ @staticmethod
360
+ def determine_mime_type(file_type: str) -> Optional[str]:
361
+ """
362
+ Function to determine the mime type of a file
363
+
364
+ :param str file_type: The file type
365
+ :return: The mime type, if able to determine
366
+ :rtype: Optional[str]
367
+ """
368
+ # see file_type is an acceptable format and set the file_type_header accordingly
369
+ try:
370
+ file_type_header = mimetypes.types_map[file_type]
371
+ except KeyError:
372
+ if file_type == ".xlsx":
373
+ file_type_header = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
374
+ elif file_type == ".nessus":
375
+ file_type_header = "text/xml"
376
+ elif file_type == ".gz":
377
+ file_type_header = "application/gzip"
378
+ else:
379
+ logger = logging.getLogger("regscale")
380
+ logger.warning(f"Unacceptable file type for upload: {file_type}")
381
+ return None
382
+ return file_type_header
@@ -0,0 +1,37 @@
1
+ from enum import Enum
2
+ from typing import Optional
3
+
4
+ from pydantic import Field
5
+
6
+ from regscale.core.app.utils.app_utils import get_current_datetime
7
+ from regscale.models.regscale_models.regscale_model import RegScaleModel
8
+
9
+
10
+ class TagTypeEnum(Enum):
11
+ """
12
+ TagTypeEnum class
13
+ """
14
+
15
+ SYSTEM = "System"
16
+ EXPORT = "Export"
17
+ IMPORT = "Import"
18
+ DATA = "Data"
19
+ USER = "User"
20
+
21
+
22
+ class FileTag(RegScaleModel):
23
+ _module_slug = "filetags"
24
+
25
+ id: int = 0
26
+ uuid: Optional[str] = None
27
+ title: str = "" # required
28
+ tagType: TagTypeEnum = TagTypeEnum.USER.value # default to user. Use System for Export or Import
29
+ isPublic: bool = True
30
+ OscalRequired: bool = False
31
+ tagColor: Optional[str] = None # not currently implemented yet
32
+ tagTextColor: Optional[str] = None # not currently implemented yet
33
+ tenantsId: int = 1
34
+ createdById: str = Field(default_factory=RegScaleModel.get_user_id)
35
+ dateCreated: str = Field(default_factory=get_current_datetime)
36
+ lastUpdatedById: str = Field(default_factory=RegScaleModel.get_user_id)
37
+ dateLastUpdated: str = Field(default_factory=get_current_datetime)
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Model for Custom Fields in the application"""
4
+
5
+ import logging
6
+ from typing import Any, List, Optional, Dict
7
+
8
+ from pydantic import ConfigDict, Field
9
+
10
+ from regscale.models.regscale_models.regscale_model import RegScaleModel
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class FormFieldValue(RegScaleModel):
16
+ _module_slug = "formFieldValue"
17
+ formFieldName: Optional[str] = None
18
+ formFieldId: Optional[int] = None
19
+ data: Optional[str] = Field(default=None, alias="fieldValue")
20
+
21
+ @staticmethod
22
+ def _get_additional_endpoints() -> ConfigDict:
23
+ """
24
+ Get additional endpoints for the CustomFieldData model.
25
+ record id is the recordId like cases id or change id
26
+ formId is the tab_id from the module calls
27
+ :return: A dictionary of additional endpoints
28
+ :rtype: ConfigDict
29
+ """
30
+ return ConfigDict(
31
+ post_save_form_fields="/api/{model_slug}/saveFormFields/{recordId}/{moduleName}",
32
+ get_field_value="/api/{model_slug}/getFieldValues/{recordId}/{moduleName}/{formId}",
33
+ )
34
+
35
+ @staticmethod
36
+ def filter_dict_keys(data: Dict[str, Any], allowed_fields: List[str]) -> Dict[str, Any]:
37
+ """
38
+ Return a new dictionary containing only the keys from allowed_fields.
39
+
40
+ :param data: The original dictionary.
41
+ :param allowed_fields: A list of keys to keep in the dictionary.
42
+ :return: A new dictionary with only the allowed keys.
43
+ """
44
+ return {key: value for key, value in data.items() if key in allowed_fields}
45
+
46
+ @classmethod
47
+ def save_custom_data(cls, record_id: int, module_name: str, data: List[Any]) -> bool:
48
+ """
49
+ Save custom data for a record
50
+ :param record_id: record id
51
+ :param module_name: module name
52
+ :param data: data to save
53
+ :return: list of custom fields
54
+ """
55
+ fields = ["formFieldId", "data"]
56
+
57
+ # Suppose data is a list of Pydantic model instances.
58
+ # First, convert each instance to a dict.
59
+ data_dicts = [d.dict() for d in data]
60
+
61
+ # Now, filter each dictionary so that only the keys in `fields` remain.
62
+ filtered_data = [cls.filter_dict_keys(item, fields) for item in data_dicts]
63
+
64
+ result = cls._get_api_handler().post(
65
+ endpoint=cls.get_endpoint("post_save_form_fields").format(
66
+ model_slug=cls.get_module_slug(), recordId=record_id, moduleName=module_name
67
+ ),
68
+ data=filtered_data,
69
+ )
70
+ if result and result.ok:
71
+ return True
72
+ else:
73
+ cls.log_response_error(response=result)
74
+ return False
75
+
76
+ @classmethod
77
+ def get_field_values(cls, record_id: int, module_name: str, form_id: int) -> List["FormFieldValue"]:
78
+ """
79
+ Get custom data for a record
80
+ :param record_id: record id
81
+ :param module_name: module name
82
+ :param form_id: form id
83
+ :return: list of custom fields
84
+ """
85
+ result = cls._get_api_handler().get(
86
+ endpoint=cls.get_endpoint("get_field_value").format(
87
+ model_slug=cls.get_module_slug(), recordId=record_id, moduleName=module_name, formId=form_id
88
+ )
89
+ )
90
+ if result and result.ok:
91
+ return [cls(**o) for o in result.json()]
92
+ else:
93
+ cls.log_response_error(response=result)
94
+ return []