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,720 @@
1
+ """
2
+ Qualys Scan information
3
+ """
4
+
5
+ import ast
6
+ import logging
7
+
8
+ # pylint: disable=C0415
9
+ import re
10
+ from datetime import datetime
11
+ from typing import Any, Iterator, Optional, TypeVar, TextIO
12
+
13
+ from openpyxl.reader.excel import load_workbook
14
+
15
+ from regscale.core.app import create_logger
16
+ from regscale.core.app.application import Application
17
+ from regscale.core.app.utils.app_utils import get_current_datetime
18
+ from regscale.core.app.utils.parser_utils import safe_int, safe_float
19
+ from regscale.core.utils.date import date_str, datetime_str
20
+ from regscale.integrations.scanner_integration import IntegrationAsset
21
+ from regscale.models import (
22
+ Asset,
23
+ AssetCategory,
24
+ AssetType,
25
+ ImportValidater,
26
+ IssueSeverity,
27
+ SecurityPlan,
28
+ Vulnerability,
29
+ VulnerabilityStatus,
30
+ AssetStatus,
31
+ VulnerabilitySeverity,
32
+ )
33
+ from regscale.models.integration_models.flat_file_importer import FlatFileImporter
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+ T = TypeVar("T")
38
+ QG_HOST_ID = "QG Host ID"
39
+ CVE_ID = "CVE ID"
40
+ SEVERITY = "Severity"
41
+ EXPLOITABILITY = "Exploitability"
42
+ SOLUTION = "Solution"
43
+ DNS = "DNS"
44
+ IP = "IP"
45
+ OS = "OS"
46
+ NETBIOS = "NetBIOS"
47
+ FQDN = "FQDN"
48
+ IMAGE_ID_FIELD = "IMAGE ID"
49
+ CVE_ID_FIELD = "CVE ID"
50
+
51
+
52
+ class Qualys(FlatFileImporter):
53
+ """Qualys Scan information"""
54
+
55
+ title = "Qualys Scanner Export Integration"
56
+ asset_identifier_field = "name"
57
+
58
+ def __init__(self, **kwargs):
59
+ self.name = kwargs.get("name")
60
+ self.vuln_title = "Title"
61
+ self.fmt = "%Y-%m-%d"
62
+ self.dt_format = "%Y-%m-%d %H:%M:%S"
63
+ self.required_headers = [
64
+ SEVERITY,
65
+ self.vuln_title,
66
+ EXPLOITABILITY,
67
+ CVE_ID,
68
+ SOLUTION,
69
+ DNS,
70
+ IP,
71
+ QG_HOST_ID,
72
+ OS,
73
+ NETBIOS,
74
+ FQDN,
75
+ ]
76
+ logger = create_logger()
77
+ skip_rows = kwargs.pop("skip_rows")
78
+ self.mapping_file = kwargs.get("mappings_path")
79
+ self.disable_mapping = kwargs.get("disable_mapping")
80
+ self.validater = ImportValidater(
81
+ self.required_headers, kwargs.get("file_path"), self.mapping_file, self.disable_mapping, skip_rows=skip_rows
82
+ )
83
+ self.headers = self.validater.parsed_headers
84
+ self.mapping = self.validater.mapping
85
+ super().__init__(
86
+ logger=logger,
87
+ app=Application(),
88
+ headers=self.headers,
89
+ header_line_number=skip_rows,
90
+ asset_func=self.create_asset,
91
+ vuln_func=self.create_vuln,
92
+ **kwargs,
93
+ )
94
+ # header is line# 11
95
+ # start self.file_data from line #12
96
+
97
+ def create_asset(self, dat: Optional[dict] = None) -> Optional[Asset]:
98
+ """
99
+ Create an asset from a row in the Qualys file
100
+
101
+ :param Optional[dict] dat: Data row from CSV file
102
+ :return: RegScale Issue object or None
103
+ :rtype: Optional[Asset]
104
+ """
105
+ return Asset(
106
+ **{
107
+ "id": 0,
108
+ "name": self.mapping.get_value(dat, DNS),
109
+ "ipAddress": self.mapping.get_value(dat, IP),
110
+ "isPublic": True,
111
+ "status": "Active (On Network)",
112
+ "assetCategory": "Hardware",
113
+ "qualysId": self.mapping.get_value(dat, QG_HOST_ID), # UUID from Nessus HostProperties tag
114
+ "bLatestScan": True,
115
+ "bAuthenticatedScan": True,
116
+ "scanningTool": "Qualys",
117
+ "assetOwnerId": self.attributes.app.config["userId"],
118
+ "netBIOS": self.mapping.get_value(dat, NETBIOS),
119
+ "assetType": "Other",
120
+ "fqdn": self.mapping.get_value(dat, FQDN),
121
+ "operatingSystem": Asset.find_os(self.mapping.get_value(dat, OS)),
122
+ "operatingSystemVersion": self.mapping.get_value(dat, OS),
123
+ "systemAdministratorId": self.attributes.app.config["userId"],
124
+ "parentId": self.attributes.parent_id,
125
+ "parentModule": self.attributes.parent_module,
126
+ }
127
+ )
128
+
129
+ def create_vuln(self, dat: Optional[dict] = None, **kwargs) -> None:
130
+ """
131
+ Create a vuln from a row in the Qualys file
132
+
133
+ :param Optional[dict] dat: Data row from CSV file, defaults to None
134
+ :rtype: None
135
+ """
136
+ from regscale.integrations.commercial.qualys import map_qualys_severity_to_regscale
137
+
138
+ dns: str = self.mapping.get_value(dat, DNS)
139
+ other_id: str = self.mapping.get_value(dat, QG_HOST_ID)
140
+ distro: str = self.mapping.get_value(dat, OS)
141
+ cve: str = self.mapping.get_value(dat, CVE_ID)
142
+ description: str = self.mapping.get_value(dat, "Threat")
143
+ title = self.mapping.get_value(dat, self.vuln_title)
144
+ regscale_vuln = None
145
+ severity = self.mapping.get_value(dat, SEVERITY)
146
+ regscale_severity = map_qualys_severity_to_regscale(int(severity))[1]
147
+ config = self.attributes.app.config
148
+ asset_match = [asset for asset in self.data["assets"] if asset.name == dns]
149
+ asset = asset_match[0] if asset_match else None
150
+ if dat and asset_match:
151
+ regscale_vuln = Vulnerability(
152
+ id=0,
153
+ scanId=0, # set later
154
+ parentId=asset.id,
155
+ parentModule="assets",
156
+ ipAddress=self.mapping.get_value(dat, IP),
157
+ lastSeen=self.mapping.get_value(dat, "Last Detected"),
158
+ firstSeen=self.mapping.get_value(dat, "First Detected"),
159
+ daysOpen=None,
160
+ dns=self.mapping.get_value(dat, DNS, other_id),
161
+ mitigated=None,
162
+ operatingSystem=(Asset.find_os(distro) if Asset.find_os(distro) else None),
163
+ severity=regscale_severity,
164
+ plugInName=self.mapping.get_value(dat, self.vuln_title),
165
+ plugInId=self.mapping.get_value(dat, "QID"),
166
+ cve=cve,
167
+ vprScore=None,
168
+ cvsSv3BaseScore=self.extract_float(self.mapping.get_value(dat, "CVSS3.1 Base", 0.0)),
169
+ tenantsId=0,
170
+ title=title,
171
+ description=description,
172
+ plugInText=title,
173
+ createdById=config["userId"],
174
+ lastUpdatedById=config["userId"],
175
+ dateCreated=get_current_datetime(),
176
+ )
177
+ return regscale_vuln
178
+
179
+ @staticmethod
180
+ def extract_float(s: str) -> Any:
181
+ """
182
+ Extract a float from a string
183
+
184
+ :param str s: String to extract float from
185
+ :return: Float extracted from string or None
186
+ :rtype: Any
187
+ """
188
+ matches = re.findall(r"[-+]?[0-9]*\.?[0-9]+", s)
189
+ if matches:
190
+ return float(matches[0])
191
+ else:
192
+ return None
193
+
194
+
195
+ class QualysContainerScansImporter(FlatFileImporter): # (ScannerIntegration):
196
+ """
197
+ Import Qualys Container Scans data
198
+ """
199
+
200
+ plan_id = 0
201
+ # asset_identifier_field: str = "otherTrackingNumber"
202
+ threat_field = "THREAT"
203
+ severity_field = "SEVERITY"
204
+ title_field = "TITLE"
205
+ asset_notes_field = "IMAGE LABEL"
206
+ cve_v3_base = "CVSS3 BASE"
207
+ cve_base = "CVSS BASE"
208
+ mitigation_field = "SOLUTION"
209
+ external_id_field = "QID"
210
+ first_seen_field = "CREATED ON"
211
+ last_seen_field = "UPDATED"
212
+ container_id = "IMAGE UUID"
213
+ records = []
214
+
215
+ def __init__(self, **kwargs: dict):
216
+ self.asset_identifier_field = "otherTrackingNumber"
217
+ self.name = kwargs.get("name")
218
+ self.fmt = "%Y-%m-%d"
219
+ self.dt_format = "%Y-%m-%d %H:%M:%S"
220
+ self.plan_id = kwargs.get("parent_id")
221
+ self.required_headers = [
222
+ self.severity_field,
223
+ self.title_field,
224
+ self.asset_notes_field,
225
+ self.threat_field,
226
+ CVE_ID_FIELD,
227
+ self.mitigation_field,
228
+ self.cve_v3_base,
229
+ self.cve_base,
230
+ self.external_id_field,
231
+ self.first_seen_field,
232
+ self.last_seen_field,
233
+ self.container_id,
234
+ ]
235
+ self.file_path: str = kwargs.get("file_path")
236
+ skip_rows = kwargs.pop("skip_rows")
237
+ self.mapping_file = kwargs.get("mappings_path")
238
+ self.disable_mapping = kwargs.get("disable_mapping")
239
+ self.validater = ImportValidater(
240
+ required_headers=self.required_headers,
241
+ file_path=self.file_path,
242
+ mapping_file_path=self.mapping_file,
243
+ disable_mapping=self.disable_mapping,
244
+ skip_rows=skip_rows,
245
+ )
246
+ self.headers = self.validater.parsed_headers
247
+ self.header = self.headers
248
+ self.mapping = self.validater.mapping
249
+ # super().__init__(plan_id=self.plan_id)
250
+ # self.import_data()
251
+ kwargs["asset_identifier_field"] = "otherTrackingNumber"
252
+ super().__init__(
253
+ logger=logger,
254
+ app=Application(),
255
+ headers=self.required_headers,
256
+ header_line_number=skip_rows,
257
+ asset_func=self.create_asset,
258
+ vuln_func=self.create_vuln,
259
+ **kwargs,
260
+ )
261
+
262
+ def create_asset(self, row: Optional[dict] = None, **kwargs) -> Optional[Asset]:
263
+ """
264
+ Fetch assets from the Qualys CSV file
265
+
266
+ :return: Iterator of IntegrationAsset objects
267
+ :rtype: Iterator[IntegrationAsset]
268
+ """
269
+ bad_ids = ["", "0", "None", "Unknown"]
270
+ if self.mapping.get_value(row, self.container_id) in bad_ids:
271
+ return None
272
+ max_length = 450 # max length of asset name
273
+ asset = Asset(
274
+ name=self.mapping.get_value(row, self.asset_notes_field)[:max_length] or "Unknown",
275
+ notes=self.mapping.get_value(row, self.asset_notes_field),
276
+ otherTrackingNumber=self.mapping.get_value(row, self.container_id),
277
+ # identifier=self.mapping.get_value(row, IMAGE_ID_FIELD),
278
+ assetType=AssetType.VM.value,
279
+ assetCategory=AssetCategory.Software.value,
280
+ parentId=safe_int(self.plan_id),
281
+ parentModule=SecurityPlan.get_module_slug(),
282
+ status=AssetStatus.Active,
283
+ )
284
+ return asset
285
+
286
+ def handle_integration_date(self, input_date_str: str) -> str:
287
+ """
288
+ Handle the integration date to ingest to date and back to string to get into the correct format if needed.
289
+
290
+ :param str date_str: Date string
291
+ :return: Date string
292
+ :rtype: str
293
+ """
294
+ date_obj_value = datetime.strptime(
295
+ input_date_str,
296
+ "%Y-%m-%d %H:%M:%S %z %Z",
297
+ )
298
+ return date_str(date_obj_value, self.dt_format)
299
+
300
+ def create_vuln(self, row: Optional[dict] = None, **kwargs) -> Optional[Vulnerability]:
301
+ """
302
+ Fetch vulnerabilities from the Qualys CSV file
303
+
304
+ :return: Iterator of IntegrationFinding objects
305
+ :rtype: Vulnerability
306
+ """
307
+
308
+ if self.mapping.get_value(row, CVE_ID_FIELD) and isinstance(self.mapping.get_value(row, CVE_ID_FIELD), str):
309
+ for cve_id in self.mapping.get_value(row, CVE_ID_FIELD, "").split(","):
310
+ finding = Vulnerability(
311
+ title=self.mapping.get_value(row, self.title_field),
312
+ description=self.mapping.get_value(row, self.threat_field),
313
+ cve=cve_id,
314
+ severity=severity_to_regscale(self.mapping.get_value(row, self.severity_field)),
315
+ status=VulnerabilityStatus.Open,
316
+ cvsSv3BaseScore=safe_float(self.mapping.get_value(row, self.cve_v3_base, 0.0)),
317
+ vprScore=safe_float(self.mapping.get_value(row, self.cve_base, 0.0)),
318
+ firstSeen=self.handle_integration_date(
319
+ self.mapping.get_value(row, self.first_seen_field, get_current_datetime())
320
+ ),
321
+ lastSeen=self.handle_integration_date(
322
+ self.mapping.get_value(row, self.last_seen_field, get_current_datetime())
323
+ ),
324
+ plugInName=cve_id,
325
+ dns=self.mapping.get_value(row, self.container_id), # really asset_identifier
326
+ )
327
+ return finding
328
+
329
+
330
+ class QualysWasScansImporter(FlatFileImporter):
331
+ """
332
+ Import Qualys Container Scans data
333
+ """
334
+
335
+ plan_id = 0
336
+ threat_field = "THREAT"
337
+ severity_field = "SEVERITY"
338
+ title_field = "Title"
339
+ asset_notes_field = "Url"
340
+ cve_v3_base = "CVSS V3 Base"
341
+ # cve_base = "CVSS BASE"
342
+ # mitigation_field = "SOLUTION"
343
+ external_id_field = "ID"
344
+ first_seen_field = "Detection Date"
345
+ last_seen_field = "Detection Date"
346
+ container_id = "ID"
347
+ row_key = "VULNERABILITY"
348
+ records = []
349
+
350
+ def __init__(self, **kwargs: dict):
351
+ self.asset_identifier_field = "otherTrackingNumber"
352
+ self.name = kwargs.get("name")
353
+ self.fmt = "%Y-%m-%d"
354
+ self.dt_format = "%Y-%m-%d %H:%M:%S"
355
+ self.plan_id = kwargs.get("parent_id")
356
+ self.required_headers = [
357
+ "VULNERABILITY",
358
+ "ID",
359
+ "Detection ID",
360
+ "QID",
361
+ "Url",
362
+ "Param/Cookie",
363
+ "Function",
364
+ "Form Entry Point",
365
+ "Access Path",
366
+ "Authentication",
367
+ "Ajax Request",
368
+ "Ajax Request ID",
369
+ "Ignored",
370
+ "Ignore Reason",
371
+ "Ignore Date",
372
+ "Ignore User",
373
+ "Ignore Comments",
374
+ "Detection Date",
375
+ "Payload #1",
376
+ "Request Method #1",
377
+ "Request URL #1",
378
+ "Request Headers #1",
379
+ "Response #1",
380
+ "Evidence #1",
381
+ "Unique ID",
382
+ "Flags",
383
+ "Protocol",
384
+ "Virtual Host",
385
+ "IP",
386
+ "Port",
387
+ "Result",
388
+ "Info#1",
389
+ "CVSS V3 Base",
390
+ "CVSS V3 Temporal",
391
+ "CVSS V3 Attack Vector",
392
+ "Request Body #1",
393
+ "Potential",
394
+ ]
395
+ self.file_path: str = kwargs.get("file_path")
396
+ skip_rows = kwargs.pop("skip_rows")
397
+ self.mapping_file = kwargs.get("mappings_path")
398
+ self.disable_mapping = kwargs.get("disable_mapping")
399
+ self.validater = ImportValidater(
400
+ required_headers=self.required_headers,
401
+ file_path=self.file_path,
402
+ mapping_file_path=self.mapping_file,
403
+ disable_mapping=self.disable_mapping,
404
+ skip_rows=skip_rows,
405
+ )
406
+ self.headers = self.validater.parsed_headers
407
+ self.header = self.headers
408
+ self.mapping = self.validater.mapping
409
+ # super().__init__(plan_id=self.plan_id)
410
+ # self.import_data()
411
+ kwargs["asset_identifier_field"] = "otherTrackingNumber"
412
+ super().__init__(
413
+ logger=logger,
414
+ app=Application(),
415
+ headers=self.required_headers,
416
+ header_line_number=skip_rows,
417
+ asset_func=self.create_asset,
418
+ vuln_func=self.create_vuln,
419
+ **kwargs,
420
+ )
421
+
422
+ def convert_xlsx_to_dict(self, file: TextIO, start_line_number: int = 0) -> tuple:
423
+ """
424
+ Converts an xlsx file to a list of dictionaries, handling multiple sections.
425
+
426
+ :param TextIO file: The xlsx file to convert
427
+ :param int start_line_number: The line number to start reading from
428
+ :return: Tuple of merged data and headers
429
+ :rtype: tuple
430
+ """
431
+ # Load the workbook and select the first sheet
432
+ workbook = load_workbook(filename=file.name)
433
+ sheet = workbook.active
434
+
435
+ # Get all data from the sheet
436
+ data = list(sheet.values)
437
+
438
+ # Identify the start of the second section (QID header row)
439
+ second_section_start = next((i for i, row in enumerate(data) if row and row[0] == "QID"), None)
440
+ second_section_end = next(
441
+ (
442
+ i
443
+ for i, row in enumerate(data[second_section_start + 1 :], start=second_section_start + 1)
444
+ if row and row[0] != "QID"
445
+ ),
446
+ None,
447
+ )
448
+ first_section_end = next(
449
+ (
450
+ i
451
+ for i, row in enumerate(data[start_line_number + 1 :], start=start_line_number + 1)
452
+ if row and row[0] != "VULNERABILITY"
453
+ ),
454
+ None,
455
+ )
456
+
457
+ # Extract the first section
458
+ first_section_header = list(data[start_line_number])
459
+ first_section_data = data[start_line_number + 1 : first_section_end]
460
+ first_section_dict = [dict(zip(first_section_header, row)) for row in first_section_data]
461
+
462
+ # Extract the second section
463
+ second_section_header = list(data[second_section_start])
464
+ second_section_data = data[second_section_start + 1 : second_section_end]
465
+ second_section_dict = [dict(zip(second_section_header, row)) for row in second_section_data]
466
+
467
+ # Convert second section into a lookup dictionary based on QID
468
+ second_section_lookup = {item.get("Id"): item for item in second_section_dict}
469
+
470
+ # Keys to extract from the second section
471
+ keys_to_merge = ["Title", "Severity Level", "CVSS Base", "CWE", "Solution"]
472
+
473
+ # Merge the two sections by adding specific keys from the second section
474
+ merged_data = []
475
+ for item in first_section_dict:
476
+ qid = item.get("QID")
477
+ if qid in second_section_lookup:
478
+ for key in keys_to_merge:
479
+ item[key] = second_section_lookup[qid].get(key)
480
+ merged_data.append(item)
481
+
482
+ # Convert any string lists to actual lists
483
+ for dat in merged_data:
484
+ for key, val in dat.items():
485
+ if isinstance(val, str) and val.startswith("["):
486
+ try:
487
+ dat[key] = ast.literal_eval(val)
488
+ except SyntaxError as rex:
489
+ self.attributes.app.logger.debug("SyntaxError: %s", rex)
490
+
491
+ # Return merged data and headers
492
+ return merged_data, first_section_header + keys_to_merge
493
+
494
+ def create_asset(self, row: Optional[dict] = None, **kwargs) -> Optional[Asset]:
495
+ """
496
+ Fetch assets from the Qualys CSV file
497
+
498
+ :return: Iterator of IntegrationAsset objects
499
+ :rtype: Iterator[IntegrationAsset]
500
+ """
501
+ bad_ids = ["", "0", "None", "Unknown"]
502
+ if self.mapping.get_value(row, self.container_id) in bad_ids:
503
+ return None
504
+ max_length = 450 # max length of asset name
505
+ asset = Asset(
506
+ name=self.mapping.get_value(row, self.asset_notes_field)[:max_length] or "Unknown",
507
+ notes=self.mapping.get_value(row, self.asset_notes_field),
508
+ otherTrackingNumber=self.mapping.get_value(row, self.container_id),
509
+ # identifier=self.mapping.get_value(row, IMAGE_ID_FIELD),
510
+ assetType=AssetType.Other.value,
511
+ assetCategory=AssetCategory.Software.value,
512
+ parentId=safe_int(self.plan_id),
513
+ parentModule=SecurityPlan.get_module_slug(),
514
+ status=AssetStatus.Active,
515
+ )
516
+ return asset
517
+
518
+ def handle_integration_date(self, input_date_str: str) -> str:
519
+ """
520
+ Handle the integration date to ingest to date and back to string to get into the correct format if needed.
521
+
522
+ :param str input_date_str: Date string
523
+ :return: Date string
524
+ :rtype: str
525
+ """
526
+ if not input_date_str:
527
+ return get_current_datetime()
528
+ try:
529
+ date_obj_value = datetime.strptime(
530
+ input_date_str,
531
+ "%d %b %Y %I:%M%p %Z%z",
532
+ )
533
+ except ValueError:
534
+ return get_current_datetime()
535
+ return date_str(date_obj_value, self.dt_format)
536
+
537
+ def create_vuln(self, row: Optional[dict] = None, **kwargs) -> Optional[Vulnerability]:
538
+ """
539
+ Fetch vulnerabilities from the Qualys CSV file
540
+
541
+ :return: Iterator of IntegrationFinding objects
542
+ :rtype: Vulnerability
543
+ """
544
+ # additional_fields = ["Title", "Severity Level", "CVSS Base", "CWE", "Solution"]
545
+ finding = Vulnerability(
546
+ title=row.get("Title", self.mapping.get_value(row, "Url")),
547
+ description=row.get("Solution"),
548
+ cve=row.get("CWE"),
549
+ severity=severity_to_regscale(row.get("Severity Level", "1")),
550
+ status=VulnerabilityStatus.Open,
551
+ cvsSv3BaseScore=safe_float(self.mapping.get_value(row, self.cve_v3_base, 0.0)),
552
+ vprScore=safe_float(row.get("CVSS Base", 0.0)),
553
+ firstSeen=self.handle_integration_date(
554
+ self.mapping.get_value(row, self.first_seen_field, get_current_datetime())
555
+ ),
556
+ lastSeen=self.handle_integration_date(
557
+ self.mapping.get_value(row, self.last_seen_field, get_current_datetime())
558
+ ),
559
+ plugInName=row.get("CWE"),
560
+ dns=self.mapping.get_value(row, self.container_id), # really asset_identifier
561
+ )
562
+ return finding
563
+
564
+
565
+ class QualysPolicyScansImporter(FlatFileImporter):
566
+ """
567
+ Import Qualys Policy Scans data
568
+ """
569
+
570
+ plan_id = 0
571
+ records = []
572
+
573
+ def __init__(self, **kwargs: dict):
574
+ self.asset_identifier_field = "qualysId"
575
+ self.name = kwargs.get("name")
576
+ self.fmt = "%Y-%m-%d"
577
+ self.dt_format = "%m/%d/%Y at %H:%M:%S (%Z%z)"
578
+ self.plan_id = kwargs.get("parent_id")
579
+ self.required_headers = [
580
+ "Host IP",
581
+ "DNS Hostname",
582
+ "NetBIOS Hostname",
583
+ "Tracking Method",
584
+ "Operating System",
585
+ "NETWORK",
586
+ "Last Scan Date",
587
+ "Evaluation Date",
588
+ "Control ID",
589
+ "Technology",
590
+ "Control",
591
+ "Criticality Label",
592
+ "Criticality Value",
593
+ "Instance",
594
+ "Rationale",
595
+ "Status",
596
+ "Remediation",
597
+ "Deprecated",
598
+ "Evidence",
599
+ "Exception Assignee",
600
+ "Exception Status",
601
+ "Exception End Date",
602
+ "Exception Creator",
603
+ "Exception Created Date",
604
+ "Exception Modifier",
605
+ "Exception Modified Date",
606
+ "Exception Comments History",
607
+ "Cause of Failure",
608
+ "Qualys Host ID",
609
+ "Previous Status",
610
+ ]
611
+ self.file_path: str = kwargs.get("file_path")
612
+ skip_rows = kwargs.pop("skip_rows")
613
+ self.mapping_file = kwargs.get("mappings_path")
614
+ self.disable_mapping = kwargs.get("disable_mapping")
615
+ self.validater = ImportValidater(
616
+ required_headers=self.required_headers,
617
+ file_path=self.file_path,
618
+ mapping_file_path=self.mapping_file,
619
+ disable_mapping=self.disable_mapping,
620
+ skip_rows=skip_rows,
621
+ )
622
+ self.headers = self.validater.parsed_headers
623
+ self.header = self.headers
624
+ self.mapping = self.validater.mapping
625
+ # super().__init__(plan_id=self.plan_id)
626
+ # self.import_data()
627
+ kwargs["asset_identifier_field"] = self.asset_identifier_field
628
+ super().__init__(
629
+ logger=logger,
630
+ app=Application(),
631
+ headers=self.required_headers,
632
+ header_line_number=skip_rows,
633
+ asset_func=self.create_asset,
634
+ vuln_func=self.create_vuln,
635
+ **kwargs,
636
+ )
637
+
638
+ def create_asset(self, row: Optional[dict] = None, **kwargs) -> Optional[Asset]:
639
+ """
640
+ Fetch assets from the Qualys CSV file
641
+
642
+ :return: Iterator of IntegrationAsset objects
643
+ :rtype: Iterator[IntegrationAsset]
644
+ """
645
+ max_length = 450 # max length of asset name
646
+ asset = Asset(
647
+ name=row.get("Control", "Uknown")[:max_length],
648
+ # notes=self.mapping.get_value(row, self.asset_notes_field),
649
+ ipAddress=self.mapping.get_value(row, "Host IP"),
650
+ operatingSystem=self.mapping.get_value(row, "Operating System"),
651
+ qualysId=self.mapping.get_value(row, "Control ID"),
652
+ otherTrackingNumber=self.mapping.get_value(row, "Control ID"),
653
+ # identifier=self.mapping.get_value(row, IMAGE_ID_FIELD),
654
+ assetType=AssetType.Other.value,
655
+ assetCategory=AssetCategory.Software.value,
656
+ parentId=safe_int(self.plan_id),
657
+ parentModule=SecurityPlan.get_module_slug(),
658
+ status=AssetStatus.Active,
659
+ )
660
+ return asset
661
+
662
+ def handle_integration_date(self, input_date_str: Optional[str]) -> str:
663
+ """
664
+ Handle the integration date to ingest to date and back to string to get into the correct format if needed.
665
+
666
+ :param str input_date_str: Date string
667
+ :return: Date string
668
+ :rtype: str
669
+ """
670
+ if not input_date_str:
671
+ return get_current_datetime()
672
+ date_obj_value = datetime.strptime(
673
+ input_date_str,
674
+ self.dt_format,
675
+ )
676
+ return datetime_str(date_obj_value)
677
+
678
+ def create_vuln(self, row: Optional[dict] = None, **kwargs) -> Optional[Vulnerability]:
679
+ """
680
+ Fetch vulnerabilities from the Qualys CSV file
681
+
682
+ :return: Iterator of IntegrationFinding objects
683
+ :rtype: Vulnerability
684
+ """
685
+ finding = Vulnerability(
686
+ title=self.mapping.get_value(row, "Control"),
687
+ description=self.mapping.get_value(row, "Rationale"),
688
+ severity=severity_to_regscale(self.mapping.get_value(row, "Criticality Value")),
689
+ status=(
690
+ VulnerabilityStatus.Open
691
+ if self.mapping.get_value(row, "Status") == "Failed"
692
+ else VulnerabilityStatus.Closed
693
+ ),
694
+ cvsSv3BaseScore=safe_float(0.0),
695
+ vprScore=safe_float(0.0),
696
+ firstSeen=self.handle_integration_date(self.mapping.get_value(row, "Last Scan Date", None)),
697
+ lastSeen=self.handle_integration_date(self.mapping.get_value(row, "Evaluation Date", None)),
698
+ plugInId=self.mapping.get_value(row, "Control ID"),
699
+ plugInName="QualysPolicyScan",
700
+ dns=self.mapping.get_value(row, "Control ID"), # really asset_identifier
701
+ )
702
+ return finding
703
+
704
+
705
+ def severity_to_regscale(severity: str) -> VulnerabilitySeverity:
706
+ """
707
+ Convert Qualys severity to RegScale severity
708
+ severity is given in numbers from 1-5, 5 being the highest
709
+ :param str severity: Qualys severity to map to a RegScale severity
710
+ :return: RegScale severity
711
+ :rtype: IssueSeverity
712
+ """
713
+ severity_mapping = {
714
+ "1": VulnerabilitySeverity.Low,
715
+ "2": VulnerabilitySeverity.Low,
716
+ "3": VulnerabilitySeverity.Medium,
717
+ "4": VulnerabilitySeverity.High,
718
+ "5": VulnerabilitySeverity.Critical,
719
+ }
720
+ return severity_mapping.get(severity, VulnerabilitySeverity.Low)