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
@@ -61,7 +61,62 @@ class Vulnerabilities(SynqlyModel):
61
61
  self.can_fetch_vulns = "query_findings" in self.capabilities
62
62
 
63
63
  @staticmethod
64
- def _handle_scan_date_options(regscale_ssp_id: int, **kwargs) -> list[str]:
64
+ def _build_severity_filter(severity: Optional[str] = None) -> str:
65
+ """
66
+ Build severity filter string for Synqly queries
67
+
68
+ :param Optional[str] severity: Minimum severity level, if None, defaults to 'low'
69
+ :return: Comma-separated severity filter string
70
+ :rtype: str
71
+ """
72
+ if not severity:
73
+ severity = "low"
74
+ severity_map = {
75
+ "critical": ["critical"],
76
+ "high": ["high", "critical"],
77
+ "medium": ["medium", "high", "critical"],
78
+ "low": ["low", "medium", "high", "critical"],
79
+ "info": ["info", "low", "medium", "high", "critical"],
80
+ }
81
+ if severity[0].lower() == "a":
82
+ mapped_severities = list(severity_map.keys())
83
+ else:
84
+ mapped_severities = severity_map.get(severity.lower(), severity)
85
+ severity_filter = f"severity[in]{','.join(mapped_severities)}"
86
+ return severity_filter
87
+
88
+ def _translate_asset_filter(self, replace: str, replace_with: str, asset_filters: Optional[list[str]]) -> list[str]:
89
+ """
90
+ Translate asset filters to the correct format for the integration
91
+
92
+ :param str replace: The string to replace
93
+ :param str replace_with: The string to replace with
94
+ :param list[str] asset_filters: The asset filters to translate
95
+ :return: The translated asset filters
96
+ :rtype: list[str]
97
+ """
98
+ translated_asset_filters = []
99
+ for asset_filter in asset_filters:
100
+ # Remove outer double quotes if present
101
+ cleaned_filter = asset_filter
102
+ if cleaned_filter.startswith('"') and cleaned_filter.endswith('"') and len(cleaned_filter) > 1:
103
+ cleaned_filter = cleaned_filter[1:-1]
104
+ if cleaned_filter.startswith("'") and cleaned_filter.endswith("'") and len(cleaned_filter) > 1:
105
+ cleaned_filter = cleaned_filter[1:-1]
106
+
107
+ if replace_with in cleaned_filter:
108
+ possible_filter = cleaned_filter
109
+ elif replace in cleaned_filter:
110
+ possible_filter = cleaned_filter.replace(replace, replace_with)
111
+ self.logger.debug(f"Translated filter: from {cleaned_filter} to {possible_filter}")
112
+ else:
113
+ continue
114
+ valid_filter, _ = self.filter_parser.validate_filter(self.integration_id, possible_filter)
115
+ if valid_filter:
116
+ translated_asset_filters.append(possible_filter)
117
+ return translated_asset_filters
118
+
119
+ def _handle_scan_date_options(self, regscale_ssp_id: int, **kwargs) -> list[str]:
65
120
  """
66
121
  Handle scan date options for the integration sync process
67
122
 
@@ -71,21 +126,22 @@ class Vulnerabilities(SynqlyModel):
71
126
  """
72
127
  from regscale.integrations.commercial.tenablev2.utils import get_last_pull_epoch
73
128
 
74
- if vuln_filter := kwargs.get("vuln_filter"):
75
- kwargs.pop("vuln_filter")
76
- if isinstance(vuln_filter, str):
77
- vuln_filter = [f"severity[in]{vuln_filter}"]
78
- else:
79
- vuln_filter = ["severity[in]critical,high,medium,low"]
129
+ vuln_filter = [self._build_severity_filter(kwargs.get("minimum_severity_filter"))]
130
+
131
+ if asset_filters := kwargs.get("filter", []):
132
+ vuln_filter.extend(
133
+ self._translate_asset_filter(replace="device.", replace_with="resources.", asset_filters=asset_filters)
134
+ )
135
+
80
136
  if kwargs.get("all_scans"):
81
137
  vuln_filter.append("finding.last_seen_time[gte]915148800") # Friday, January 1, 1999 12:00:00 AM UTC
82
138
  elif scan_date := kwargs.get("scan_date"):
83
- from regscale.core.utils.date import date_obj
139
+ from regscale.core.utils.date import datetime_obj
84
140
 
85
- if scan_date := date_obj(scan_date):
86
- vuln_filter.append(f"finding.last_seen_time[gte]{scan_date.isoformat()}")
141
+ if scan_date := datetime_obj(scan_date):
142
+ vuln_filter.append(f"finding.last_seen_time[gte]{int(scan_date.timestamp())}")
87
143
  else:
88
- raise ValueError(f"Invalid scan date: {scan_date}")
144
+ vuln_filter.append(f"finding.last_seen_time[gte]{get_last_pull_epoch(regscale_ssp_id)}")
89
145
  else:
90
146
  vuln_filter.append(f"finding.last_seen_time[gte]{get_last_pull_epoch(regscale_ssp_id)}")
91
147
  return vuln_filter
@@ -99,22 +155,35 @@ class Vulnerabilities(SynqlyModel):
99
155
  """
100
156
  vuln_filter = self._handle_scan_date_options(regscale_ssp_id=regscale_ssp_id, **kwargs)
101
157
  self.logger.debug(f"Vulnerability filter: {vuln_filter}")
158
+
159
+ # Pop the filter from kwargs so it doesn't get passed to query_findings
160
+ if asset_filter := kwargs.pop("filter", []):
161
+ asset_filter = self._translate_asset_filter(
162
+ replace="resources.", replace_with="device.", asset_filters=asset_filter
163
+ )
164
+ self.logger.debug(f"Asset filter: {asset_filter}")
165
+
166
+ self.logger.info(f"Fetching asset data from {self.integration_name}...")
167
+ assets = (
168
+ self.fetch_integration_data(
169
+ func=self.tenant.engine_client.vulnerabilities.query_assets,
170
+ filter=asset_filter, # Field-based filters only for assets
171
+ **kwargs,
172
+ )
173
+ if self.can_fetch_assets
174
+ else []
175
+ )
176
+
102
177
  self.logger.info(f"Fetching vulnerability data from {self.integration_name}...")
103
178
  findings = (
104
179
  self.fetch_integration_data(
105
180
  func=self.tenant.engine_client.vulnerabilities.query_findings,
106
- filter=vuln_filter,
181
+ filter=vuln_filter, # Only severity/date filters for findings
107
182
  **kwargs,
108
183
  )
109
184
  if self.can_fetch_vulns
110
185
  else []
111
186
  )
112
- self.logger.info(f"Fetching asset data from {self.integration_name}...")
113
- assets = (
114
- self.fetch_integration_data(func=self.tenant.engine_client.vulnerabilities.query_assets, **kwargs)
115
- if self.can_fetch_assets
116
- else []
117
- )
118
187
 
119
188
  self.scanner_integration = VulnerabilitiesIntegration(plan_id=regscale_ssp_id)
120
189
  self.logger.info(f"Mapping {self.provider} asset(s) data to RegScale asset(s)...")
@@ -0,0 +1,332 @@
1
+ """
2
+ Centralized parser for extracting filter definitions from Synqly capabilities.
3
+ Used by both code generation and query builder.
4
+ """
5
+
6
+ import json
7
+ import re
8
+ from typing import Dict, List, Optional, Set, Tuple
9
+ import importlib.resources as pkg_resources
10
+
11
+ from regscale.models.integration_models.synqly_models.connector_types import ConnectorType
12
+
13
+
14
+ class FilterParser:
15
+ """Parser for Synqly filter definitions from capabilities.json"""
16
+
17
+ # Define which connectors support filtering
18
+ FILTERABLE_CONNECTORS: Set[str] = {ConnectorType.Assets.value, ConnectorType.Vulnerabilities.value}
19
+
20
+ def __init__(self, capabilities_data: Optional[List[dict]] = None):
21
+ """
22
+ Initialize the filter parser with provided or loaded capabilities.
23
+
24
+ :param Optional[List[dict]] capabilities_data: Pre-loaded capabilities data.
25
+ If None, will load from package resources.
26
+ """
27
+ if capabilities_data is None:
28
+ self.capabilities_data = self._load_capabilities()
29
+ else:
30
+ self.capabilities_data = capabilities_data
31
+ self.filter_mapping = self._build_filter_mapping()
32
+
33
+ def _load_capabilities(self) -> List[dict]:
34
+ """
35
+ Load capabilities.json from package resources.
36
+
37
+ :return: List of capability definitions
38
+ :rtype: List[dict]
39
+ """
40
+ try:
41
+ files = pkg_resources.files("regscale.models.integration_models.synqly_models")
42
+ capabilities_file = files / "capabilities.json"
43
+ with capabilities_file.open("r") as file:
44
+ data = json.load(file)
45
+ return data.get("result", [])
46
+ except Exception as e:
47
+ print(f"Error loading capabilities.json: {e}")
48
+ return []
49
+
50
+ def _build_filter_mapping(self) -> Dict[str, Dict[str, List[dict]]]:
51
+ """
52
+ Build comprehensive filter mapping for all providers.
53
+
54
+ Structure:
55
+ {
56
+ 'assets_armis_centrix': {
57
+ 'query_devices': [
58
+ {
59
+ 'name': 'device.ip',
60
+ 'type': 'string',
61
+ 'operators': ['eq', 'ne', 'in', 'not_in'],
62
+ 'values': [] # For enum types
63
+ },
64
+ ...
65
+ ]
66
+ },
67
+ 'vulnerabilities_qualys': {
68
+ 'query_findings': [...],
69
+ 'query_assets': [...]
70
+ },
71
+ ...
72
+ }
73
+
74
+ :return: Mapping of provider IDs to their operations and filters
75
+ :rtype: Dict[str, Dict[str, List[dict]]]
76
+ """
77
+ filter_mapping = {}
78
+
79
+ for provider in self.capabilities_data:
80
+ provider_id = provider.get("id", "")
81
+ connector_type = provider.get("connector", "")
82
+
83
+ # Only process filterable connector types
84
+ if connector_type not in self.FILTERABLE_CONNECTORS:
85
+ continue
86
+
87
+ # Skip mock providers if desired (they end with _mock)
88
+ # if provider_id.endswith('_mock'):
89
+ # continue
90
+
91
+ operations = provider.get("operations", [])
92
+ for operation in operations:
93
+ # Only process supported operations with filters
94
+ if not operation.get("supported", False):
95
+ continue
96
+
97
+ filters = operation.get("filters", [])
98
+ if filters:
99
+ if provider_id not in filter_mapping:
100
+ filter_mapping[provider_id] = {}
101
+
102
+ operation_name = operation.get("name", "")
103
+ filter_mapping[provider_id][operation_name] = filters
104
+
105
+ return filter_mapping
106
+
107
+ def get_filters_for_provider(self, provider_id: str, operation: Optional[str] = None) -> List[dict]:
108
+ """
109
+ Get filters for a specific provider and optionally a specific operation.
110
+
111
+ :param str provider_id: Provider ID (e.g., 'assets_armis_centrix')
112
+ :param Optional[str] operation: Operation name (e.g., 'query_devices')
113
+ :return: List of filter definitions
114
+ :rtype: List[dict]
115
+ """
116
+ provider_filters = self.filter_mapping.get(provider_id, {})
117
+
118
+ if operation:
119
+ return provider_filters.get(operation, [])
120
+
121
+ # Return all filters for all operations if no specific operation
122
+ all_filters = []
123
+ seen_fields = set() # Avoid duplicates
124
+
125
+ for op_filters in provider_filters.values():
126
+ for filter_def in op_filters:
127
+ field_name = filter_def.get("name", "")
128
+ if field_name not in seen_fields:
129
+ all_filters.append(filter_def)
130
+ seen_fields.add(field_name)
131
+
132
+ return all_filters
133
+
134
+ def get_providers_with_filters(self, connector_type: str) -> List[str]:
135
+ """
136
+ Get list of providers that support filtering for a connector type.
137
+
138
+ :param str connector_type: Connector type (e.g., 'assets', 'vulnerabilities')
139
+ :return: List of provider IDs that have filters
140
+ :rtype: List[str]
141
+ """
142
+ providers = []
143
+
144
+ for provider_id, operations in self.filter_mapping.items():
145
+ # Check if provider matches connector type and has filters
146
+ if provider_id.startswith(f"{connector_type}_") and operations:
147
+ providers.append(provider_id)
148
+
149
+ # Sort for consistent ordering
150
+ return sorted(providers)
151
+
152
+ def has_filters(self, provider_id: str) -> bool:
153
+ """
154
+ Check if a provider has any filters defined.
155
+
156
+ :param str provider_id: Provider ID to check
157
+ :return: True if provider has filters
158
+ :rtype: bool
159
+ """
160
+ return provider_id in self.filter_mapping and bool(self.filter_mapping[provider_id])
161
+
162
+ @staticmethod
163
+ def format_filter_string(field: str, operator: str, value: str) -> str:
164
+ """
165
+ Convert user input to Synqly filter format.
166
+
167
+ :param str field: Field name (e.g., 'device.ip')
168
+ :param str operator: Operator (e.g., 'eq', 'gte')
169
+ :param str value: Filter value
170
+ :return: Formatted filter string
171
+ :rtype: str
172
+
173
+ Example:
174
+ format_filter_string('device.ip', 'eq', '192.168.1.1')
175
+ Returns: 'device.ip[eq]192.168.1.1'
176
+ """
177
+ return f"{field}[{operator}]{value}"
178
+
179
+ @staticmethod
180
+ def parse_filter_string(filter_string: str) -> Optional[Tuple[str, str, str]]:
181
+ """
182
+ Parse a filter string into its components.
183
+
184
+ :param str filter_string: Filter in format 'field[operator]value'
185
+ :return: Tuple of (field, operator, value) or None if invalid
186
+ :rtype: Optional[Tuple[str, str, str]]
187
+ """
188
+ match = re.match(r"^([a-z._]+)\[([a-z_]+)\](.+)$", filter_string, re.IGNORECASE)
189
+ if match:
190
+ return match.groups()
191
+ return None
192
+
193
+ def validate_filter(self, provider_id: str, filter_string: str) -> Tuple[bool, str]:
194
+ """
195
+ Validate a filter string against provider capabilities.
196
+
197
+ :param str provider_id: Provider ID (e.g., 'assets_armis_centrix')
198
+ :param str filter_string: Filter in format 'field[operator]value'
199
+ :return: Tuple of (is_valid, error_message)
200
+ :rtype: Tuple[bool, str]
201
+ """
202
+ # Parse the filter string
203
+ parsed = self.parse_filter_string(filter_string)
204
+ if not parsed:
205
+ return False, f"Invalid filter format: {filter_string}. Expected format: field[operator]value"
206
+
207
+ field, operator, value = parsed
208
+
209
+ # Get all filters for this provider
210
+ provider_filters = self.get_filters_for_provider(provider_id)
211
+
212
+ if not provider_filters:
213
+ return False, f"Provider '{provider_id}' does not support filtering"
214
+
215
+ # Check if field exists
216
+ field_filter = None
217
+ for f in provider_filters:
218
+ if f.get("name") == field:
219
+ field_filter = f
220
+ break
221
+
222
+ if not field_filter:
223
+ available_fields = [f.get("name", "") for f in provider_filters]
224
+ return (
225
+ False,
226
+ f"Field '{field}' not supported by {provider_id}. Available fields: {', '.join(available_fields)}",
227
+ )
228
+
229
+ # Check if operator is valid for this field
230
+ valid_operators = field_filter.get("operators", [])
231
+ if operator not in valid_operators:
232
+ return (
233
+ False,
234
+ f"Operator '{operator}' not valid for field '{field}'. Valid operators: {', '.join(valid_operators)}",
235
+ )
236
+
237
+ # Optionally validate value type and format
238
+ field_type = field_filter.get("type", "string")
239
+
240
+ # Handle comma-separated values for 'in' and 'not_in' operators
241
+ if operator in ["in", "not_in"]:
242
+ values_to_check = [v.strip() for v in value.split(",")]
243
+ else:
244
+ values_to_check = [value]
245
+
246
+ for val in values_to_check:
247
+ if field_type == "number":
248
+ try:
249
+ float(val)
250
+ except ValueError:
251
+ return False, f"Value '{val}' is not a valid number for field '{field}'"
252
+ elif field_type == "enum":
253
+ valid_values = field_filter.get("values", [])
254
+ if valid_values and val not in valid_values:
255
+ return (
256
+ False,
257
+ f"Value '{val}' not valid for field '{field}'. Valid values: {', '.join(valid_values)}",
258
+ )
259
+
260
+ return True, ""
261
+
262
+ def get_operator_display_name(self, operator: str) -> str:
263
+ """
264
+ Get human-friendly display name for an operator.
265
+
266
+ :param str operator: Operator code
267
+ :return: Display name
268
+ :rtype: str
269
+ """
270
+ operator_map = {
271
+ "eq": "equals",
272
+ "ne": "not equals",
273
+ "in": "in list",
274
+ "not_in": "not in list",
275
+ "like": "matches pattern",
276
+ "not_like": "does not match pattern",
277
+ "gt": "greater than",
278
+ "gte": "greater than or equal to",
279
+ "lt": "less than",
280
+ "lte": "less than or equal to",
281
+ }
282
+ return operator_map.get(operator, operator)
283
+
284
+ def get_field_display_name(self, field: str) -> str:
285
+ """
286
+ Convert field name to human-friendly display name.
287
+
288
+ :param str field: Field name (e.g., 'device.hw_info.serial_number')
289
+ :return: Display name (e.g., 'Device Hardware Info Serial Number')
290
+ :rtype: str
291
+ """
292
+ # Replace dots and underscores with spaces, then title case
293
+ display = field.replace(".", " ").replace("_", " ").title()
294
+ return display
295
+
296
+ def get_connector_operations(self, connector_type: str) -> Dict[str, List[str]]:
297
+ """
298
+ Get all operations that support filtering for a connector type.
299
+
300
+ :param str connector_type: Connector type (e.g., 'assets')
301
+ :return: Dict mapping provider IDs to their filterable operations
302
+ :rtype: Dict[str, List[str]]
303
+ """
304
+ operations_map = {}
305
+
306
+ for provider_id, operations in self.filter_mapping.items():
307
+ if provider_id.startswith(f"{connector_type}_"):
308
+ operations_map[provider_id] = list(operations.keys())
309
+
310
+ return operations_map
311
+
312
+ def get_stats(self) -> dict:
313
+ """
314
+ Get statistics about loaded filters.
315
+
316
+ :return: Dictionary with filter statistics
317
+ :rtype: dict
318
+ """
319
+ stats = {
320
+ "total_providers": len(self.capabilities_data),
321
+ "providers_with_filters": len(self.filter_mapping),
322
+ "total_filters": 0,
323
+ "by_connector": {},
324
+ }
325
+
326
+ for connector in self.FILTERABLE_CONNECTORS:
327
+ providers = self.get_providers_with_filters(connector)
328
+ filter_count = sum(len(self.get_filters_for_provider(p)) for p in providers)
329
+ stats["by_connector"][connector] = {"providers": len(providers), "filters": filter_count}
330
+ stats["total_filters"] += filter_count
331
+
332
+ return stats