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,714 @@
1
+ from datetime import datetime, timedelta
2
+ from typing import Any, Dict, List, Optional, Tuple
3
+
4
+ from lxml import etree
5
+
6
+ from regscale.core.app.logz import create_logger
7
+ from regscale.integrations.public.fedramp.fedramp_traversal import FedrampTraversal
8
+ from regscale.core.app.utils.api_handler import APIHandler
9
+ from regscale.core.app.utils.app_utils import get_current_datetime
10
+ from regscale.models import Asset, Component, InterConnection, Link, PortsProtocol, Property, Reference
11
+
12
+ logger = create_logger()
13
+
14
+ namespaces = {
15
+ "oscal": "http://csrc.nist.gov/ns/oscal/1.0",
16
+ "fedramp": "https://fedramp.gov/ns/oscal",
17
+ }
18
+
19
+ COMP = "Component"
20
+ REMARKS_XPATH = "oscal:remarks/p/text()"
21
+
22
+
23
+ # Use a helper function to safely get the first item in a list or return None
24
+ def safe_first_item(lst):
25
+ return lst[0] if lst else None
26
+
27
+
28
+ def parse_common_component_data(component):
29
+ """Parse common data for all component types."""
30
+ component_data = {
31
+ "uuid": component.get("uuid"),
32
+ "type": component.get("type"),
33
+ "title": safe_first_item(component.xpath("oscal:title/text()", namespaces=namespaces)),
34
+ "description": safe_first_item(component.xpath("oscal:description/oscal:p/text()", namespaces=namespaces)),
35
+ "status": safe_first_item(component.xpath("oscal:status/@state", namespaces=namespaces)),
36
+ "remarks": " ".join(component.xpath(REMARKS_XPATH, namespaces=namespaces)),
37
+ }
38
+ return component_data
39
+
40
+
41
+ def parse_interconnection_data(component: etree.Element) -> Dict:
42
+ """
43
+ Parse data specific to interconnection components.
44
+
45
+ :param etree.Element component: The component XML element
46
+ :return: A dictionary of interconnection data
47
+ :rtype: Dict
48
+ """
49
+ interconnection_data = {
50
+ "direction": safe_first_item(component.xpath('oscal:prop[@name="direction"]/@value', namespaces=namespaces)),
51
+ "ipv4_address_local": safe_first_item(
52
+ component.xpath(
53
+ 'oscal:prop[@name="ipv4-address"][@class="local"]/@value',
54
+ namespaces=namespaces,
55
+ )
56
+ ),
57
+ "ipv4_address_remote": safe_first_item(
58
+ component.xpath(
59
+ 'oscal:prop[@name="ipv4-address"][@class="remote"]/@value',
60
+ namespaces=namespaces,
61
+ )
62
+ ),
63
+ "service_processor": safe_first_item(
64
+ component.xpath(
65
+ 'oscal:prop[@name="service-processor"][@ns="https://fedramp.gov/ns/oscal"]/@value',
66
+ namespaces=namespaces,
67
+ )
68
+ ),
69
+ "information": safe_first_item(
70
+ component.xpath(
71
+ 'oscal:prop[@name="information"][@ns="https://fedramp.gov/ns/oscal"]/@value',
72
+ namespaces=namespaces,
73
+ )
74
+ ),
75
+ "port_or_circuit_name": safe_first_item(
76
+ component.xpath(
77
+ 'oscal:prop[@name="port" or @name="circuit"][@ns="https://fedramp.gov/ns/oscal"]/@name',
78
+ namespaces=namespaces,
79
+ )
80
+ ),
81
+ "port_or_circuit_value": safe_first_item(
82
+ component.xpath(
83
+ 'oscal:prop[@name="port" or @name="circuit"][@ns="https://fedramp.gov/ns/oscal"]/@value',
84
+ namespaces=namespaces,
85
+ )
86
+ ),
87
+ "connection_security": safe_first_item(
88
+ component.xpath(
89
+ 'oscal:prop[@name="connection-security"][@ns="https://fedramp.gov/ns/oscal"]/@value',
90
+ namespaces=namespaces,
91
+ )
92
+ ),
93
+ "connection_security_remarks": safe_first_item(
94
+ component.xpath(
95
+ 'oscal:prop[@name="connection-security"][@ns="https://fedramp.gov/ns/oscal"]/oscal:remarks/oscal:p/text()',
96
+ namespaces=namespaces,
97
+ )
98
+ ),
99
+ }
100
+ return interconnection_data
101
+
102
+
103
+ def parse_roles(component: etree.Element, component_data: Dict) -> list:
104
+ """Parse responsible roles for a component.
105
+
106
+ :param etree.Element component: The component XML element
107
+ :param Dict component_data: The component data dictionary
108
+ :return: A list of responsible roles
109
+ :rtype: list
110
+ """
111
+ roles = []
112
+ for role in component.xpath("oscal:responsible-role", namespaces=namespaces):
113
+ role_data = dict()
114
+ role_data["role_id"] = role.get("role-id")
115
+ role_data["party_uuid"] = safe_first_item(role.xpath("oscal:party-uuid/text()", namespaces=namespaces))
116
+ if role_data["role_id"] == "asset-owner":
117
+ component_data["asset_owner"] = role_data["party_uuid"]
118
+ role_data["job_title"] = safe_first_item(
119
+ role.xpath('oscal:prop[@name="job-title"]/@value', namespaces=namespaces)
120
+ )
121
+ roles.append(role_data)
122
+ return roles
123
+
124
+
125
+ def parse_links(component, component_data):
126
+ links = component.xpath("oscal:link/@href", namespaces=namespaces)
127
+ rels = component.xpath("oscal:link/@rel", namespaces=namespaces)
128
+ component_data["links"] = [
129
+ {"rel": rel, "component_uuid": component_data["uuid"], "href": href} for rel, href in zip(rels, links)
130
+ ]
131
+
132
+
133
+ def parse_properties(component, component_data):
134
+ properties = []
135
+ for prop in component.xpath("oscal:prop", namespaces=namespaces):
136
+ name = prop.get("name")
137
+ value = prop.get("value")
138
+ remarks = safe_first_item(prop.xpath("oscal:remarks/oscal:p/text()", namespaces=namespaces))
139
+ properties.append({"key": name, "value": value, "remarks": remarks})
140
+ logger.debug(f"Parsed {len(properties)} properties")
141
+ if "properties" in component_data:
142
+ component_data["properties"].extend(properties)
143
+ else:
144
+ component_data["properties"] = properties
145
+ return component_data
146
+
147
+
148
+ def parse_component(component):
149
+ component_data: dict()
150
+ component_data = parse_common_component_data(component)
151
+ parse_links(component, component_data)
152
+ component_data["responsible_roles"] = parse_roles(component, component_data)
153
+ component_data = parse_properties(component, component_data)
154
+ # this is a seperate module interconnections in regscale
155
+ if component_data["type"] == "interconnection":
156
+ # Parse properties using specific XPath queries and the safe_first_item function
157
+ interconnection_data = parse_interconnection_data(component)
158
+ component_data = {**component_data, **interconnection_data}
159
+
160
+ properties = [
161
+ {
162
+ "key": "service-processor",
163
+ "value": component_data.get("service_processor"),
164
+ },
165
+ {
166
+ "key": "ipv4_address_local",
167
+ "value": component_data.get("ipv4_address_local"),
168
+ },
169
+ {
170
+ "key": "ipv4_address_remote",
171
+ "value": component_data.get("ipv4_address_remote"),
172
+ },
173
+ {"key": "direction", "value": component_data.get("direction")},
174
+ {"key": "information", "value": component_data.get("information")},
175
+ {
176
+ "key": "connection_security",
177
+ "value": component_data.get("connection_security"),
178
+ },
179
+ {
180
+ "key": "connection_security_remarks",
181
+ "value": component_data.get("connection_security_remarks"),
182
+ },
183
+ ]
184
+
185
+ component_data["properties"] = properties
186
+ # Parse responsible roles
187
+ component_data["aoid_remote"] = safe_first_item(
188
+ component.xpath(
189
+ 'oscal:responsible-role[@role-id="isa-authorizing-official-remote"]/oscal:party-uuid/text()',
190
+ namespaces=namespaces,
191
+ )
192
+ )
193
+ component_data["aoid_local"] = safe_first_item(
194
+ component.xpath(
195
+ 'oscal:responsible-role[@role-id="isa-authorizing-official-local"]/oscal:party-uuid/text()',
196
+ namespaces=namespaces,
197
+ )
198
+ )
199
+ component_data["isa_remote"] = safe_first_item(
200
+ component.xpath(
201
+ 'oscal:responsible-role[@role-id="isa-poc-remote"]/oscal:party-uuid/text()',
202
+ namespaces=namespaces,
203
+ )
204
+ )
205
+ component_data["isa_local"] = safe_first_item(
206
+ component.xpath(
207
+ 'oscal:responsible-role[@role-id="isa-poc-local"]/oscal:party-uuid/text()',
208
+ namespaces=namespaces,
209
+ )
210
+ )
211
+ roles = []
212
+ for role in component.xpath("oscal:responsible-role", namespaces=namespaces):
213
+ role_data = dict()
214
+ role_data["party_uuid"] = role.text
215
+ role_data["job_title"] = safe_first_item(
216
+ role.xpath('oscal:prop[@name="job-title"]/@value', namespaces=namespaces)
217
+ )
218
+ roles.append(role_data)
219
+ component_data["responsible_roles"] = roles
220
+
221
+ elif component_data["type"] == "service":
222
+ # Parsing logic for service type
223
+ component_data["purpose"] = " ".join(component.xpath("oscal:purpose/text()", namespaces=namespaces))
224
+ component_data["used_by"] = safe_first_item(
225
+ component.xpath('oscal:prop[@name="used-by"]/@value', namespaces=namespaces)
226
+ )
227
+
228
+ protocols = []
229
+ for protocol in component.xpath("oscal:protocol", namespaces=namespaces):
230
+ protocol_data = dict()
231
+ protocol_data["used_by"] = safe_first_item(
232
+ component.xpath(
233
+ 'oscal:prop[@name="used-by"][@ns="https://fedramp.gov/ns/oscal"]/@value',
234
+ namespaces=namespaces,
235
+ )
236
+ )
237
+ protocol_data["name"] = protocol.get("name")
238
+ protocol_data["port_range_start"] = safe_first_item(
239
+ protocol.xpath("oscal:port-range/@start", namespaces=namespaces)
240
+ )
241
+ protocol_data["port_range_end"] = safe_first_item(
242
+ protocol.xpath("oscal:port-range/@end", namespaces=namespaces)
243
+ )
244
+ protocol_data["transport"] = safe_first_item(
245
+ protocol.xpath("oscal:port-range/@transport", namespaces=namespaces)
246
+ )
247
+ protocols.append(protocol_data)
248
+
249
+ component_data["protocols"] = protocols
250
+ component_data["remarks"] = (
251
+ " ".join(component.xpath(REMARKS_XPATH, namespaces=namespaces))
252
+ if component.xpath(REMARKS_XPATH, namespaces=namespaces)
253
+ else None
254
+ )
255
+
256
+ elif component_data["type"] == "subnet":
257
+ # Parsing logic for subnet type
258
+ component_data["asset_id"] = safe_first_item(
259
+ component.xpath('oscal:prop[@name="asset-id"]/@value', namespaces=namespaces)
260
+ )
261
+ component_data["ipv4_subnet"] = safe_first_item(
262
+ component.xpath('oscal:prop[@name="ipv4-subnet"]/@value', namespaces=namespaces)
263
+ )
264
+ component_data["is_scanned"] = safe_first_item(
265
+ component.xpath('oscal:prop[@name="is-scanned"]/@value', namespaces=namespaces)
266
+ )
267
+
268
+ elif component_data["type"] == "hardware":
269
+ # Parsing logic for hardware type
270
+ component_data["asset_type"] = safe_first_item(
271
+ component.xpath('oscal:prop[@name="asset-type"]/@value', namespaces=namespaces)
272
+ )
273
+ component_data["vendor_name"] = safe_first_item(
274
+ component.xpath('oscal:prop[@name="vendor-name"]/@value', namespaces=namespaces)
275
+ )
276
+ component_data["model"] = safe_first_item(
277
+ component.xpath('oscal:prop[@name="model"]/@value', namespaces=namespaces)
278
+ )
279
+ component_data["version"] = safe_first_item(
280
+ component.xpath('oscal:prop[@name="version"]/@value', namespaces=namespaces)
281
+ )
282
+
283
+ elif component_data["type"] == "software":
284
+ # Parsing logic for software type
285
+ component_data["asset_type"] = safe_first_item(
286
+ component.xpath('oscal:prop[@name="asset-type"]/@value', namespaces=namespaces)
287
+ )
288
+ component_data["baseline_configuration_name"] = safe_first_item(
289
+ component.xpath(
290
+ 'oscal:prop[@name="baseline-configuration-name"]/@value',
291
+ namespaces=namespaces,
292
+ )
293
+ )
294
+ component_data["allows_authenticated_scan"] = safe_first_item(
295
+ component.xpath(
296
+ 'oscal:prop[@name="allows-authenticated-scan"]/@value',
297
+ namespaces=namespaces,
298
+ )
299
+ )
300
+
301
+ return component_data
302
+
303
+
304
+ def map_asset_status(status) -> str:
305
+ """Map asset status from OSCAL to internal representation."""
306
+ status_mapping = {
307
+ "operational": "Active (On Network)",
308
+ "disposition": "Decommissioned",
309
+ "under-development": "Off-Network",
310
+ }
311
+ return status_mapping.get(status, "Active (On Network)")
312
+
313
+
314
+ # Asset Creation Functions
315
+ def create_asset(component_data: Dict, user_id: str, spp_id: int) -> Optional[Dict]:
316
+ """
317
+ Creates an asset record in the database
318
+ :param Dict component_data:
319
+ :param str user_id:
320
+ :param int spp_id:
321
+ :return: Dict of the created asset, if successful
322
+ :rtype: Optional[Dict]
323
+ """
324
+ if asset := Asset(
325
+ name=component_data["title"],
326
+ description=component_data["description"],
327
+ status=map_asset_status(component_data["status"]),
328
+ assetType="Other",
329
+ assetOwnerId=user_id,
330
+ parentId=spp_id,
331
+ parentModule="securityplans",
332
+ ram=0,
333
+ diskStorage=0,
334
+ cpu=0,
335
+ assetCategory=("software" if component_data["type"] == "software" else "hardware"),
336
+ createdById=user_id,
337
+ lastUpdatedById=user_id,
338
+ ).create():
339
+ return asset.dict()
340
+ return None
341
+
342
+
343
+ def create_links(*args, **kwargs) -> list:
344
+ """
345
+ Creates links for a component in RegScale.
346
+
347
+ :param args: list of links
348
+ :param kwargs: component, parent_id, parent_module, user_id, etc.
349
+ :return: list of created links responses
350
+ :rtype: list
351
+ """
352
+ links = args[0]
353
+ component = kwargs.get("component")
354
+ parent_id = kwargs.get("parent_id")
355
+ parent_module = kwargs.get("parent_module")
356
+ user_id = kwargs.get("user_id")
357
+ api_handler = kwargs.get("api_handler")
358
+ link_responses = []
359
+ for link in links:
360
+ if not link["href"].startswith("#"):
361
+ if link["component_uuid"] == component["uuid"]:
362
+ obj = Link(
363
+ title=link["rel"],
364
+ url=link["href"],
365
+ parentID=parent_id,
366
+ parentModule=parent_module,
367
+ createdById=user_id,
368
+ lastUpdatedById=user_id,
369
+ )
370
+ resp = api_handler.post("/api/links", data=obj.dict())
371
+ link_responses.append(resp)
372
+ else:
373
+ obj = Reference(
374
+ title=link["rel"],
375
+ identificationNumber=link["href"],
376
+ link=link["href"],
377
+ referenceType="Other",
378
+ parentId=parent_id,
379
+ parentModule=parent_module,
380
+ createdById=user_id,
381
+ )
382
+ obj.create()
383
+ return link_responses
384
+
385
+
386
+ def create_component_based_on_type(
387
+ component_data: Dict, user_id: str, ssp_id: int, api_handler: APIHandler
388
+ ) -> Tuple[int, str]:
389
+ """
390
+ Create a component based on its type and return the component ID and module.
391
+
392
+ :param Dict component_data: The data for the component.
393
+ :param str user_id: User ID.
394
+ :param int ssp_id: SSP ID.
395
+ :param APIHandler api_handler: API handler instance.
396
+ :return: Tuple of component ID and module.
397
+ :rtype: Tuple[int, str]
398
+ """
399
+ if component_data["type"] == "interconnection":
400
+ logger.info(f"Creating interconnection for component {component_data['uuid']}")
401
+ if response := create_interconnection(component_data, user_id, ssp_id):
402
+ return getattr(response, "id", 0), "interconnects"
403
+ return 0, "interconnects"
404
+
405
+ elif component_data["type"] in ["subnet", "hardware", "software"]:
406
+ logger.info(f"Creating asset for component {component_data['uuid']}")
407
+ if response := create_asset(component_data, user_id, ssp_id):
408
+ return response.get("id", 0), "assets"
409
+ return 0, "assets"
410
+
411
+ elif component_data["type"] == "this-system":
412
+ return 0, "this-system"
413
+
414
+ else:
415
+ logger.info(f"Creating component for component {component_data['uuid']}")
416
+ if response := create_component(component_data, ssp_id, user_id, api_handler):
417
+ return response.get("id", 0), "components"
418
+ return 0, "components"
419
+
420
+
421
+ def create_related_items(
422
+ component_data: Dict,
423
+ comp_id: int,
424
+ regcomp_module: str,
425
+ trv: FedrampTraversal,
426
+ api_handler: APIHandler,
427
+ user_id: str,
428
+ ) -> None:
429
+ """
430
+ Create related items for a component (properties, protocols, links).
431
+
432
+ :param Dict component_data: The data for the component.
433
+ :param int comp_id: Component ID.
434
+ :param str regcomp_module: The module name for the component.
435
+ :param FedrampTraversal trv: FedRAMP Traversal object.
436
+ :param APIHandler api_handler: API handler instance.
437
+ :param str user_id: User ID.
438
+ :rtype: None
439
+ """
440
+ for item_type in ["properties", "protocols", "links"]:
441
+ if item_type in component_data and component_data[item_type]:
442
+ logger.info(f"Creating {item_type} for component {component_data['uuid']}")
443
+ create_func = globals()[f"create_{item_type}"]
444
+ if response := create_func(
445
+ component_data[item_type],
446
+ component=component_data,
447
+ parent_id=comp_id,
448
+ parent_module=regcomp_module,
449
+ api_handler=api_handler,
450
+ user_id=user_id,
451
+ ):
452
+ if isinstance(response, list):
453
+ trv.log_info(
454
+ {
455
+ "record_type": item_type.capitalize(),
456
+ "event_msg": f"Created {len(response)} {item_type} for component {component_data['uuid']}.",
457
+ "model_layer": COMP,
458
+ }
459
+ )
460
+ else:
461
+ trv.log_info(
462
+ {
463
+ "record_type": item_type.capitalize(),
464
+ "event_msg": f"Created {item_type} for component {component_data['uuid']}.",
465
+ "model_layer": COMP,
466
+ }
467
+ )
468
+ else:
469
+ trv.log_error(
470
+ {
471
+ "record_type": item_type.capitalize(),
472
+ "event_msg": f"Failed to create {item_type} for component {component_data['uuid']}.",
473
+ "missing_element": item_type.capitalize(),
474
+ "model_layer": COMP,
475
+ }
476
+ )
477
+
478
+
479
+ def parse_ssp_components(trv: FedrampTraversal) -> Dict:
480
+ """
481
+ Parses the OSCAL XML file and extracts the component data.
482
+
483
+ :param FedrampTraversal trv: FedRAMP Traversal object
484
+ :return: Dictionary containing the component data
485
+ :rtype: Dict
486
+ """
487
+ try:
488
+ ssp_id = trv.ssp_id
489
+ api_handler = APIHandler()
490
+ user_id = trv.api.config.get("userId")
491
+ components = trv.root.xpath("//oscal:component", namespaces=namespaces)
492
+ logger.info(f"Found {len(components)} components for parsing.")
493
+ trv.log_info(
494
+ {
495
+ "model_layer": "System Implementation",
496
+ "record_type": "Component",
497
+ "event_msg": f"Found {len(components)} components for parsing.",
498
+ }
499
+ )
500
+
501
+ components_dict = {}
502
+
503
+ for component in components:
504
+ component_data = parse_component(component)
505
+ comp_id, regcomp_module = create_component_based_on_type(component_data, user_id, ssp_id, api_handler)
506
+
507
+ if comp_id == 0:
508
+ continue
509
+
510
+ component_data["id"] = comp_id
511
+ component_data["module"] = regcomp_module
512
+ components_dict[component_data["uuid"]] = component_data
513
+
514
+ logger.info(f"Component {component_data['uuid']} created.")
515
+ create_related_items(component_data, comp_id, regcomp_module, trv, api_handler, user_id)
516
+
517
+ return components_dict
518
+
519
+ except Exception as e:
520
+ trv.log_error(
521
+ {
522
+ "record_type": "Component",
523
+ "event_msg": f"Unable to create component {component_data['uuid']}.",
524
+ "missing_element": "Component",
525
+ "model_layer": COMP,
526
+ }
527
+ )
528
+ logger.error(f"Error parsing components item: {str(e)}")
529
+
530
+
531
+ def create_properties(
532
+ *args: Tuple,
533
+ **kwargs: Dict,
534
+ ) -> list:
535
+ """
536
+ Creates a new property in the Regscale.
537
+ :param Tuple *args: list of properties
538
+ :param Dict **kwargs: parent_id, parent_module, api_handler, user_id, etc.
539
+ :return: list of created properties
540
+ :rtype: list
541
+ """
542
+ properties = args[0]
543
+ parent_id = kwargs.get("parent_id")
544
+ parent_module = kwargs.get("parent_module")
545
+ user_id = kwargs.get("user_id")
546
+
547
+ created_properties = []
548
+ for prop in properties:
549
+ prop_obj = Property(
550
+ parentId=parent_id,
551
+ parentModule=parent_module,
552
+ key=prop["key"],
553
+ value=prop["value"],
554
+ createdById=user_id,
555
+ )
556
+ new_prop = prop_obj.create().model_dump()
557
+ logger.info(f"Created property {new_prop['key']} with ID of {new_prop['id']}")
558
+ if new_prop:
559
+ created_properties.append(new_prop)
560
+ else:
561
+ continue
562
+ return created_properties
563
+
564
+
565
+ def create_component(component_data: Dict, ssp_id: int, user_id: str, api_handler: APIHandler) -> Optional[Any]:
566
+ """
567
+ Creates a new component in the OSCAL database.
568
+
569
+ :param Dict component_data: The component data
570
+ :param int ssp_id: The ID of the SSP
571
+ :param str user_id: The ID of the user creating the component
572
+ :param APIHandler api_handler: The API handler
573
+ :return: The response object of the newly created component
574
+ :rtype: Optional[Any]
575
+ """
576
+ comp = Component(
577
+ uuid=component_data["uuid"],
578
+ title=component_data["title"],
579
+ description=component_data["description"],
580
+ purpose=component_data["purpose"] if "purpose" in component_data else "",
581
+ componentType=component_data["type"],
582
+ componentOwnerId=(
583
+ component_data["asset_owner"]
584
+ if "asset_owner" in component_data and component_data["asset_owner"] is not None
585
+ else user_id
586
+ ),
587
+ securityPlansId=ssp_id,
588
+ status=(
589
+ "Active" if "status" in component_data and component_data["status"] == "operational" else "Draft/Pending"
590
+ ),
591
+ cmmcAssetType=(component_data["asset_type"] if "asset_type" in component_data else None),
592
+ )
593
+ # insert component
594
+ resp_json = api_handler.post("/api/components", data=comp.model_dump()).json()
595
+ if resp_json:
596
+ create_component_mapping(resp_json, ssp_id=ssp_id)
597
+ return resp_json
598
+ else:
599
+ logger.error(f"failed to insert comp {comp.dict()}")
600
+ return None
601
+
602
+
603
+ def create_component_mapping(component_data: Dict, ssp_id: int) -> None:
604
+ """
605
+ Creates a new component mapping record.
606
+
607
+ :param Dict component_data: The component data
608
+ :param int ssp_id: The ID of the SSP
609
+ :rtype: None
610
+ """
611
+ from regscale.models.regscale_models.component_mapping import ComponentMapping
612
+
613
+ logger.info(f"Creating component mapping for component {component_data['id']}")
614
+ new_comp_mapping = ComponentMapping(
615
+ componentId=component_data["id"],
616
+ securityPlanId=ssp_id,
617
+ isPublic=True,
618
+ ).create()
619
+ logger.info(f"Successfully created component mapping {new_comp_mapping.id}")
620
+ return None
621
+
622
+
623
+ def create_protocols(
624
+ *args,
625
+ **kwargs,
626
+ ) -> Optional[List[Any]]:
627
+ """
628
+ Creates a new ports and protocol record.
629
+
630
+ :return: The response object of the newly created component or None
631
+ :rtype: Optional[List[Any]]
632
+ """
633
+ protocols = args[0]
634
+ parent_id = kwargs.get("parent_id")
635
+ parent_module = kwargs.get("parent_module")
636
+ user_id = kwargs.get("user_id")
637
+ responses = []
638
+ for protocol in protocols:
639
+ protocol_obj = PortsProtocol(
640
+ parentId=parent_id,
641
+ parentModule=parent_module,
642
+ startPort=protocol["port_range_start"],
643
+ endPort=protocol["port_range_end"],
644
+ protocol=protocol["transport"],
645
+ service=protocol["name"],
646
+ purpose=None,
647
+ usedBy=protocol["used_by"],
648
+ createdById=user_id,
649
+ lastUpdatedById=user_id,
650
+ )
651
+ response = protocol_obj.create().model_dump()
652
+ responses.append(response)
653
+ return responses
654
+
655
+
656
+ def create_interconnection(component_data: Dict, user_id: str, ssp_id: int) -> Optional[Any]:
657
+ """
658
+ Creates a new interconnection record.
659
+
660
+ :param Dict component_data: The component data
661
+ :param str user_id: The ID of the user creating the component
662
+ :param int ssp_id: The ID of the SSP
663
+ :param APIHandler api_handler: The API handler
664
+ :return: API response object
665
+ :rtype: Optional[Any]
666
+ """
667
+ intercon = InterConnection(
668
+ uuid=component_data["uuid"],
669
+ description=component_data["description"],
670
+ name=component_data["title"],
671
+ dataDirection=component_data["direction"],
672
+ status="Approved",
673
+ authorizationType="Interconnect Security Agreement (ISA)",
674
+ createdById=user_id,
675
+ dateCreated=get_current_datetime(),
676
+ dateLastUpdated=get_current_datetime(),
677
+ lastUpdatedById=user_id,
678
+ tenantsId=0,
679
+ parentId=ssp_id,
680
+ parentModule="securityplans",
681
+ externalIpAddress=component_data["ipv4_address_remote"],
682
+ sourceIpAddress=component_data["ipv4_address_local"],
683
+ categorization="Moderate",
684
+ connectionType="Internet or Firewall Rule",
685
+ organization=(
686
+ component_data["aoid_local"] if "aoid_local" in component_data else component_data["aoid_remote"]
687
+ ),
688
+ # component_data['aoid_local'] if 'aoid_local' in component_data else component_data['aoid_remote']
689
+ aOId=user_id,
690
+ # component_data['isa_local'] if 'isa_local' in component_data else component_data['isa_remote']
691
+ interconnectOwnerId=user_id,
692
+ expirationDate=get_expiration_date(),
693
+ agreementDate=get_current_datetime(),
694
+ )
695
+
696
+ return intercon.create()
697
+
698
+
699
+ def get_expiration_date() -> str:
700
+ """
701
+ Get the expiration date for an interconnection.
702
+
703
+ :return: The expiration date
704
+ :rtype: str
705
+ """
706
+ # Get the current date and time in UTC + 30 days
707
+ now = datetime.utcnow()
708
+
709
+ # Add 30 days to the current date and time
710
+ expiration_date = now + timedelta(days=30)
711
+
712
+ # Convert to Zulu time format (ISO 8601)
713
+ expiration_date_str = expiration_date.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
714
+ return expiration_date_str