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,459 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """RegScale File Comparison"""
4
+
5
+ # standard python imports
6
+ from typing import TYPE_CHECKING, Optional
7
+
8
+ if TYPE_CHECKING:
9
+ import pandas as pd # Type Checking
10
+ from datetime import datetime, timedelta
11
+ from os.path import exists
12
+ from pathlib import Path
13
+ from typing import Any, Tuple
14
+ from rich.progress import TaskID
15
+
16
+ import click
17
+
18
+ from regscale.core.app.api import Api
19
+ from regscale.core.app.utils.app_utils import (
20
+ check_license,
21
+ create_progress_object,
22
+ error_and_exit,
23
+ get_current_datetime,
24
+ get_file_name,
25
+ get_file_type,
26
+ get_recent_files,
27
+ )
28
+ from regscale.core.app.utils.regscale_utils import verify_provided_module
29
+ from regscale.models.app_models.click import NotRequiredIf, regscale_id, regscale_module
30
+ from regscale.models.regscale_models import Assessment, File
31
+
32
+ job_progress = create_progress_object()
33
+ XLSX = ".xlsx"
34
+ CSV = ".csv"
35
+ XLS = ".xls"
36
+ SUPPORTED_FILE_TYPES = [CSV, XLSX, XLS]
37
+
38
+
39
+ @click.group()
40
+ def compare():
41
+ """Create RegScale Assessment of differences after comparing two files."""
42
+
43
+
44
+ @compare.command(name="compare_files")
45
+ @click.option(
46
+ "--most_recent_in_file_path",
47
+ type=click.Path(exists=True, dir_okay=True, file_okay=False, path_type=Path),
48
+ help="Grab two most recent files in the provided directory for comparison.",
49
+ default=None,
50
+ cls=NotRequiredIf,
51
+ not_required_if=["old_file", "new_file"],
52
+ )
53
+ @click.option(
54
+ "--most_recent_file_type",
55
+ type=click.Choice([".csv", XLSX], case_sensitive=False),
56
+ help="Filter the directory for .csv or .xlsx file types.",
57
+ default=None,
58
+ cls=NotRequiredIf,
59
+ not_required_if=["old_file", "new_file"],
60
+ )
61
+ @click.option(
62
+ "--old_file",
63
+ type=click.Path(exists=True, dir_okay=False, file_okay=True, path_type=Path),
64
+ help=(
65
+ "Enter file path of the original file to compare: must be used with --new_file, "
66
+ + "not required if --most_recent_in_file_path & --most_recent_file_type is used."
67
+ ),
68
+ cls=NotRequiredIf,
69
+ not_required_if=["most_recent_in_file_path", "most_recent_file_type"],
70
+ )
71
+ @click.option(
72
+ "--new_file",
73
+ type=click.Path(exists=True, dir_okay=False, file_okay=True, path_type=Path),
74
+ help=(
75
+ "Enter file path of new file to compare: must be used with --old_file, "
76
+ + "not required if --most_recent_in_file_path & --most_recent_file_type is used."
77
+ ),
78
+ cls=NotRequiredIf,
79
+ not_required_if=["most_recent_in_file_path", "most_recent_file_type"],
80
+ )
81
+ @click.option(
82
+ "--key",
83
+ type=click.STRING,
84
+ help="Enter unique key to compare the files.",
85
+ prompt="Enter the key/column to compare files",
86
+ required=True,
87
+ )
88
+ @regscale_id()
89
+ @regscale_module()
90
+ def compare_files_cli(
91
+ old_file: str,
92
+ new_file: str,
93
+ most_recent_in_file_path: Path,
94
+ most_recent_file_type: str,
95
+ key: str,
96
+ regscale_id: int,
97
+ regscale_module: str,
98
+ ):
99
+ """Compare the two given files while using the provided key for any differences.
100
+ Supports csv, xls and xlsx files."""
101
+ compare_files(
102
+ old_file=old_file,
103
+ new_file=new_file,
104
+ most_recent_in_file_path=most_recent_in_file_path,
105
+ most_recent_file_type=most_recent_file_type,
106
+ key=key,
107
+ parent_id=regscale_id,
108
+ parent_module=regscale_module,
109
+ )
110
+
111
+
112
+ def compare_files(
113
+ key: str,
114
+ parent_id: int,
115
+ parent_module: str,
116
+ old_file: Optional[str] = None,
117
+ new_file: Optional[str] = None,
118
+ most_recent_in_file_path: Optional[Path] = None,
119
+ most_recent_file_type: Optional[str] = None,
120
+ ) -> None:
121
+ """Compare the two given files while using the provided key for any differences
122
+
123
+ :param str key: Unique key to compare the files
124
+ :param int parent_id: ID of the RegScale Module
125
+ :param str parent_module: Name of the RegScale Module
126
+ :param str old_file: File path of the original file to compare
127
+ :param str new_file: File path of new file to compare
128
+ :param Path most_recent_in_file_path: Path to the directory to find the two most recent files
129
+ :param str most_recent_file_type: File type to filter the directory for
130
+ :rtype: None
131
+ """
132
+ app = check_license()
133
+ api = Api()
134
+
135
+ # see if provided RegScale Module is an accepted option
136
+ verify_provided_module(parent_module)
137
+
138
+ # see if most_recent_in argument was used, get the old and new file
139
+ if most_recent_in_file_path:
140
+ # get the two most_recent_file_type in the provided most_recent_in_file_path
141
+ recent_files = get_recent_files(
142
+ file_path=most_recent_in_file_path,
143
+ file_count=2,
144
+ file_type=most_recent_file_type,
145
+ )
146
+ # verify we have two files to compare
147
+ if len(recent_files) == 2:
148
+ # set the old_file and new_file accordingly
149
+ old_file = recent_files[1]
150
+ new_file = recent_files[0]
151
+ else:
152
+ # notify user we don't have two files to compare and exit application
153
+ error_and_exit(
154
+ f"Required 2 files to compare, but only 1 {most_recent_file_type}"
155
+ f"file found in {most_recent_in_file_path}!"
156
+ )
157
+ # make sure both file paths exist
158
+ if not exists(old_file) and not exists(new_file):
159
+ error_and_exit("Please check the file paths of the provided files and try again.")
160
+ with job_progress:
161
+ # check the file extensions and compare them
162
+ old_file_type, new_file_type = get_file_type(old_file), get_file_type(new_file)
163
+ file_type = None
164
+ for supported_file_type in SUPPORTED_FILE_TYPES:
165
+ if old_file_type == new_file_type:
166
+ file_type = supported_file_type
167
+ if not file_type:
168
+ error_and_exit(
169
+ f"{old_file_type or new_file_type} files are not a supported file type provided for comparison."
170
+ )
171
+ # get the file names of from the provided file paths
172
+ old_file_name, new_file_name = get_file_name(old_file), get_file_name(new_file)
173
+
174
+ # create task for progress bar
175
+ comparing = job_progress.add_task(f"[#f8b737]Comparing {file_type} files for differences...", total=1)
176
+ output, old_row_count, new_row_count = comparison(old_file, new_file, key, file_type)
177
+
178
+ # mark the comparing task as complete
179
+ job_progress.update(comparing, advance=1)
180
+
181
+ # create task for formatting data
182
+ formatting = job_progress.add_task(
183
+ "[#ef5d23]Formatting data of comparison outcome...",
184
+ total=1,
185
+ )
186
+
187
+ # drop any rows that has no value for the provided key
188
+ output = output.dropna(subset=[key])
189
+
190
+ # check if there were any changes, if no changes the assessment
191
+ # will be created with complete as the status
192
+ if output.empty:
193
+ status = "Complete"
194
+ actual_finish = get_current_datetime()
195
+ report = f"<h3>No differences between {old_file_name} & {new_file_name}</h3>"
196
+ else:
197
+ status = "Scheduled"
198
+ actual_finish = False
199
+ # format report string for assessment
200
+ report = (
201
+ f"<h3>{old_file_name} Deleted Rows</h3>"
202
+ f"{create_filtered_html_table(output, 'flag', 'deleted')}"
203
+ f"<h3>{new_file_name} Added Rows</h3>"
204
+ f"{create_filtered_html_table(output, 'flag', 'added')}"
205
+ )
206
+
207
+ # get data overview
208
+ overview = create_overview(
209
+ data=output,
210
+ old_row_count=old_row_count,
211
+ new_row_count=new_row_count,
212
+ old_file=old_file_name,
213
+ new_file=new_file_name,
214
+ )
215
+
216
+ # set up descript for assessment
217
+ desc = (
218
+ f"Comparing two {file_type} files using {key} as a key.<br>"
219
+ f"<b>{old_file_name}</b> contains <b>{old_row_count}</b> row(s) and<br>"
220
+ f"<b>{new_file_name}</b> contains <b>{new_row_count}</b> row(s)<br>{overview}"
221
+ )
222
+
223
+ # create new task for creating assessment in RegScale
224
+ create_assessment_task = job_progress.add_task(
225
+ "[#21a5bb]Creating assessment in RegScale...",
226
+ total=1,
227
+ )
228
+
229
+ if created_assessment := create_assessment(
230
+ config=app.config,
231
+ file_type=file_type,
232
+ desc=desc,
233
+ parent_id=parent_id,
234
+ parent_module=parent_module,
235
+ report=report,
236
+ status=status,
237
+ formatting=formatting,
238
+ actual_finish=actual_finish,
239
+ ):
240
+ # mark the create_assessment task as complete
241
+ job_progress.update(create_assessment_task, advance=1)
242
+
243
+ # create new task for file uploads
244
+ upload_files = job_progress.add_task(
245
+ "[#0866b4]Uploading files to the new RegScale Assessment...",
246
+ total=2,
247
+ )
248
+ upload_files_to_assessment(
249
+ api=api,
250
+ assessment_id=created_assessment.id,
251
+ file_paths=[old_file, new_file],
252
+ upload_files_task=upload_files,
253
+ )
254
+
255
+ # notify user assessment was created and output a link to it
256
+ app.logger.info(
257
+ "New assessment has been created and marked as %s: %s/form/assessments/%s",
258
+ status,
259
+ app.config["domain"].rstrip("/"),
260
+ created_assessment.id,
261
+ )
262
+ else:
263
+ # notify the user we were unable to create the assessment int RegScale
264
+ error_and_exit("Unable to create new RegScale Assessment!")
265
+
266
+
267
+ def upload_files_to_assessment(api: Api, assessment_id: int, file_paths: list[str], upload_files_task: TaskID):
268
+ """
269
+ Function to upload files to a RegScale Assessment
270
+
271
+ :param Api api: RegScale API object
272
+ :param int assessment_id: ID of the RegScale Assessment
273
+ :param list[str] file_paths: List of file paths to upload
274
+ :param TaskID upload_files_task: TaskID for progress bar
275
+ :rtype: None
276
+ """
277
+ upload_count = 0
278
+ for file in file_paths:
279
+ if File.upload_file_to_regscale(
280
+ file_name=file,
281
+ parent_id=assessment_id,
282
+ parent_module="assessments",
283
+ api=api,
284
+ ):
285
+ upload_count += 1
286
+ job_progress.update(upload_files_task, advance=1)
287
+ if upload_count == 2:
288
+ api.logger.info("Files uploaded to the new assessment in RegScale successfully.")
289
+ else:
290
+ api.logger.error("Unable to upload both files to the assessment in RegScale.")
291
+
292
+
293
+ def create_assessment(
294
+ config: dict,
295
+ file_type: str,
296
+ desc: str,
297
+ parent_id: int,
298
+ parent_module: str,
299
+ report: str,
300
+ status: str,
301
+ formatting: TaskID,
302
+ actual_finish: Optional[str] = None,
303
+ ) -> Assessment:
304
+ """
305
+ Function to create a new Assessment in RegScale
306
+
307
+ :param dict config: Config file for the application
308
+ :param str file_type: Type of file being compared
309
+ :param str desc: Description of the comparison
310
+ :param int parent_id: ID of the parent object
311
+ :param str parent_module: Module of the parent object
312
+ :param str report: Report of the comparison
313
+ :param str status: Status of the comparison
314
+ :param TaskID formatting: TaskID for formatting task
315
+ :param Optional[str] actual_finish: Actual finish date of the assessment
316
+ :return: New Assessment object created in RegScale
317
+ :rtype: Assessment
318
+ """
319
+
320
+ # set up title for the new Assessment
321
+ title = f"{file_type} Comparison for {parent_module.title()}-{parent_id}"
322
+
323
+ # set up plannedFinish date with days from config file
324
+ finish_date = (datetime.now() + timedelta(days=config["assessmentDays"])).strftime("%Y-%m-%dT%H:%M:%S")
325
+
326
+ # map to assessment dataclass
327
+ new_assessment = Assessment(
328
+ title=title,
329
+ assessmentType="Control Testing",
330
+ plannedStart=get_current_datetime(),
331
+ plannedFinish=finish_date,
332
+ assessmentReport=report,
333
+ assessmentPlan=desc,
334
+ dateCreated=get_current_datetime(),
335
+ dateLastUpdated=get_current_datetime(),
336
+ parentModule=parent_module,
337
+ parentId=parent_id,
338
+ status=status,
339
+ )
340
+ # update the appropriate fields to complete the assessment
341
+ if actual_finish:
342
+ new_assessment.actualFinish = actual_finish
343
+ new_assessment.assessmentResult = "Pass"
344
+
345
+ # mark the formatting task as complete
346
+ job_progress.update(formatting, advance=1)
347
+
348
+ return new_assessment.create()
349
+
350
+
351
+ def comparison(file_one: str, file_two: str, key: str, file_type: str) -> Tuple["pd.DataFrame", int, int]:
352
+ """
353
+ Function that will compare two files using the provided key, uses
354
+ a comparison method depending on the provided file_type and will
355
+ return the differences between the two files in a pandas dataframe
356
+
357
+ :param str file_one: Old file to compare
358
+ :param str file_two: New file to compare
359
+ :param str key: key field to compare the files on
360
+ :param str file_type: file type of the two files
361
+ :return: Tuple[difference between two files as panda's dataframe, # of rows in file_one, # of rows in file_two]
362
+ :rtype: Tuple[pd.DataFrame, int, int]
363
+ """
364
+ import pandas as pd # Optimize import performance
365
+
366
+ if file_type.lower() not in [".csv", XLSX, ".xls"]:
367
+ error_and_exit("Unsupported file type provided for comparison.")
368
+
369
+ df1, df2 = [], []
370
+ if file_type.lower() == ".csv":
371
+ # open the files
372
+ df1 = pd.read_csv(file_one)
373
+ df2 = pd.read_csv(file_two)
374
+ elif file_type.lower() in [XLSX, ".xls"]:
375
+ # open the files
376
+ df1 = pd.read_excel(file_one)
377
+ df2 = pd.read_excel(file_two)
378
+ # add flags to each dataset
379
+ df1["flag"] = "deleted"
380
+ df2["flag"] = "added"
381
+
382
+ # combine the two datasets
383
+ df = pd.concat([df1, df2])
384
+
385
+ # get the differences between the two datasets
386
+ output = df.drop_duplicates(df.columns.difference(["flag", key]), keep=False)
387
+
388
+ # return the differences and the row counts for each file
389
+ return output, len(df1), len(df2)
390
+
391
+
392
+ def create_overview(
393
+ data: "pd.DataFrame",
394
+ old_row_count: int,
395
+ new_row_count: int,
396
+ old_file: str,
397
+ new_file: str,
398
+ ) -> str:
399
+ """
400
+ Function to create html formatted description from comparing
401
+ data from provided pandas dataframe and row counts
402
+
403
+ :param pd.DataFrame data: Pandas dataframe of data to format and filter
404
+ :param int old_row_count: Number of rows in the old file
405
+ :param int new_row_count: Number of rows in the new file
406
+ :param str old_file: Old file name
407
+ :param str new_file: New file name
408
+ :return: string of HTML formatted table of the provided data
409
+ :rtype: str
410
+ """
411
+ # convert data frame to a series style dictionary
412
+ data = data.to_dict("series")
413
+
414
+ # create dictionary to store all the changes
415
+ changes = {"deletes": 0, "additions": 0}
416
+
417
+ # combine the flags and update the changes dictionary
418
+ for change in data["flag"].items():
419
+ if change[1] == "deleted":
420
+ changes["deletes"] += 1
421
+ elif change[1] == "added":
422
+ changes["additions"] += 1
423
+
424
+ # calculate % of rows deleted from old_file
425
+ percent_deleted = round(((old_row_count - changes["deletes"]) / old_row_count) * -100 + 100, 2)
426
+
427
+ # calculate % of rows added to new_file
428
+ percent_added = round(((new_row_count - changes["additions"]) / new_row_count) * -100 + 100, 2)
429
+
430
+ # format the html string with the changes and percentages
431
+ overview = f"<br>{changes['deletes']} row(s) deleted from {old_file}: ({percent_deleted}%)<br>"
432
+ overview += f"<br>{changes['additions']} row(s) added to {new_file}: ({percent_added}%)<br>"
433
+
434
+ # return the html formatted string
435
+ return overview
436
+
437
+
438
+ def create_filtered_html_table(data: "pd.DataFrame", column: str, value: Any, pop_flag: bool = True) -> str:
439
+ """
440
+ Function to return an HTML formatted table of data from the provided
441
+ pandas dataframe where the provided column == provided value
442
+
443
+ :param pd.DataFrame data: Data to create into an HTML table
444
+ :param str column: Column to filter the data on
445
+ :param Any value: Value to filter the data with
446
+ :param bool pop_flag: Whether to remove the column used to filter the data defaults to True
447
+ :return: String of HTML formatted table
448
+ :rtype: str
449
+ """
450
+ # filter the provided pandas dataframe on the column and value provided
451
+ filtered_data = data.loc[data[column] == value]
452
+
453
+ # remove the field if requested, default is True
454
+ if pop_flag:
455
+ # remove the column from the dataset
456
+ filtered_data.pop(column)
457
+
458
+ # return HTML formatted data table
459
+ return None if filtered_data.empty else filtered_data.to_html(justify="left", index=False)