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
@@ -0,0 +1,476 @@
1
+ """AWS Inspector v2 resource collection."""
2
+
3
+ import logging
4
+ from typing import Any, Dict, List, Optional
5
+
6
+ from botocore.exceptions import ClientError
7
+
8
+ from regscale.integrations.commercial.aws.inventory.base import BaseCollector
9
+
10
+ logger = logging.getLogger("regscale")
11
+
12
+
13
+ class InspectorCollector(BaseCollector):
14
+ """Collector for AWS Inspector v2 resources."""
15
+
16
+ def __init__(
17
+ self,
18
+ session: Any,
19
+ region: str,
20
+ account_id: Optional[str] = None,
21
+ tags: Optional[Dict[str, str]] = None,
22
+ collect_findings: bool = True,
23
+ ):
24
+ """
25
+ Initialize Inspector collector.
26
+
27
+ :param session: AWS session to use for API calls
28
+ :param str region: AWS region to collect from
29
+ :param str account_id: Optional AWS account ID to filter resources
30
+ :param Dict[str, str] tags: Optional tags to filter resources (all must match)
31
+ :param bool collect_findings: Whether to collect Inspector findings. Default True.
32
+ """
33
+ super().__init__(session, region)
34
+ self.account_id = account_id
35
+ self.tags = tags or {}
36
+ self.collect_findings = collect_findings
37
+
38
+ def collect(self) -> Dict[str, Any]:
39
+ """
40
+ Collect AWS Inspector v2 resources.
41
+
42
+ :return: Dictionary containing Inspector findings and coverage information
43
+ :rtype: Dict[str, Any]
44
+ """
45
+ result = {
46
+ "Findings": [],
47
+ "Coverage": [],
48
+ "AccountStatus": {},
49
+ "Members": [],
50
+ "CoverageStatistics": {},
51
+ }
52
+
53
+ try:
54
+ client = self._get_client("inspector2")
55
+
56
+ # Get account status
57
+ account_status = self._get_account_status(client)
58
+ result["AccountStatus"] = account_status
59
+
60
+ # Get coverage information
61
+ coverage = self._list_coverage(client)
62
+ result["Coverage"] = coverage
63
+
64
+ # Get coverage statistics
65
+ coverage_stats = self._list_coverage_statistics(client)
66
+ result["CoverageStatistics"] = coverage_stats
67
+
68
+ # Get findings only if requested
69
+ if self.collect_findings:
70
+ findings = self._list_findings(client)
71
+ result["Findings"] = findings
72
+ else:
73
+ findings = []
74
+ logger.debug("Skipping Inspector findings collection (collect_findings=False)")
75
+
76
+ # Get member accounts
77
+ members = self._list_members(client)
78
+ result["Members"] = members
79
+
80
+ if self.collect_findings:
81
+ logger.info(
82
+ f"Collected {len(findings)} Inspector finding(s), "
83
+ f"{len(coverage)} covered resource(s) from {self.region}"
84
+ )
85
+ else:
86
+ logger.info(f"Collected {len(coverage)} covered resource(s) from {self.region}")
87
+
88
+ except ClientError as e:
89
+ self._handle_error(e, "AWS Inspector resources")
90
+ except Exception as e:
91
+ logger.error(f"Unexpected error collecting AWS Inspector resources: {e}", exc_info=True)
92
+
93
+ return result
94
+
95
+ def _get_account_status(self, client: Any) -> Dict[str, Any]:
96
+ """
97
+ Get account status for Inspector.
98
+
99
+ :param client: Inspector client
100
+ :return: Account status information
101
+ :rtype: Dict[str, Any]
102
+ """
103
+ try:
104
+ if self.account_id:
105
+ response = client.batch_get_account_status(accountIds=[self.account_id])
106
+ else:
107
+ # If no account ID specified, get current account status
108
+ response = client.batch_get_account_status()
109
+
110
+ accounts = response.get("accounts", [])
111
+ if accounts:
112
+ status = accounts[0]
113
+ status["Region"] = self.region
114
+ return status
115
+
116
+ return {}
117
+ except ClientError as e:
118
+ if e.response["Error"]["Code"] == "AccessDeniedException":
119
+ logger.warning(f"Access denied to get Inspector account status in {self.region}")
120
+ else:
121
+ logger.error(f"Error getting Inspector account status: {e}")
122
+ return {}
123
+
124
+ def _list_coverage(self, client: Any, max_results: int = 100) -> List[Dict[str, Any]]:
125
+ """
126
+ List resources covered by Inspector with tag filtering.
127
+
128
+ :param client: Inspector client
129
+ :param int max_results: Maximum number of results to retrieve
130
+ :return: List of covered resources
131
+ :rtype: List[Dict[str, Any]]
132
+ """
133
+ coverage = []
134
+ next_token = None
135
+
136
+ try:
137
+ while True:
138
+ params = self._build_coverage_params(max_results, next_token)
139
+ response = client.list_coverage(**params)
140
+ covered_resources = response.get("coveredResources", [])
141
+
142
+ processed_resources = self._process_coverage_resources(covered_resources)
143
+ coverage.extend(processed_resources)
144
+
145
+ next_token = response.get("nextToken")
146
+ if not next_token:
147
+ break
148
+
149
+ except ClientError as e:
150
+ self._handle_client_error(e, "list Inspector coverage")
151
+
152
+ return coverage
153
+
154
+ def _build_coverage_params(self, max_results: int, next_token: Optional[str]) -> Dict[str, Any]:
155
+ """
156
+ Build parameters for list_coverage API call.
157
+
158
+ :param int max_results: Maximum number of results to retrieve
159
+ :param next_token: Pagination token
160
+ :return: API parameters dictionary
161
+ :rtype: Dict[str, Any]
162
+ """
163
+ params = {"maxResults": max_results}
164
+
165
+ if next_token:
166
+ params["nextToken"] = next_token
167
+
168
+ if self.account_id:
169
+ params["filterCriteria"] = {"accountId": [{"comparison": "EQUALS", "value": self.account_id}]}
170
+
171
+ return params
172
+
173
+ def _process_coverage_resources(self, resources: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
174
+ """
175
+ Process and filter coverage resources by tags.
176
+
177
+ :param resources: List of coverage resources
178
+ :return: Filtered list of resources
179
+ :rtype: List[Dict[str, Any]]
180
+ """
181
+ processed = []
182
+
183
+ for resource in resources:
184
+ resource["Region"] = self.region
185
+
186
+ if self._should_include_coverage_resource(resource):
187
+ processed.append(resource)
188
+
189
+ return processed
190
+
191
+ def _should_include_coverage_resource(self, resource: Dict[str, Any]) -> bool:
192
+ """
193
+ Check if coverage resource should be included based on tag filter.
194
+
195
+ :param resource: Coverage resource to check
196
+ :return: True if resource should be included
197
+ :rtype: bool
198
+ """
199
+ if not self.tags:
200
+ return True
201
+
202
+ resource_metadata = resource.get("resourceMetadata", {})
203
+ resource_tags = resource_metadata.get("tags", {})
204
+
205
+ if self._matches_tags(resource_tags):
206
+ return True
207
+
208
+ logger.debug(
209
+ "Filtering out Inspector coverage resource %s - tags do not match filter",
210
+ resource.get("resourceId", "unknown"),
211
+ )
212
+ return False
213
+
214
+ def _list_coverage_statistics(self, client: Any) -> Dict[str, Any]:
215
+ """
216
+ Get coverage statistics.
217
+
218
+ :param client: Inspector client
219
+ :return: Coverage statistics
220
+ :rtype: Dict[str, Any]
221
+ """
222
+ try:
223
+ params = {}
224
+
225
+ if self.account_id:
226
+ params["filterCriteria"] = {"accountId": [{"comparison": "EQUALS", "value": self.account_id}]}
227
+
228
+ response = client.list_coverage_statistics(**params)
229
+ counts_by_group = response.get("countsByGroup", [])
230
+
231
+ stats = {"Region": self.region, "CountsByGroup": counts_by_group}
232
+
233
+ return stats
234
+ except ClientError as e:
235
+ if e.response["Error"]["Code"] == "AccessDeniedException":
236
+ logger.debug(f"Access denied to list Inspector coverage statistics in {self.region}")
237
+ else:
238
+ logger.error(f"Error listing Inspector coverage statistics: {e}")
239
+ return {}
240
+
241
+ def _list_findings(self, client: Any, max_results: int = 100) -> List[Dict[str, Any]]:
242
+ """
243
+ List Inspector findings with pagination support and tag filtering.
244
+
245
+ :param client: Inspector client
246
+ :param int max_results: Maximum number of results per page
247
+ :return: List of findings
248
+ :rtype: List[Dict[str, Any]]
249
+ """
250
+ findings = []
251
+ next_token = None
252
+
253
+ try:
254
+ while True:
255
+ params = self._build_findings_params(max_results, next_token)
256
+ response = client.list_findings(**params)
257
+ finding_list = response.get("findings", [])
258
+
259
+ processed_findings = self._process_findings(finding_list)
260
+ findings.extend(processed_findings)
261
+
262
+ next_token = response.get("nextToken")
263
+ if not next_token:
264
+ break
265
+
266
+ except ClientError as e:
267
+ self._handle_client_error(e, "list Inspector findings")
268
+
269
+ return findings
270
+
271
+ def _build_findings_params(self, max_results: int, next_token: Optional[str]) -> Dict[str, Any]:
272
+ """
273
+ Build parameters for list_findings API call.
274
+
275
+ :param int max_results: Maximum number of results per page
276
+ :param next_token: Pagination token
277
+ :return: API parameters dictionary
278
+ :rtype: Dict[str, Any]
279
+ """
280
+ params = {"maxResults": max_results}
281
+
282
+ if next_token:
283
+ params["nextToken"] = next_token
284
+
285
+ if self.account_id:
286
+ params["filterCriteria"] = {"awsAccountId": [{"comparison": "EQUALS", "value": self.account_id}]}
287
+
288
+ return params
289
+
290
+ def _process_findings(self, finding_list: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
291
+ """
292
+ Process and filter findings by tags.
293
+
294
+ :param finding_list: List of findings to process
295
+ :return: Filtered list of findings
296
+ :rtype: List[Dict[str, Any]]
297
+ """
298
+ processed = []
299
+
300
+ for finding in finding_list:
301
+ finding["Region"] = self.region
302
+
303
+ if self._should_include_finding(finding):
304
+ processed.append(finding)
305
+
306
+ return processed
307
+
308
+ def _should_include_finding(self, finding: Dict[str, Any]) -> bool:
309
+ """
310
+ Check if finding should be included based on tag filter.
311
+
312
+ :param finding: Finding to check
313
+ :return: True if finding should be included
314
+ :rtype: bool
315
+ """
316
+ if not self.tags:
317
+ return True
318
+
319
+ if self._finding_matches_tag_filter(finding):
320
+ return True
321
+
322
+ logger.debug(
323
+ "Filtering out Inspector finding %s - tags do not match filter", finding.get("findingArn", "unknown")
324
+ )
325
+ return False
326
+
327
+ def _finding_matches_tag_filter(self, finding: Dict[str, Any]) -> bool:
328
+ """
329
+ Check if any resource in the finding matches the tag filter.
330
+
331
+ :param finding: Finding to check
332
+ :return: True if any resource matches the tag filter
333
+ :rtype: bool
334
+ """
335
+ resources = finding.get("resources", [])
336
+
337
+ for resource in resources:
338
+ resource_tags = resource.get("tags", {})
339
+ if self._matches_tags(resource_tags):
340
+ return True
341
+
342
+ return False
343
+
344
+ def _list_members(self, client: Any) -> List[Dict[str, Any]]:
345
+ """
346
+ List member accounts for Inspector organization.
347
+
348
+ :param client: Inspector client
349
+ :return: List of member accounts
350
+ :rtype: List[Dict[str, Any]]
351
+ """
352
+ members = []
353
+ next_token = None
354
+
355
+ try:
356
+ while True:
357
+ params = {}
358
+
359
+ if next_token:
360
+ params["nextToken"] = next_token
361
+
362
+ response = client.list_members(**params)
363
+ member_list = response.get("members", [])
364
+
365
+ for member in member_list:
366
+ # Filter by account ID if specified
367
+ if self.account_id and member.get("accountId") != self.account_id:
368
+ continue
369
+
370
+ member["Region"] = self.region
371
+
372
+ members.extend(member_list)
373
+
374
+ next_token = response.get("nextToken")
375
+ if not next_token:
376
+ break
377
+
378
+ except ClientError as e:
379
+ if e.response["Error"]["Code"] == "AccessDeniedException":
380
+ logger.debug(f"Access denied to list Inspector members in {self.region}")
381
+ else:
382
+ logger.error(f"Error listing Inspector members: {e}")
383
+
384
+ return members
385
+
386
+ def _convert_tags_to_dict(self, tags: Any) -> Dict[str, str]:
387
+ """
388
+ Convert tags from various formats to a dictionary.
389
+
390
+ :param tags: Tags in list format [{"key": "k", "value": "v"}] or dict format
391
+ :return: Dictionary of tag key-value pairs
392
+ :rtype: Dict[str, str]
393
+ """
394
+ if not tags:
395
+ return {}
396
+
397
+ if isinstance(tags, dict):
398
+ return tags
399
+
400
+ if isinstance(tags, list):
401
+ return self._convert_tag_list_to_dict(tags)
402
+
403
+ return {}
404
+
405
+ def _convert_tag_list_to_dict(self, tags: List[Any]) -> Dict[str, str]:
406
+ """
407
+ Convert a list of tag dictionaries to a single dictionary.
408
+
409
+ :param tags: List of tag dictionaries
410
+ :return: Dictionary of tag key-value pairs
411
+ :rtype: Dict[str, str]
412
+ """
413
+ tag_dict = {}
414
+
415
+ for tag in tags:
416
+ if isinstance(tag, dict):
417
+ key_value = self._extract_tag_key_value(tag)
418
+ if key_value:
419
+ tag_dict[key_value[0]] = key_value[1]
420
+
421
+ return tag_dict
422
+
423
+ def _extract_tag_key_value(self, tag: Dict[str, Any]) -> Optional[tuple]:
424
+ """
425
+ Extract key and value from a tag dictionary.
426
+
427
+ Handles both lowercase and uppercase formats:
428
+ - {"key": "k", "value": "v"}
429
+ - {"Key": "k", "Value": "v"}
430
+
431
+ :param tag: Tag dictionary
432
+ :return: Tuple of (key, value) or None if key not found
433
+ :rtype: Optional[tuple]
434
+ """
435
+ key = tag.get("key") or tag.get("Key")
436
+ if key is None:
437
+ return None
438
+
439
+ value = tag.get("value") or tag.get("Value")
440
+ return (key, value if value is not None else "")
441
+
442
+ def _handle_client_error(self, error: ClientError, operation: str) -> None:
443
+ """
444
+ Handle ClientError exceptions consistently.
445
+
446
+ :param error: The ClientError exception
447
+ :param str operation: Description of the operation that failed
448
+ """
449
+ error_code = error.response["Error"]["Code"]
450
+ if error_code == "AccessDeniedException":
451
+ logger.warning("Access denied to %s in %s", operation, self.region)
452
+ else:
453
+ logger.error("Error %s: %s", operation, error)
454
+
455
+ def _matches_tags(self, resource_tags: Any) -> bool:
456
+ """
457
+ Check if resource tags match all filter tags.
458
+
459
+ :param resource_tags: Tags from the resource (can be dict or list)
460
+ :return: True if all filter tags match, False otherwise
461
+ :rtype: bool
462
+ """
463
+ if not self.tags:
464
+ # No tag filter, so all resources match
465
+ return True
466
+
467
+ resource_tag_dict = self._convert_tags_to_dict(resource_tags)
468
+
469
+ # All filter tags must match
470
+ for filter_key, filter_value in self.tags.items():
471
+ if filter_key not in resource_tag_dict:
472
+ return False
473
+ if resource_tag_dict[filter_key] != filter_value:
474
+ return False
475
+
476
+ return True