regscale-cli 6.21.2.0__py3-none-any.whl → 6.28.2.1__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.
Files changed (314) hide show
  1. regscale/_version.py +1 -1
  2. regscale/airflow/hierarchy.py +2 -2
  3. regscale/core/app/api.py +5 -2
  4. regscale/core/app/application.py +36 -6
  5. regscale/core/app/internal/control_editor.py +73 -21
  6. regscale/core/app/internal/evidence.py +727 -204
  7. regscale/core/app/internal/login.py +4 -2
  8. regscale/core/app/internal/model_editor.py +219 -64
  9. regscale/core/app/utils/app_utils.py +86 -12
  10. regscale/core/app/utils/catalog_utils/common.py +1 -1
  11. regscale/core/login.py +21 -4
  12. regscale/core/utils/async_graphql_client.py +363 -0
  13. regscale/core/utils/date.py +77 -1
  14. regscale/dev/cli.py +26 -0
  15. regscale/dev/code_gen.py +109 -24
  16. regscale/dev/version.py +72 -0
  17. regscale/integrations/commercial/__init__.py +30 -2
  18. regscale/integrations/commercial/aws/audit_manager_compliance.py +3908 -0
  19. regscale/integrations/commercial/aws/cli.py +3107 -54
  20. regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
  21. regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
  22. regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
  23. regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
  24. regscale/integrations/commercial/{amazon → aws}/common.py +71 -19
  25. regscale/integrations/commercial/aws/config_compliance.py +914 -0
  26. regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
  27. regscale/integrations/commercial/aws/control_compliance_analyzer.py +439 -0
  28. regscale/integrations/commercial/aws/evidence_generator.py +283 -0
  29. regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
  30. regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
  31. regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
  32. regscale/integrations/commercial/aws/iam_evidence.py +574 -0
  33. regscale/integrations/commercial/aws/inventory/__init__.py +338 -22
  34. regscale/integrations/commercial/aws/inventory/base.py +107 -5
  35. regscale/integrations/commercial/aws/inventory/resources/analytics.py +390 -0
  36. regscale/integrations/commercial/aws/inventory/resources/applications.py +234 -0
  37. regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
  38. regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
  39. regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
  40. regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
  41. regscale/integrations/commercial/aws/inventory/resources/compute.py +328 -9
  42. regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
  43. regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
  44. regscale/integrations/commercial/aws/inventory/resources/database.py +481 -31
  45. regscale/integrations/commercial/aws/inventory/resources/developer_tools.py +253 -0
  46. regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
  47. regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
  48. regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
  49. regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
  50. regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
  51. regscale/integrations/commercial/aws/inventory/resources/machine_learning.py +358 -0
  52. regscale/integrations/commercial/aws/inventory/resources/networking.py +390 -67
  53. regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
  54. regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
  55. regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
  56. regscale/integrations/commercial/aws/inventory/resources/storage.py +288 -29
  57. regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
  58. regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
  59. regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
  60. regscale/integrations/commercial/aws/kms_evidence.py +879 -0
  61. regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
  62. regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
  63. regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
  64. regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
  65. regscale/integrations/commercial/aws/org_evidence.py +666 -0
  66. regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
  67. regscale/integrations/commercial/aws/s3_evidence.py +632 -0
  68. regscale/integrations/commercial/aws/scanner.py +1072 -205
  69. regscale/integrations/commercial/aws/security_hub.py +319 -0
  70. regscale/integrations/commercial/aws/session_manager.py +282 -0
  71. regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
  72. regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
  73. regscale/integrations/commercial/jira.py +489 -153
  74. regscale/integrations/commercial/microsoft_defender/defender.py +326 -5
  75. regscale/integrations/commercial/microsoft_defender/defender_api.py +348 -14
  76. regscale/integrations/commercial/microsoft_defender/defender_constants.py +157 -0
  77. regscale/integrations/commercial/qualys/__init__.py +167 -68
  78. regscale/integrations/commercial/qualys/scanner.py +305 -39
  79. regscale/integrations/commercial/sarif/sairf_importer.py +432 -0
  80. regscale/integrations/commercial/sarif/sarif_converter.py +67 -0
  81. regscale/integrations/commercial/sicura/api.py +79 -42
  82. regscale/integrations/commercial/sicura/commands.py +8 -2
  83. regscale/integrations/commercial/sicura/scanner.py +83 -44
  84. regscale/integrations/commercial/stigv2/ckl_parser.py +5 -5
  85. regscale/integrations/commercial/synqly/assets.py +133 -16
  86. regscale/integrations/commercial/synqly/edr.py +2 -8
  87. regscale/integrations/commercial/synqly/query_builder.py +536 -0
  88. regscale/integrations/commercial/synqly/ticketing.py +27 -0
  89. regscale/integrations/commercial/synqly/vulnerabilities.py +165 -28
  90. regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
  91. regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
  92. regscale/integrations/commercial/tenablev2/commands.py +146 -5
  93. regscale/integrations/commercial/tenablev2/scanner.py +1 -3
  94. regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
  95. regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
  96. regscale/integrations/commercial/wizv2/click.py +191 -76
  97. regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
  98. regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
  99. regscale/integrations/commercial/wizv2/compliance_report.py +1592 -0
  100. regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
  101. regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +7 -3
  102. regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +92 -89
  103. regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
  104. regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
  105. regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +66 -9
  106. regscale/integrations/commercial/wizv2/file_cleanup.py +104 -0
  107. regscale/integrations/commercial/wizv2/issue.py +776 -28
  108. regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
  109. regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
  110. regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
  111. regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
  112. regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
  113. regscale/integrations/commercial/wizv2/reports.py +243 -0
  114. regscale/integrations/commercial/wizv2/sbom.py +1 -1
  115. regscale/integrations/commercial/wizv2/scanner.py +1031 -441
  116. regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
  117. regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
  118. regscale/integrations/commercial/wizv2/variables.py +89 -3
  119. regscale/integrations/compliance_integration.py +1036 -151
  120. regscale/integrations/control_matcher.py +432 -0
  121. regscale/integrations/due_date_handler.py +333 -0
  122. regscale/integrations/milestone_manager.py +291 -0
  123. regscale/integrations/public/__init__.py +14 -0
  124. regscale/integrations/public/cci_importer.py +834 -0
  125. regscale/integrations/public/csam/__init__.py +0 -0
  126. regscale/integrations/public/csam/csam.py +938 -0
  127. regscale/integrations/public/csam/csam_agency_defined.py +179 -0
  128. regscale/integrations/public/csam/csam_common.py +154 -0
  129. regscale/integrations/public/csam/csam_controls.py +432 -0
  130. regscale/integrations/public/csam/csam_poam.py +124 -0
  131. regscale/integrations/public/fedramp/click.py +77 -6
  132. regscale/integrations/public/fedramp/docx_parser.py +10 -1
  133. regscale/integrations/public/fedramp/fedramp_cis_crm.py +675 -289
  134. regscale/integrations/public/fedramp/fedramp_five.py +1 -1
  135. regscale/integrations/public/fedramp/poam/scanner.py +75 -7
  136. regscale/integrations/public/fedramp/poam_export_v5.py +888 -0
  137. regscale/integrations/scanner_integration.py +1961 -430
  138. regscale/models/integration_models/CCI_List.xml +1 -0
  139. regscale/models/integration_models/aqua.py +2 -2
  140. regscale/models/integration_models/cisa_kev_data.json +805 -11
  141. regscale/models/integration_models/flat_file_importer/__init__.py +5 -8
  142. regscale/models/integration_models/nexpose.py +36 -10
  143. regscale/models/integration_models/qualys.py +3 -4
  144. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  145. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +87 -18
  146. regscale/models/integration_models/synqly_models/filter_parser.py +332 -0
  147. regscale/models/integration_models/synqly_models/ocsf_mapper.py +124 -25
  148. regscale/models/integration_models/synqly_models/synqly_model.py +89 -16
  149. regscale/models/locking.py +12 -8
  150. regscale/models/platform.py +4 -2
  151. regscale/models/regscale_models/__init__.py +7 -0
  152. regscale/models/regscale_models/assessment.py +2 -1
  153. regscale/models/regscale_models/catalog.py +1 -1
  154. regscale/models/regscale_models/compliance_settings.py +251 -1
  155. regscale/models/regscale_models/component.py +1 -0
  156. regscale/models/regscale_models/control_implementation.py +236 -41
  157. regscale/models/regscale_models/control_objective.py +74 -5
  158. regscale/models/regscale_models/file.py +2 -0
  159. regscale/models/regscale_models/form_field_value.py +5 -3
  160. regscale/models/regscale_models/inheritance.py +44 -0
  161. regscale/models/regscale_models/issue.py +301 -102
  162. regscale/models/regscale_models/milestone.py +33 -14
  163. regscale/models/regscale_models/organization.py +3 -0
  164. regscale/models/regscale_models/regscale_model.py +310 -73
  165. regscale/models/regscale_models/security_plan.py +4 -2
  166. regscale/models/regscale_models/vulnerability.py +3 -3
  167. regscale/regscale.py +25 -4
  168. regscale/templates/__init__.py +0 -0
  169. regscale/utils/threading/threadhandler.py +20 -15
  170. regscale/validation/record.py +23 -1
  171. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/METADATA +17 -33
  172. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/RECORD +310 -111
  173. tests/core/__init__.py +0 -0
  174. tests/core/utils/__init__.py +0 -0
  175. tests/core/utils/test_async_graphql_client.py +472 -0
  176. tests/fixtures/test_fixture.py +13 -8
  177. tests/regscale/core/test_login.py +171 -4
  178. tests/regscale/integrations/commercial/__init__.py +0 -0
  179. tests/regscale/integrations/commercial/aws/__init__.py +0 -0
  180. tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
  181. tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
  182. tests/regscale/integrations/commercial/aws/test_aws_analytics_collector.py +260 -0
  183. tests/regscale/integrations/commercial/aws/test_aws_applications_collector.py +242 -0
  184. tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
  185. tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
  186. tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
  187. tests/regscale/integrations/commercial/aws/test_aws_developer_tools_collector.py +203 -0
  188. tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
  189. tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
  190. tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
  191. tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
  192. tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
  193. tests/regscale/integrations/commercial/aws/test_aws_machine_learning_collector.py +237 -0
  194. tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
  195. tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
  196. tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
  197. tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
  198. tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
  199. tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
  200. tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
  201. tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
  202. tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
  203. tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
  204. tests/regscale/integrations/commercial/aws/test_control_compliance_analyzer.py +375 -0
  205. tests/regscale/integrations/commercial/aws/test_datetime_parsing.py +223 -0
  206. tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
  207. tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
  208. tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
  209. tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
  210. tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
  211. tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
  212. tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
  213. tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
  214. tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
  215. tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
  216. tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
  217. tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
  218. tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
  219. tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
  220. tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
  221. tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
  222. tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
  223. tests/regscale/integrations/commercial/conftest.py +28 -0
  224. tests/regscale/integrations/commercial/microsoft_defender/__init__.py +1 -0
  225. tests/regscale/integrations/commercial/microsoft_defender/test_defender.py +1517 -0
  226. tests/regscale/integrations/commercial/microsoft_defender/test_defender_api.py +1748 -0
  227. tests/regscale/integrations/commercial/microsoft_defender/test_defender_constants.py +327 -0
  228. tests/regscale/integrations/commercial/microsoft_defender/test_defender_scanner.py +487 -0
  229. tests/regscale/integrations/commercial/test_aws.py +3742 -0
  230. tests/regscale/integrations/commercial/test_burp.py +48 -0
  231. tests/regscale/integrations/commercial/test_crowdstrike.py +49 -0
  232. tests/regscale/integrations/commercial/test_dependabot.py +341 -0
  233. tests/regscale/integrations/commercial/test_gcp.py +1543 -0
  234. tests/regscale/integrations/commercial/test_gitlab.py +549 -0
  235. tests/regscale/integrations/commercial/test_ip_mac_address_length.py +84 -0
  236. tests/regscale/integrations/commercial/test_jira.py +2204 -0
  237. tests/regscale/integrations/commercial/test_npm_audit.py +42 -0
  238. tests/regscale/integrations/commercial/test_okta.py +1228 -0
  239. tests/regscale/integrations/commercial/test_sarif_converter.py +251 -0
  240. tests/regscale/integrations/commercial/test_sicura.py +349 -0
  241. tests/regscale/integrations/commercial/test_snow.py +423 -0
  242. tests/regscale/integrations/commercial/test_sonarcloud.py +394 -0
  243. tests/regscale/integrations/commercial/test_sqlserver.py +186 -0
  244. tests/regscale/integrations/commercial/test_stig.py +33 -0
  245. tests/regscale/integrations/commercial/test_stig_mapper.py +153 -0
  246. tests/regscale/integrations/commercial/test_stigv2.py +406 -0
  247. tests/regscale/integrations/commercial/test_wiz.py +1365 -0
  248. tests/regscale/integrations/commercial/test_wiz_inventory.py +256 -0
  249. tests/regscale/integrations/commercial/wizv2/__init__.py +339 -0
  250. tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
  251. tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
  252. tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
  253. tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
  254. tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
  255. tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
  256. tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
  257. tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
  258. tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
  259. tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
  260. tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
  261. tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
  262. tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
  263. tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
  264. tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
  265. tests/regscale/integrations/commercial/wizv2/test_compliance_report_normalization.py +138 -0
  266. tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
  267. tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
  268. tests/regscale/integrations/commercial/wizv2/test_issue.py +343 -0
  269. tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
  270. tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
  271. tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
  272. tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
  273. tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +165 -0
  274. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +1394 -0
  275. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_unit.py +341 -0
  276. tests/regscale/integrations/commercial/wizv2/test_wiz_control_normalization.py +138 -0
  277. tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
  278. tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
  279. tests/regscale/integrations/commercial/wizv2/test_wiz_status_mapping.py +149 -0
  280. tests/regscale/integrations/commercial/wizv2/test_wizv2.py +1218 -0
  281. tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +519 -0
  282. tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
  283. tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
  284. tests/regscale/integrations/public/__init__.py +0 -0
  285. tests/regscale/integrations/public/fedramp/__init__.py +1 -0
  286. tests/regscale/integrations/public/fedramp/test_gen_asset_list.py +150 -0
  287. tests/regscale/integrations/public/fedramp/test_poam_export_v5.py +1293 -0
  288. tests/regscale/integrations/public/test_alienvault.py +220 -0
  289. tests/regscale/integrations/public/test_cci.py +1053 -0
  290. tests/regscale/integrations/public/test_cisa.py +1021 -0
  291. tests/regscale/integrations/public/test_emass.py +518 -0
  292. tests/regscale/integrations/public/test_fedramp.py +1152 -0
  293. tests/regscale/integrations/public/test_fedramp_cis_crm.py +3661 -0
  294. tests/regscale/integrations/public/test_file_uploads.py +506 -0
  295. tests/regscale/integrations/public/test_oscal.py +453 -0
  296. tests/regscale/integrations/test_compliance_status_mapping.py +406 -0
  297. tests/regscale/integrations/test_control_matcher.py +1421 -0
  298. tests/regscale/integrations/test_control_matching.py +155 -0
  299. tests/regscale/integrations/test_milestone_manager.py +408 -0
  300. tests/regscale/models/test_control_implementation.py +118 -3
  301. tests/regscale/models/test_form_field_value_integration.py +304 -0
  302. tests/regscale/models/test_issue.py +378 -1
  303. tests/regscale/models/test_module_integration.py +582 -0
  304. tests/regscale/models/test_tenable_integrations.py +811 -105
  305. regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3057
  306. regscale/integrations/public/fedramp/mappings/fedramp_r4_parts.json +0 -7388
  307. regscale/integrations/public/fedramp/mappings/fedramp_r5_parts.json +0 -9605
  308. regscale/integrations/public/fedramp/parts_mapper.py +0 -107
  309. /regscale/integrations/commercial/{amazon → sarif}/__init__.py +0 -0
  310. /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
  311. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/LICENSE +0 -0
  312. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/WHEEL +0 -0
  313. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/entry_points.txt +0 -0
  314. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,11 @@ from typing import Any, Union, TYPE_CHECKING, Optional
5
5
 
6
6
  if TYPE_CHECKING:
7
7
  from regscale.models.integration_models.synqly_models.connectors import Edr, Ticketing, Vulnerabilities
8
- from synqly.engine.resources.ocsf.resources.v_1_1_0.resources.securityfinding import ResourceDetails, Vulnerability
8
+ from synqly.engine.resources.ocsf.resources.v_1_3_0.resources.securityfinding import (
9
+ Finding,
10
+ ResourceDetails,
11
+ Vulnerability,
12
+ )
9
13
 
10
14
  from synqly import engine
11
15
  from synqly.engine import CreateTicketRequest
@@ -78,7 +82,7 @@ class Mapper:
78
82
  """
79
83
  due_date = convert_datetime_to_regscale_string(ticket.due_date)
80
84
  if not due_date:
81
- due_date = self._determine_due_date(ticket.priority)
85
+ due_date = self._determine_due_date(ticket.priority, connector)
82
86
  ticket_dict = ticket.dict()
83
87
  key_val_desc = "All fields:\n"
84
88
  for k, v in ticket_dict.items():
@@ -102,9 +106,8 @@ class Mapper:
102
106
  regscale_issue.manualDetectionId = ticket.id
103
107
  return regscale_issue
104
108
 
105
- @staticmethod
106
109
  def _ocsf_asset_to_regscale(
107
- connector: Union["Edr", "Vulnerabilities"], asset: Union[InventoryInfo, OCSFAsset, SoftwareInfo], **kwargs
110
+ self, connector: Union["Edr", "Vulnerabilities"], asset: Union[InventoryInfo, OCSFAsset, SoftwareInfo], **kwargs
108
111
  ) -> IntegrationAsset:
109
112
  """
110
113
  Convert OCSF Asset to RegScale Asset
@@ -130,6 +133,9 @@ class Mapper:
130
133
  else:
131
134
  name = device_data.name or device_data.hostname or f"{connector.provider} Asset: {device_data.uid}"
132
135
  category = AssetCategory.Software
136
+ ip_v4s, ip_v6s = self._determine_ip_addresses(
137
+ device_data.ip_addresses if device_data.ip_addresses else [device_data.ip]
138
+ )
133
139
  return IntegrationAsset(
134
140
  name=name,
135
141
  identifier=device_data.uid,
@@ -139,7 +145,8 @@ class Mapper:
139
145
  parent_module=SecurityPlan.get_module_string(),
140
146
  mac_address=device_data.mac,
141
147
  fqdn=device_data.hostname,
142
- ip_address=", ".join(device_data.ip_addresses) if device_data.ip_addresses else device_data.ip,
148
+ ip_address=", ".join(ip_v4s),
149
+ ipv6_address=", ".join(ip_v6s),
143
150
  location=device_data.location or device_data.zone,
144
151
  vlan_id=device_data.vlan_uid,
145
152
  other_tracking_number=device_data.uid,
@@ -152,7 +159,64 @@ class Mapper:
152
159
  )
153
160
 
154
161
  @staticmethod
155
- def _parse_finding_data(finding: "SecurityFinding", vuln: Optional["Vulnerability"] = None) -> dict:
162
+ def _determine_ip_addresses(ips: list[str]) -> tuple[list[str], list[str]]:
163
+ """
164
+ Parse the list of ips and return a tuple of two lists: list of IP v4 and IP v6 addresses
165
+
166
+ :param list[str] ips: List of IP addresses
167
+ :return: Tuple containing two lists, list of IP v4 addresses and list of IP v6 addresses
168
+ :rtype: tuple[list[str], list[str]]
169
+ """
170
+ import ipaddress
171
+
172
+ ip_v4s = []
173
+ ip_v6s = []
174
+ for ip in ips:
175
+ try:
176
+ ipaddress.IPv4Address(ip)
177
+ ip_v4s.append(ip)
178
+ except ipaddress.AddressValueError:
179
+ try:
180
+ ipaddress.IPv6Address(ip)
181
+ ip_v6s.append(ip)
182
+ except ipaddress.AddressValueError:
183
+ continue
184
+ return ip_v4s, ip_v6s
185
+
186
+ @staticmethod
187
+ def _determine_date(
188
+ attribute: str, finding: Optional["Finding"] = None, vuln: Optional["Vulnerability"] = None
189
+ ) -> datetime:
190
+ """
191
+ Determine the date based on the provided vulnerability or finding
192
+
193
+ :param str attribute: The attribute to determine the date for
194
+ :param Optional[SecurityFinding] finding: The finding to determine the date for
195
+ :param Optional[Vulnerability] vuln: The vulnerability to determine the date for
196
+ :return: The date
197
+ :rtype: datetime
198
+ """
199
+ from regscale.core.utils.date import normalize_timestamp
200
+
201
+ vuln_date = getattr(vuln, attribute) if vuln else None
202
+ fallback_vuln_date = getattr(vuln, attribute.replace("_dt", ""), None) if vuln else None
203
+ finding_date = getattr(finding, attribute) if finding else None
204
+ fallback_finding_date = getattr(finding, attribute.replace("_dt", ""), None) if finding else None
205
+ if vuln_date:
206
+ return vuln_date
207
+ elif finding_date:
208
+ return finding_date
209
+ # No datetime objects, lets try the epoch integers
210
+ if fallback_vuln_date:
211
+ fallback_vuln_date = normalize_timestamp(fallback_vuln_date)
212
+ return datetime.fromtimestamp(fallback_vuln_date)
213
+ elif fallback_finding_date:
214
+ fallback_finding_date = normalize_timestamp(fallback_finding_date)
215
+ return datetime.fromtimestamp(fallback_finding_date)
216
+ else:
217
+ return datetime.now()
218
+
219
+ def _parse_finding_data(self, finding: "SecurityFinding", vuln: Optional["Vulnerability"] = None) -> dict:
156
220
  """
157
221
  Parse the data from the SecurityFinding object
158
222
 
@@ -160,20 +224,25 @@ class Mapper:
160
224
  :return: A dictionary of the parsed data
161
225
  :rtype: dict
162
226
  """
163
- from synqly.engine.resources.ocsf.resources.v_1_1_0.resources.securityfinding import Remediation
227
+ from synqly.engine.resources.ocsf.resources.v_1_3_0.resources.securityfinding import Remediation
164
228
 
165
229
  finding_data = {
166
230
  "cve": None,
167
- "first_seen": vuln.first_seen_time_dt if vuln else finding.finding.first_seen_time_dt,
168
- "last_seen": vuln.last_seen_time_dt if vuln else finding.finding.last_seen_time_dt,
231
+ "first_seen": self._determine_date("first_seen_time_dt", getattr(finding, "finding", None), vuln),
232
+ "last_seen": self._determine_date("last_seen_time_dt", getattr(finding, "finding", None), vuln),
169
233
  "plugin_id": vuln.cve.uid if vuln else finding.finding.product_uid,
170
- "severity": int(vuln.severity) if vuln else finding.severity_id,
234
+ "severity": vuln.severity if vuln and getattr(vuln, "severity") else finding.severity_id,
171
235
  "remediation": getattr(vuln, "remediation") if vuln else finding.finding.remediation.desc,
236
+ "title": getattr(vuln, "title") or finding.finding.title,
172
237
  }
173
238
  if vuln:
174
239
  finding_data["cve"] = vuln.cve.uid
175
240
  else:
176
241
  finding_data["cve"] = finding.finding.title if "cve" in finding.finding.title.lower() else None
242
+ try:
243
+ finding_data["severity"] = int(finding_data["severity"])
244
+ except ValueError:
245
+ finding_data["severity"] = 0
177
246
  if isinstance(finding_data["remediation"], Remediation):
178
247
  finding_data["remediation"] = finding_data["remediation"].desc
179
248
  elif isinstance(finding_data["remediation"], dict):
@@ -183,8 +252,26 @@ class Mapper:
183
252
  finding_data["remediation"] = remediation_string
184
253
  elif isinstance(finding_data["remediation"], list):
185
254
  finding_data["remediation"] = "\n".join(finding_data["remediation"])
255
+ finding_data["title"] = self._determine_title(finding_data["title"], finding_data["cve"])
186
256
  return finding_data
187
257
 
258
+ @staticmethod
259
+ def _determine_title(title: Optional[str], cve: Optional[str]) -> str:
260
+ """
261
+ Determine the title based on the provided title and cve
262
+
263
+ :param Optional[str] title: The title to determine the title for, defaults to None
264
+ :param Optional[str] cve: The cve to determine the title for, defaults to None
265
+ :return: The title
266
+ :rtype: str
267
+ """
268
+ if title and cve:
269
+ return f"{title} - {cve}"
270
+ elif title is None and cve:
271
+ return cve
272
+ else:
273
+ return title
274
+
188
275
  @staticmethod
189
276
  def _populate_cvs_scores(finding_obj: IntegrationFinding, vuln: Optional["Vulnerability"] = None) -> None:
190
277
  """
@@ -203,6 +290,17 @@ class Mapper:
203
290
  else:
204
291
  finding_obj.cvss_score = cvs.base_score
205
292
 
293
+ # validate that only one of the CVSS scores is populated
294
+ if finding_obj.cvss_v3_score:
295
+ finding_obj.cvss_v2_score = None
296
+ finding_obj.cvss_score = None
297
+ elif finding_obj.cvss_v2_score:
298
+ finding_obj.cvss_v3_score = None
299
+ finding_obj.cvss_score = None
300
+ elif finding_obj.cvss_score:
301
+ finding_obj.cvss_v3_score = None
302
+ finding_obj.cvss_v2_score = None
303
+
206
304
  def _security_finding_to_regscale(
207
305
  self, connector: "Vulnerabilities", finding: SecurityFinding, **_
208
306
  ) -> list[IntegrationFinding]:
@@ -227,28 +325,29 @@ class Mapper:
227
325
  """
228
326
  base = vuln if vuln else finding.finding
229
327
  finding_data = self._parse_finding_data(finding, vuln)
230
- if resource.data:
231
- dns = resource.data.get("hostname")
232
- ip_address = resource.data.get("ipv4")
233
- else:
234
- dns = resource.uid if vuln else None
235
- ip_address = None
328
+ resource_data = getattr(resource, "data", {})
329
+ dns = resource_data.get("hostname") or resource.uid if vuln else None
330
+ ip_v4s, ip_v6s = self._determine_ip_addresses(
331
+ resource_data["ipAddresses"] if resource_data.get("ipAddresses") else [resource_data.get("ip")]
332
+ )
333
+ ips = ip_v4s + ip_v6s
236
334
 
237
335
  finding_obj = IntegrationFinding(
238
336
  control_labels=[],
239
337
  category=f"{connector.integration_name} Vulnerability",
240
- title=base.title,
338
+ title=finding_data["title"],
241
339
  plugin_name=connector.integration_name,
242
340
  severity=Issue.assign_severity(finding.severity), # type: ignore
243
341
  description=base.desc,
244
342
  status=finding.status or "Open",
245
343
  first_seen=self._datetime_to_str(finding_data["first_seen"]),
246
344
  last_seen=self._datetime_to_str(finding_data["last_seen"]),
247
- ip_address=ip_address,
345
+ ip_address=", ".join(ips),
248
346
  plugin_id=finding_data["plugin_id"],
347
+ due_date=self._determine_due_date(finding_data["severity"], connector),
249
348
  dns=dns,
250
349
  severity_int=finding_data["severity"],
251
- issue_title=base.title,
350
+ issue_title=finding_data["title"],
252
351
  cve=finding_data["cve"],
253
352
  evidence=finding.evidence,
254
353
  impact=finding.impact,
@@ -330,22 +429,22 @@ class Mapper:
330
429
  else:
331
430
  return Priority.LOW
332
431
 
333
- def _determine_due_date(self, severity: str) -> str:
432
+ def _determine_due_date(self, severity: str, connector: Union["Ticketing", "Vulnerabilities"]) -> str:
334
433
  """
335
434
  Determine the due date based on the provided severity
336
435
 
337
436
  :param str severity: RegScale severity
437
+ :param Union["Ticketing", "Vulnerabilities"] connector: Ticketing or Vulnerabilities connector class object
338
438
  :return: Due date for the issue
339
439
  :rtype: str
340
440
  """
341
- from datetime import timedelta
342
-
343
441
  if "high" in severity.lower():
344
- return (datetime.now() + timedelta(days=30)).strftime(self.date_format)
442
+ default_days = 30
345
443
  elif "medium" in severity.lower():
346
- return (datetime.now() + timedelta(days=60)).strftime(self.date_format)
444
+ default_days = 90
347
445
  else:
348
- return (datetime.now() + timedelta(days=90)).strftime(self.date_format)
446
+ default_days = 180
447
+ return Issue.get_due_date(severity, connector.app.config, connector.integration, default_days=default_days)
349
448
 
350
449
  def _regscale_issue_to_ticket(self, regscale_issue: Issue, **kwargs) -> CreateTicketRequest:
351
450
  """
@@ -20,6 +20,7 @@ from regscale.core.app.api import Api
20
20
  from regscale.core.app.application import Application
21
21
  from regscale.core.app.utils.app_utils import create_progress_object, error_and_exit
22
22
  from regscale.models.integration_models.synqly_models.connector_types import ConnectorType
23
+ from regscale.models.integration_models.synqly_models.filter_parser import FilterParser
23
24
  from regscale.models.integration_models.synqly_models.ocsf_mapper import Mapper
24
25
  from regscale.models.integration_models.synqly_models.param import Param
25
26
  from regscale.models.integration_models.synqly_models.tenants import Tenant
@@ -37,7 +38,7 @@ class SynqlyModel(BaseModel, ABC):
37
38
  client: Optional[Any] = None
38
39
  connectors: dict = Field(default_factory=dict)
39
40
  # defined using the openApi spec on 7/16/2024, this is updated via _get_integrations_and_secrets()
40
- connector_types: set = Field(default_factory=lambda: set([connector.__str__() for connector in ConnectorType]))
41
+ connector_types: set = Field(default_factory=lambda: {connector.__str__() for connector in ConnectorType})
41
42
  terminated: Optional[bool] = False
42
43
  app: Application = Field(default_factory=Application, alias="app")
43
44
  api: Api = Field(default_factory=Api, alias="api")
@@ -60,6 +61,7 @@ class SynqlyModel(BaseModel, ABC):
60
61
  created_regscale_objects: list = Field(default_factory=list)
61
62
  updated_regscale_objects: list = Field(default_factory=list)
62
63
  regscale_objects_to_update: list = Field(default_factory=list)
64
+ filter_parser: Optional[FilterParser] = None
63
65
 
64
66
  def __init__(self: S, connector_type: Optional[str] = None, integration: Optional[str] = None, **kwargs):
65
67
  try:
@@ -241,7 +243,14 @@ class SynqlyModel(BaseModel, ABC):
241
243
  config[f"{self._connector_type}_{self.integration}_{key}"] = attribute[key].default
242
244
  elif not skip_prompts:
243
245
  self.logger.info(f"Enter the {key} for {self.integration}. Description: {attribute[key].description}")
244
- provided_secret = input(f"{key}: ")
246
+ if key.lower() in ["secret", "password"] or "token" in key.lower():
247
+ from getpass import getpass
248
+
249
+ print(f"{attribute[key].description} (input will be hidden)")
250
+ provided_secret = getpass(f"{key}: ")
251
+ else:
252
+ print(f"{attribute[key].description} (input will be visible)")
253
+ provided_secret = input(f"{key}: ")
245
254
  kwargs[key] = provided_secret
246
255
  config[f"{self._connector_type}_{self.integration}_{key}"] = provided_secret
247
256
  else:
@@ -278,6 +287,8 @@ class SynqlyModel(BaseModel, ABC):
278
287
  :rtype: dict
279
288
  """
280
289
  raw_data = self._load_from_package()
290
+ # Initialize FilterParser with the loaded capabilities data
291
+ self.filter_parser = FilterParser(capabilities_data=raw_data)
281
292
  return self._parse_api_spec_data(raw_data, return_params)
282
293
 
283
294
  def _parse_api_spec_data(self, data: dict, return_params: bool = False) -> dict:
@@ -441,12 +452,12 @@ class SynqlyModel(BaseModel, ABC):
441
452
 
442
453
  operations = schema.get("operations", [])
443
454
  capabilities = [item["name"] for item in operations if item.get("supported")]
444
- capabilities_params = list(
455
+ capabilities_params = [
445
456
  field
446
457
  for item in operations
447
458
  if item.get("supported") and "required_fields" in item.keys()
448
459
  for field in item.get("required_fields", [])
449
- )
460
+ ]
450
461
  if self.integration.lower() in key.lower():
451
462
  self.capabilities = capabilities
452
463
  schema = schema["provider_config"]
@@ -636,6 +647,42 @@ class SynqlyModel(BaseModel, ABC):
636
647
  """
637
648
  pass
638
649
 
650
+ def validate_filters(self, filters: Union[tuple, list, str]) -> list[str]:
651
+ """
652
+ Validate filter strings against provider capabilities.
653
+
654
+ :param Union[tuple, list, str] filters: Filter(s) to validate
655
+ :return: Validated filter list
656
+ :rtype: list[str]
657
+ :raises: SystemExit if validation fails
658
+ """
659
+ if not self.filter_parser:
660
+ self.logger.warning("FilterParser not available for filter validation")
661
+ if isinstance(filters, list):
662
+ return filters
663
+ elif filters:
664
+ return [filters]
665
+ else:
666
+ return []
667
+
668
+ provider_id = f"{self._connector_type}_{self.integration}"
669
+ validated_filters = []
670
+
671
+ # Normalize to list for processing
672
+ if isinstance(filters, str):
673
+ filters = [filters]
674
+ elif filters is None:
675
+ return []
676
+
677
+ for filter_string in filters:
678
+ is_valid, error_message = self.filter_parser.validate_filter(provider_id, filter_string)
679
+ if not is_valid:
680
+ error_and_exit(f"Filter validation failed: {error_message}")
681
+ validated_filters.append(filter_string)
682
+ self.logger.debug(f"Filter '{filter_string}' validated successfully")
683
+
684
+ return validated_filters
685
+
639
686
  def fetch_integration_data(
640
687
  self, func: Callable, **kwargs
641
688
  ) -> list[Union["InventoryAsset", "SecurityFinding", "Ticket"]]:
@@ -648,11 +695,12 @@ class SynqlyModel(BaseModel, ABC):
648
695
  """
649
696
  query_filter = kwargs.get("filter")
650
697
  limit = kwargs.get("limit", 200)
698
+
699
+ # Validate filters if provided
700
+ if query_filter:
701
+ query_filter = self.validate_filters(query_filter)
651
702
  integration_data: list = []
652
- fetch_res = func(
653
- filter=query_filter,
654
- limit=limit,
655
- )
703
+ fetch_res = self._fetch_data_with_retries(func=func, limit=limit, filter=query_filter)
656
704
  self.logger.info(f"Received {len(fetch_res.result)} record(s) from {self.integration_name}.")
657
705
  integration_data.extend(fetch_res.result)
658
706
  # check and handle pagination
@@ -660,24 +708,49 @@ class SynqlyModel(BaseModel, ABC):
660
708
  try:
661
709
  # fetch.cursor can be an int as a string, or a continuation token
662
710
  while int(fetch_res.cursor) == len(integration_data):
663
- fetch_res = func(
664
- filter=query_filter,
665
- limit=limit,
666
- cursor=fetch_res.cursor,
711
+ fetch_res = self._fetch_data_with_retries(
712
+ func=func, limit=limit, cursor=fetch_res.cursor, filter=query_filter
667
713
  )
668
714
  integration_data.extend(fetch_res.result)
669
715
  except ValueError:
670
716
  while fetch_res.cursor:
671
- fetch_res = func(
672
- filter=query_filter,
673
- limit=limit,
674
- cursor=fetch_res.cursor,
717
+ fetch_res = self._fetch_data_with_retries(
718
+ func=func, limit=limit, cursor=fetch_res.cursor, filter=query_filter
675
719
  )
676
720
  integration_data.extend(fetch_res.result)
677
721
  self.logger.info(f"Received {len(integration_data)} record(s) from {self.integration_name}...")
678
722
  self.logger.info(f"Fetched {len(integration_data)} total record(s) from {self.integration_name}...")
679
723
  return integration_data
680
724
 
725
+ def _fetch_data_with_retries(
726
+ self, func: Callable, limit: int, cursor: Union[str, int, None] = None, filter: Optional[str] = None
727
+ ) -> Any:
728
+ """
729
+ Fetches data from the integration using the provided function and handles pagination
730
+
731
+ :param Callable func: The function to fetch data from the integration
732
+ :param int limit: The limit of records to fetch
733
+ :param Optional[str] filter: The filter to apply to the data
734
+ :return: The data from the integration
735
+ :rtype: Any
736
+ """
737
+ import time
738
+
739
+ retries = 0
740
+ sleep_timers = [10, 20, 40]
741
+ while retries < 3:
742
+ try:
743
+ if cursor:
744
+ return func(limit=limit, filter=filter, cursor=cursor)
745
+ else:
746
+ return func(limit=limit, filter=filter)
747
+ except Exception as e:
748
+ self.logger.error(f"Error fetching data with retries: {e}")
749
+ self.logger.error(f"Retrying in {sleep_timers[retries]} seconds...")
750
+ time.sleep(sleep_timers[retries])
751
+ retries += 1
752
+ error_and_exit("Failed to fetch data after 3 retries")
753
+
681
754
  def run_integration_sync(self, *args, **kwargs) -> None:
682
755
  """
683
756
  Runs the sync process for the integration
@@ -90,11 +90,15 @@ class FileLock:
90
90
 
91
91
  :rtype: None
92
92
  """
93
- if os.path.isfile(self.lock_file):
94
- with open(self.lock_file, "r") as lockfile:
95
- scope = str(lockfile.read().strip())
96
-
97
- if scope == str(self.lock_scope):
98
- os.remove(self.lock_file) # Lock released
99
- else:
100
- pass # Lock can only be released by {self.lock_scope}
93
+ try:
94
+ if os.path.isfile(self.lock_file):
95
+ with open(self.lock_file, "r") as lockfile:
96
+ scope = str(lockfile.read().strip())
97
+
98
+ if scope == str(self.lock_scope):
99
+ os.remove(self.lock_file) # Lock released
100
+ else:
101
+ pass # Lock can only be released by {self.lock_scope}
102
+ except FileNotFoundError:
103
+ # Lock file was already removed by another process, this is acceptable in parallel execution
104
+ pass
@@ -5,10 +5,9 @@ from typing import Optional, Union
5
5
 
6
6
  from pydantic import BaseModel, SecretStr
7
7
 
8
+ from regscale.core.app.api import Api
8
9
  from regscale.core.login import get_regscale_token
9
10
  from regscale.core.utils.urls import generate_regscale_domain_url
10
- from regscale.core.app.api import Api
11
- from regscale.core.app.application import Application
12
11
 
13
12
 
14
13
  class RegScaleAuth(BaseModel):
@@ -45,6 +44,7 @@ class RegScaleAuth(BaseModel):
45
44
  password: Optional[Union[str, SecretStr]] = None,
46
45
  domain: Optional[str] = None,
47
46
  mfa_token: Optional[str] = None,
47
+ app_id: Optional[int] = 1,
48
48
  ) -> "RegScaleAuth":
49
49
  """
50
50
  Authenticate with RegScale and return a token and a user_id
@@ -53,6 +53,7 @@ class RegScaleAuth(BaseModel):
53
53
  :param Optional[Union[str, SecretStr]] password: Password to log in with, defaults to None
54
54
  :param Optional[str] domain: Domain to log into, defaults to None
55
55
  :param Optional[str] mfa_token: mfa_token to verify, defaults to None
56
+ :param Optional[int] app_id: The app ID to login with
56
57
  :return: RegScaleAuth object
57
58
  :rtype: RegScaleAuth
58
59
  """
@@ -89,6 +90,7 @@ class RegScaleAuth(BaseModel):
89
90
  password=password.get_secret_value(),
90
91
  domain=domain,
91
92
  mfa_token=mfa_token,
93
+ app_id=app_id,
92
94
  )
93
95
  return cls(
94
96
  user_id=uid,
@@ -41,6 +41,7 @@ from .implementation_objective import *
41
41
  from .implementation_option import *
42
42
  from .implementation_role import *
43
43
  from .incident import *
44
+ from .inheritance import *
44
45
  from .inherited_control import *
45
46
  from .interconnection import *
46
47
  from .issue import *
@@ -50,7 +51,10 @@ from .link import *
50
51
  from .master_assessment import *
51
52
  from .milestone import *
52
53
  from .meta_data import *
54
+ from .module import *
55
+ from .modules import *
53
56
  from .objective import *
57
+ from .organization import *
54
58
  from .parameter import *
55
59
  from .policy import *
56
60
  from .ports_protocol import *
@@ -79,10 +83,13 @@ from .stig import *
79
83
  from .supply_chain import *
80
84
  from .system_role import *
81
85
  from .system_role_external_assignment import *
86
+ from .tag import *
87
+ from .tag_mapping import *
82
88
  from .task import *
83
89
  from .threat import *
84
90
  from .team import *
85
91
  from .user import *
92
+ from .user_group import *
86
93
  from .vulnerability import *
87
94
  from .vulnerability_mapping import *
88
95
  from .workflow import *
@@ -5,6 +5,7 @@ from concurrent.futures import ThreadPoolExecutor
5
5
  from enum import Enum
6
6
  from typing import Any, List, Optional, Union
7
7
 
8
+ from pydantic import Field
8
9
  from requests import JSONDecodeError
9
10
 
10
11
  from regscale.core.app.api import Api
@@ -60,7 +61,7 @@ class Assessment(RegScaleModel):
60
61
  _module_slug = "assessments"
61
62
 
62
63
  id: int = 0
63
- leadAssessorId: str = ""
64
+ leadAssessorId: str = Field(default_factory=RegScaleModel.get_user_id)
64
65
  title: Optional[str] = None
65
66
  assessmentType: Optional[Union[AssessmentType, str]] = None
66
67
  plannedStart: Optional[str] = None
@@ -273,5 +273,5 @@ class Catalog(RegScaleModel):
273
273
  response = cls._get_api_handler().get(endpoint)
274
274
 
275
275
  if response and response.ok and response.status_code not in [204, 404]:
276
- return cls.model_validate(response.json())
276
+ return cls(**response.json())
277
277
  return None