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
@@ -4,6 +4,7 @@ Qualys Total Cloud scanner integration class using JSONLScannerIntegration.
4
4
 
5
5
  import logging
6
6
  import os
7
+ import threading
7
8
  import time
8
9
  import traceback
9
10
  import xml.etree.ElementTree as ET
@@ -27,18 +28,20 @@ from regscale.integrations.scanner_integration import (
27
28
  )
28
29
  from regscale.integrations.variables import ScannerVariables
29
30
  from regscale.models import AssetStatus, IssueSeverity, IssueStatus
31
+ from regscale import models as regscale_models
30
32
 
31
33
  logger = logging.getLogger("regscale")
32
34
 
33
35
  NO_RESULTS = "No results available"
34
36
  NO_DESCRIPTION = "No description available"
35
37
  NO_REMEDIATION = "No remediation information available"
38
+ SCANNING_TOOL_NAME = "Qualys Total Cloud"
36
39
 
37
40
 
38
41
  class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
39
42
  """Class for handling Qualys Total Cloud scanner integration using JSONL."""
40
43
 
41
- title: str = "Qualys Total Cloud"
44
+ title: str = SCANNING_TOOL_NAME
42
45
  asset_identifier_field: str = "qualysId"
43
46
  finding_severity_map: Dict[str, Any] = {
44
47
  "0": IssueSeverity.NotAssigned.value,
@@ -73,34 +76,52 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
73
76
  self.xml_data = kwargs.pop("xml_data", None)
74
77
  self.containers = kwargs.pop("containers", None)
75
78
  self.is_component = kwargs.get("is_component", False)
76
- # Setting a dummy file path to avoid validation errors
79
+
80
+ self._setup_file_path(kwargs)
81
+ self._apply_vulnerability_creation_setting(kwargs)
82
+ self._apply_ssl_verification_setting(kwargs)
83
+ self._apply_thread_workers_setting(kwargs)
84
+
85
+ super().__init__(*args, **kwargs)
86
+ # No need to initialize clients, they are inherited from the parent class
87
+
88
+ def _setup_file_path(self, kwargs: Dict[str, Any]) -> None:
89
+ """Setup file path for XML data processing."""
77
90
  if self.xml_data and "file_path" not in kwargs:
78
91
  kwargs["file_path"] = None
79
92
 
80
- # Apply ScannerVariables settings
81
- if not kwargs.get("vulnerability_creation"):
82
- # Check QualysVariables-specific override first
83
- if hasattr(QualysVariables, "vulnerabilityCreation") and QualysVariables.vulnerabilityCreation:
84
- kwargs["vulnerability_creation"] = QualysVariables.vulnerabilityCreation
85
- logger.info(f"Using Qualys-specific vulnerability creation mode: {kwargs['vulnerability_creation']}")
86
- # Use global ScannerVariables if no Qualys-specific setting
87
- elif hasattr(ScannerVariables, "vulnerabilityCreation"):
88
- kwargs["vulnerability_creation"] = ScannerVariables.vulnerabilityCreation
89
- logger.info(f"Using global vulnerability creation mode: {kwargs['vulnerability_creation']}")
90
-
91
- # Apply SSL verification setting from ScannerVariables
93
+ def _apply_vulnerability_creation_setting(self, kwargs: Dict[str, Any]) -> None:
94
+ """Apply vulnerability creation setting from variables."""
95
+ if kwargs.get("vulnerability_creation"):
96
+ return
97
+
98
+ if self._has_qualys_vulnerability_creation():
99
+ kwargs["vulnerability_creation"] = QualysVariables.vulnerabilityCreation
100
+ logger.info(f"Using Qualys-specific vulnerability creation mode: {kwargs['vulnerability_creation']}")
101
+ elif self._has_scanner_vulnerability_creation():
102
+ kwargs["vulnerability_creation"] = ScannerVariables.vulnerabilityCreation
103
+ logger.info(f"Using global vulnerability creation mode: {kwargs['vulnerability_creation']}")
104
+
105
+ def _has_qualys_vulnerability_creation(self) -> bool:
106
+ """Check if QualysVariables has vulnerability creation setting."""
107
+ return hasattr(QualysVariables, "vulnerabilityCreation") and QualysVariables.vulnerabilityCreation
108
+
109
+ def _has_scanner_vulnerability_creation(self) -> bool:
110
+ """Check if ScannerVariables has vulnerability creation setting."""
111
+ return hasattr(ScannerVariables, "vulnerabilityCreation")
112
+
113
+ def _apply_ssl_verification_setting(self, kwargs: Dict[str, Any]) -> None:
114
+ """Apply SSL verification setting from ScannerVariables."""
92
115
  if not kwargs.get("ssl_verify") and hasattr(ScannerVariables, "sslVerify"):
93
116
  kwargs["ssl_verify"] = ScannerVariables.sslVerify
94
117
  logger.debug(f"Using SSL verification setting: {kwargs['ssl_verify']}")
95
118
 
96
- # Apply ScannerVariables.threadMaxWorkers if available
119
+ def _apply_thread_workers_setting(self, kwargs: Dict[str, Any]) -> None:
120
+ """Apply thread max workers setting from ScannerVariables."""
97
121
  if not kwargs.get("max_workers") and hasattr(ScannerVariables, "threadMaxWorkers"):
98
122
  kwargs["max_workers"] = ScannerVariables.threadMaxWorkers
99
123
  logger.debug(f"Using thread max workers: {kwargs['max_workers']}")
100
124
 
101
- super().__init__(*args, **kwargs)
102
- # No need to initialize clients, they are inherited from the parent class
103
-
104
125
  def is_valid_file(self, data: Any, file_path: Union[Path, str]) -> Tuple[bool, Optional[Dict[str, Any]]]:
105
126
  """
106
127
  Check if the XML data is valid for Qualys Total Cloud.
@@ -230,6 +251,12 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
230
251
  # Extract host information
231
252
  host_info = self._extract_host_information(processed_host)
232
253
 
254
+ # Log asset creation for debugging
255
+ logger.debug(f"Creating asset for host ID: {host_info['host_id']}")
256
+ logger.debug(f"Asset name: {host_info['name']}")
257
+ logger.debug(f"Plan ID: {self.plan_id}, Parent Module: {self.parent_module}")
258
+ logger.debug(f"Is Component: {self.is_component}")
259
+
233
260
  # Create and return the asset
234
261
  return IntegrationAsset(
235
262
  name=host_info["name"],
@@ -448,6 +475,23 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
448
475
 
449
476
  current_time = self.scan_date or get_current_datetime()
450
477
 
478
+ # Extract CVSS scores and convert to float if possible
479
+ cvss_v3_score = detection.get("CVSS3_BASE")
480
+ cvss_v2_score = detection.get("CVSS_BASE")
481
+
482
+ # Convert CVSS scores to float if they're strings
483
+ if cvss_v3_score and isinstance(cvss_v3_score, str):
484
+ try:
485
+ cvss_v3_score = float(cvss_v3_score)
486
+ except (ValueError, TypeError):
487
+ cvss_v3_score = None
488
+
489
+ if cvss_v2_score and isinstance(cvss_v2_score, str):
490
+ try:
491
+ cvss_v2_score = float(cvss_v2_score)
492
+ except (ValueError, TypeError):
493
+ cvss_v2_score = None
494
+
451
495
  return {
452
496
  "qid": detection.get("QID", "Unknown"),
453
497
  "severity": detection.get("SEVERITY", "0"),
@@ -456,9 +500,9 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
456
500
  "last_found": detection.get("LAST_FOUND_DATETIME", current_time),
457
501
  "unique_id": detection.get("UNIQUE_VULN_ID", f"QID-{detection.get('QID', 'Unknown')}"),
458
502
  "results": detection.get("RESULTS", NO_RESULTS),
459
- "cvss_v3_score": detection.get("CVSS3_BASE"),
503
+ "cvss_v3_score": cvss_v3_score,
460
504
  "cvss_v3_vector": detection.get("CVSS3_VECTOR", ""),
461
- "cvss_v2_score": detection.get("CVSS_BASE"),
505
+ "cvss_v2_score": cvss_v2_score,
462
506
  "cvss_v2_vector": detection.get("CVSS_VECTOR", ""),
463
507
  }
464
508
 
@@ -504,29 +548,79 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
504
548
  if not detection:
505
549
  return ""
506
550
 
507
- cve_id = ""
508
551
  try:
509
- cve_list = detection.get("CVE_ID_LIST", {})
510
- if not cve_list:
511
- return ""
512
-
513
- if not isinstance(cve_list, dict):
514
- logger.warning(f"Expected dictionary for CVE_ID_LIST, got {type(cve_list)}")
515
- return ""
516
-
517
- if "CVE_ID" in cve_list:
518
- cve_data = cve_list.get("CVE_ID", [])
519
- if isinstance(cve_data, list) and cve_data:
520
- cve_id = str(cve_data[0]) if cve_data[0] else ""
521
- elif isinstance(cve_data, str):
522
- cve_id = cve_data
523
- elif cve_data:
524
- # Try to convert to string if it's something else
525
- cve_id = str(cve_data)
552
+ # Try to extract CVE from CVE_ID_LIST first
553
+ cve_id = self._extract_cve_from_cve_list(detection)
554
+ if cve_id:
555
+ return cve_id
556
+
557
+ # Try direct CVE fields if CVE_ID_LIST didn't work
558
+ cve_id = self._extract_cve_from_direct_fields(detection)
559
+ if cve_id:
560
+ return cve_id
561
+
526
562
  except Exception as e:
527
563
  logger.warning(f"Error extracting CVE_ID: {str(e)}")
528
564
 
529
- return cve_id
565
+ return ""
566
+
567
+ def _extract_cve_from_cve_list(self, detection: Dict[str, Any]) -> str:
568
+ """
569
+ Extract CVE ID from CVE_ID_LIST field.
570
+
571
+ :param Dict[str, Any] detection: Detection data
572
+ :return: CVE ID string
573
+ :rtype: str
574
+ """
575
+ cve_list = detection.get("CVE_ID_LIST", {})
576
+ if not cve_list:
577
+ return ""
578
+
579
+ if not isinstance(cve_list, dict):
580
+ logger.warning(f"Expected dictionary for CVE_ID_LIST, got {type(cve_list)}")
581
+ return ""
582
+
583
+ if "CVE_ID" not in cve_list:
584
+ return ""
585
+
586
+ cve_data = cve_list.get("CVE_ID", [])
587
+ return self._convert_cve_data_to_string(cve_data)
588
+
589
+ def _extract_cve_from_direct_fields(self, detection: Dict[str, Any]) -> str:
590
+ """
591
+ Extract CVE ID from direct CVE fields.
592
+
593
+ :param Dict[str, Any] detection: Detection data
594
+ :return: CVE ID string
595
+ :rtype: str
596
+ """
597
+ # Try CVE field directly
598
+ cve_id = detection.get("CVE", "")
599
+ if cve_id:
600
+ return str(cve_id)
601
+
602
+ # Try CVE_ID field directly
603
+ cve_id = detection.get("CVE_ID", "")
604
+ if cve_id:
605
+ return str(cve_id)
606
+
607
+ return ""
608
+
609
+ def _convert_cve_data_to_string(self, cve_data: Any) -> str:
610
+ """
611
+ Convert CVE data to string format.
612
+
613
+ :param Any cve_data: CVE data to convert
614
+ :return: CVE ID string
615
+ :rtype: str
616
+ """
617
+ if isinstance(cve_data, list) and cve_data:
618
+ return str(cve_data[0]) if cve_data[0] else ""
619
+ elif isinstance(cve_data, str):
620
+ return cve_data
621
+ elif cve_data:
622
+ return str(cve_data)
623
+ return ""
530
624
 
531
625
  def _extract_cve_id_from_xml(self, detection: Optional[Union[Dict[str, Any], ET.Element]]) -> str:
532
626
  """
@@ -648,6 +742,12 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
648
742
  current_time = self.scan_date or get_current_datetime()
649
743
  qid = finding_data.get("qid", "Unknown")
650
744
 
745
+ # Log the finding data for debugging
746
+ logger.debug(f"Creating finding for QID {qid}, host {host_id}")
747
+ logger.debug(f"CVE: {finding_data.get('cve_id', 'None')}")
748
+ logger.debug(f"CVSS V3 Score: {finding_data.get('cvss_v3_score', 'None')}")
749
+ logger.debug(f"CVSS V2 Score: {finding_data.get('cvss_v2_score', 'None')}")
750
+
651
751
  return IntegrationFinding(
652
752
  title=finding_data.get("title", f"Qualys Vulnerability QID-{qid}"),
653
753
  description=finding_data.get("diagnosis", NO_DESCRIPTION),
@@ -1376,3 +1476,169 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
1376
1476
 
1377
1477
  # Default to Open for any unknown status
1378
1478
  return IssueStatus.Open
1479
+
1480
+ def create_vulnerability_from_finding(
1481
+ self, finding: IntegrationFinding, asset: regscale_models.Asset, scan_history: regscale_models.ScanHistory
1482
+ ) -> regscale_models.Vulnerability:
1483
+ """
1484
+ Override the parent method to add better debugging and ensure proper vulnerability mapping creation.
1485
+
1486
+ :param IntegrationFinding finding: The integration finding
1487
+ :param regscale_models.Asset asset: The associated asset
1488
+ :param regscale_models.ScanHistory scan_history: The scan history
1489
+ :return: The created vulnerability
1490
+ :rtype: regscale_models.Vulnerability
1491
+ """
1492
+ logger.debug(f"Creating vulnerability from finding: {finding.title}")
1493
+ logger.debug(f"Asset ID: {asset.id}, Asset Name: {asset.name}")
1494
+ logger.debug(f"Scan History ID: {scan_history.id}")
1495
+ logger.debug(f"Plan ID: {self.plan_id}, Parent Module: {self.parent_module}")
1496
+ logger.debug(f"Is Component: {self.is_component}")
1497
+
1498
+ # Call the parent method
1499
+ vulnerability = super().create_vulnerability_from_finding(finding, asset, scan_history)
1500
+
1501
+ logger.debug(f"Created vulnerability with ID: {vulnerability.id}")
1502
+ logger.debug(f"Vulnerability parentId: {vulnerability.parentId}")
1503
+ logger.debug(f"Vulnerability parentModule: {vulnerability.parentModule}")
1504
+
1505
+ # Verify the vulnerability mapping was created
1506
+ try:
1507
+ mappings = regscale_models.VulnerabilityMapping.find_by_vulnerability(vulnerability.id)
1508
+ logger.debug(f"Found {len(mappings)} vulnerability mappings for vulnerability {vulnerability.id}")
1509
+ for mapping in mappings:
1510
+ logger.debug(
1511
+ f"Mapping - Asset ID: {mapping.assetId}, Scan ID: {mapping.scanId}, Security Plan ID: {mapping.securityPlanId}"
1512
+ )
1513
+ except Exception as e:
1514
+ logger.warning(f"Error checking vulnerability mappings: {e}")
1515
+
1516
+ return vulnerability
1517
+
1518
+ def handle_vulnerability(
1519
+ self,
1520
+ finding: IntegrationFinding,
1521
+ asset: Optional[regscale_models.Asset],
1522
+ scan_history: regscale_models.ScanHistory,
1523
+ ) -> Optional[int]:
1524
+ """
1525
+ Override parent method to ensure Qualys findings always create vulnerabilities.
1526
+ This ensures that Qualys vulnerabilities are properly populated in RegScale.
1527
+
1528
+ :param IntegrationFinding finding: The integration finding
1529
+ :param Optional[regscale_models.Asset] asset: The associated asset
1530
+ :param regscale_models.ScanHistory scan_history: The scan history
1531
+ :rtype: Optional[int]
1532
+ :return: The vulnerability ID
1533
+ """
1534
+ # Check for required fields - either plugin_name or cve must be present
1535
+ if not (finding.plugin_name or finding.cve):
1536
+ logger.warning(
1537
+ f"Qualys: Skipping vulnerability creation - missing plugin_name and cve for finding {finding.external_id}"
1538
+ )
1539
+ return None
1540
+
1541
+ # Ensure vulnerability creation is enabled for Qualys
1542
+ logger.debug(f"Qualys: Vulnerability creation setting: {self.vulnerability_creation}")
1543
+ if self.vulnerability_creation == "NoIssue":
1544
+ logger.debug(f"Qualys: Vulnerability creation disabled, skipping finding {finding.external_id}")
1545
+ return None
1546
+
1547
+ # Create vulnerability using parent method
1548
+ logger.debug(f"Qualys: Calling parent handle_vulnerability for finding {finding.external_id}")
1549
+ vulnerability_id = super().handle_vulnerability(finding, asset, scan_history)
1550
+
1551
+ if vulnerability_id:
1552
+ logger.debug(f"Qualys: Created vulnerability {vulnerability_id} for finding {finding.external_id}")
1553
+ else:
1554
+ logger.warning(f"Qualys: Failed to create vulnerability for finding {finding.external_id}")
1555
+
1556
+ return vulnerability_id
1557
+
1558
+ def set_severity_count_for_scan(
1559
+ self, severity: str, scan_history: regscale_models.ScanHistory, lock: Optional[threading.RLock] = None
1560
+ ) -> None:
1561
+ """
1562
+ Override parent method to ensure Qualys scan history severity counts are properly updated.
1563
+ This ensures that the vulnerability counts are accurately reflected in the scan history.
1564
+
1565
+ :param str severity: Severity of the vulnerability
1566
+ :param regscale_models.ScanHistory scan_history: Scan history object
1567
+ :param Optional[threading.RLock] lock: Thread lock for synchronization
1568
+ :rtype: None
1569
+ """
1570
+ # Use parent method to update severity counts with thread-safe locking
1571
+ # Pass lock if provided, otherwise use our instance lock
1572
+ super().set_severity_count_for_scan(severity, scan_history, lock or self.scan_history_lock)
1573
+
1574
+ def create_scan_history(self) -> regscale_models.ScanHistory:
1575
+ """
1576
+ Override parent method to ensure Qualys scan history is properly created.
1577
+ This ensures that the scanning tool name is correctly set for Qualys scans.
1578
+ Also reuses existing scan history records for the same day and tool to avoid duplicates.
1579
+
1580
+ :return: A newly created or reused ScanHistory object
1581
+ :rtype: regscale_models.ScanHistory
1582
+ """
1583
+ logger.debug(f"Creating scan history for plan {self.plan_id}, module {self.parent_module}")
1584
+
1585
+ try:
1586
+ # Load existing scans for the plan/module
1587
+ existing_scans = regscale_models.ScanHistory.get_all_by_parent(
1588
+ parent_id=self.plan_id, parent_module=self.parent_module
1589
+ )
1590
+
1591
+ # Normalize target date to date component only
1592
+ target_dt = self.scan_date if self.scan_date else get_current_datetime()
1593
+ target_date_only = target_dt.split("T")[0] if isinstance(target_dt, str) else str(target_dt)[:10]
1594
+
1595
+ # Find an existing scan for today and this tool
1596
+ for scan in existing_scans:
1597
+ try:
1598
+ if getattr(scan, "scanningTool", None) == SCANNING_TOOL_NAME and getattr(scan, "scanDate", None):
1599
+ scan_date = str(scan.scanDate)
1600
+ scan_date_only = scan_date.split("T")[0]
1601
+ if scan_date_only == target_date_only:
1602
+ # Reuse this scan history; refresh last updated
1603
+ logger.debug(f"Reusing existing scan history {scan.id} for {target_date_only}")
1604
+ scan.dateLastUpdated = get_current_datetime()
1605
+ scan.lastUpdatedById = self.assessor_id
1606
+ scan.save()
1607
+ return scan
1608
+ except Exception:
1609
+ # Skip any malformed scan records
1610
+ continue
1611
+
1612
+ # No existing same-day scan found, create new
1613
+ logger.debug("No existing scan history found for today, creating new one")
1614
+ scan_history = regscale_models.ScanHistory(
1615
+ parentId=self.plan_id,
1616
+ parentModule=self.parent_module,
1617
+ scanningTool=SCANNING_TOOL_NAME, # Ensure proper scanning tool name
1618
+ scanDate=self.scan_date if self.scan_date else get_current_datetime(),
1619
+ createdById=self.assessor_id,
1620
+ lastUpdatedById=self.assessor_id,
1621
+ tenantsId=self.tenant_id,
1622
+ vLow=0,
1623
+ vMedium=0,
1624
+ vHigh=0,
1625
+ vCritical=0,
1626
+ ).create()
1627
+
1628
+ logger.debug(f"Created new scan history with ID: {scan_history.id}")
1629
+
1630
+ # Ensure the scan history is properly created and cached
1631
+ count = 0
1632
+ regscale_models.ScanHistory.delete_object_cache(scan_history)
1633
+ while not regscale_models.ScanHistory.get_object(object_id=scan_history.id) or count > 10:
1634
+ logger.info("Waiting for ScanHistory to be created...")
1635
+ time.sleep(1)
1636
+ count += 1
1637
+ regscale_models.ScanHistory.delete_object_cache(scan_history)
1638
+
1639
+ return scan_history
1640
+
1641
+ except Exception as e:
1642
+ logger.error(f"Error in create_scan_history: {e}")
1643
+ # Fallback: create new scan history using parent method
1644
+ return super().create_scan_history()