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
regscale/dev/docs.py ADDED
@@ -0,0 +1,384 @@
1
+ """Tools and functions for the generation of documentation."""
2
+
3
+ import os
4
+ import subprocess
5
+ from typing import Optional, TYPE_CHECKING
6
+
7
+ import requests
8
+
9
+ if TYPE_CHECKING:
10
+ from regscale.core.app.api import Api
11
+
12
+ from sphinx.application import Sphinx
13
+ from sphinx.cmd.quickstart import generate
14
+
15
+ from regscale import __version__
16
+
17
+ INDEX_RST = "index.rst"
18
+ APP_JSON = "application/json"
19
+
20
+
21
+ def init_sphinx(
22
+ docs_path: str = "docs",
23
+ project_name: str = "RegScale-CLI",
24
+ project_version: str = __version__,
25
+ project_author: str = "Travis Howerton",
26
+ ) -> None:
27
+ """Initialize Sphinx for generating documentation
28
+
29
+ :param str docs_path: Path to the docs folder
30
+ :param str project_name: Name of the project
31
+ :param str project_version: Version of the project
32
+ :param str project_author: Author of the project
33
+ :rtype: None
34
+ """
35
+ generate(
36
+ {
37
+ "path": docs_path,
38
+ "sep": False,
39
+ "dot": "_",
40
+ "project": project_name,
41
+ "name": project_name,
42
+ "author": project_author,
43
+ "version": project_version,
44
+ "release": project_version,
45
+ "suffix": ".rst",
46
+ "ext_autodoc": True,
47
+ "ext_doctest": False, # change to true later?
48
+ "ext_intersphinx": False, # change to true later?
49
+ "ext_todo": False, # change to true later?
50
+ "ext_coverage": False, # change to true later?
51
+ "ext_mathjax": False, # change to true later?
52
+ "ext_ifconfig": False, # change to true later?
53
+ "ext_viewcode": False, # change to true later?
54
+ "makefile": True,
55
+ "batchfile": False,
56
+ "make_mode": False,
57
+ "quiet": True,
58
+ "master": "index",
59
+ "root_doc": "index",
60
+ }
61
+ )
62
+
63
+
64
+ def enable_extensions_in_conf(docs_path: str, hide_source: bool = False) -> None:
65
+ """Enable extensions in the conf.py file
66
+
67
+ :param str docs_path: Path to the docs folder
68
+ :param bool hide_source: Hide the source code link in the generated documentation, defaults to False
69
+ :rtype: None
70
+ """
71
+ conf_file_path = os.path.join(docs_path, "conf.py")
72
+ with open(conf_file_path, "r+") as f:
73
+ lines = f.readlines()
74
+ f.seek(0)
75
+ for line in lines:
76
+ if line.startswith("extensions = []"):
77
+ f.write("extensions = [\n")
78
+ f.write(
79
+ " 'sphinx.ext.autodoc',\n"
80
+ " 'sphinx.ext.napoleon',\n"
81
+ " 'sphinx.ext.autosectionlabel',\n"
82
+ " 'sphinx.ext.autodoc.typehints',\n"
83
+ " 'sphinx.ext.coverage',\n"
84
+ " 'sphinx_click',\n"
85
+ )
86
+ if not hide_source:
87
+ f.write(" 'sphinx.ext.viewcode',\n")
88
+ f.write("]\n")
89
+ elif line.startswith("html_theme"):
90
+ f.write("html_theme = 'classic'\ntodo_include_todos = True\n")
91
+ else:
92
+ f.write(line)
93
+ f.truncate()
94
+
95
+
96
+ def create_master_index_rst(docs_path: str, src_path: str, title: str = "RegScale-CLI Documentation") -> None:
97
+ """Create the master index.rst file
98
+
99
+ :param str docs_path: Path to the docs folder
100
+ :param str src_path: Path to the source folder
101
+ :param str title: Title of the documentation, defaults to "RegScale-CLI Documentation"
102
+ :rtype: None
103
+ """
104
+ with open(os.path.join(docs_path, INDEX_RST), "w") as f:
105
+ f.write(f"{title}\n")
106
+ f.write("=" * len(title) + "\n\n")
107
+ f.write(".. toctree::\n")
108
+ f.write(" :maxdepth: 2\n\n")
109
+ f.write(" click_commands\n")
110
+ f.write(" regscale_regscale\n")
111
+ for item in os.listdir(src_path):
112
+ if os.path.isdir(os.path.join(src_path, item)) and item not in [
113
+ "__pycache__",
114
+ "__init__.py",
115
+ ]:
116
+ f.write(f" {item}/index\n")
117
+ for item in os.listdir(docs_path):
118
+ if item.endswith(".rst") and item != INDEX_RST:
119
+ module_name = os.path.splitext(item)[0]
120
+ f.write(f" {module_name}\n")
121
+
122
+
123
+ def create_click_commands_rst(docs_path: str) -> None:
124
+ """Create .rst file for the click commands
125
+
126
+ :param str docs_path: Path to the docs folder
127
+ :rtype: None
128
+ """
129
+ # this format cannot be altered because it will break the Sphinx build
130
+ content = """
131
+ RegScale-CLI Click Commands
132
+ ===========================
133
+
134
+ .. click:: regscale.regscale:cli
135
+ :prog: regscale
136
+ :show-nested:
137
+
138
+ .. click:: regscale.dev.cli:cli
139
+ :prog: regscale-dev
140
+ :show-nested:
141
+
142
+ """
143
+ with open(os.path.join(docs_path, "click_commands.rst"), "w") as f:
144
+ f.write(content)
145
+
146
+
147
+ def create_rst_for_module(module_name: str, rst_path: str) -> None:
148
+ """Create .rst file for a Python module
149
+
150
+ :param str module_name: Name of the module
151
+ :param str rst_path: Path to the .rst file
152
+ :rtype: None
153
+ """
154
+ # this format cannot be altered because it will break the Sphinx build
155
+ content = f"""
156
+ {module_name}
157
+ {'=' * len(module_name)}
158
+
159
+ .. automodule:: {module_name.replace(',', '_')}
160
+ :members:
161
+ :undoc-members:
162
+ :show-inheritance:
163
+ """
164
+ with open(os.path.join(rst_path, f"{module_name.replace('.', '_')}.rst"), "w") as f:
165
+ f.write(content)
166
+
167
+
168
+ def traverse_and_create_rst(
169
+ root_path: str,
170
+ current_path: str,
171
+ rst_path: str,
172
+ prefix: str = "",
173
+ exclude_dirs: Optional[list[str]] = None,
174
+ ) -> None:
175
+ """
176
+ Traverse through the directory recursively and create .rst files for each Python module
177
+
178
+ :param str root_path: Root path of the project
179
+ :param str current_path: Current path of the directory
180
+ :param str rst_path: Path to the .rst file(s)
181
+ :param str prefix: Prefix to be added to the module name, defaults to ""
182
+ :param Optional[list[str]] exclude_dirs: List of directories to exclude, defaults to ["__pycache__"]
183
+ :rtype: None
184
+ """
185
+ # Check if the directory has any subdirectories with Python files
186
+ if exclude_dirs is None:
187
+ exclude_dirs = ["__pycache__"]
188
+ has_subdir_with_py = any(
189
+ os.path.isdir(os.path.join(current_path, item))
190
+ and any(sub_item.endswith(".py") for sub_item in os.listdir(os.path.join(current_path, item)))
191
+ for item in os.listdir(current_path)
192
+ if item not in exclude_dirs
193
+ )
194
+
195
+ for item in os.listdir(current_path):
196
+ if item in exclude_dirs:
197
+ continue
198
+ item_path = os.path.join(current_path, item)
199
+ if os.path.isdir(item_path):
200
+ new_rst_path = os.path.join(rst_path, item)
201
+ os.makedirs(new_rst_path, exist_ok=True)
202
+ new_prefix = f"{prefix}.{item}" if prefix else item
203
+
204
+ # Recur further only if this directory has subdirectories with Python files
205
+ if has_subdir_with_py:
206
+ traverse_and_create_rst(root_path, item_path, new_rst_path, new_prefix, exclude_dirs)
207
+
208
+ # Generate index.rst for this sub-directory
209
+ with open(os.path.join(new_rst_path, "index.rst"), "w") as f:
210
+ f.write(f"{item}\n")
211
+ f.write("=" * len(item) + "\n\n")
212
+ f.write(".. toctree::\n :maxdepth: 1\n\n")
213
+ for sub_item in os.listdir(new_rst_path):
214
+ if sub_item.endswith(".rst"):
215
+ module_name = os.path.splitext(sub_item)[0]
216
+ f.write(f" {module_name}\n")
217
+ elif os.path.isdir(os.path.join(new_rst_path, sub_item)):
218
+ f.write(f" {sub_item}/index\n")
219
+ elif item.endswith(".py") and item != "__init__.py":
220
+ # Generate RST only if this directory does not have subdirectories with Python files
221
+ if not has_subdir_with_py:
222
+ module_name = f"{prefix}.{os.path.splitext(item)[0]}" if prefix else os.path.splitext(item)[0]
223
+ create_rst_for_module(module_name, rst_path)
224
+
225
+
226
+ def build_docs(src_path: str, build_path: str, doctype: str) -> None:
227
+ """Build the documentation
228
+
229
+ :param str src_path: Path to the source folder
230
+ :param str build_path: Path to the build folder
231
+ :param str doctype: Type of documentation
232
+ :rtype: None
233
+ """
234
+ app = Sphinx(src_path, src_path, build_path, build_path, doctype)
235
+ app.build()
236
+
237
+
238
+ def generate_docs(
239
+ project_path: str = os.getcwd(),
240
+ src_folder_name: str = "regscale",
241
+ docs_folder_name: str = "docs",
242
+ hide_source: bool = False,
243
+ ) -> None:
244
+ """Generate the documentation
245
+
246
+ :param str project_path: Path to the project, defaults to os.getcwd()
247
+ :param str src_folder_name: Name of the source folder, defaults to "regscale"
248
+ :param str docs_folder_name: Name of the docs folder, defaults to "docs"
249
+ :param bool hide_source: Hide the source code in the documentation, defaults to False
250
+ :rtype: None
251
+ """
252
+ docs_path = os.path.join(project_path, docs_folder_name)
253
+ src_path = os.path.join(project_path, src_folder_name)
254
+ exclude_dirs = None
255
+ os.makedirs(docs_path, exist_ok=True)
256
+ init_sphinx(docs_path)
257
+ enable_extensions_in_conf(docs_path="docs", hide_source=hide_source)
258
+ create_click_commands_rst(docs_path)
259
+ create_master_index_rst(docs_path, src_path)
260
+ # traverse through the regscale directory recursively to create .rst files
261
+ if hide_source:
262
+ exclude_dirs = ["dev", "airflow", "ansible", "regscale-dev", "__pycache__", "docs"]
263
+ traverse_and_create_rst(
264
+ root_path=src_path, current_path=src_path, rst_path=docs_path, prefix=src_folder_name, exclude_dirs=exclude_dirs
265
+ )
266
+ # build the documentation
267
+ build_path = os.path.join(docs_path, "_build")
268
+ build_docs(docs_path, build_path, "html")
269
+ # build_docs(docs_path, build_path, 'text')
270
+ subprocess.run([f"sphinx-apidoc -o {docs_path} {src_path}"], shell=True)
271
+
272
+
273
+ def update_readme_io(api: "Api", root: str, file: str) -> None:
274
+ """
275
+ Update the readme.io documentation for the CLI
276
+
277
+ :param Api api: The API object to use to update the documentation
278
+ :param str root: The root directory of containing file with readme io slug and data
279
+ :param str file: File name to parse the readme io slug and data to update readme.io
280
+ :rtype: None
281
+ """
282
+ readme_slug = None
283
+ payload_data = ""
284
+ with open(f"{root}/{file}", "r") as f:
285
+ for line in f:
286
+ if "# readme_slug:" in line:
287
+ readme_slug = line.split(":")[1].strip()
288
+ else:
289
+ for payload_line in f:
290
+ payload_data += payload_line
291
+ payload_data = payload_data.lstrip("\n")
292
+
293
+ # get the current data from the readme.io project
294
+ res = api.get(f"https://dash.readme.com/api/v1/docs/{readme_slug}")
295
+ if res.raise_for_status() and not res.ok:
296
+ api.logger.error(
297
+ f"Failed to retrieve readme.io slug: {readme_slug}\n{res.status_code}: {res.reason}\n{res.text}"
298
+ )
299
+ current_data = {"body": "", "hidden": False}
300
+ else:
301
+ current_data = res.json()
302
+
303
+ if current_data["body"] == payload_data:
304
+ api.logger.info(f"Readme.io slug: {readme_slug} is already up to date")
305
+ return
306
+ api.logger.info(f"Updating readme.io slug: {readme_slug} with {root}/{file} and payload {payload_data}")
307
+ res = api.put(
308
+ f"https://dash.readme.com/api/v1/docs/{readme_slug}", # This is the same url for every readme.io project
309
+ json={"hidden": current_data["hidden"], "body": payload_data},
310
+ headers={"Content-Type": APP_JSON},
311
+ )
312
+ if not res.raise_for_status() and res.ok:
313
+ api.logger.info(f"Successfully updated readme.io slug: {readme_slug}")
314
+ else:
315
+ api.logger.error(f"Failed to update readme.io slug: {readme_slug}\n{res.status_code}: {res.reason}\n{res.text}")
316
+
317
+
318
+ def update_confluence(api: "Api", url: str, root: str, file: str) -> None:
319
+ """
320
+ Update the confluence documentation for the CLI
321
+
322
+ :param Api api: The API object to use to update the documentation
323
+ :param str url: The base URL for the confluence instance
324
+ :param str root: The root directory of containing file with confluence id and data
325
+ :param str file: File name to parse the confluence id and data from to update confluence
326
+ :rtype: None
327
+ """
328
+ import json
329
+
330
+ payload = {
331
+ "id": None,
332
+ "type": "page",
333
+ "status": "current",
334
+ "title": None,
335
+ "version": {"number": 1},
336
+ "body": {"storage": {"value": "", "representation": "storage"}},
337
+ }
338
+ headers = {"Content-Type": APP_JSON}
339
+ confluence_id, parsed_data = _parse_confluence_md(root, file)
340
+
341
+ # get the current version of the confluence page
342
+ res = api.get(f"{url}/rest/api/content/{confluence_id}?expand=body.storage,version", headers=headers)
343
+ if res.raise_for_status() or not res.ok:
344
+ api.logger.error(f"Failed to get confluence id: {confluence_id}\n{res.status_code}: {res.reason}\n{res.text}")
345
+ return
346
+ json_data = res.json()
347
+ payload["title"] = json_data["title"]
348
+ payload["body"]["storage"]["value"] = parsed_data
349
+ payload["version"]["number"] = json_data["version"]["number"] + 1
350
+ api.logger.debug(f"Updated version number from {json_data['version']['number']} to {payload['version']['number']}")
351
+ api.logger.info(f"Updating confluence id {confluence_id} with {root}/{file} and body {json_data}")
352
+ res = api.put(
353
+ f"{url}/rest/api/content/{confluence_id}",
354
+ json=payload,
355
+ headers=headers,
356
+ )
357
+ if not res.raise_for_status() and res.ok:
358
+ api.logger.info(f"Successfully updated confluence id: {confluence_id}")
359
+ else:
360
+ api.logger.error(
361
+ f"Failed to update confluence id: {confluence_id}\n{res.status_code}: {res.reason}\n{res.text}"
362
+ )
363
+
364
+
365
+ def _parse_confluence_md(root: str, file: str) -> tuple[str, str]:
366
+ """
367
+ Parse the confluence id and data from the markdown file
368
+
369
+ :param str root: The root directory of containing file with confluence id and data
370
+ :param str file: File name to parse the confluence id and data from
371
+ :rtype: tuple[str, str]
372
+ """
373
+ confluence_id = None
374
+ payload = ""
375
+ with open(f"{root}/{file}", "r") as f:
376
+ for line in f:
377
+ if "# ConfluenceID:" in line:
378
+ confluence_id = line.split(":")[1].strip()
379
+ else:
380
+ for payload_line in f:
381
+ payload += payload_line
382
+ # parse the string data between the first and last occurrence of ``` to get the payload
383
+ payload = payload[payload.find("```html") + 7 : payload.rfind("```")]
384
+ return confluence_id, payload
@@ -0,0 +1,26 @@
1
+ """Provide development tools for monitoring."""
2
+
3
+ from watchdog.events import FileSystemEventHandler, FileSystemEvent
4
+
5
+ from regscale.utils.files import print_file_contents
6
+
7
+
8
+ class FileModifiedHandler(FileSystemEventHandler):
9
+ """Handler for file modification events
10
+
11
+ :param str file_path: Path to the file to monitor
12
+ """
13
+
14
+ def __init__(self, file_path: str) -> None:
15
+ super().__init__()
16
+ self.file_path = file_path
17
+
18
+ def on_modified(self, event: FileSystemEvent) -> None:
19
+ """Handle a file modification event and print the file contents
20
+
21
+ :param FileSystemEvent event: The event to handle
22
+ :rtype: None
23
+ """
24
+ if not event.is_directory and event.src_path == self.file_path:
25
+ print(f"{self.file_path} modified!")
26
+ print_file_contents(self.file_path)
@@ -0,0 +1,216 @@
1
+ """Profiling tools for use to measure performance of RegScale-CLI."""
2
+
3
+ import cProfile
4
+ import csv
5
+ import gc
6
+ import platform
7
+ import re
8
+ import subprocess
9
+ import sys
10
+ import time
11
+ from contextlib import contextmanager
12
+ from contextlib import suppress
13
+ from pstats import Stats
14
+ from typing import Callable, List, Tuple
15
+
16
+ from click.testing import CliRunner
17
+ from rich.console import Console
18
+ from rich.progress import track
19
+ from rich.table import Table
20
+
21
+ from regscale.dev.dirs import trim_relative_subpath, PROFILING_DIRS
22
+
23
+
24
+ @contextmanager
25
+ def suppress_system_exit():
26
+ """Suppress SystemExit exceptions when profiling functions in the CLI
27
+
28
+ :yields: Generator
29
+ """
30
+ with suppress(SystemExit):
31
+ yield
32
+
33
+
34
+ def parse_time(output: str, time_type: str) -> float:
35
+ """Parse the time from the output of the time command
36
+
37
+ :param str output: The output of the time command
38
+ :param str time_type: The type of time to parse
39
+ :return: The parsed time
40
+ :rtype: float
41
+ """
42
+ time_type_2 = "total" if time_type == "real" else time_type
43
+ match = re.search(rf"{time_type}\s+(?:(\d+)m)?(\d+\.\d+)s", output) or re.search(
44
+ rf"(?:(\d+)m)?(\d+\.\d+)s\s+{time_type_2}", output
45
+ )
46
+ if match:
47
+ return sum(float(x) * 60**i if x is not None else 0 for i, x in enumerate(reversed(match.groups())))
48
+ return 0
49
+
50
+
51
+ def profile_my_function(func: Callable, *args: Tuple, iterations: int = 100, **kwargs: dict) -> None:
52
+ """Profile a function using cProfile
53
+
54
+ :param Callable func: The function to profile
55
+ :param Tuple *args: The args to pass to the function
56
+ :param int iterations: The number of times to run the function, defaults to 100
57
+ :param dict **kwargs: The kwargs to pass to the function
58
+ :rtype: None
59
+ """
60
+ stats_list: List[Stats] = []
61
+ for _ in track(
62
+ range(iterations),
63
+ description=f"Timing RegScale-CLI function {func.__name__} {iterations} times...",
64
+ ):
65
+ gc.collect()
66
+ profiler = cProfile.Profile()
67
+ profiler.enable()
68
+ with suppress_system_exit():
69
+ profiler.runcall(func, *args, **kwargs)
70
+ profiler.disable()
71
+ stats = Stats(profiler)
72
+ stats_list.append(stats)
73
+
74
+ master_stats = Stats()
75
+
76
+ for stats in stats_list:
77
+ master_stats.add(stats)
78
+
79
+ master_stats.dump_stats("profile_stats.pstat")
80
+ # initialize summary variables
81
+ total_time: float = 0
82
+ all_times: List[float] = []
83
+ # write the stats to a CSV file
84
+ with open("profile_stats.csv", "w") as file:
85
+ csv_writer = csv.writer(file)
86
+ csv_writer.writerow(
87
+ [
88
+ "Module",
89
+ "Function",
90
+ "Primitive Calls",
91
+ "Total Calls",
92
+ "Time",
93
+ "Cumulative Time",
94
+ "Percentage",
95
+ ]
96
+ )
97
+ for func_info, func_stats in master_stats.stats.items():
98
+ _, _, func_name = func_info
99
+ cc, nc, tt, ct, _ = func_stats
100
+ total_time += tt
101
+ all_times.append(tt)
102
+
103
+ for func_info, func_stats in master_stats.stats.items():
104
+ filename, _, func_name = func_info
105
+ cc, nc, tt, ct, _ = func_stats
106
+ percentage = "{:.3f}%".format(((tt * 1000) / (total_time / 1000)) * 100)
107
+ row = [
108
+ trim_relative_subpath(filename, PROFILING_DIRS) or filename, # trim the relative path from the filename
109
+ func_name,
110
+ cc,
111
+ nc,
112
+ "{:.8f}".format(tt),
113
+ "{:.8f}".format(ct),
114
+ percentage,
115
+ ]
116
+ csv_writer.writerow(row)
117
+
118
+
119
+ def profile_about_command() -> None:
120
+ """Profile the about command
121
+
122
+ :rtype: None
123
+ """
124
+ runner = CliRunner()
125
+ from regscale.regscale import cli
126
+
127
+ _ = runner.invoke(cli, ["about"])
128
+
129
+
130
+ def calculate_cli_import_time() -> float:
131
+ """Calculate the import time for the CLI
132
+
133
+ :return: The import time
134
+ :rtype: float
135
+ """
136
+ start_time = time.time()
137
+ # pylint: disable=unused-import
138
+
139
+ # pylint: enable=unused-import
140
+ end_time = time.time()
141
+ return end_time - start_time
142
+
143
+
144
+ def calculate_load_times(command: str = "regscale about", iterations: int = 100, no_output: bool = False) -> dict:
145
+ """Calculate the load times for a command
146
+
147
+ :param str command: The command to run
148
+ :param int iterations: The number of times to run the command
149
+ :param bool no_output: Whether to output the results to the console
150
+ :return: The load times
151
+ :rtype: dict
152
+ """
153
+ total_user, min_user, max_user = 0, float("inf"), 0
154
+ total_sys, min_sys, max_sys = 0, float("inf"), 0
155
+ total_real, min_real, max_real = 0, float("inf"), 0
156
+ if platform.system() == "Windows":
157
+ console = Console()
158
+ console.print("[red]Calculating start time on Windows is not supported.")
159
+ sys.exit(0)
160
+ else:
161
+ sub_process_command = f'bash -c "time {command}"'
162
+ for _ in track(range(iterations), description=f"Timing RegScale-CLI load {iterations} times..."):
163
+ result = subprocess.run(
164
+ sub_process_command,
165
+ shell=True,
166
+ stderr=subprocess.PIPE,
167
+ stdout=subprocess.PIPE,
168
+ )
169
+ output = result.stderr.decode("utf-8")
170
+ # extract user, sys, and real time using regular expressions
171
+ user_time = parse_time(output, "user")
172
+ sys_time = parse_time(output, "sys")
173
+ real_time = parse_time(output, "real")
174
+
175
+ # update total, min, and max values
176
+ total_user += user_time
177
+ min_user = min(min_user, user_time)
178
+ max_user = max(max_user, user_time)
179
+ total_sys += sys_time
180
+ min_sys = min(min_sys, sys_time)
181
+ max_sys = max(max_sys, sys_time)
182
+ total_real += real_time
183
+ min_real = min(min_real, real_time)
184
+ max_real = max(max_real, real_time)
185
+
186
+ avg_user = round(total_user / iterations, 3)
187
+ avg_sys = round(total_sys / iterations, 3)
188
+ avg_real = round(total_real / iterations, 3)
189
+
190
+ if not no_output:
191
+ console = Console()
192
+ table = Table(title=f"Load times over {iterations} iterations")
193
+ table.add_column("Metric")
194
+ table.add_column("User Time (s)")
195
+ table.add_column("Sys Time (s)")
196
+ table.add_column("Real Time (s)")
197
+ table.add_row(
198
+ "[green]Min",
199
+ f"[green]{min_user}",
200
+ f"[green]{min_sys}",
201
+ f"[green]{min_real}",
202
+ )
203
+ table.add_row("[red]Max", f"[red]{max_user}", f"[red]{max_sys}", f"[red]{max_real}")
204
+ table.add_row(
205
+ "[yellow]Avg",
206
+ f"[yellow]{avg_user}",
207
+ f"[yellow]{avg_sys}",
208
+ f"[yellow]{avg_real}",
209
+ )
210
+ console.print(table)
211
+ sys.exit(0)
212
+ return {
213
+ "Avg User Time": avg_user,
214
+ "Avg Sys Time": avg_sys,
215
+ "Avg Real Time": avg_real,
216
+ }
@@ -0,0 +1,4 @@
1
+ """RegScale exceptions"""
2
+
3
+ from .license_exception import LicenseException
4
+ from .validation_exception import ValidationException
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # standard python imports
4
+
5
+
6
+ class LicenseException(Exception):
7
+ """Exception raised when trying an action on an unlicensed RegScale instance."""
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # standard python imports
4
+
5
+
6
+ class ValidationException(Exception):
7
+ """Exception raised when trying to import an invalid file based off of required headers."""
8
+
9
+ pass
@@ -0,0 +1 @@
1
+ from regscale.integrations.integration import Integration