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,198 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """AWS Config Conformance Pack to Control Mappings."""
4
+
5
+ import logging
6
+ import re
7
+ from typing import Dict, List, Optional
8
+
9
+ logger = logging.getLogger("regscale")
10
+
11
+ # Control ID constants
12
+ CONTROL_IA_2_1 = "IA-2(1)"
13
+
14
+ # NIST 800-53 R5 Conformance Pack Control Mappings
15
+ # Maps AWS Config rule names to NIST 800-53 R5 control IDs
16
+ NIST_80053_R5_MAPPINGS = {
17
+ # Access Control (AC) Family
18
+ "iam-password-policy": ["AC-2", "IA-5"],
19
+ "iam-user-mfa-enabled": ["AC-2", CONTROL_IA_2_1],
20
+ "iam-root-access-key-check": ["AC-2", "AC-6"],
21
+ "iam-user-unused-credentials-check": ["AC-2"],
22
+ "iam-user-group-membership-check": ["AC-2"],
23
+ "iam-policy-no-statements-with-admin-access": ["AC-6"],
24
+ "iam-policy-no-statements-with-full-access": ["AC-6"],
25
+ "s3-bucket-public-read-prohibited": ["AC-3", "AC-4"],
26
+ "s3-bucket-public-write-prohibited": ["AC-3", "AC-4"],
27
+ "ec2-security-group-attached-to-eni": ["AC-4"],
28
+ "restricted-ssh": ["AC-4", "AC-17"],
29
+ "restricted-common-ports": ["AC-4"],
30
+ # Audit and Accountability (AU) Family
31
+ "cloudtrail-enabled": ["AU-2", "AU-3", "AU-6", "AU-12"],
32
+ "cloud-trail-cloud-watch-logs-enabled": ["AU-6"],
33
+ "cloudtrail-log-file-validation-enabled": ["AU-9"],
34
+ "cloudtrail-encryption-enabled": ["AU-9"],
35
+ "cloudtrail-s3-dataevents-enabled": ["AU-2"],
36
+ "multi-region-cloudtrail-enabled": ["AU-2"],
37
+ "s3-bucket-logging-enabled": ["AU-2"],
38
+ "rds-logging-enabled": ["AU-2"],
39
+ "elb-logging-enabled": ["AU-2"],
40
+ "cloudwatch-alarm-action-check": ["AU-6"],
41
+ # Configuration Management (CM) Family
42
+ "ec2-instance-managed-by-systems-manager": ["CM-2", "CM-6"],
43
+ "ec2-managedinstance-patch-compliance-status-check": ["CM-6", "SI-2"], # Maps to both CM and SI families
44
+ "approved-amis-by-tag": ["CM-2"],
45
+ # Identification and Authentication (IA) Family
46
+ "mfa-enabled-for-iam-console-access": [CONTROL_IA_2_1],
47
+ "root-account-mfa-enabled": [CONTROL_IA_2_1],
48
+ # System and Communications Protection (SC) Family
49
+ "s3-bucket-ssl-requests-only": ["SC-8", "SC-13"],
50
+ "alb-http-to-https-redirection-check": ["SC-8"],
51
+ "elb-tls-https-listeners-only": ["SC-8"],
52
+ "rds-snapshot-encrypted": ["SC-13"],
53
+ "encrypted-volumes": ["SC-13"],
54
+ "s3-bucket-server-side-encryption-enabled": ["SC-13"],
55
+ "ec2-ebs-encryption-by-default": ["SC-13"],
56
+ "rds-storage-encrypted": ["SC-13"],
57
+ "dynamodb-table-encrypted-kms": ["SC-13"],
58
+ # System and Information Integrity (SI) Family
59
+ "guardduty-enabled-centralized": ["SI-4"],
60
+ "securityhub-enabled": ["SI-4"],
61
+ "access-keys-rotated": ["SI-4"],
62
+ "vpc-flow-logs-enabled": ["SI-4"],
63
+ # Risk Assessment (RA) Family
64
+ "security-account-information-provided": ["RA-5"],
65
+ }
66
+
67
+
68
+ def extract_control_ids_from_rule_name(rule_name: str) -> List[str]:
69
+ """
70
+ Extract control IDs from AWS Config rule name using pattern matching.
71
+
72
+ Supports patterns like:
73
+ - "ac-2-iam-user-mfa-enabled"
74
+ - "nist-800-53-r5-ac-2"
75
+ - "iam-password-policy-ac-2-ia-5"
76
+
77
+ :param str rule_name: Config rule name
78
+ :return: List of extracted control IDs
79
+ :rtype: List[str]
80
+ """
81
+ control_ids = []
82
+
83
+ # Pattern for NIST control IDs: AC-2, SI-3(1), etc.
84
+ pattern = r"\b([A-Z]{2}-\d+(?:\(\d+\))?)\b"
85
+
86
+ matches = re.findall(pattern, rule_name.upper())
87
+ control_ids.extend(matches)
88
+
89
+ return list(set(control_ids)) # Remove duplicates
90
+
91
+
92
+ def extract_control_ids_from_tags(tags: Dict[str, str]) -> List[str]:
93
+ """
94
+ Extract control IDs from AWS Config rule tags.
95
+
96
+ Expected tag format:
97
+ - ControlID=AC-2
98
+ - ControlID=AC-2,AU-3,SI-2
99
+ - ControlIDs=AC-2,AU-3
100
+
101
+ :param Dict[str, str] tags: Dictionary of tag key-value pairs
102
+ :return: List of extracted control IDs
103
+ :rtype: List[str]
104
+ """
105
+ control_ids = []
106
+
107
+ # Check for ControlID or ControlIDs tags
108
+ for tag_key in ["ControlID", "ControlIDs", "Control-ID", "Control-IDs"]:
109
+ if tag_key in tags:
110
+ tag_value = tags[tag_key]
111
+ # Split by comma and clean up
112
+ ids = [cid.strip().upper() for cid in tag_value.split(",") if cid.strip()]
113
+ control_ids.extend(ids)
114
+
115
+ return list(set(control_ids)) # Remove duplicates
116
+
117
+
118
+ def get_control_mappings_for_framework(framework: str) -> Dict[str, List[str]]:
119
+ """
120
+ Get control mappings for a specific framework.
121
+
122
+ :param str framework: Framework name (e.g., "NIST800-53R5")
123
+ :return: Dictionary mapping rule names to control IDs
124
+ :rtype: Dict[str, List[str]]
125
+ """
126
+ framework_upper = framework.upper().replace("-", "").replace("_", "")
127
+
128
+ if "NIST80053" in framework_upper or "NIST800" in framework_upper:
129
+ return NIST_80053_R5_MAPPINGS
130
+
131
+ # Add more framework mappings as needed
132
+ # elif "PCI" in framework_upper:
133
+ # return PCI_DSS_MAPPINGS
134
+ # elif "CIS" in framework_upper:
135
+ # return CIS_MAPPINGS
136
+
137
+ logger.warning(f"No built-in control mappings available for framework: {framework}")
138
+ return {}
139
+
140
+
141
+ def map_rule_to_controls(
142
+ rule_name: str,
143
+ rule_description: Optional[str] = None,
144
+ rule_tags: Optional[Dict[str, str]] = None,
145
+ framework: str = "NIST800-53R5",
146
+ ) -> List[str]:
147
+ """
148
+ Map an AWS Config rule to control IDs using multiple strategies.
149
+
150
+ Priority order:
151
+ 1. Framework-specific mappings (conformance pack)
152
+ 2. Rule tags (ControlID tag)
153
+ 3. Pattern matching in rule name
154
+ 4. Pattern matching in rule description
155
+
156
+ :param str rule_name: Config rule name
157
+ :param Optional[str] rule_description: Config rule description
158
+ :param Optional[Dict[str, str]] rule_tags: Config rule tags
159
+ :param str framework: Target framework
160
+ :return: List of mapped control IDs
161
+ :rtype: List[str]
162
+ """
163
+ control_ids = []
164
+
165
+ # Strategy 1: Check framework-specific mappings
166
+ framework_mappings = get_control_mappings_for_framework(framework)
167
+ if rule_name in framework_mappings:
168
+ control_ids.extend(framework_mappings[rule_name])
169
+ logger.debug(f"Rule '{rule_name}' mapped to controls via framework mappings: {control_ids}")
170
+
171
+ # Strategy 2: Check rule tags
172
+ if rule_tags:
173
+ tag_control_ids = extract_control_ids_from_tags(rule_tags)
174
+ if tag_control_ids:
175
+ control_ids.extend(tag_control_ids)
176
+ logger.debug(f"Rule '{rule_name}' mapped to controls via tags: {tag_control_ids}")
177
+
178
+ # Strategy 3: Pattern matching in rule name
179
+ if not control_ids:
180
+ name_control_ids = extract_control_ids_from_rule_name(rule_name)
181
+ if name_control_ids:
182
+ control_ids.extend(name_control_ids)
183
+ logger.debug(f"Rule '{rule_name}' mapped to controls via name pattern: {name_control_ids}")
184
+
185
+ # Strategy 4: Pattern matching in rule description
186
+ if not control_ids and rule_description:
187
+ desc_control_ids = extract_control_ids_from_rule_name(rule_description)
188
+ if desc_control_ids:
189
+ control_ids.extend(desc_control_ids)
190
+ logger.debug(f"Rule '{rule_name}' mapped to controls via description pattern: {desc_control_ids}")
191
+
192
+ # Remove duplicates and sort
193
+ control_ids = sorted(set(control_ids))
194
+
195
+ if not control_ids:
196
+ logger.debug(f"Rule '{rule_name}' could not be mapped to any controls")
197
+
198
+ return control_ids
@@ -0,0 +1,439 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Control Compliance Analyzer for AWS Audit Manager
5
+
6
+ This module provides enhanced control pass/fail determination based on evidence insights
7
+ from AWS Audit Manager. It implements the logic for determining control compliance status
8
+ based on compliant, non-compliant, and inconclusive evidence counts.
9
+ """
10
+
11
+ import logging
12
+ from dataclasses import dataclass, field
13
+ from enum import Enum
14
+ from typing import Any, Dict, List, Optional, Tuple
15
+
16
+ logger = logging.getLogger("regscale")
17
+
18
+ # Constants
19
+ AWS_AUDIT_MANAGER_INSIGHTS_SOURCE = "AWS Audit Manager Insights"
20
+
21
+
22
+ class ComplianceStatus(Enum):
23
+ """Enumeration of possible compliance statuses."""
24
+
25
+ PASS = "PASS"
26
+ FAIL = "FAIL"
27
+ INCONCLUSIVE = "INCONCLUSIVE"
28
+ NO_DATA = "NO_DATA"
29
+ NOT_APPLICABLE = "NOT_APPLICABLE"
30
+
31
+
32
+ class EvidenceType(Enum):
33
+ """Types of evidence compliance checks."""
34
+
35
+ COMPLIANT = "COMPLIANT"
36
+ NON_COMPLIANT = "NON_COMPLIANT"
37
+ INCONCLUSIVE = "INCONCLUSIVE"
38
+ NOT_APPLICABLE = "NOT_APPLICABLE"
39
+
40
+
41
+ @dataclass
42
+ class EvidenceInsight:
43
+ """Represents a single piece of evidence with its compliance status."""
44
+
45
+ evidence_id: str
46
+ source: str
47
+ compliance_check: str
48
+ resource_arn: Optional[str] = None
49
+ timestamp: Optional[str] = None
50
+ attributes: Dict[str, Any] = field(default_factory=dict)
51
+ confidence: float = 1.0
52
+
53
+
54
+ @dataclass
55
+ class ComplianceAnalysis:
56
+ """Results of control compliance analysis."""
57
+
58
+ control_id: str
59
+ compliance_status: ComplianceStatus
60
+ compliant_evidence_count: int
61
+ noncompliant_evidence_count: int
62
+ inconclusive_evidence_count: int
63
+ not_applicable_count: int
64
+ total_evidence_count: int
65
+ compliance_score: float
66
+ confidence_level: float
67
+ reasoning: str
68
+ evidence_sources: List[str]
69
+ details: Dict[str, Any] = field(default_factory=dict)
70
+
71
+
72
+ class ControlComplianceAnalyzer:
73
+ """
74
+ Analyzes control compliance based on evidence insights from AWS Audit Manager.
75
+
76
+ This class implements the control pass/fail determination logic based on the
77
+ AWS Audit Manager documentation:
78
+ - FAIL: noncompliantEvidenceCount > 0 (any non-compliance fails the control)
79
+ - PASS: compliantEvidenceCount > 0 AND noncompliantEvidenceCount = 0
80
+ (inconclusive evidence is allowed and doesn't prevent passing)
81
+ - INCONCLUSIVE: Only inconclusiveEvidenceCount > 0 (no compliant/non-compliant evidence)
82
+ - NOT_APPLICABLE: Only notApplicableCount > 0
83
+ - NO DATA: All counts = 0
84
+
85
+ Note: Inconclusive evidence does not prevent a control from passing as long as there
86
+ is at least one piece of compliant evidence and no non-compliant evidence.
87
+ """
88
+
89
+ # Evidence source confidence mapping
90
+ EVIDENCE_CONFIDENCE_MAP = {
91
+ "AWS Security Hub": 0.95,
92
+ "AWS Config": 0.95,
93
+ "AWS CloudTrail": 0.75,
94
+ "AWS Audit Manager": 0.85,
95
+ "Manual": 0.60,
96
+ "Unknown": 0.50,
97
+ }
98
+
99
+ def __init__(self, control_id: str):
100
+ """
101
+ Initialize the analyzer for a specific control.
102
+
103
+ :param str control_id: The control identifier (e.g., "AC-2", "CC1.1")
104
+ """
105
+ self.control_id = control_id
106
+ self.evidence_insights: List[EvidenceInsight] = []
107
+ self._compliant_count = 0
108
+ self._noncompliant_count = 0
109
+ self._inconclusive_count = 0
110
+ self._not_applicable_count = 0
111
+
112
+ def add_evidence_insight(self, evidence_data: Dict[str, Any]) -> None:
113
+ """
114
+ Add evidence from AWS Audit Manager to the analysis.
115
+
116
+ :param Dict[str, Any] evidence_data: Evidence data from AWS Audit Manager
117
+ """
118
+ # Normalize the compliance check value
119
+ compliance_check = self._normalize_compliance_check(evidence_data.get("complianceCheck", ""))
120
+
121
+ # Create evidence insight
122
+ insight = EvidenceInsight(
123
+ evidence_id=evidence_data.get("id", ""),
124
+ source=evidence_data.get("dataSource", "AWS Audit Manager"),
125
+ compliance_check=compliance_check,
126
+ resource_arn=evidence_data.get("resourceArn"),
127
+ timestamp=evidence_data.get("time"),
128
+ attributes=evidence_data.get("attributes", {}),
129
+ confidence=self._get_evidence_confidence(evidence_data.get("dataSource", "Unknown")),
130
+ )
131
+
132
+ self.evidence_insights.append(insight)
133
+
134
+ # Update counts
135
+ self._update_evidence_counts(compliance_check)
136
+
137
+ def add_evidence_from_insights_api(self, insights_data: Dict[str, Any]) -> None:
138
+ """
139
+ Add evidence from AWS Audit Manager Control Insights API.
140
+
141
+ :param Dict[str, Any] insights_data: Control insights data from API
142
+ """
143
+ # Extract evidence counts from insights API response
144
+ evidence_insights = insights_data.get("evidenceInsights", {})
145
+
146
+ compliant_count = evidence_insights.get("compliantEvidenceCount", 0)
147
+ noncompliant_count = evidence_insights.get("noncompliantEvidenceCount", 0)
148
+ inconclusive_count = evidence_insights.get("inconclusiveEvidenceCount", 0)
149
+
150
+ # Create synthetic evidence insights for counted evidence
151
+ for _ in range(compliant_count):
152
+ self._compliant_count += 1
153
+ self.evidence_insights.append(
154
+ EvidenceInsight(
155
+ evidence_id=f"insights-compliant-{self._compliant_count}",
156
+ source=AWS_AUDIT_MANAGER_INSIGHTS_SOURCE,
157
+ compliance_check="COMPLIANT",
158
+ confidence=0.9,
159
+ )
160
+ )
161
+
162
+ for _ in range(noncompliant_count):
163
+ self._noncompliant_count += 1
164
+ self.evidence_insights.append(
165
+ EvidenceInsight(
166
+ evidence_id=f"insights-noncompliant-{self._noncompliant_count}",
167
+ source=AWS_AUDIT_MANAGER_INSIGHTS_SOURCE,
168
+ compliance_check="NON_COMPLIANT",
169
+ confidence=0.9,
170
+ )
171
+ )
172
+
173
+ for _ in range(inconclusive_count):
174
+ self._inconclusive_count += 1
175
+ self.evidence_insights.append(
176
+ EvidenceInsight(
177
+ evidence_id=f"insights-inconclusive-{self._inconclusive_count}",
178
+ source=AWS_AUDIT_MANAGER_INSIGHTS_SOURCE,
179
+ compliance_check="INCONCLUSIVE",
180
+ confidence=0.7,
181
+ )
182
+ )
183
+
184
+ def determine_control_status(self) -> Tuple[str, Dict[str, Any]]:
185
+ """
186
+ Determine control pass/fail based on evidence counts.
187
+
188
+ This implements the AWS Audit Manager logic:
189
+ - FAIL: noncompliantEvidenceCount > 0 (any non-compliance = fail)
190
+ - PASS: compliantEvidenceCount > 0 AND noncompliantEvidenceCount = 0
191
+ (inconclusive evidence is allowed and doesn't prevent passing)
192
+ - INCONCLUSIVE: Only inconclusiveEvidenceCount > 0 (no compliant or non-compliant)
193
+ - NOT_APPLICABLE: Only notApplicableCount > 0
194
+ - NO DATA: All counts = 0
195
+
196
+ :return: Tuple of (status, details)
197
+ :rtype: Tuple[str, Dict[str, Any]]
198
+ """
199
+ # Check for failures first (any non-compliant evidence = FAIL)
200
+ if self._noncompliant_count > 0:
201
+ return ComplianceStatus.FAIL.value, {
202
+ "reason": "Evidence indicates non-compliance",
203
+ "noncompliant_count": self._noncompliant_count,
204
+ "compliant_count": self._compliant_count,
205
+ "inconclusive_count": self._inconclusive_count,
206
+ "not_applicable_count": self._not_applicable_count,
207
+ }
208
+
209
+ # Check for pass (compliant evidence with no non-compliant, inconclusive evidence allowed)
210
+ if self._compliant_count > 0 and self._noncompliant_count == 0:
211
+ # Determine the appropriate reason message
212
+ if self._inconclusive_count > 0:
213
+ reason = "Compliant evidence found with no non-compliance (some evidence inconclusive)"
214
+ else:
215
+ reason = "All evidence indicates compliance"
216
+
217
+ return ComplianceStatus.PASS.value, {
218
+ "reason": reason,
219
+ "compliant_count": self._compliant_count,
220
+ "inconclusive_count": self._inconclusive_count,
221
+ "not_applicable_count": self._not_applicable_count,
222
+ }
223
+
224
+ # Check for inconclusive (only inconclusive evidence)
225
+ if self._inconclusive_count > 0 and self._compliant_count == 0 and self._noncompliant_count == 0:
226
+ return ComplianceStatus.INCONCLUSIVE.value, {
227
+ "reason": "Only inconclusive evidence available",
228
+ "inconclusive_count": self._inconclusive_count,
229
+ "not_applicable_count": self._not_applicable_count,
230
+ }
231
+
232
+ # Check for not applicable (only not applicable evidence)
233
+ if (
234
+ self._not_applicable_count > 0
235
+ and self._compliant_count == 0
236
+ and self._noncompliant_count == 0
237
+ and self._inconclusive_count == 0
238
+ ):
239
+ return ComplianceStatus.NOT_APPLICABLE.value, {
240
+ "reason": "Evidence is not applicable to this control",
241
+ "not_applicable_count": self._not_applicable_count,
242
+ }
243
+
244
+ # No data available
245
+ return ComplianceStatus.NO_DATA.value, {
246
+ "reason": "No evidence available for assessment",
247
+ "total_evidence_checked": len(self.evidence_insights),
248
+ }
249
+
250
+ def get_compliance_analysis(self) -> ComplianceAnalysis:
251
+ """
252
+ Get comprehensive compliance analysis for the control.
253
+
254
+ :return: Detailed compliance analysis
255
+ :rtype: ComplianceAnalysis
256
+ """
257
+ status_str, details = self.determine_control_status()
258
+ status = ComplianceStatus(status_str)
259
+
260
+ # Calculate compliance score
261
+ compliance_score = self._calculate_compliance_score()
262
+
263
+ # Calculate confidence level
264
+ confidence_level = self._calculate_confidence_level()
265
+
266
+ # Get unique evidence sources
267
+ evidence_sources = list({insight.source for insight in self.evidence_insights})
268
+
269
+ return ComplianceAnalysis(
270
+ control_id=self.control_id,
271
+ compliance_status=status,
272
+ compliant_evidence_count=self._compliant_count,
273
+ noncompliant_evidence_count=self._noncompliant_count,
274
+ inconclusive_evidence_count=self._inconclusive_count,
275
+ not_applicable_count=self._not_applicable_count,
276
+ total_evidence_count=len(self.evidence_insights),
277
+ compliance_score=compliance_score,
278
+ confidence_level=confidence_level,
279
+ reasoning=details.get("reason", ""),
280
+ evidence_sources=evidence_sources,
281
+ details=details,
282
+ )
283
+
284
+ def get_compliance_score(self) -> float:
285
+ """
286
+ Calculate compliance score (0-1) based on evidence.
287
+
288
+ :return: Compliance score between 0 and 1
289
+ :rtype: float
290
+ """
291
+ return self._calculate_compliance_score()
292
+
293
+ def get_confidence_level(self) -> float:
294
+ """
295
+ Calculate confidence level based on evidence sources and quantity.
296
+
297
+ :return: Confidence level between 0 and 1
298
+ :rtype: float
299
+ """
300
+ return self._calculate_confidence_level()
301
+
302
+ def get_compliance_setting_note(self) -> str:
303
+ """
304
+ Get a note about how the status will be mapped based on compliance settings.
305
+
306
+ This is informational - the actual mapping is done by ComplianceIntegration
307
+ based on the security plan's compliance settings.
308
+
309
+ :return: Informational note about status mapping
310
+ :rtype: str
311
+ """
312
+ status_str, details = self.determine_control_status()
313
+
314
+ if status_str == "PASS":
315
+ note = (
316
+ "PASS status will be mapped to compliance-specific value: "
317
+ "DoD/RMF → 'Implemented', FedRAMP → 'Fully Implemented', "
318
+ "Default → 'Fully Implemented'"
319
+ )
320
+ # Add clarification if there's inconclusive evidence
321
+ if details.get("inconclusive_count", 0) > 0:
322
+ note += " (includes inconclusive evidence that doesn't affect passing)"
323
+ return note
324
+ elif status_str == "FAIL":
325
+ return (
326
+ "FAIL status will be mapped to compliance-specific value: "
327
+ "DoD/RMF → 'Not Implemented' or 'Planned', FedRAMP → 'In Remediation' or 'Partially Implemented', "
328
+ "Default → 'In Remediation'"
329
+ )
330
+ elif status_str == "NOT_APPLICABLE":
331
+ return "NOT_APPLICABLE status will be mapped to 'Not Applicable' across all compliance settings"
332
+ else:
333
+ return "Status cannot be determined - control implementation will not be updated"
334
+
335
+ def _normalize_compliance_check(self, compliance_check: str) -> str:
336
+ """
337
+ Normalize compliance check values from various AWS services.
338
+
339
+ :param str compliance_check: Raw compliance check value
340
+ :return: Normalized compliance status
341
+ :rtype: str
342
+ """
343
+ if not compliance_check:
344
+ return "INCONCLUSIVE"
345
+
346
+ check_upper = compliance_check.upper()
347
+
348
+ # Map success values
349
+ if check_upper in ["COMPLIANT", "PASS", "PASSED", "SUCCESS"]:
350
+ return "COMPLIANT"
351
+
352
+ # Map failure values
353
+ if check_upper in ["NON_COMPLIANT", "NON-COMPLIANT", "FAIL", "FAILED", "FAILURE"]:
354
+ return "NON_COMPLIANT"
355
+
356
+ # Map not applicable
357
+ if check_upper in ["NOT_APPLICABLE", "NOT-APPLICABLE", "N/A", "NA"]:
358
+ return "NOT_APPLICABLE"
359
+
360
+ # Default to inconclusive
361
+ return "INCONCLUSIVE"
362
+
363
+ def _update_evidence_counts(self, compliance_check: str) -> None:
364
+ """
365
+ Update evidence counts based on compliance check value.
366
+
367
+ :param str compliance_check: Normalized compliance check value
368
+ """
369
+ if compliance_check == "COMPLIANT":
370
+ self._compliant_count += 1
371
+ elif compliance_check == "NON_COMPLIANT":
372
+ self._noncompliant_count += 1
373
+ elif compliance_check == "NOT_APPLICABLE":
374
+ self._not_applicable_count += 1
375
+ else:
376
+ self._inconclusive_count += 1
377
+
378
+ def _get_evidence_confidence(self, source: str) -> float:
379
+ """
380
+ Get confidence level for an evidence source.
381
+
382
+ :param str source: Evidence source name
383
+ :return: Confidence level between 0 and 1
384
+ :rtype: float
385
+ """
386
+ return self.EVIDENCE_CONFIDENCE_MAP.get(source, 0.5)
387
+
388
+ def _calculate_compliance_score(self) -> float:
389
+ """
390
+ Calculate compliance score based on evidence.
391
+
392
+ Score calculation:
393
+ - 1.0: All evidence is compliant
394
+ - 0.0: Any evidence is non-compliant
395
+ - 0.5: Only inconclusive evidence
396
+ - Weighted average based on evidence counts
397
+
398
+ :return: Compliance score between 0 and 1
399
+ :rtype: float
400
+ """
401
+ total_evidence = self._compliant_count + self._noncompliant_count + self._inconclusive_count
402
+
403
+ if total_evidence == 0:
404
+ return 0.0
405
+
406
+ # If any non-compliant evidence, score is 0
407
+ if self._noncompliant_count > 0:
408
+ return 0.0
409
+
410
+ # If all compliant, score is 1
411
+ if self._compliant_count > 0 and self._inconclusive_count == 0:
412
+ return 1.0
413
+
414
+ # Mixed compliant and inconclusive
415
+ if self._compliant_count > 0:
416
+ return self._compliant_count / total_evidence
417
+
418
+ # Only inconclusive
419
+ return 0.5
420
+
421
+ def _calculate_confidence_level(self) -> float:
422
+ """
423
+ Calculate confidence level based on evidence sources and quantity.
424
+
425
+ :return: Confidence level between 0 and 1
426
+ :rtype: float
427
+ """
428
+ if not self.evidence_insights:
429
+ return 0.0
430
+
431
+ # Calculate weighted average confidence
432
+ total_confidence = sum(insight.confidence for insight in self.evidence_insights)
433
+ avg_confidence = total_confidence / len(self.evidence_insights)
434
+
435
+ # Adjust for evidence quantity (more evidence = higher confidence)
436
+ quantity_factor = min(1.0, len(self.evidence_insights) / 10.0)
437
+
438
+ # Combine average confidence with quantity factor
439
+ return (avg_confidence * 0.7) + (quantity_factor * 0.3)