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,823 @@
1
+ """Module for AWS resource inventory scanning integration."""
2
+
3
+ import json
4
+ import logging
5
+ import os
6
+ import time
7
+ from typing import Any, Dict, Iterator, List, Optional, Tuple
8
+
9
+ from regscale.core.utils.date import date_str, datetime_str
10
+ from regscale.integrations.commercial.amazon.common import (
11
+ check_finding_severity,
12
+ determine_status_and_results,
13
+ get_comments,
14
+ get_due_date,
15
+ )
16
+ from regscale.integrations.scanner_integration import (
17
+ IntegrationAsset,
18
+ IntegrationFinding,
19
+ ScannerIntegration,
20
+ ScannerIntegrationType,
21
+ )
22
+ from regscale.models import IssueStatus, regscale_models
23
+ from .inventory import AWSInventoryCollector
24
+
25
+ logger = logging.getLogger("regscale")
26
+
27
+ # Constants for file paths
28
+ INVENTORY_FILE_PATH = os.path.join("artifacts", "aws", "inventory.json")
29
+ CACHE_TTL_SECONDS = 8 * 60 * 60 # 8 hours in seconds
30
+
31
+
32
+ class AWSInventoryIntegration(ScannerIntegration):
33
+ """Integration class for AWS resource inventory scanning."""
34
+
35
+ title = "AWS"
36
+ asset_identifier_field = "awsIdentifier"
37
+ issue_identifier_field = "awsIdentifier"
38
+ finding_severity_map = {
39
+ "CRITICAL": regscale_models.IssueSeverity.High,
40
+ "HIGH": regscale_models.IssueSeverity.High,
41
+ "MEDIUM": regscale_models.IssueSeverity.Moderate,
42
+ "LOW": regscale_models.IssueSeverity.Low,
43
+ "INFORMATIONAL": regscale_models.IssueSeverity.NotAssigned,
44
+ }
45
+ checklist_status_map = {
46
+ "Pass": regscale_models.ChecklistStatus.PASS,
47
+ "Fail": regscale_models.ChecklistStatus.FAIL,
48
+ }
49
+ type = ScannerIntegrationType.CHECKLIST
50
+
51
+ def __init__(self, plan_id: int, **kwargs):
52
+ """
53
+ Initialize the AWS inventory integration.
54
+
55
+ :param int plan_id: The RegScale plan ID
56
+ """
57
+ super().__init__(plan_id=plan_id, kwargs=kwargs)
58
+ self.collector: Optional[AWSInventoryCollector] = None
59
+
60
+ def authenticate(
61
+ self,
62
+ aws_access_key_id: Optional[str],
63
+ aws_secret_access_key: Optional[str],
64
+ region: str = os.getenv("AWS_REGION", "us-east-1"),
65
+ aws_session_token: Optional[str] = os.getenv("AWS_SESSION_TOKEN"),
66
+ ) -> None:
67
+ """
68
+ Authenticate with AWS and initialize the inventory collector.
69
+
70
+ :param str aws_access_key_id: Optional AWS access key ID
71
+ :param str aws_secret_access_key: Optional AWS secret access key
72
+ :param str region: AWS region to collect inventory from
73
+ :param str aws_session_token: Optional AWS session ID
74
+ """
75
+ self.collector = AWSInventoryCollector(
76
+ region=region,
77
+ aws_access_key_id=aws_access_key_id,
78
+ aws_secret_access_key=aws_secret_access_key,
79
+ aws_session_token=aws_session_token,
80
+ )
81
+
82
+ def fetch_aws_data_if_needed(
83
+ self,
84
+ region: str,
85
+ aws_access_key_id: Optional[str],
86
+ aws_secret_access_key: Optional[str],
87
+ aws_session_token: Optional[str] = None,
88
+ ) -> Dict[str, Any]:
89
+ """
90
+ Fetch AWS inventory data, using cached data if available and not expired.
91
+
92
+ :param str region: AWS region to collect inventory from
93
+ :param str aws_access_key_id: Optional AWS access key ID
94
+ :param str aws_secret_access_key: Optional AWS secret access key
95
+ :param str aws_session_token: Optional AWS session ID
96
+ :return: Dictionary containing AWS inventory data
97
+ :rtype: Dict[str, Any]
98
+ """
99
+ from regscale.models import DateTimeEncoder
100
+
101
+ # Check if we have cached data that's still valid
102
+ if os.path.exists(INVENTORY_FILE_PATH):
103
+ file_age = time.time() - os.path.getmtime(INVENTORY_FILE_PATH)
104
+ if file_age < CACHE_TTL_SECONDS:
105
+ with open(INVENTORY_FILE_PATH, "r", encoding="utf-8") as file:
106
+ return json.load(file)
107
+
108
+ # No valid cache, need to fetch new data
109
+ if not self.collector:
110
+ self.authenticate(aws_access_key_id, aws_secret_access_key, region, aws_session_token)
111
+
112
+ if not self.collector:
113
+ raise RuntimeError("Failed to initialize AWS inventory collector")
114
+
115
+ inventory = self.collector.collect_all()
116
+
117
+ # Ensure the artifacts directory exists
118
+ os.makedirs(os.path.dirname(INVENTORY_FILE_PATH), exist_ok=True)
119
+
120
+ with open(INVENTORY_FILE_PATH, "w", encoding="utf-8") as file:
121
+ json.dump(inventory, file, cls=DateTimeEncoder, indent=2)
122
+
123
+ return inventory
124
+
125
+ def _process_asset_collection(
126
+ self, assets: List[Dict[str, Any]], asset_type: str, parser_method
127
+ ) -> Iterator[IntegrationAsset]:
128
+ """
129
+ Process a collection of assets using the specified parser method.
130
+
131
+ :param List[Dict[str, Any]] assets: List of assets to process
132
+ :param str asset_type: Type of asset being processed
133
+ :param callable parser_method: Method to parse the asset
134
+ :yield: Iterator[IntegrationAsset]
135
+ """
136
+ for asset in assets:
137
+ if not isinstance(asset, dict) and asset not in ["Users", "Roles"]:
138
+ logger.warning(f"Skipping {asset_type} due to invalid data format: {asset}")
139
+ continue
140
+ try:
141
+ if asset in ["Users", "Roles"]:
142
+ for user in assets[asset]:
143
+ self.num_assets_to_process += 1
144
+ yield parser_method(user)
145
+ else:
146
+ self.num_assets_to_process += 1
147
+ yield parser_method(asset)
148
+ except Exception as e:
149
+ logger.error(f"Error parsing {asset_type} {asset}: {str(e)}", exc_info=True)
150
+
151
+ def _process_inventory_section(
152
+ self, inventory: Dict[str, Any], section_key: str, asset_type: str, parser_method
153
+ ) -> Iterator[IntegrationAsset]:
154
+ """
155
+ Process a section of the inventory.
156
+
157
+ :param Dict[str, Any] inventory: The complete inventory data
158
+ :param str section_key: Key for the section in the inventory
159
+ :param str asset_type: Type of asset being processed
160
+ :param callable parser_method: Method to parse the asset
161
+ :yield: Iterator[IntegrationAsset]
162
+ """
163
+ assets = inventory.get(section_key, [])
164
+ yield from self._process_asset_collection(assets, asset_type, parser_method)
165
+
166
+ def get_asset_configs(self) -> List[Tuple[str, str, callable]]:
167
+ """
168
+ Get the asset configurations for parsing.
169
+
170
+ :return: List of asset configurations
171
+ :rtype: List[Tuple[str, str, callable]]
172
+ """
173
+ return [
174
+ ("IAM", "Roles", self.parse_aws_account),
175
+ ("EC2Instances", "EC2 instance", self.parse_ec2_instance),
176
+ ("LambdaFunctions", "Lambda function", self.parse_lambda_function),
177
+ ("S3Buckets", "S3 bucket", self.parse_s3_bucket),
178
+ ("RDSInstances", "RDS instance", self.parse_rds_instance),
179
+ ("DynamoDBTables", "DynamoDB table", self.parse_dynamodb_table),
180
+ ("VPCs", "VPC", self.parse_vpc),
181
+ ("LoadBalancers", "Load Balancer", self.parse_load_balancer),
182
+ ("ECRRepositories", "ECR repository", self.parse_ecr_repository),
183
+ ]
184
+
185
+ def fetch_assets(
186
+ self,
187
+ region: str,
188
+ aws_access_key_id: Optional[str] = None,
189
+ aws_secret_access_key: Optional[str] = None,
190
+ aws_session_token: Optional[str] = None,
191
+ ) -> Iterator[IntegrationAsset]:
192
+ """
193
+ Fetch AWS assets from the inventory.
194
+
195
+ :param str region: AWS region to collect inventory from
196
+ :param str aws_access_key_id: Optional AWS access key ID
197
+ :param str aws_secret_access_key: Optional AWS secret access key
198
+ :param str aws_session_token: Optional AWS session ID
199
+ :yield: Iterator[IntegrationAsset]
200
+ """
201
+ inventory = self.fetch_aws_data_if_needed(region, aws_access_key_id, aws_secret_access_key, aws_session_token)
202
+
203
+ # Process each asset type using the corresponding parser
204
+ asset_configs = self.get_asset_configs()
205
+
206
+ self.num_assets_to_process = 0
207
+
208
+ for section_key, asset_type, parser_method in asset_configs:
209
+ yield from self._process_inventory_section(inventory, section_key, asset_type, parser_method)
210
+
211
+ def parse_ec2_instance(self, instance: Dict[str, Any]) -> IntegrationAsset:
212
+ """Parse EC2 instance data into an IntegrationAsset.
213
+
214
+ :param Dict[str, Any] instance: The EC2 instance data
215
+ :return: The parsed IntegrationAsset
216
+ :rtype: IntegrationAsset
217
+ """
218
+ # Get instance name from tags
219
+ instance_name = next(
220
+ (tag["Value"] for tag in instance.get("Tags", []) if tag["Key"] == "Name"), instance.get("InstanceId", "")
221
+ )
222
+ name = instance_name
223
+
224
+ # Calculate total storage from block devices
225
+ total_storage = 0
226
+ for device in instance.get("BlockDeviceMappings", []):
227
+ if "Ebs" in device:
228
+ # Note: We need to add a call to describe_volumes to get actual size
229
+ total_storage += 8 # Default to 8 GB if size unknown
230
+
231
+ # Calculate RAM based on instance type
232
+ # This would need a mapping of instance types to RAM
233
+ ram = 16 # Default to 16 GB for c5.2xlarge
234
+
235
+ # Get CPU info
236
+ cpu_options = instance.get("CpuOptions", {})
237
+ cpu_count = int(cpu_options.get("CoreCount", 0) * cpu_options.get("ThreadsPerCore", 0))
238
+
239
+ # Determine if instance is public facing
240
+ is_public_facing = bool(instance.get("PublicIpAddress"))
241
+
242
+ # Get OS details from platform and image info
243
+ image_info = instance.get("ImageInfo", {})
244
+ image_name = image_info.get("Name", "").lower()
245
+
246
+ # Check for Palo Alto device first
247
+ if "pa-vm-aws" in image_name:
248
+ operating_system = regscale_models.AssetOperatingSystem.PaloAlto
249
+ # Also update the asset type to reflect it's a network security device
250
+ asset_type = regscale_models.AssetType.Appliance
251
+ asset_category = regscale_models.AssetCategory.Hardware
252
+ component_type = regscale_models.ComponentType.Hardware
253
+ component_names = ["Palo Alto Networks IDPS"]
254
+ elif instance.get("Platform") == "windows":
255
+ operating_system = regscale_models.AssetOperatingSystem.WindowsServer
256
+ asset_type = regscale_models.AssetType.VM
257
+ asset_category = regscale_models.AssetCategory.Hardware
258
+ component_type = regscale_models.ComponentType.Hardware
259
+ component_names = ["EC2 Instances"]
260
+ else:
261
+ operating_system = regscale_models.AssetOperatingSystem.Linux
262
+ asset_type = regscale_models.AssetType.VM
263
+ asset_category = regscale_models.AssetCategory.Hardware
264
+ component_type = regscale_models.ComponentType.Hardware
265
+ component_names = ["EC2 Instances"]
266
+
267
+ os_version = image_info.get("Description", "")
268
+
269
+ # Get FQDN - use public DNS name, private DNS name, or instance name
270
+ fqdn = (
271
+ instance.get("PublicDnsName")
272
+ or instance.get("PrivateDnsName")
273
+ or instance_name
274
+ or instance.get("InstanceId", "")
275
+ )
276
+
277
+ # Create description
278
+ description = f"{instance_name} - {instance.get('PlatformDetails', 'Linux')} instance running on {instance.get('InstanceType', '')} with {cpu_count} vCPUs and {ram}GB RAM"
279
+
280
+ # Build notes with additional details
281
+ notes = f"""Description: {description}
282
+ AMI ID: {instance.get('ImageId', '')}
283
+ AMI Description: {image_info.get('Description', '')}
284
+ Architecture: {instance.get('Architecture', '')}
285
+ Root Device Type: {image_info.get('RootDeviceType', '')}
286
+ Virtualization: {image_info.get('VirtualizationType', '')}
287
+ Instance Type: {instance.get('InstanceType', '')}
288
+ vCPUs: {cpu_count}
289
+ RAM: {ram}GB
290
+ State: {instance.get('State')}
291
+ Platform Details: {instance.get('PlatformDetails', 'Linux')}
292
+ Private IP: {instance.get('PrivateIpAddress', 'N/A')}
293
+ Public IP: {instance.get('PublicIpAddress', 'N/A')}
294
+ VPC ID: {instance.get('VpcId', 'N/A')}
295
+ Subnet ID: {instance.get('SubnetId', 'N/A')}"""
296
+
297
+ # Create URI for AWS Console link
298
+ uri = f"https://console.aws.amazon.com/ec2/v2/home?region={instance.get('Region', 'us-east-1')}#InstanceDetails:instanceId={instance.get('InstanceId', '')}"
299
+
300
+ return IntegrationAsset(
301
+ name=name,
302
+ identifier=instance.get("InstanceId", ""),
303
+ asset_type=asset_type,
304
+ asset_category=asset_category,
305
+ component_type=component_type,
306
+ component_names=component_names,
307
+ parent_id=self.plan_id,
308
+ parent_module="securityplans",
309
+ status=(
310
+ regscale_models.AssetStatus.Active
311
+ if instance.get("State") == "running"
312
+ else regscale_models.AssetStatus.Inactive
313
+ ),
314
+ ip_address=instance.get("PrivateIpAddress") or instance.get("PublicIpAddress", ""),
315
+ mac_address=None, # Would need to get from network interfaces
316
+ fqdn=fqdn,
317
+ disk_storage=total_storage,
318
+ cpu=cpu_count,
319
+ ram=ram,
320
+ operating_system=operating_system,
321
+ os_version=os_version,
322
+ location=instance.get("Region", "us-east-1"),
323
+ notes=notes,
324
+ model=instance.get("InstanceType"),
325
+ manufacturer="AWS",
326
+ is_public_facing=is_public_facing,
327
+ aws_identifier=instance.get("InstanceId"),
328
+ vlan_id=instance.get("SubnetId"),
329
+ uri=uri,
330
+ source_data=instance,
331
+ description=description,
332
+ is_virtual=True, # EC2 instances are always virtual
333
+ )
334
+
335
+ def parse_lambda_function(self, function: Dict[str, Any]) -> IntegrationAsset:
336
+ """
337
+ Parse Lambda function data into RegScale asset format.
338
+
339
+ :param Dict[str, Any] function: Lambda function data
340
+ :return: RegScale asset
341
+ :rtype: IntegrationAsset
342
+ """
343
+ name = function.get("FunctionName", "")
344
+ notes: str = "" # Initialize notes with type hint
345
+
346
+ # Handle description - only slice if it's a string
347
+ description = function.get("Description")
348
+ if isinstance(description, str) and description:
349
+ # Move description to notes instead
350
+ notes = f"Description: {description}\n{notes}"
351
+
352
+ # Create full description
353
+ full_description = f"AWS Lambda function {function.get('FunctionName', '')} running {function.get('Runtime', 'unknown runtime')} with {function.get('MemorySize', 0)}MB memory"
354
+ if isinstance(description, str) and description:
355
+ full_description += f"\nFunction description: {description}"
356
+
357
+ # Build notes with additional details
358
+ notes = f"""Function Name: {function.get('FunctionName', '')}
359
+ Runtime: {function.get('Runtime', 'unknown')}
360
+ Memory Size: {function.get('MemorySize', 0)} MB
361
+ Timeout: {function.get('Timeout', 0)} seconds
362
+ Handler: {function.get('Handler', '')}
363
+ Description: {description if isinstance(description, str) else ''}"""
364
+
365
+ return IntegrationAsset(
366
+ # Required fields
367
+ name=name,
368
+ identifier=str(function.get("FunctionName", "")),
369
+ asset_type=regscale_models.AssetType.Other,
370
+ asset_category=regscale_models.AssetCategory.Software,
371
+ component_type=regscale_models.ComponentType.Software,
372
+ component_names=["Lambda Functions"],
373
+ # Parent information
374
+ parent_id=self.plan_id,
375
+ parent_module="securityplans",
376
+ # Status and location
377
+ status=regscale_models.AssetStatus.Active, # Lambda functions are always available
378
+ location=function.get("Region"),
379
+ # Software details
380
+ software_name=function.get("Runtime"),
381
+ software_version=function.get("Runtime", "").split(".")[-1] if function.get("Runtime") else None,
382
+ ram=function.get("MemorySize"),
383
+ # Cloud identifiers
384
+ external_id=function.get("FunctionName"),
385
+ aws_identifier=function.get("FunctionArn"),
386
+ uri=function.get("FunctionUrl"),
387
+ # Additional metadata
388
+ manufacturer="AWS",
389
+ source_data=function,
390
+ notes=notes,
391
+ description=full_description,
392
+ is_virtual=True, # Lambda functions are serverless/virtual
393
+ )
394
+
395
+ def parse_aws_account(self, iam: Dict[str, Any]) -> IntegrationAsset:
396
+ """
397
+ Parse IAM data to an AWS Account RegScale asset.
398
+
399
+ :param Dict[str, Any] iam: iam data
400
+ :return: RegScale asset
401
+ :rtype: IntegrationAsset
402
+ """
403
+
404
+ def get_aws_account_id(arn: str) -> str:
405
+ """
406
+ Get the AWS account ID from an ARN.
407
+
408
+ :param str arn: The ARN to extract the account ID from
409
+ :return: The AWS account ID
410
+ :rtype: str
411
+ """
412
+ return arn.split(":")[4]
413
+
414
+ name = get_aws_account_id(iam.get("Arn", ""))
415
+
416
+ return IntegrationAsset(
417
+ # Required fields
418
+ name=name,
419
+ identifier=f"AWS::::Account:{name}",
420
+ asset_type=regscale_models.AssetType.Other,
421
+ asset_category=regscale_models.AssetCategory.Hardware,
422
+ component_type=regscale_models.ComponentType.Software,
423
+ component_names=["AWS Account"],
424
+ # Parent information
425
+ parent_id=self.plan_id,
426
+ parent_module="securityplans",
427
+ # Status and location
428
+ status=regscale_models.AssetStatus.Active,
429
+ location="Unknown",
430
+ # Cloud identifiers
431
+ external_id=name,
432
+ aws_identifier=f"AWS::::Account:{name}",
433
+ # Additional metadata
434
+ manufacturer="AWS",
435
+ source_data=iam,
436
+ )
437
+
438
+ def parse_s3_bucket(self, bucket: Dict[str, Any]) -> IntegrationAsset:
439
+ """
440
+ Parse S3 bucket data into RegScale asset format.
441
+
442
+ :param Dict[str, Any] bucket: S3 bucket data
443
+ :return: RegScale asset
444
+ :rtype: IntegrationAsset
445
+ """
446
+ name = bucket.get("Name", "")
447
+
448
+ return IntegrationAsset(
449
+ # Required fields
450
+ name=name,
451
+ identifier=str(bucket.get("Name", "")),
452
+ asset_type=regscale_models.AssetType.Other,
453
+ asset_category=regscale_models.AssetCategory.Hardware,
454
+ component_type=regscale_models.ComponentType.Hardware,
455
+ component_names=["S3 Buckets"],
456
+ # Parent information
457
+ parent_id=self.plan_id,
458
+ parent_module="securityplans",
459
+ # Status and location
460
+ status=regscale_models.AssetStatus.Active,
461
+ location=bucket.get("Region"),
462
+ # Cloud identifiers
463
+ external_id=bucket.get("Name"),
464
+ aws_identifier=f"arn:aws:s3:::{bucket.get('Name')}",
465
+ uri=f"https://{bucket.get('Name')}.s3.amazonaws.com",
466
+ # Additional metadata
467
+ manufacturer="AWS",
468
+ is_public_facing=any(
469
+ grant.get("Grantee", {}).get("URI") == "http://acs.amazonaws.com/groups/global/AllUsers"
470
+ for grant in bucket.get("Grants", [])
471
+ ),
472
+ source_data=bucket,
473
+ )
474
+
475
+ def parse_rds_instance(self, db: Dict[str, Any]) -> IntegrationAsset:
476
+ """
477
+ Parse RDS instance data into RegScale asset format.
478
+
479
+ :param Dict[str, Any] db: RDS instance data
480
+ :return: RegScale asset
481
+ :rtype: IntegrationAsset
482
+ """
483
+ name = db.get("DBInstanceIdentifier", "")
484
+ if db.get("EngineVersion"):
485
+ name += f" {db.get('EngineVersion')}"
486
+ name += f") - {db.get('DBInstanceClass', '')}"
487
+
488
+ return IntegrationAsset(
489
+ # Required fields
490
+ name=name,
491
+ identifier=str(db.get("DBInstanceIdentifier", "")),
492
+ asset_type=regscale_models.AssetType.VM,
493
+ asset_category=regscale_models.AssetCategory.Hardware,
494
+ component_type=regscale_models.ComponentType.Hardware,
495
+ component_names=["RDS Instances"],
496
+ # Parent information
497
+ parent_id=self.plan_id,
498
+ parent_module="securityplans",
499
+ # Network information
500
+ fqdn=db.get("Endpoint", {}).get("Address"),
501
+ vlan_id=db.get("VpcId"),
502
+ # Status and location
503
+ status=(
504
+ regscale_models.AssetStatus.Active
505
+ if db.get("DBInstanceStatus") == "available"
506
+ else regscale_models.AssetStatus.Inactive
507
+ ),
508
+ location=db.get("AvailabilityZone"),
509
+ # Hardware details
510
+ model=db.get("DBInstanceClass"),
511
+ manufacturer="AWS",
512
+ disk_storage=db.get("AllocatedStorage"),
513
+ # Software details
514
+ software_name=db.get("Engine"),
515
+ software_version=db.get("EngineVersion"),
516
+ # Cloud identifiers
517
+ external_id=db.get("DBInstanceIdentifier"),
518
+ aws_identifier=db.get("DBInstanceArn"),
519
+ # Additional metadata
520
+ is_public_facing=db.get("PubliclyAccessible", False),
521
+ source_data=db,
522
+ )
523
+
524
+ def parse_dynamodb_table(self, table: Dict[str, Any]) -> IntegrationAsset:
525
+ """
526
+ Parse DynamoDB table data into RegScale asset format.
527
+
528
+ :param Dict[str, Any] table: DynamoDB table data
529
+ :return: RegScale asset
530
+ :rtype: IntegrationAsset
531
+ """
532
+ name = table.get("TableName", "")
533
+ if table.get("TableStatus"):
534
+ name += f" ({table.get('TableStatus')})"
535
+
536
+ return IntegrationAsset(
537
+ # Required fields
538
+ name=name,
539
+ identifier=str(table.get("TableName", "")),
540
+ asset_type=regscale_models.AssetType.Other,
541
+ asset_category=regscale_models.AssetCategory.Software,
542
+ component_type=regscale_models.ComponentType.Software,
543
+ component_names=["DynamoDB Tables"],
544
+ # Parent information
545
+ parent_id=self.plan_id,
546
+ parent_module="securityplans",
547
+ # Status and location
548
+ status=(
549
+ regscale_models.AssetStatus.Active
550
+ if table.get("TableStatus") == "ACTIVE"
551
+ else regscale_models.AssetStatus.Inactive
552
+ ),
553
+ location=table.get("Region"),
554
+ # Hardware details
555
+ disk_storage=table.get("TableSizeBytes"),
556
+ # Cloud identifiers
557
+ external_id=table.get("TableName"),
558
+ aws_identifier=table.get("TableArn"),
559
+ # Additional metadata
560
+ manufacturer="AWS",
561
+ source_data=table,
562
+ )
563
+
564
+ def parse_vpc(self, vpc: Dict[str, Any]) -> IntegrationAsset:
565
+ """
566
+ Parse VPC data into RegScale asset format.
567
+
568
+ :param Dict[str, Any] vpc: VPC data
569
+ :return: RegScale asset
570
+ :rtype: IntegrationAsset
571
+ """
572
+ # Get VPC name from tags
573
+ name = next((tag["Value"] for tag in vpc.get("Tags", []) if tag["Key"] == "Name"), vpc.get("VpcId", ""))
574
+ notes: str = "" # Initialize notes with type hint
575
+ if vpc.get("IsDefault"):
576
+ notes = "Default VPC\n" + notes
577
+
578
+ return IntegrationAsset(
579
+ # Required fields
580
+ name=name,
581
+ identifier=str(vpc.get("VpcId", "")),
582
+ asset_type=regscale_models.AssetType.NetworkRouter,
583
+ asset_category=regscale_models.AssetCategory.Hardware,
584
+ component_type=regscale_models.ComponentType.Hardware,
585
+ component_names=["VPCs"],
586
+ # Parent information
587
+ parent_id=self.plan_id,
588
+ parent_module="securityplans",
589
+ # Status and location
590
+ status=(
591
+ regscale_models.AssetStatus.Active
592
+ if vpc.get("State") == "available"
593
+ else regscale_models.AssetStatus.Inactive
594
+ ),
595
+ location=vpc.get("Region"),
596
+ # Network information
597
+ vlan_id=vpc.get("VpcId"),
598
+ # Cloud identifiers
599
+ external_id=vpc.get("VpcId"),
600
+ aws_identifier=vpc.get("VpcId"),
601
+ # Additional metadata
602
+ manufacturer="AWS",
603
+ notes=f"CIDR: {vpc.get('CidrBlock')}",
604
+ source_data=vpc,
605
+ )
606
+
607
+ def parse_load_balancer(self, lb: Dict[str, Any]) -> IntegrationAsset:
608
+ """
609
+ Parse Load Balancer data into RegScale asset format.
610
+
611
+ :param Dict[str, Any] lb: Load Balancer data
612
+ :return: RegScale asset
613
+ :rtype: IntegrationAsset
614
+ """
615
+ name = lb.get("LoadBalancerName", "")
616
+ notes: str = "" # Initialize notes with type hint
617
+ if lb.get("Scheme"):
618
+ notes = f"Scheme: {lb.get('Scheme')}\n{notes}"
619
+
620
+ return IntegrationAsset(
621
+ # Required fields
622
+ name=name,
623
+ identifier=str(lb.get("LoadBalancerName", "")),
624
+ asset_type=regscale_models.AssetType.NetworkRouter,
625
+ asset_category=regscale_models.AssetCategory.Hardware,
626
+ component_type=regscale_models.ComponentType.Hardware,
627
+ component_names=["Load Balancers"],
628
+ # Parent information
629
+ parent_id=self.plan_id,
630
+ parent_module="securityplans",
631
+ # Network information
632
+ fqdn=lb.get("DNSName"),
633
+ vlan_id=lb.get("VpcId"),
634
+ # Status and location
635
+ status=(
636
+ regscale_models.AssetStatus.Active
637
+ if lb.get("State") == "active"
638
+ else regscale_models.AssetStatus.Inactive
639
+ ),
640
+ location=lb.get("Region"),
641
+ # Cloud identifiers
642
+ external_id=lb.get("LoadBalancerName"),
643
+ aws_identifier=lb.get("LoadBalancerArn"),
644
+ # Additional metadata
645
+ manufacturer="AWS",
646
+ is_public_facing=lb.get("Scheme") == "internet-facing",
647
+ source_data=lb,
648
+ # Ports and protocols
649
+ ports_and_protocols=[
650
+ {"port": listener.get("Port"), "protocol": listener.get("Protocol")}
651
+ for listener in lb.get("Listeners", [])
652
+ ],
653
+ )
654
+
655
+ def parse_ecr_repository(self, repo: Dict[str, Any]) -> IntegrationAsset:
656
+ """
657
+ Parse ECR repository data into RegScale asset format.
658
+
659
+ :param Dict[str, Any] repo: ECR repository data
660
+ :return: RegScale asset
661
+ :rtype: IntegrationAsset
662
+ """
663
+ name = repo.get("RepositoryName", "")
664
+ notes: str = "" # Initialize notes with type hint
665
+ if repo.get("ImageTagMutability"):
666
+ notes = f"Image Tag Mutability: {repo.get('ImageTagMutability')}\n{notes}"
667
+ if repo.get("ImageScanningConfiguration", {}).get("ScanOnPush"):
668
+ notes = "Scan on Push enabled\n" + notes
669
+
670
+ return IntegrationAsset(
671
+ # Required fields
672
+ name=name,
673
+ identifier=str(repo.get("RepositoryName", "")),
674
+ asset_type=regscale_models.AssetType.Other,
675
+ asset_category=regscale_models.AssetCategory.Software,
676
+ component_type=regscale_models.ComponentType.Software,
677
+ component_names=["ECR Repositories"],
678
+ # Parent information
679
+ parent_id=self.plan_id,
680
+ parent_module="securityplans",
681
+ # Status and location
682
+ status=regscale_models.AssetStatus.Active,
683
+ location=repo.get("Region"),
684
+ # Cloud identifiers
685
+ external_id=repo.get("RepositoryName"),
686
+ aws_identifier=repo.get("RepositoryArn"),
687
+ uri=repo.get("RepositoryUri"),
688
+ # Additional metadata
689
+ manufacturer="AWS",
690
+ source_data=repo,
691
+ )
692
+
693
+ def fetch_findings(self, *args, **kwargs) -> Iterator[IntegrationFinding]:
694
+ """
695
+ Fetch security findings.
696
+ Currently, we're only doing inventory collection.
697
+
698
+ :yield: Iterator[IntegrationFinding]
699
+ """
700
+ import boto3
701
+
702
+ from regscale.integrations.commercial.amazon.common import fetch_aws_findings
703
+
704
+ aws_secret_key_id = kwargs.get("aws_access_key_id") or os.getenv("AWS_ACCESS_KEY_ID")
705
+ aws_secret_access_key = kwargs.get("aws_secret_access_key") or os.getenv("AWS_SECRET_ACCESS_KEY")
706
+ region = kwargs.get("region")
707
+ if not aws_secret_key_id or not aws_secret_access_key:
708
+ raise ValueError(
709
+ "AWS Access Key ID and Secret Access Key are required.\nPlease update in environment "
710
+ "variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) or pass as arguments."
711
+ )
712
+ if not region:
713
+ logger.warning("AWS region not provided. Defaulting to 'us-east-1'.")
714
+ client = boto3.client(
715
+ "securityhub",
716
+ region_name=kwargs.get(region, "us-east-1"),
717
+ aws_access_key_id=aws_secret_key_id,
718
+ aws_secret_access_key=aws_secret_access_key,
719
+ )
720
+ aws_findings = fetch_aws_findings(aws_client=client)
721
+ self.num_findings_to_process = len(aws_findings)
722
+ for finding in aws_findings:
723
+ yield from iter(self.parse_finding(finding))
724
+
725
+ @staticmethod
726
+ def get_baseline(resource: dict) -> str:
727
+ """
728
+ Get Baseline
729
+
730
+ :param dict resource: AWS Resource
731
+ :return: AWS Baseline string
732
+ :rtype: str
733
+ """
734
+ baseline = resource.get("Type", "")
735
+ baseline_map = {
736
+ "AwsAccount": "AWS Account",
737
+ "AwsS3Bucket": "S3 Bucket",
738
+ "AwsIamRole": "IAM Role",
739
+ "AwsEc2Instance": "EC2 Instance",
740
+ }
741
+ return baseline_map.get(baseline, baseline)
742
+
743
+ @staticmethod
744
+ def extract_name_from_arn(arn: str) -> Optional[str]:
745
+ """
746
+ Extract the name from an ARN.
747
+
748
+ :param str arn: The ARN to extract the name from
749
+ :return: The extracted name, or None if not found
750
+ :rtype: Optional[str]
751
+ """
752
+ # Get the last part after the last '/'
753
+ try:
754
+ return arn.split("/")[-1]
755
+ except IndexError:
756
+ # For ARNs without '/', try getting the last part after ':'
757
+ try:
758
+ return arn.split(":")[-1]
759
+ except IndexError:
760
+ return None
761
+
762
+ def parse_finding(self, finding: dict) -> list[IntegrationFinding]:
763
+ """
764
+ Parse AWS Security Hub to RegScale IntegrationFinding format.
765
+
766
+ :param dict finding: AWS Security Hub finding
767
+ :return: RegScale IntegrationFinding
768
+ :rtype: list[IntegrationFinding]
769
+ """
770
+ findings = []
771
+ try:
772
+ for resource in finding["Resources"]:
773
+ status, results = determine_status_and_results(finding)
774
+ comments = get_comments(finding)
775
+ severity = check_finding_severity(comments)
776
+ friendly_sev = "low"
777
+ if severity in ["CRITICAL", "HIGH"]:
778
+ friendly_sev = "high"
779
+ elif severity in ["MEDIUM", "MODERATE"]:
780
+ friendly_sev = "moderate"
781
+ try:
782
+ days = self.app.config["issues"]["amazon"][friendly_sev]
783
+ except KeyError:
784
+ logger.warning("Invalid severity level: %s, defaulting to 30 day due date", severity)
785
+ days = 30
786
+ due_date = datetime_str(get_due_date(date_str(finding["CreatedAt"]), days))
787
+
788
+ plugin_name = next(iter(finding.get("Types", [])))
789
+ findings.append(
790
+ IntegrationFinding(
791
+ asset_identifier=self.extract_name_from_arn(resource["Id"]),
792
+ external_id=self.extract_name_from_arn(resource["Id"]),
793
+ control_labels=[], # Determine how to populate this
794
+ title=finding["Title"],
795
+ category="SecurityHub",
796
+ issue_title=finding["Title"],
797
+ severity=self.finding_severity_map.get(severity),
798
+ description=finding["Description"],
799
+ status=IssueStatus.Open if status == "Fail" else IssueStatus.Closed,
800
+ checklist_status=self.get_checklist_status(status),
801
+ vulnerability_number="",
802
+ results=results,
803
+ recommendation_for_mitigation=finding.get("Remediation", {})
804
+ .get("Recommendation", {})
805
+ .get("Text", ""),
806
+ comments=comments,
807
+ poam_comments=comments,
808
+ date_created=date_str(finding["CreatedAt"]),
809
+ due_date=due_date,
810
+ plugin_name=plugin_name,
811
+ baseline=self.get_baseline(resource),
812
+ observations=comments,
813
+ gaps="",
814
+ evidence="",
815
+ impact="",
816
+ vulnerability_type="Vulnerability Scan",
817
+ )
818
+ )
819
+
820
+ except Exception as e:
821
+ logger.error(f"Error parsing AWS Security Hub finding: {str(e)}", exc_info=True)
822
+
823
+ return findings