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,91 @@
1
+ from typing import List, Type, TypeVar
2
+
3
+ import pytest
4
+ from unittest.mock import patch
5
+ from regscale.models.regscale_models.regscale_model import RegScaleModel
6
+ from regscale.models import regscale_models
7
+
8
+ T = TypeVar("T", bound=RegScaleModel)
9
+
10
+
11
+ @pytest.fixture
12
+ def mock_regscale_models(mock_api_handler):
13
+ """
14
+ Fixture to patch various methods for RegScaleModel and its subclasses to use model cache and avoid API calls.
15
+
16
+ :param mock_api_handler: Mocked API handler
17
+ :yield: None
18
+ """
19
+
20
+ def mock_get_all_by_parent(cls: Type[T], parent_id: int, *args, **kwargs) -> List[T]:
21
+ """
22
+ Mock method to get all objects by parent ID.
23
+
24
+ :param Type[T] cls: The class of the objects to retrieve
25
+ :param int parent_id: The ID of the parent object
26
+ :return: List of objects matching the parent ID and other criteria
27
+ """
28
+ parent_module = kwargs.get("parent_module")
29
+
30
+ return [
31
+ obj
32
+ for obj in cls._object_cache.values()
33
+ if isinstance(obj, cls)
34
+ and parent_id == getattr(obj, cls._parent_id_field)
35
+ and (not parent_module or obj.parentModule == parent_module)
36
+ ]
37
+
38
+ def mock_issue_find_by_other_identifier(other_identifier: str) -> List[regscale_models.Issue]:
39
+ """
40
+ Mock method to find issues by other identifier.
41
+
42
+ :param str other_identifier: The other identifier to search for
43
+ :return: List of Issue objects matching the other identifier
44
+ """
45
+ return [
46
+ issue
47
+ for issue in regscale_models.Issue._object_cache.values()
48
+ if isinstance(issue, regscale_models.Issue) and issue.otherIdentifier == other_identifier
49
+ ]
50
+
51
+ def mock_vulnerability_mapping_find_by_issue(
52
+ issue_id: int, status: str = "all"
53
+ ) -> List[regscale_models.VulnerabilityMapping]:
54
+ """
55
+ Mock method to find vulnerability mappings by issue ID and status.
56
+
57
+ :param int issue_id: The ID of the issue to search for
58
+ :param str status: The status of the vulnerability mapping (default: "all")
59
+ :return: List of VulnerabilityMapping objects matching the criteria
60
+ """
61
+ return [
62
+ mapping
63
+ for mapping in regscale_models.VulnerabilityMapping._object_cache.values()
64
+ if isinstance(mapping, regscale_models.VulnerabilityMapping)
65
+ and (status == "all" or mapping.status.lower() == status.lower())
66
+ ]
67
+
68
+ def mock_get_object(cls: Type[T], object_id: int) -> T:
69
+ """
70
+ Mock method to get an object by its ID.
71
+
72
+ :param Type[T] cls: The class of the object to retrieve
73
+ :param int object_id: The ID of the object to retrieve
74
+ :return: The object if found, None otherwise
75
+ """
76
+ # Try to get the object from the cache first
77
+ cached_object = next((obj for obj in cls._object_cache.values() if obj.id == object_id), None)
78
+ if cached_object:
79
+ return cached_object
80
+
81
+ # If not in cache, simulate an API call (you might want to raise an exception here instead)
82
+ return None
83
+
84
+ with patch.object(RegScaleModel, "get_all_by_parent", classmethod(mock_get_all_by_parent)), patch.object(
85
+ regscale_models.Issue, "find_by_other_identifier", mock_issue_find_by_other_identifier
86
+ ), patch.object(
87
+ regscale_models.VulnerabilityMapping, "find_by_issue", mock_vulnerability_mapping_find_by_issue
88
+ ), patch.object(
89
+ RegScaleModel, "get_object", classmethod(mock_get_object)
90
+ ):
91
+ yield
@@ -0,0 +1,144 @@
1
+ """Test fixture class used during CLI testing"""
2
+
3
+ import logging
4
+ import os
5
+ import random
6
+ import sys
7
+ import uuid
8
+ from logging import Logger
9
+ from pathlib import Path
10
+ from typing import Optional, Union
11
+
12
+ import pytest
13
+ import yaml
14
+
15
+ from regscale.core.app.api import Api
16
+ from regscale.core.app.application import Application
17
+ from regscale.core.app.internal.login import login
18
+ from tests.conftest_utils import INIT_CONF_PYTEST
19
+
20
+
21
+ class CLITestFixture:
22
+ """
23
+ Test fixture for the CLI application
24
+ """
25
+
26
+ app: Application
27
+ api: Api
28
+ config: dict
29
+ logger: Logger
30
+ title_prefix: str
31
+
32
+ def update_config_with_env(self):
33
+ """
34
+ Update the Application.config values with the corresponding env values if both conditions are met:
35
+ - the config value is the same as the template
36
+ - the env value is not empty
37
+ """
38
+ key_env = {
39
+ "domain": "REGSCALE_DOMAIN",
40
+ "token": "REGSCALE_TOKEN",
41
+ "jiraUrl": "JIRA_URL",
42
+ "jiraUserName": "JIRA_USERNAME",
43
+ "jiraApiToken": "JIRA_API_TOKEN",
44
+ "wizClientId": "WIZCLIENTID",
45
+ "wizClientSecret": "WIZCLIENTSECRET",
46
+ "snowUrl": "SNOW_URL",
47
+ "snowUserName": "SNOW_USERNAME",
48
+ "snowPassword": "SNOW_PASSWORD",
49
+ "sicuraUrl": "SICURA_URL",
50
+ "sicuraToken": "SICURA_TOKEN",
51
+ "nistCpeApiKey": "NIST_CPE_API_KEY",
52
+ "qualysUserName": "QUALYS_USERNAME",
53
+ "qualysPassword": "QUALYS_PASSWORD",
54
+ "qualysUrl": "QUALYS_URL",
55
+ }
56
+ for key, env in key_env.items():
57
+ config_value = self.app.config.get(key, "")
58
+ template_value = self.app.template.get(key, "")
59
+ env_value = os.getenv(env, "")
60
+ self.app.config[key] = env_value if config_value == template_value and env_value else config_value
61
+
62
+ hard_coded_test_values = {
63
+ "gcpScanType": "organization",
64
+ "gcpOrganizationId": "000000000000",
65
+ "timeout": 360,
66
+ }
67
+ for key, value in hard_coded_test_values.items():
68
+ self.app.config[key] = value
69
+
70
+ def verify_config(self, config_key: Union[str, list[str]], compare_template: bool = True) -> None:
71
+ """
72
+ Verify the configuration values and compare them to the template
73
+
74
+ :param Union[str, list[str]] config_key: Configuration key or list of keys to verify
75
+ :param bool compare_template: Whether to compare the configuration values to the template, defaults to True
76
+ :rtype: None
77
+ """
78
+ if isinstance(config_key, list):
79
+ for key in config_key:
80
+ self.verify_config(key, compare_template)
81
+ else:
82
+ assert self.app.config.get(config_key) is not None
83
+ if compare_template:
84
+ assert self.app.config.get(config_key) != self.app.template.get(config_key)
85
+
86
+ @staticmethod
87
+ def get_tests_dir(suffix: Optional[Union[str, Path]] = None) -> Path:
88
+ """
89
+ Get the current working directory
90
+
91
+ :param Optional[Union[str, Path]] suffix: The suffix to append to the path, defaults to None
92
+ :return: Path of the responses directory with a trailing slash
93
+ :rtype: Path
94
+ """
95
+ cur_dir = Path(os.getcwd())
96
+ if cur_dir.stem.lower() in ["commercial", "internal", "public"]:
97
+ # get the repo root
98
+ # EX: regscale-cli/tests/regscale/integrations/INTEGRATIONTYPE/test_name.py
99
+ cwd = cur_dir.parent.parent.parent.parent
100
+ elif cur_dir.stem.lower() == "tests":
101
+ cwd = Path("../")
102
+ else:
103
+ cwd = Path(os.getcwd())
104
+ return cwd / suffix if suffix else cwd
105
+
106
+ @pytest.fixture(autouse=True)
107
+ def cli_test_fixture(self, request):
108
+ """
109
+ Test fixture for the CLI application
110
+
111
+ :param request: Pytest request object
112
+ :return: RegScale CLI Application object
113
+ :rtype: Application
114
+ """
115
+ logger = logging.getLogger(__name__)
116
+ logger.info("Test Setup")
117
+
118
+ # Application is already set up, so we just need to update the config with the environment variables
119
+ self.app = Application()
120
+ self.app.local_config = False
121
+ self.update_config_with_env()
122
+ # Convert the INIT_CONF_PYTEST string into a dictionary
123
+ config_dict = yaml.safe_load(INIT_CONF_PYTEST)
124
+ test_config = {**config_dict, **self.app.config}
125
+ self.app.save_config(test_config)
126
+ self.config = self.app.config
127
+ python_info = f"Python{sys.version_info.major}.{sys.version_info.minor}"
128
+ random_id = uuid.uuid3(uuid.NAMESPACE_DNS, python_info)
129
+ self.title_prefix = f"{python_info} Test {random_id} - "
130
+ # login with token if available
131
+ if token := os.getenv("REGSCALE_TOKEN") or self.config.get("token"):
132
+ login(token=token, app=self.app)
133
+ elif "REGSCALE_USERNAME" in os.environ and "REGSCALE_PASSWORD" in os.environ:
134
+ login(
135
+ str_user=os.getenv("REGSCALE_USERNAME"),
136
+ str_password=os.getenv("REGSCALE_PASSWORD"),
137
+ app=self.app,
138
+ )
139
+ self.api = Api()
140
+ self.logger = logger
141
+
142
+ logger.info(f"Test Setup Complete: {self.title_prefix}")
143
+ # Test Execution
144
+ yield self.app
File without changes
tests/mocks/objects.py ADDED
@@ -0,0 +1,3 @@
1
+ """Provide generic and mocked python objects to call in tests."""
2
+
3
+ GENERIC_DICT = {"key": "value"}
@@ -0,0 +1,32 @@
1
+ """Create mocked requests.Response objects."""
2
+
3
+ from unittest.mock import Mock
4
+
5
+ import requests
6
+
7
+
8
+ def create_mock_response(
9
+ status_code=200,
10
+ json_data=None,
11
+ text_data=None,
12
+ headers=None,
13
+ ok=True,
14
+ ):
15
+ """
16
+ Generate a custom requests.Response object using unittest.mock.
17
+
18
+ :status_code (int, optional): The HTTP status code for the response.
19
+ :json_data (dict, optional): The JSON data to be returned by the response's json() method.
20
+ :text_data (str, optional): The text data to be returned by the response's text attribute.
21
+ :headers (dict, optional): The headers for the response.
22
+ :ok (bool, optional): The response.ok value.
23
+
24
+ :returns Mock: A unittest.mock.Mock instance with the specified attributes and behavior.
25
+ """
26
+ mock_response = Mock(spec=requests.Response)
27
+ mock_response.status_code = status_code
28
+ mock_response.json.return_value = json_data
29
+ mock_response.text = text_data
30
+ mock_response.headers = headers or {}
31
+ mock_response.ok = ok
32
+ return mock_response
tests/mocks/xml.py ADDED
@@ -0,0 +1,13 @@
1
+ """Provide mock content for XML functions."""
2
+
3
+ XML_CONTENT = """<?xml version="1.0" encoding="UTF-8"?>
4
+ <root>
5
+ <item>
6
+ <id>1</id>
7
+ <name>Item 1</name>
8
+ </item>
9
+ <item>
10
+ <id>2</id>
11
+ <name>Item 2</name>
12
+ </item>
13
+ </root>"""
File without changes
File without changes
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Tests for the Api class"""
4
+ import os
5
+ from unittest.mock import MagicMock, patch
6
+
7
+ import pytest
8
+
9
+ from regscale.core.app.api import Api, normalize_url
10
+ from regscale.core.app.application import Application
11
+
12
+
13
+ @pytest.fixture
14
+ def mock_config():
15
+ """Fixture for mocking config"""
16
+ return {
17
+ "token": "test-token",
18
+ "domain": "https://api.example.com",
19
+ "maxThreads": 10,
20
+ "ssl_verify": True,
21
+ "timeout": 10,
22
+ }
23
+
24
+
25
+ @pytest.fixture
26
+ def api(mock_config):
27
+ """Fixture for creating an Api instance with mocked config"""
28
+ with patch("regscale.core.app.application.Application") as mock_app:
29
+ mock_app.return_value.config = mock_config
30
+ api = Api()
31
+ api.session = MagicMock()
32
+ return api
33
+
34
+
35
+ def test_handle_headers_with_no_merge(api):
36
+ """Test _handle_headers when merge_headers is False"""
37
+ custom_headers = {"Custom-Header": "value"}
38
+ result = api._handle_headers(custom_headers, merge_headers=False)
39
+ assert result == custom_headers
40
+
41
+
42
+ def test_handle_headers_with_merge(api):
43
+ """Test _handle_headers when merge_headers is True"""
44
+ custom_headers = {"Custom-Header": "value"}
45
+ result = api._handle_headers(custom_headers, merge_headers=True)
46
+
47
+ assert result["Custom-Header"] == "value"
48
+ assert result["accept"] == api.accept
49
+ assert result["Content-Type"] == api.content_type
50
+ assert result["Authorization"] == "test-token"
51
+
52
+
53
+ def test_handle_headers_with_no_headers(api):
54
+ """Test _handle_headers when headers is None"""
55
+ # When merge_headers is False, should return empty dict for None headers
56
+ result = api._handle_headers(None, merge_headers=False)
57
+ assert result["accept"] == api.accept
58
+ assert result["Content-Type"] == api.content_type
59
+ assert result["Authorization"] == "test-token"
60
+
61
+ # When merge_headers is True, should return default headers
62
+ result = api._handle_headers(None, merge_headers=True)
63
+ assert result["accept"] == api.accept
64
+ assert result["Content-Type"] == api.content_type
65
+ assert result["Authorization"] == "test-token"
66
+
67
+
68
+ def test_handle_headers_with_empty_headers(api):
69
+ """Test _handle_headers when headers is an empty dict"""
70
+ result = api._handle_headers({}, merge_headers=True)
71
+
72
+ assert len(result) == 3
73
+ assert result["accept"] == api.accept
74
+ assert result["Content-Type"] == api.content_type
75
+ assert result["Authorization"] == "test-token"
76
+
77
+ # Test with merge_headers=False
78
+ result = api._handle_headers({}, merge_headers=False)
79
+ assert result == {}
80
+
81
+
82
+ def test_get_request_success(api):
83
+ """Test successful GET request"""
84
+ mock_response = MagicMock()
85
+ mock_response.status_code = 200
86
+ mock_response.text = '{"data": "test"}'
87
+ api.session.get.return_value = mock_response
88
+
89
+ response = api.get("https://api.example.com/test")
90
+
91
+ assert response.status_code == 200
92
+ assert response.text == '{"data": "test"}'
93
+ api.session.get.assert_called_once()
94
+
95
+
96
+ def test_post_request_success(api):
97
+ """Test successful POST request"""
98
+ mock_response = MagicMock()
99
+ mock_response.status_code = 200
100
+ mock_response.text = '{"data": "created"}'
101
+ api.session.post.return_value = mock_response
102
+
103
+ json_data = {"key": "value"}
104
+ response = api.post("https://api.example.com/test", json=json_data)
105
+
106
+ assert response.status_code == 200
107
+ assert response.text == '{"data": "created"}'
108
+ api.session.post.assert_called_once()
109
+
110
+
111
+ def test_put_request_success(api):
112
+ """Test successful PUT request"""
113
+ mock_response = MagicMock()
114
+ mock_response.status_code = 200
115
+ mock_response.text = '{"data": "updated"}'
116
+ api.session.put.return_value = mock_response
117
+
118
+ json_data = {"key": "value"}
119
+ response = api.put("https://api.example.com/test", json=json_data)
120
+
121
+ assert response.status_code == 200
122
+ assert response.text == '{"data": "updated"}'
123
+ api.session.put.assert_called_once()
124
+
125
+
126
+ def test_delete_request_success(api):
127
+ """Test successful DELETE request"""
128
+ mock_response = MagicMock()
129
+ mock_response.status_code = 200
130
+ mock_response.text = '{"data": "deleted"}'
131
+ api.session.delete.return_value = mock_response
132
+
133
+ response = api.delete("https://api.example.com/test")
134
+
135
+ assert response.status_code == 200
136
+ assert response.text == '{"data": "deleted"}'
137
+ api.session.delete.assert_called_once()
138
+
139
+
140
+ def test_request_with_401_retry(api):
141
+ """Test request handling 401 unauthorized with retry"""
142
+ mock_response_401 = MagicMock()
143
+ mock_response_401.status_code = 401
144
+
145
+ mock_response_200 = MagicMock()
146
+ mock_response_200.status_code = 200
147
+ mock_response_200.text = '{"data": "success"}'
148
+
149
+ api.session.get.side_effect = [mock_response_401, mock_response_200]
150
+
151
+ with patch.object(api, "_handle_401", return_value=True):
152
+ response = api.get("https://api.example.com/test")
153
+
154
+ assert response.status_code == 200
155
+ assert response.text == '{"data": "success"}'
156
+ assert api.session.get.call_count == 2
157
+
158
+
159
+ def test_normalize_url():
160
+ """Test URL normalization"""
161
+ test_cases = [
162
+ ("http://example.com//api//v1/", "http://example.com/api/v1"),
163
+ ("example.com/api/v1", "http://example.com/api/v1"),
164
+ ("https://example.com/api//v1", "https://example.com/api/v1"),
165
+ ("http://example.com/api/v1/", "http://example.com/api/v1"),
166
+ ]
167
+
168
+ for input_url, expected_url in test_cases:
169
+ assert normalize_url(input_url) == expected_url
170
+
171
+
172
+ def test_graph_query_success(api):
173
+ """Test successful GraphQL query"""
174
+ mock_response = MagicMock()
175
+ mock_response.status_code = 200
176
+ mock_response.json.return_value = {
177
+ "data": {"vulnerabilities": {"items": [{"id": 1}], "pageInfo": {"hasNextPage": False}}}
178
+ }
179
+
180
+ api.session.post.return_value = mock_response
181
+ query = """
182
+ query {
183
+ vulnerabilities {
184
+ items {
185
+ id
186
+ }
187
+ }
188
+ }
189
+ """
190
+ result = api.graph(query)
191
+
192
+ assert "vulnerabilities" in result
193
+ assert isinstance(result["vulnerabilities"]["items"], list)
194
+ assert result["vulnerabilities"]["items"][0]["id"] == 1
195
+ api.session.post.assert_called_once()
196
+
197
+
198
+ def test_ssl_verify_false_envar():
199
+ """Test that SSL verification setting is passed to the session via envar"""
200
+ with patch.dict(os.environ, {"sslVerify": "false"}):
201
+ assert os.getenv("sslVerify") == "false"
202
+ api = Api()
203
+ assert api.session.verify is False
204
+
205
+
206
+ def test_ssl_verify_true_envar():
207
+ """Test that SSL verification setting is passed to the session via envar"""
208
+ with patch.dict(os.environ, {"sslVerify": "true"}):
209
+ assert os.getenv("sslVerify") == "true"
210
+ api = Api()
211
+ assert api.verify is True
212
+ assert api.session.verify is True
213
+
214
+
215
+ def test_ssl_verify_false_config():
216
+ """Test that SSL verification setting is passed to the session via init.yaml"""
217
+ app = Application(config={"sslVerify": False})
218
+ app.config_file = "test_ssl_verify_false_config.yaml"
219
+ app.save_config(app.config)
220
+ assert app.config["sslVerify"] is False
221
+ api = Api()
222
+ assert api.verify is False
223
+ assert api.session.verify is False
224
+ # os.remove(app.config_file)
225
+
226
+
227
+ def test_ssl_verify_true_config():
228
+ """Test that SSL verification setting is passed to the session init.yaml"""
229
+ app = Application(config={"sslVerify": True})
230
+ assert app.config["sslVerify"] is True
231
+ api = Api()
232
+ assert api.session.verify is True