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
@@ -203,7 +203,6 @@ class SicuraAPI:
203
203
  FILTER_TYPE = "filter[type]"
204
204
  FILTER_REJECTED = "filter[rejected]"
205
205
  FILTER_TASK_ID = "filter[task_id]"
206
- csrf_token: Optional[str] = None
207
206
 
208
207
  def __init__(self):
209
208
  """
@@ -246,12 +245,9 @@ class SicuraAPI:
246
245
 
247
246
  if data:
248
247
  self.session.headers["Content-Type"] = "application/json"
249
- if not endpoint.endswith("/auth/token"):
250
- if not self.csrf_token:
251
- self.csrf_token = self.get_csrf_token()
252
- if self.csrf_token:
253
- self.session.headers["X-CSRF-TOKEN"] = str(self.csrf_token)
254
- self.session.headers["auth-token-signature"] = SicuraVariables.sicuraToken
248
+
249
+ # Always set the auth token signature for API authentication
250
+ self.session.headers["auth-token-signature"] = SicuraVariables.sicuraToken
255
251
 
256
252
  try:
257
253
  # Use the session object to maintain cookies between requests
@@ -313,22 +309,6 @@ class SicuraAPI:
313
309
  logging.error(f"Error validating response: {e}", exc_info=True)
314
310
  return None
315
311
 
316
- def get_csrf_token(self) -> Optional[AuthResponse]:
317
- """
318
- Get authentication token from Sicura API.
319
-
320
- :return: Authentication response
321
- :rtype: Optional[AuthResponse]
322
- :raises requests.exceptions.RequestException: If the request fails
323
- """
324
- try:
325
- response = self._make_request("GET", "/auth/token")
326
- self.csrf_token = response
327
- return response
328
- except requests.exceptions.RequestException as e:
329
- logger.error(f"Error getting authentication token: {e}", exc_info=True)
330
- raise
331
-
332
312
  class Device(SicuraModel):
333
313
  """Model for Sicura device information."""
334
314
 
@@ -356,7 +336,7 @@ class SicuraAPI:
356
336
  try:
357
337
  response = self._make_request(
358
338
  "GET",
359
- "/backend/api/jaeger/v1/nodes",
339
+ "/api/jaeger/v1/nodes",
360
340
  params={
361
341
  "verbose": "true",
362
342
  "attributes": "platforms,scannable_profiles,most_recent_scan",
@@ -384,11 +364,56 @@ class SicuraAPI:
384
364
  logger.error(f"Failed to get devices: {e}", exc_info=True)
385
365
  return []
386
366
 
367
+ def create_or_update_control_profile(self, profile_name: str, controls: list[dict]) -> Optional[dict]:
368
+ """
369
+ Create or update a control profile.
370
+
371
+ :param str profile_name: Name of the control profile
372
+ :param list[dict] controls: List of controls to add to the profile
373
+ :return: The control profile if successfully created or updated, None otherwise
374
+ :rtype: Optional[dict]
375
+ """
376
+ profile_id = None
377
+ profile_data = None
378
+ params = {"verbose": "true"}
379
+ try:
380
+ # see if the profile already exists
381
+ response = self._make_request("GET", "/api/jaeger/v1/control_profiles", params=params)
382
+ for profile in response:
383
+ if profile["name"] == profile_name:
384
+ profile_id = profile["id"]
385
+ break
386
+ payload = {
387
+ "name": profile_name,
388
+ "description": f"Profile for {profile_name} with {len(controls)} controls.",
389
+ "controls": controls,
390
+ }
391
+ if not profile_id:
392
+ crud_operation = "Created"
393
+ response = self._make_request("POST", "/api/jaeger/v1/control_profiles", data=payload, params=params)
394
+ profile_id = response
395
+ profile_data = self._make_request("GET", f"/api/jaeger/v1/control_profiles/{profile_id}", params=params)
396
+ else:
397
+ crud_operation = "Updated"
398
+ response = self._make_request(
399
+ "PUT", f"/api/jaeger/v1/control_profiles/{profile_id}", data=payload, params=params
400
+ )
401
+ profile_id = response["id"]
402
+ profile_data = response
403
+ logger.info(f"{crud_operation} control profile #{profile_id} in Sicura with {len(controls)} controls.")
404
+ return profile_data
405
+
406
+ return profile_id
407
+ except Exception as e:
408
+ logger.error(f"Failed to create or update control profile: {e}", exc_info=True)
409
+ return None
410
+
387
411
  def create_scan_task(
388
412
  self,
389
413
  device_id: int,
390
414
  platform: str,
391
- profile: SicuraProfile,
415
+ profile: Union[SicuraProfile, str],
416
+ author: Optional[str] = None,
392
417
  task_name: Optional[str] = None,
393
418
  scheduled_time: Optional[datetime.datetime] = None,
394
419
  ) -> Optional[str]:
@@ -398,6 +423,7 @@ class SicuraAPI:
398
423
  :param int device_id: ID of the device to scan
399
424
  :param str platform: Platform name (e.g., 'Red Hat Enterprise Linux 9')
400
425
  :param SicuraProfile profile: Scan profile name (e.g., 'I - Mission Critical Classified')
426
+ :param Optional[str] author: Author of the scan task (default: None)
401
427
  :param Optional[str] task_name: Name for the scan task (default: auto-generated)
402
428
  :param Optional[datetime.datetime] scheduled_time: When to run the scan (default: now)
403
429
  :return: Task ID if successful, None otherwise
@@ -425,7 +451,10 @@ class SicuraAPI:
425
451
  "scanAttributes": {"platform": platform, "profile": profile},
426
452
  }
427
453
 
428
- result = self._make_request("POST", "/backend/api/jaeger/v1/tasks/", data=payload)
454
+ if author:
455
+ payload["scanAttributes"]["author"] = author
456
+
457
+ result = self._make_request("POST", "/api/jaeger/v1/tasks/", data=payload)
429
458
 
430
459
  if result:
431
460
  logger.info(f"Successfully created scan task with ID: {result}")
@@ -448,7 +477,7 @@ class SicuraAPI:
448
477
  """
449
478
  try:
450
479
  response = self._make_request(
451
- "GET", "/backend/api/jaeger/v1/jobs", params={"verbose": "true", self.FILTER_TASK_ID: task_id}
480
+ "GET", "/api/jaeger/v1/jobs", params={"verbose": "true", self.FILTER_TASK_ID: task_id}
452
481
  )
453
482
 
454
483
  # Handle 404 or empty response
@@ -483,14 +512,16 @@ class SicuraAPI:
483
512
  self,
484
513
  fqdn: str,
485
514
  platform: Optional[str] = None,
486
- profile: SicuraProfile = SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
515
+ profile: Union[SicuraProfile, str] = SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
516
+ author: Optional[str] = None,
487
517
  ) -> Optional[ScanReport]:
488
518
  """
489
519
  Get scan results for a specific device.
490
520
 
491
521
  :param str fqdn: Fully qualified domain name of the device
492
522
  :param Optional[str] platform: Platform name to filter results (e.g., 'Red Hat Enterprise Linux 9')
493
- :param SicuraProfile profile: Profile name to filter results (e.g., 'I - Mission Critical Classified')
523
+ :param Union[SicuraProfile, str] profile: Profile name to filter results, defaults to I - Mission Critical Classified
524
+ :param Optional[str] author: Author of the scan task (default: None)
494
525
  :return: Scan report containing device info and scan results, or None if not found
495
526
  :rtype: Optional[ScanReport]
496
527
  """
@@ -503,7 +534,10 @@ class SicuraAPI:
503
534
  if profile:
504
535
  params["profile"] = profile
505
536
 
506
- response = self._make_request("GET", "/backend/api/jaeger/v1/nodes", params=params)
537
+ if author:
538
+ params["author"] = author
539
+
540
+ response = self._make_request("GET", "/api/jaeger/v1/nodes", params=params)
507
541
 
508
542
  # Handle 404 or empty response
509
543
  if not response or (isinstance(response, list) and not response):
@@ -524,16 +558,17 @@ class SicuraAPI:
524
558
  return None # Return None if no scans available
525
559
 
526
560
  # Calculate summary stats
527
- pass_count = sum(1 for scan in device.get("scans", []) if scan.get("result") == "pass")
528
- fail_count = sum(1 for scan in device.get("scans", []) if scan.get("result") == "fail")
529
- total_count = len(device.get("scans", []))
561
+ scan_results = device.get("scans", {}).get("results", [])
562
+ pass_count = sum(1 for scan in scan_results if scan.get("result") == "pass")
563
+ fail_count = sum(1 for scan in scan_results if scan.get("result") == "fail")
564
+ total_count = len(scan_results)
530
565
 
531
566
  # Create the raw result data
532
567
  result_data = {
533
568
  "device_id": device.get("id"),
534
569
  "fqdn": device.get("fqdn"),
535
570
  "ip_address": device.get("ip_address"),
536
- "scans": device.get("scans", []),
571
+ "scans": scan_results,
537
572
  "summary": {
538
573
  "total": total_count,
539
574
  "pass": pass_count,
@@ -559,7 +594,8 @@ class SicuraAPI:
559
594
  task_id: Union[int, str],
560
595
  fqdn: str,
561
596
  platform: Optional[str] = None,
562
- profile: SicuraProfile = SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
597
+ profile: Union[SicuraProfile, str] = SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
598
+ author: Optional[str] = None,
563
599
  max_wait_time: int = 600,
564
600
  poll_interval: int = 10,
565
601
  ) -> Optional[Union[ScanReport, Dict[str, Any]]]:
@@ -569,7 +605,8 @@ class SicuraAPI:
569
605
  :param Union[int, str] task_id: ID of the scan task to monitor
570
606
  :param str fqdn: Fully qualified domain name of the device
571
607
  :param Optional[str] platform: Platform name to filter results
572
- :param SicuraProfile profile: Profile name to filter results
608
+ :param Union[SicuraProfile, str] profile: Profile name to filter results, defaults to I - Mission Critical Classified
609
+ :param Optional[str] author: Author of the scan task (default: None)
573
610
  :param int max_wait_time: Maximum time to wait in seconds (default: 10 minutes)
574
611
  :param int poll_interval: Time between status checks in seconds (default: 10 seconds)
575
612
  :return: Scan results once the task is complete, or None if timeout or error
@@ -601,7 +638,7 @@ class SicuraAPI:
601
638
  logger.info(f"Scan task {task_id} completed successfully, fetching results...")
602
639
  # Wait a moment for results to be processed
603
640
  time.sleep(2)
604
- return self.get_scan_results(fqdn, platform, profile)
641
+ return self.get_scan_results(fqdn=fqdn, platform=platform, profile=profile, author=author)
605
642
  else:
606
643
  logger.error(f"Scan task {task_id} ended with status {latest_status}")
607
644
  return None
@@ -638,7 +675,7 @@ class SicuraAPI:
638
675
  if ip_address:
639
676
  params[self.FILTER_IP_ADDRESS] = ip_address
640
677
 
641
- response = self._make_request("GET", "/backend/api/jaeger/v1/node_templates/", params=params)
678
+ response = self._make_request("GET", "/api/jaeger/v1/node_templates/", params=params)
642
679
 
643
680
  # Handle 404 or empty response
644
681
  if not response or (isinstance(response, dict) and "detail" in response):
@@ -670,7 +707,7 @@ class SicuraAPI:
670
707
  # Use PUT method with the correct endpoint and payload
671
708
  result = self._make_request(
672
709
  "PUT",
673
- f"/backend/api/jaeger/v1/node_templates/{device_id}",
710
+ f"/api/jaeger/v1/node_templates/{device_id}",
674
711
  params={"verbose": "true", "include_controls": "true", "action": "promote"},
675
712
  )
676
713
 
@@ -697,7 +734,7 @@ class SicuraAPI:
697
734
  # Use PUT method with the correct endpoint and payload
698
735
  result = self._make_request(
699
736
  "PUT",
700
- f"/backend/api/jaeger/v1/node_templates/{device_id}",
737
+ f"/api/jaeger/v1/node_templates/{device_id}",
701
738
  params={"verbose": "true", "include_controls": "true", "action": "reject"},
702
739
  )
703
740
 
@@ -721,7 +758,7 @@ class SicuraAPI:
721
758
  :rtype: bool
722
759
  """
723
760
  try:
724
- result = self._make_request("DELETE", f"/backend/api/jaeger/v1/nodes/{device_id}")
761
+ result = self._make_request("DELETE", f"/api/jaeger/v1/nodes/{device_id}")
725
762
 
726
763
  # For DELETE operations, an empty result typically indicates success
727
764
  if result is None or result == "" or (isinstance(result, dict) and not result):
@@ -854,7 +891,7 @@ class SicuraAPI:
854
891
  }
855
892
 
856
893
  logger.info(f"Creating enforcement task for device {device_id} with {len(ce_names)} CE names")
857
- result = self._make_request("POST", "/backend/api/jaeger/v1/tasks/", data=payload)
894
+ result = self._make_request("POST", "/api/jaeger/v1/tasks/", data=payload)
858
895
 
859
896
  if result:
860
897
  logger.info(f"Successfully created enforcement task with ID: {result}")
@@ -46,7 +46,13 @@ def sync_assets(regscale_id: int):
46
46
 
47
47
  @sicura.command(name="sync_findings")
48
48
  @regscale_id(help="RegScale will create and update findings as children of this record.")
49
- def sync_findings(regscale_id: int):
49
+ @click.option(
50
+ "--trigger_scan",
51
+ "-s",
52
+ is_flag=True,
53
+ help="Trigger a new scan on Sicura assets before syncing.",
54
+ )
55
+ def sync_findings(regscale_id: int, trigger_scan: bool):
50
56
  """
51
57
  Sync Sicura findings to RegScale.
52
58
 
@@ -60,7 +66,7 @@ def sync_findings(regscale_id: int):
60
66
  )
61
67
 
62
68
  # Using import_findings method which handles the synchronization
63
- integration.sync_findings(plan_id=regscale_id)
69
+ integration.sync_findings(plan_id=regscale_id, trigger_scan=trigger_scan)
64
70
 
65
71
  logger.info("[bold green]Finding synchronization complete.")
66
72
 
@@ -4,7 +4,7 @@
4
4
  RegScale Sicura Integration
5
5
  """
6
6
  import datetime
7
- from typing import Generator, Iterator, Any
7
+ from typing import Any, Generator, Iterator, Union
8
8
 
9
9
  from regscale.core.utils.date import date_str
10
10
  from regscale.integrations.commercial.sicura.api import SicuraAPI, ScanReport, SicuraProfile, Device, ScanResult
@@ -55,6 +55,8 @@ class SicuraIntegration(ScannerIntegration):
55
55
  """
56
56
  super().__init__(*args, **kwargs)
57
57
  self.api = SicuraAPI()
58
+ self.control_scan = False
59
+ self.control_scan_profile = None
58
60
 
59
61
  def fetch_findings(self, **kwargs) -> Generator[IntegrationFinding, None, None]:
60
62
  """
@@ -74,6 +76,10 @@ class SicuraIntegration(ScannerIntegration):
74
76
  logger.warning("No devices found in Sicura")
75
77
  return
76
78
 
79
+ if kwargs.pop("trigger_scan", False):
80
+ logger.info(f"Triggering scans on Sicura {len(devices)} devices...")
81
+ self.trigger_scans(devices)
82
+
77
83
  self.num_findings_to_process = 0
78
84
  findings_count = 0
79
85
 
@@ -99,23 +105,28 @@ class SicuraIntegration(ScannerIntegration):
99
105
  return
100
106
 
101
107
  # Profiles to scan
102
- profiles = [SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED, SicuraProfile.LEVEL_1_SERVER]
108
+ profiles = [SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED]
109
+ if self.control_scan:
110
+ profiles = [self.control_scan_profile]
103
111
 
104
112
  for profile in profiles:
105
113
  yield from self._process_profile_findings(device, profile)
106
114
 
107
115
  def _process_profile_findings(
108
- self, device: Device, profile: SicuraProfile
116
+ self, device: Device, profile: Union[SicuraProfile, str]
109
117
  ) -> Generator[IntegrationFinding, None, None]:
110
118
  """
111
119
  Process findings for a device with a specific profile
112
120
 
113
121
  :param Device device: The device to process
114
- :param SicuraProfile profile: The profile to process
122
+ :param Union[SicuraProfile, str] profile: The profile to process
115
123
  :yield: IntegrationFinding objects
116
124
  :rtype: Generator[IntegrationFinding, None, None]
117
125
  """
118
- scan_report = self.api.get_scan_results(fqdn=device.fqdn, profile=profile)
126
+ if self.control_scan and profile == self.control_scan_profile:
127
+ scan_report = self.api.get_scan_results(fqdn=device.fqdn, profile=profile, author="control")
128
+ else:
129
+ scan_report = self.api.get_scan_results(fqdn=device.fqdn, profile=profile)
119
130
 
120
131
  if not scan_report:
121
132
  logger.warning(f"No scan results found for device: {device.fqdn} with profile: {profile}")
@@ -206,7 +217,8 @@ class SicuraIntegration(ScannerIntegration):
206
217
  mitigation=mitigation,
207
218
  )
208
219
 
209
- def _extract_scan_data(self, scan: Any) -> dict:
220
+ @staticmethod
221
+ def _extract_scan_data(scan: Any) -> dict:
210
222
  """
211
223
  Extract scan data from the scan object
212
224
 
@@ -235,7 +247,8 @@ class SicuraIntegration(ScannerIntegration):
235
247
  "controls": scan.controls if hasattr(scan, "controls") else {},
236
248
  }
237
249
 
238
- def _extract_control_refs(self, controls: dict) -> tuple:
250
+ @staticmethod
251
+ def _extract_control_refs(controls: dict) -> tuple:
239
252
  """
240
253
  Extract CCI and SRG references from controls
241
254
 
@@ -353,8 +366,9 @@ class SicuraIntegration(ScannerIntegration):
353
366
  baseline=platform,
354
367
  )
355
368
 
369
+ @staticmethod
356
370
  def _create_results_text(
357
- self, title, ce_name, result, description, state, state_reason, cci_ref, srg_refs, is_cci_finding
371
+ title, ce_name, result, description, state, state_reason, cci_ref, srg_refs, is_cci_finding
358
372
  ) -> str:
359
373
  """
360
374
  Create the results text for a finding
@@ -401,42 +415,6 @@ class SicuraIntegration(ScannerIntegration):
401
415
  :rtype: Iterator[IntegrationAsset]
402
416
  """
403
417
  logger.info("Fetching assets from Sicura...")
404
-
405
- # Get all devices
406
- pending_devices = self.api.get_pending_devices()
407
- for pending_device in pending_devices:
408
- print(f"Pending device: {pending_device}")
409
- self.api.accept_pending_device(pending_device.id)
410
- task_id = self.api.create_scan_task(
411
- device_id=pending_device.id,
412
- platform=pending_device.platform,
413
- profile=SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
414
- )
415
- if task_id:
416
- self.api.wait_for_scan_results(
417
- task_id=task_id,
418
- fqdn=pending_device.fqdn,
419
- platform=pending_device.platform,
420
- profile=SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
421
- )
422
- else:
423
- logger.warning(f"Failed to create scan task for device {pending_device.fqdn}")
424
- continue # Continue to next device if creating scan task failed
425
-
426
- task_id = self.api.create_scan_task(
427
- device_id=pending_device.id, platform=pending_device.platform, profile=SicuraProfile.LEVEL_1_SERVER
428
- )
429
- if task_id:
430
- self.api.wait_for_scan_results(
431
- task_id=task_id,
432
- fqdn=pending_device.fqdn,
433
- platform=pending_device.platform,
434
- profile=SicuraProfile.LEVEL_1_SERVER,
435
- )
436
- else:
437
- logger.warning(f"Failed to create scan task for device {pending_device.fqdn}")
438
- # No continue needed here as we're at the end of the loop iteration
439
-
440
418
  devices = self.api.get_devices()
441
419
 
442
420
  if not devices:
@@ -479,3 +457,64 @@ class SicuraIntegration(ScannerIntegration):
479
457
  )
480
458
 
481
459
  self.asset_progress.update(loading_devices, advance=1)
460
+
461
+ def trigger_and_wait_for_scan(self, device: Device) -> None:
462
+ """
463
+ Trigger a scan and wait for the results
464
+
465
+ :param Device device: The device to trigger a scan for
466
+ :return: None
467
+ """
468
+ profile = self.control_scan_profile if self.control_scan else SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED
469
+ author = "control" if self.control_scan else None
470
+ task_id = self.api.create_scan_task(
471
+ device_id=device.id,
472
+ platform=device.platforms,
473
+ profile=profile,
474
+ author=author,
475
+ )
476
+ if task_id:
477
+ self.api.wait_for_scan_results(
478
+ task_id=task_id,
479
+ fqdn=device.fqdn,
480
+ platform=device.platforms,
481
+ profile=profile,
482
+ author=author,
483
+ )
484
+ else:
485
+ logger.warning(f"Failed to create scan task for device {device.fqdn}")
486
+
487
+ def trigger_scans(self, devices: list[Device]) -> None:
488
+ """
489
+ Trigger scans for a list of devices
490
+
491
+ :param list[Device] devices: The devices to trigger scans for
492
+ :return: None
493
+ """
494
+ # get the SSP's controlImplementations
495
+ if control_imps := regscale_models.ControlImplementation.get_list_by_parent(
496
+ regscale_id=self.plan_id, regscale_module=regscale_models.SecurityPlan.get_module_slug()
497
+ ):
498
+ if profile := self.api.create_or_update_control_profile(
499
+ profile_name=f"regscale_ssp_id_{self.plan_id}",
500
+ controls=control_imps,
501
+ ):
502
+ self.control_scan = True
503
+ self.control_scan_profile = profile["name"]
504
+ else:
505
+ logger.warning("Failed to create or update control profile")
506
+ self.control_scan = False
507
+ self.control_scan_profile = None
508
+ else:
509
+ self.control_scan = False
510
+ self.control_scan_profile = None
511
+
512
+ if len(devices) > 1:
513
+ from regscale.utils.threading import ThreadManager
514
+
515
+ # use multithreading to trigger scans for multiple devices
516
+ thread_manager = ThreadManager(max_workers=10)
517
+ thread_manager.submit_tasks_from_list(self.trigger_and_wait_for_scan, devices)
518
+ thread_manager.execute_and_verify()
519
+ else:
520
+ self.trigger_and_wait_for_scan(devices[0])
@@ -82,15 +82,15 @@ class STIGInfo(BaseModel):
82
82
  """Data model for STIG Information with optional and required attributes."""
83
83
 
84
84
  version: str
85
- classification: str
85
+ classification: Optional[str] = None
86
86
  customname: Optional[str] = None
87
87
  stigid: str
88
88
  description: Optional[str] = None
89
89
  filename: Optional[str] = None
90
90
  releaseinfo: str
91
91
  title: str
92
- uuid: str
93
- notice: str
92
+ uuid: Optional[str] = None
93
+ notice: Optional[str] = None
94
94
  source: Optional[str] = None
95
95
 
96
96
 
@@ -115,10 +115,10 @@ class Vuln(BaseModel):
115
115
  check_content: Optional[str] = None
116
116
  fix_text: str
117
117
  check_content_ref: Optional[str] = None
118
- weight: str
118
+ weight: Optional[str] = None
119
119
  stigref: Optional[str] = None
120
120
  targetkey: Optional[str] = None
121
- stig_uuid: str
121
+ stig_uuid: Optional[str] = None
122
122
  vuln_discuss: Optional[str] = None
123
123
  ia_controls: Optional[str] = None
124
124
  class_: Optional[str] = None