From 66a04b55472da8dc5ab747f73e2246d1229a6ddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Fri, 7 Nov 2025 16:59:40 +0100 Subject: [PATCH 1/3] feat(aws): improve `nist_csf_2.0` mapping (#9189) --- dashboard/compliance/nist_csf_2_0_aws.py | 24 + prowler/compliance/aws/nist_csf_2.0_aws.json | 1089 ++++++++++-------- 2 files changed, 653 insertions(+), 460 deletions(-) create mode 100644 dashboard/compliance/nist_csf_2_0_aws.py diff --git a/dashboard/compliance/nist_csf_2_0_aws.py b/dashboard/compliance/nist_csf_2_0_aws.py new file mode 100644 index 0000000000..07b956b104 --- /dev/null +++ b/dashboard/compliance/nist_csf_2_0_aws.py @@ -0,0 +1,24 @@ +import warnings + +from dashboard.common_methods import get_section_containers_format3 + +warnings.filterwarnings("ignore") + + +def get_table(data): + aux = data[ + [ + "REQUIREMENTS_ID", + "REQUIREMENTS_ATTRIBUTES_SECTION", + "REQUIREMENTS_DESCRIPTION", + "CHECKID", + "STATUS", + "REGION", + "ACCOUNTID", + "RESOURCEID", + ] + ].copy() + + return get_section_containers_format3( + aux, "REQUIREMENTS_ATTRIBUTES_SECTION", "REQUIREMENTS_ID" + ) diff --git a/prowler/compliance/aws/nist_csf_2.0_aws.json b/prowler/compliance/aws/nist_csf_2.0_aws.json index 3430763879..591e558bb1 100644 --- a/prowler/compliance/aws/nist_csf_2.0_aws.json +++ b/prowler/compliance/aws/nist_csf_2.0_aws.json @@ -17,16 +17,12 @@ "Service": "aws" } ], - "Checks": [ - "cloudtrail_multi_region_enabled", - "config_recorder_all_regions_enabled", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "oc_2", "Name": "GV.OC-2", - "Description": "Legal, regulatory, and contractual requirements regarding cybersecurity — including privacy and civil liberties obligations — are understood and managed.", + "Description": "Legal, regulatory, and contractual requirements regarding cybersecurity including privacy and civil liberties obligations are understood and managed.", "Attributes": [ { "ItemId": "oc_2", @@ -35,12 +31,7 @@ "Service": "aws" } ], - "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "config_recorder_all_regions_enabled", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "oc_3", @@ -54,10 +45,7 @@ "Service": "aws" } ], - "Checks": [ - "config_recorder_all_regions_enabled", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "rm_1", @@ -72,10 +60,18 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", "config_recorder_all_regions_enabled", - "guardduty_is_enabled", - "securityhub_enabled" + "config_recorder_using_aws_service_role", + "organizations_account_part_of_organizations", + "organizations_scp_check_deny_regions", + "organizations_tags_policies_enabled_and_attached", + "organizations_delegated_administrators", + "organizations_opt_out_ai_services_policy", + "trustedadvisor_premium_support_plan_subscribed", + "trustedadvisor_errors_and_warnings", + "securityhub_enabled", + "wellarchitected_workload_no_high_or_medium_risks", + "servicecatalog_portfolio_shared_within_organization_only" ] }, { @@ -90,11 +86,7 @@ "Service": "aws" } ], - "Checks": [ - "cloudtrail_multi_region_enabled", - "config_recorder_all_regions_enabled", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "rm_3", @@ -108,11 +100,7 @@ "Service": "aws" } ], - "Checks": [ - "config_recorder_all_regions_enabled", - "guardduty_is_enabled", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "sc_1", @@ -127,12 +115,11 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "config_recorder_all_regions_enabled", - "guardduty_is_enabled", - "securityhub_enabled" + "codeartifact_packages_external_public_publishing_disabled", + "codebuild_project_uses_allowed_github_organizations", + "codebuild_project_source_repo_url_no_sensitive_credentials", + "codebuild_project_user_controlled_buildspec", + "codebuild_project_no_secrets_in_variables" ] }, { @@ -148,9 +135,8 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", - "config_recorder_all_regions_enabled", - "securityhub_enabled" + "codeartifact_packages_external_public_publishing_disabled", + "codebuild_project_uses_allowed_github_organizations" ] }, { @@ -166,12 +152,10 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "config_recorder_all_regions_enabled", - "guardduty_is_enabled", - "securityhub_enabled" + "codeartifact_packages_external_public_publishing_disabled", + "codebuild_project_user_controlled_buildspec", + "codebuild_project_source_repo_url_no_sensitive_credentials", + "codebuild_project_uses_allowed_github_organizations" ] }, { @@ -186,11 +170,7 @@ "Service": "aws" } ], - "Checks": [ - "cloudtrail_multi_region_enabled", - "config_recorder_all_regions_enabled", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "rr_1", @@ -205,11 +185,27 @@ } ], "Checks": [ - "iam_aws_attached_policy_no_administrative_privileges", - "iam_customer_attached_policy_no_administrative_privileges", + "organizations_account_part_of_organizations", + "organizations_delegated_administrators", + "organizations_scp_check_deny_regions", + "organizations_opt_out_ai_services_policy", + "vpc_endpoint_connections_trust_boundaries", + "iam_role_administratoraccess_policy", + "iam_group_administrator_access_policy", + "iam_user_administrator_access_policy", + "iam_policy_attached_only_to_group_or_roles", + "iam_policy_allows_privilege_escalation", + "iam_inline_policy_allows_privilege_escalation", + "iam_inline_policy_no_full_access_to_kms", "iam_inline_policy_no_administrative_privileges", - "iam_no_root_access_key", - "cloudtrail_multi_region_enabled" + "iam_policy_no_full_access_to_cloudtrail", + "iam_policy_no_full_access_to_kms", + "iam_customer_attached_policy_no_administrative_privileges", + "iam_administrator_access_with_mfa", + "iam_root_mfa_enabled", + "iam_root_credentials_management_enabled", + "iam_no_custom_policy_permissive_role_assumption", + "iam_aws_attached_policy_no_administrative_privileges" ] }, { @@ -225,10 +221,21 @@ } ], "Checks": [ - "iam_aws_attached_policy_no_administrative_privileges", + "iam_group_administrator_access_policy", + "iam_role_administratoraccess_policy", + "iam_user_administrator_access_policy", "iam_customer_attached_policy_no_administrative_privileges", "iam_inline_policy_no_administrative_privileges", - "cloudtrail_multi_region_enabled" + "iam_policy_allows_privilege_escalation", + "iam_inline_policy_allows_privilege_escalation", + "iam_policy_attached_only_to_group_or_roles", + "iam_aws_attached_policy_no_administrative_privileges", + "organizations_account_part_of_organizations", + "organizations_delegated_administrators", + "organizations_opt_out_ai_services_policy", + "organizations_scp_check_deny_regions", + "iam_root_credentials_management_enabled", + "iam_role_cross_account_readonlyaccess_policy" ] }, { @@ -243,13 +250,7 @@ "Service": "aws" } ], - "Checks": [ - "iam_root_hardware_mfa_enabled", - "iam_root_mfa_enabled", - "iam_no_root_access_key", - "cloudtrail_multi_region_enabled", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "po_1", @@ -264,10 +265,21 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", - "config_recorder_all_regions_enabled", - "iam_password_policy_reuse_24", - "securityhub_enabled" + "iam_policy_attached_only_to_group_or_roles", + "iam_policy_allows_privilege_escalation", + "iam_inline_policy_allows_privilege_escalation", + "iam_inline_policy_no_full_access_to_cloudtrail", + "iam_inline_policy_no_full_access_to_kms", + "iam_inline_policy_no_administrative_privileges", + "iam_policy_no_full_access_to_cloudtrail", + "iam_policy_no_full_access_to_kms", + "iam_customer_attached_policy_no_administrative_privileges", + "iam_customer_unattached_policy_no_administrative_privileges", + "iam_group_administrator_access_policy", + "iam_user_administrator_access_policy", + "iam_role_administratoraccess_policy", + "iam_aws_attached_policy_no_administrative_privileges", + "iam_no_custom_policy_permissive_role_assumption" ] }, { @@ -282,12 +294,7 @@ "Service": "aws" } ], - "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "config_recorder_all_regions_enabled", - "cloudwatch_log_metric_filter_policy_changes" - ] + "Checks": [] }, { "Id": "po_3", @@ -302,10 +309,19 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_cloudwatch_logging_enabled", + "securityhub_enabled", "config_recorder_all_regions_enabled", - "securityhub_enabled" + "organizations_account_part_of_organizations", + "organizations_opt_out_ai_services_policy", + "trustedadvisor_premium_support_plan_subscribed", + "trustedadvisor_errors_and_warnings", + "accessanalyzer_enabled", + "vpc_endpoint_connections_trust_boundaries", + "organizations_scp_check_deny_regions", + "servicecatalog_portfolio_shared_within_organization_only", + "wellarchitected_workload_no_high_or_medium_risks", + "organizations_delegated_administrators", + "organizations_tags_policies_enabled_and_attached" ] }, { @@ -321,10 +337,21 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", "config_recorder_all_regions_enabled", - "guardduty_is_enabled", - "securityhub_enabled" + "config_recorder_using_aws_service_role", + "accessanalyzer_enabled", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_threat_detection_privilege_escalation", + "cloudtrail_threat_detection_llm_jacking", + "cloudtrail_multi_region_enabled_logging_management_events", + "organizations_scp_check_deny_regions", + "organizations_account_part_of_organizations", + "trustedadvisor_premium_support_plan_subscribed", + "trustedadvisor_errors_and_warnings", + "securityhub_enabled", + "vpc_flow_logs_enabled", + "iam_root_mfa_enabled", + "iam_root_credentials_management_enabled" ] }, { @@ -340,11 +367,11 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "config_recorder_all_regions_enabled", - "securityhub_enabled" + "codeartifact_packages_external_public_publishing_disabled", + "codebuild_project_uses_allowed_github_organizations", + "codebuild_project_source_repo_url_no_sensitive_credentials", + "codebuild_project_user_controlled_buildspec", + "codebuild_project_no_secrets_in_variables" ] }, { @@ -359,12 +386,7 @@ "Service": "aws" } ], - "Checks": [ - "cloudtrail_multi_region_enabled", - "config_recorder_all_regions_enabled", - "guardduty_is_enabled", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "ov_2", @@ -379,11 +401,13 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "config_recorder_all_regions_enabled", - "guardduty_is_enabled", - "securityhub_enabled" + "accessanalyzer_enabled_without_findings", + "wellarchitected_workload_no_high_or_medium_risks", + "securityhub_enabled", + "inspector2_active_findings_exist", + "accessanalyzer_enabled", + "guardduty_no_high_severity_findings", + "trustedadvisor_errors_and_warnings" ] }, { @@ -399,10 +423,25 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", "config_recorder_all_regions_enabled", - "guardduty_is_enabled", - "securityhub_enabled" + "config_recorder_using_aws_service_role", + "accessanalyzer_enabled", + "securityhub_enabled", + "trustedadvisor_premium_support_plan_subscribed", + "trustedadvisor_errors_and_warnings", + "cloudtrail_multi_region_enabled_logging_management_events", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_threat_detection_privilege_escalation", + "cloudtrail_threat_detection_llm_jacking", + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", + "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", + "cloudwatch_log_metric_filter_policy_changes", + "cloudwatch_log_metric_filter_security_group_changes", + "cloudwatch_log_metric_filter_unauthorized_api_calls", + "vpc_endpoint_connections_trust_boundaries", + "organizations_scp_check_deny_regions", + "organizations_tags_policies_enabled_and_attached", + "organizations_delegated_administrators" ] }, { @@ -417,12 +456,7 @@ "Service": "aws" } ], - "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "config_recorder_all_regions_enabled", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "am_1", @@ -436,10 +470,7 @@ "Service": "aws" } ], - "Checks": [ - "config_recorder_all_regions_enabled", - "ec2_instance_managed_by_ssm" - ] + "Checks": [] }, { "Id": "am_2", @@ -453,10 +484,7 @@ "Service": "aws" } ], - "Checks": [ - "ec2_instance_managed_by_ssm", - "ssm_managed_compliant_patching" - ] + "Checks": [] }, { "Id": "am_3", @@ -470,15 +498,7 @@ "Service": "aws" } ], - "Checks": [ - "apigateway_restapi_logging_enabled", - "cloudtrail_multi_region_enabled", - "elbv2_logging_enabled", - "elb_logging_enabled", - "redshift_cluster_audit_logging", - "s3_bucket_server_access_logging_enabled", - "vpc_flow_logs_enabled" - ] + "Checks": [] }, { "Id": "am_5", @@ -506,7 +526,14 @@ "Service": "iam" } ], - "Checks": [] + "Checks": [ + "organizations_account_part_of_organizations", + "organizations_delegated_administrators", + "iam_group_administrator_access_policy", + "iam_policy_attached_only_to_group_or_roles", + "iam_user_administrator_access_policy", + "iam_role_cross_account_readonlyaccess_policy" + ] }, { "Id": "be_5", @@ -521,10 +548,32 @@ } ], "Checks": [ - "elbv2_deletion_protection", - "rds_instance_backup_enabled", - "rds_instance_multi_az", - "s3_bucket_object_versioning" + "drs_job_exist", + "backup_vaults_exist", + "backup_vaults_encrypted", + "backup_reportplans_exist", + "rds_cluster_protected_by_backup_plan", + "rds_instance_protected_by_backup_plan", + "dynamodb_table_protected_by_backup_plan", + "s3_bucket_cross_region_replication", + "autoscaling_group_capacity_rebalance_enabled", + "autoscaling_group_multiple_az", + "vpc_endpoint_multi_az_enabled", + "directconnect_connection_redundancy", + "directconnect_virtual_interface_redundancy", + "vpc_subnet_different_az", + "vpc_subnet_no_public_ip_by_default", + "vpc_subnet_separate_private_public", + "elbv2_cross_zone_load_balancing_enabled", + "elb_cross_zone_load_balancing_enabled", + "efs_encryption_at_rest_enabled", + "firehose_stream_encrypted_at_rest", + "glue_ml_transform_encrypted_at_rest", + "kafka_cluster_encryption_at_rest_uses_cmk", + "kinesis_stream_encrypted_at_rest", + "opensearch_service_domains_encryption_at_rest_enabled", + "redshift_cluster_encrypted_at_rest", + "sns_topics_kms_encryption_at_rest_enabled" ] }, { @@ -540,9 +589,27 @@ } ], "Checks": [ - "guardduty_is_enabled", - "securityhub_enabled", - "ssm_managed_compliant_patching" + "acm_certificates_with_secure_key_algorithms", + "acm_certificates_expiration_check", + "awslambda_function_no_secrets_in_variables", + "awslambda_function_no_secrets_in_code", + "cloudtrail_threat_detection_llm_jacking", + "cloudtrail_threat_detection_privilege_escalation", + "ecr_repositories_not_publicly_accessible", + "rds_snapshots_public_access", + "route53_dangling_ip_subdomain_takeover", + "secretsmanager_not_publicly_accessible", + "s3_bucket_public_access", + "s3_bucket_public_write_acl", + "s3_bucket_cross_account_access", + "ssm_document_secrets", + "kms_cmk_not_deleted_unintentionally", + "kms_cmk_not_multi_region", + "glacier_vaults_policy_public_access", + "iam_policy_allows_privilege_escalation", + "iam_inline_policy_allows_privilege_escalation", + "ssm_documents_set_as_public", + "s3_bucket_shadow_resource_vulnerability" ] }, { @@ -557,10 +624,7 @@ "Service": "aws" } ], - "Checks": [ - "guardduty_is_enabled", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "ra_3", @@ -575,8 +639,9 @@ } ], "Checks": [ - "guardduty_is_enabled", - "securityhub_enabled" + "cloudtrail_threat_detection_llm_jacking", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_threat_detection_privilege_escalation" ] }, { @@ -592,26 +657,22 @@ } ], "Checks": [ - "cloudtrail_cloudwatch_logging_enabled", - "cloudwatch_changes_to_network_acls_alarm_configured", - "cloudwatch_changes_to_network_gateways_alarm_configured", - "cloudwatch_changes_to_network_route_tables_alarm_configured", - "cloudwatch_changes_to_vpcs_alarm_configured", - "config_recorder_all_regions_enabled", - "ec2_instance_imdsv2_enabled", - "guardduty_is_enabled", - "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes", + "cloudtrail_threat_detection_llm_jacking", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_threat_detection_privilege_escalation", + "cloudwatch_log_metric_filter_authentication_failures", + "cloudwatch_log_metric_filter_unauthorized_api_calls", "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", - "cloudwatch_log_metric_filter_authentication_failures", - "cloudwatch_log_metric_filter_sign_in_without_mfa", - "cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk", - "cloudwatch_log_metric_filter_policy_changes", - "cloudwatch_log_metric_filter_root_usage", - "cloudwatch_log_metric_filter_security_group_changes", - "cloudwatch_log_metric_filter_unauthorized_api_calls", - "rds_instance_enhanced_monitoring_enabled", - "securityhub_enabled" + "cloudwatch_changes_to_network_gateways_alarm_configured", + "cloudwatch_changes_to_network_acls_alarm_configured", + "cloudwatch_changes_to_vpcs_alarm_configured", + "eventbridge_bus_cross_account_access", + "eventbridge_bus_exposed", + "route53_dangling_ip_subdomain_takeover", + "vpc_endpoint_connections_trust_boundaries", + "vpc_peering_routing_tables_with_least_privilege", + "vpc_endpoint_for_ec2_enabled" ] }, { @@ -626,24 +687,7 @@ "Service": "aws" } ], - "Checks": [ - "cloudtrail_cloudwatch_logging_enabled", - "config_recorder_all_regions_enabled", - "ec2_instance_imdsv2_enabled", - "guardduty_is_enabled", - "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes", - "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", - "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", - "cloudwatch_log_metric_filter_authentication_failures", - "cloudwatch_log_metric_filter_sign_in_without_mfa", - "cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk", - "cloudwatch_log_metric_filter_policy_changes", - "cloudwatch_log_metric_filter_root_usage", - "cloudwatch_log_metric_filter_security_group_changes", - "cloudwatch_log_metric_filter_unauthorized_api_calls", - "rds_instance_enhanced_monitoring_enabled", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "ac_1", @@ -658,15 +702,27 @@ } ], "Checks": [ - "iam_password_policy_reuse_24", + "iam_root_mfa_enabled", + "iam_no_root_access_key", + "iam_user_console_access_unused", + "iam_user_accesskey_unused", + "iam_user_two_active_access_key", + "iam_root_credentials_management_enabled", + "accessanalyzer_enabled", + "cognito_user_pool_mfa_enabled", + "cognito_user_pool_password_policy_symbol", + "cognito_user_pool_password_policy_uppercase", + "cognito_user_pool_password_policy_lowercase", + "cognito_user_pool_password_policy_minimum_length_14", + "iam_policy_allows_privilege_escalation", + "iam_inline_policy_allows_privilege_escalation", + "iam_policy_attached_only_to_group_or_roles", "iam_aws_attached_policy_no_administrative_privileges", "iam_customer_attached_policy_no_administrative_privileges", - "iam_inline_policy_no_administrative_privileges", - "iam_no_root_access_key", - "iam_rotate_access_key_90_days", - "iam_user_accesskey_unused", - "iam_user_console_access_unused", - "secretsmanager_automatic_rotation_enabled" + "iam_group_administrator_access_policy", + "iam_role_administratoraccess_policy", + "iam_policy_no_full_access_to_cloudtrail", + "iam_policy_no_full_access_to_kms" ] }, { @@ -682,24 +738,27 @@ } ], "Checks": [ - "ec2_ebs_public_snapshot", - "ec2_instance_public_ip", - "emr_cluster_master_nodes_no_public_ip", - "iam_root_hardware_mfa_enabled", - "iam_root_mfa_enabled", - "iam_user_mfa_enabled_console_access", - "awslambda_function_not_publicly_accessible", - "awslambda_function_url_public", + "ec2_securitygroup_allow_ingress_from_internet_to_all_ports", + "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22", + "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389", + "ec2_instance_port_ssh_exposed_to_internet", + "ec2_instance_port_rdp_exposed_to_internet", + "ec2_instance_port_telnet_exposed_to_internet", + "ec2_instance_imdsv2_enabled", + "ec2_launch_template_imdsv2_required", + "ec2_instance_managed_by_ssm", + "ssm_managed_compliant_patching", "rds_instance_no_public_access", + "dms_instance_no_public_access", + "ec2_ebs_snapshot_account_block_public_access", + "glacier_vaults_policy_public_access", "rds_snapshots_public_access", "redshift_cluster_public_access", - "s3_bucket_public_access", - "s3_bucket_policy_public_write_access", + "s3_access_point_public_access_block", "s3_account_level_public_access_blocks", - "sagemaker_notebook_instance_without_direct_internet_access_configured", - "ec2_securitygroup_default_restrict_traffic", - "ec2_networkacl_allow_ingress_any_port", - "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22" + "s3_bucket_level_public_access_block", + "s3_bucket_public_access", + "s3_multi_region_access_point_public_access_block" ] }, { @@ -715,12 +774,20 @@ } ], "Checks": [ - "iam_aws_attached_policy_no_administrative_privileges", + "iam_administrator_access_with_mfa", + "iam_group_administrator_access_policy", + "iam_user_administrator_access_policy", + "iam_role_administratoraccess_policy", + "iam_policy_attached_only_to_group_or_roles", + "iam_policy_allows_privilege_escalation", + "iam_inline_policy_allows_privilege_escalation", "iam_customer_attached_policy_no_administrative_privileges", - "iam_inline_policy_no_administrative_privileges", - "iam_no_root_access_key", - "iam_user_accesskey_unused", - "iam_user_console_access_unused" + "iam_customer_unattached_policy_no_administrative_privileges", + "accessanalyzer_enabled_without_findings", + "eventbridge_bus_cross_account_access", + "eventbridge_bus_exposed", + "iam_policy_no_full_access_to_cloudtrail", + "iam_policy_no_full_access_to_kms" ] }, { @@ -736,22 +803,20 @@ } ], "Checks": [ - "acm_certificates_expiration_check", - "ec2_ebs_public_snapshot", - "ec2_instance_public_ip", - "emr_cluster_master_nodes_no_public_ip", - "awslambda_function_not_publicly_accessible", - "awslambda_function_url_public", - "rds_instance_no_public_access", - "rds_snapshots_public_access", - "redshift_cluster_public_access", - "s3_bucket_public_access", - "s3_bucket_policy_public_write_access", - "s3_account_level_public_access_blocks", - "sagemaker_notebook_instance_without_direct_internet_access_configured", + "vpc_subnet_different_az", + "vpc_subnet_separate_private_public", + "vpc_endpoint_connections_trust_boundaries", + "vpc_peering_routing_tables_with_least_privilege", + "ec2_networkacl_unused", "ec2_securitygroup_default_restrict_traffic", - "ec2_networkacl_allow_ingress_any_port", - "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22" + "ec2_securitygroup_not_used", + "ec2_securitygroup_allow_ingress_from_internet_to_all_ports", + "ec2_securitygroup_allow_ingress_from_internet_to_high_risk_tcp_ports", + "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389", + "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mongodb_27017_27018", + "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483", + "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432", + "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601" ] }, { @@ -767,9 +832,20 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", - "redshift_cluster_audit_logging", - "s3_bucket_server_access_logging_enabled" + "iam_root_mfa_enabled", + "iam_no_root_access_key", + "iam_rotate_access_key_90_days", + "iam_user_two_active_access_key", + "iam_user_with_temporary_credentials", + "iam_policy_attached_only_to_group_or_roles", + "iam_group_administrator_access_policy", + "iam_role_cross_account_readonlyaccess_policy", + "iam_policy_allows_privilege_escalation", + "iam_inline_policy_allows_privilege_escalation", + "iam_customer_attached_policy_no_administrative_privileges", + "iam_customer_unattached_policy_no_administrative_privileges", + "accessanalyzer_enabled", + "cognito_user_pool_password_policy_symbol" ] }, { @@ -785,9 +861,25 @@ } ], "Checks": [ - "iam_root_hardware_mfa_enabled", "iam_root_mfa_enabled", - "iam_user_mfa_enabled_console_access" + "iam_user_mfa_enabled_console_access", + "cognito_user_pool_password_policy_symbol", + "cognito_user_pool_password_policy_number", + "cognito_user_pool_password_policy_lowercase", + "cognito_user_pool_password_policy_uppercase", + "cognito_user_pool_password_policy_minimum_length_14", + "cognito_user_pool_mfa_enabled", + "cognito_identity_pool_guest_access_disabled", + "apigatewayv2_api_authorizers_enabled", + "apigateway_restapi_authorizers_enabled", + "apigateway_restapi_public_with_authorizer", + "awslambda_function_not_publicly_accessible", + "awslambda_function_url_public", + "iam_policy_attached_only_to_group_or_roles", + "iam_inline_policy_allows_privilege_escalation", + "iam_inline_policy_no_full_access_to_cloudtrail", + "iam_policy_allows_privilege_escalation", + "iam_check_saml_providers_sts" ] }, { @@ -803,15 +895,29 @@ } ], "Checks": [ + "backup_vaults_encrypted", + "backup_recovery_point_encrypted", "cloudtrail_kms_encryption_enabled", - "ec2_ebs_volume_encryption", - "efs_encryption_at_rest_enabled", - "opensearch_service_domains_encryption_at_rest_enabled", "cloudwatch_log_group_kms_encryption_enabled", - "rds_instance_storage_encrypted", + "s3_bucket_kms_encryption", "s3_bucket_default_encryption", - "sagemaker_notebook_instance_encryption_enabled", - "sns_topics_kms_encryption_at_rest_enabled" + "efs_encryption_at_rest_enabled", + "dynamodb_tables_kms_cmk_encryption_enabled", + "rds_instance_storage_encrypted", + "rds_snapshots_encrypted", + "redshift_cluster_encrypted_at_rest", + "elasticache_redis_cluster_rest_encryption_enabled", + "neptune_cluster_storage_encrypted", + "opensearch_service_domains_encryption_at_rest_enabled", + "opensearch_service_domains_node_to_node_encryption_enabled", + "glue_data_catalogs_metadata_encryption_enabled", + "glue_data_catalogs_connection_passwords_encryption_enabled", + "glue_etl_jobs_amazon_s3_encryption_enabled", + "glue_etl_jobs_cloudwatch_logs_encryption_enabled", + "glue_etl_jobs_job_bookmark_encryption_enabled", + "kinesis_stream_encrypted_at_rest", + "firehose_stream_encrypted_at_rest", + "storagegateway_fileshare_encryption_enabled" ] }, { @@ -827,10 +933,18 @@ } ], "Checks": [ - "acm_certificates_expiration_check", - "elb_ssl_listeners", - "opensearch_service_domains_node_to_node_encryption_enabled", - "s3_bucket_secure_transport_policy" + "elasticache_redis_cluster_in_transit_encryption_enabled", + "cloudfront_distributions_origin_traffic_encrypted", + "cloudfront_distributions_https_enabled", + "s3_bucket_secure_transport_policy", + "rds_instance_transport_encrypted", + "transfer_server_in_transit_encryption_enabled", + "kafka_cluster_in_transit_encryption_enabled", + "kafka_connector_in_transit_encryption_enabled", + "dms_endpoint_ssl_enabled", + "dms_endpoint_redis_in_transit_encryption_enabled", + "opensearch_service_domains_https_communications_enforced", + "opensearch_service_domains_node_to_node_encryption_enabled" ] }, { @@ -846,9 +960,23 @@ } ], "Checks": [ - "ec2_instance_managed_by_ssm", - "ssm_managed_compliant_patching", - "ec2_elastic_ip_unassigned" + "dynamodb_table_deletion_protection_enabled", + "eks_cluster_deletion_protection_enabled", + "rds_cluster_deletion_protection", + "elbv2_deletion_protection", + "cloudtrail_bucket_requires_mfa_delete", + "kms_cmk_not_deleted_unintentionally", + "dlm_ebs_snapshot_lifecycle_policy_exists", + "backup_vaults_encrypted", + "backup_vaults_exist", + "backup_reportplans_exist", + "backup_recovery_point_encrypted", + "rds_instance_backup_enabled", + "rds_cluster_protected_by_backup_plan", + "ec2_ebs_volume_protected_by_backup_plan", + "s3_bucket_lifecycle_enabled", + "s3_bucket_default_encryption", + "s3_bucket_kms_encryption" ] }, { @@ -864,11 +992,28 @@ } ], "Checks": [ - "elbv2_deletion_protection", - "rds_instance_enhanced_monitoring_enabled", - "rds_instance_backup_enabled", + "dynamodb_table_deletion_protection_enabled", + "dynamodb_table_protected_by_backup_plan", + "eks_cluster_deletion_protection_enabled", "rds_instance_multi_az", - "s3_bucket_object_versioning" + "rds_instance_backup_enabled", + "rds_cluster_deletion_protection", + "rds_instance_deletion_protection", + "redshift_cluster_multi_az_enabled", + "dynamodb_table_autoscaling_enabled", + "autoscaling_group_capacity_rebalance_enabled", + "autoscaling_group_multiple_az", + "elb_cross_zone_load_balancing_enabled", + "elbv2_cross_zone_load_balancing_enabled", + "elb_is_in_multiple_az", + "directconnect_connection_redundancy", + "s3_bucket_cross_region_replication", + "s3_bucket_lifecycle_enabled", + "backup_plans_exist", + "backup_recovery_point_encrypted", + "backup_reportplans_exist", + "rds_cluster_protected_by_backup_plan", + "rds_instance_protected_by_backup_plan" ] }, { @@ -884,24 +1029,26 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "ec2_ebs_public_snapshot", - "elbv2_logging_enabled", - "elb_logging_enabled", - "guardduty_is_enabled", - "awslambda_function_url_public", - "rds_instance_no_public_access", - "rds_snapshots_public_access", - "redshift_cluster_public_access", - "s3_bucket_server_access_logging_enabled", "s3_bucket_public_access", - "s3_bucket_policy_public_write_access", - "s3_account_level_public_access_blocks", - "sagemaker_notebook_instance_without_direct_internet_access_configured", - "securityhub_enabled", - "vpc_flow_logs_enabled" + "s3_bucket_public_write_acl", + "s3_bucket_public_list_acl", + "s3_bucket_cross_account_access", + "s3_bucket_kms_encryption", + "s3_bucket_acl_prohibited", + "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", + "cloudtrail_kms_encryption_enabled", + "cloudwatch_log_group_not_publicly_accessible", + "cloudwatch_log_group_kms_encryption_enabled", + "secretsmanager_not_publicly_accessible", + "ec2_securitygroup_allow_ingress_from_internet_to_all_ports", + "ec2_ebs_snapshot_account_block_public_access", + "ec2_ebs_public_snapshot", + "rds_snapshots_public_access", + "ec2_ami_public", + "ssm_document_secrets", + "ec2_instance_secrets_user_data", + "awslambda_function_no_secrets_in_variables", + "awslambda_function_no_secrets_in_code" ] }, { @@ -932,14 +1079,7 @@ "Service": "aws" } ], - "Checks": [ - "cloudtrail_log_file_validation_enabled", - "ec2_instance_managed_by_ssm", - "ec2_instance_older_than_specific_days", - "elbv2_deletion_protection", - "ssm_managed_compliant_patching", - "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22" - ] + "Checks": [] }, { "Id": "ds_8", @@ -953,10 +1093,7 @@ "Service": "aws" } ], - "Checks": [ - "ec2_instance_managed_by_ssm", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "ip_1", @@ -971,9 +1108,18 @@ } ], "Checks": [ - "ec2_instance_managed_by_ssm", - "ec2_instance_older_than_specific_days", - "ssm_managed_compliant_patching" + "ec2_securitygroup_default_restrict_traffic", + "ec2_securitygroup_allow_ingress_from_internet_to_all_ports", + "ec2_networkacl_unused", + "ec2_securitygroup_not_used", + "ec2_instance_public_ip", + "iam_root_mfa_enabled", + "iam_administrator_access_with_mfa", + "iam_policy_attached_only_to_group_or_roles", + "iam_inline_policy_allows_privilege_escalation", + "iam_policy_allows_privilege_escalation", + "s3_bucket_public_access", + "s3_bucket_cross_account_access" ] }, { @@ -989,7 +1135,15 @@ } ], "Checks": [ - "ec2_instance_managed_by_ssm" + "codepipeline_project_repo_private", + "codebuild_project_not_publicly_accessible", + "codebuild_project_logging_enabled", + "codebuild_project_s3_logs_encrypted", + "codebuild_project_uses_allowed_github_organizations", + "codebuild_project_source_repo_url_no_sensitive_credentials", + "codebuild_project_no_secrets_in_variables", + "codebuild_project_user_controlled_buildspec", + "codebuild_project_older_90_days" ] }, { @@ -1004,9 +1158,7 @@ "Service": "elb" } ], - "Checks": [ - "elbv2_deletion_protection" - ] + "Checks": [] }, { "Id": "ip_4", @@ -1021,9 +1173,21 @@ } ], "Checks": [ - "dynamodb_tables_pitr_enabled", + "backup_vaults_exist", + "backup_vaults_encrypted", + "backup_recovery_point_encrypted", + "backup_reportplans_exist", + "backup_plans_exist", + "documentdb_cluster_backup_enabled", + "dynamodb_table_protected_by_backup_plan", + "ec2_ebs_volume_protected_by_backup_plan", + "efs_have_backup_enabled", + "elasticache_redis_cluster_backup_enabled", + "fsx_file_system_copy_tags_to_backups_enabled", + "neptune_cluster_backup_enabled", + "rds_cluster_protected_by_backup_plan", "rds_instance_backup_enabled", - "s3_bucket_object_versioning" + "rds_instance_protected_by_backup_plan" ] }, { @@ -1038,7 +1202,21 @@ "Service": "ec2" } ], - "Checks": [] + "Checks": [ + "cloudtrail_threat_detection_llm_jacking", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_threat_detection_privilege_escalation", + "cloudwatch_log_metric_filter_unauthorized_api_calls", + "cloudwatch_log_metric_filter_authentication_failures", + "cloudwatch_alarm_actions_alarm_state_configured", + "guardduty_is_enabled", + "guardduty_ec2_malware_protection_enabled", + "guardduty_eks_runtime_monitoring_enabled", + "inspector2_is_enabled", + "inspector2_active_findings_exist", + "secretsmanager_automatic_rotation_enabled", + "secretsmanager_secret_rotated_periodically" + ] }, { "Id": "ip_8", @@ -1053,18 +1231,15 @@ } ], "Checks": [ - "ec2_ebs_public_snapshot", - "ec2_instance_public_ip", - "eks_cluster_not_publicly_accessible", - "emr_cluster_master_nodes_no_public_ip", - "awslambda_function_url_public", - "rds_instance_no_public_access", - "rds_snapshots_public_access", - "redshift_cluster_public_access", - "s3_bucket_public_access", - "s3_bucket_policy_public_write_access", - "s3_account_level_public_access_blocks", - "sagemaker_notebook_instance_without_direct_internet_access_configured" + "cloudtrail_cloudwatch_logging_enabled", + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", + "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", + "cloudwatch_alarm_actions_enabled", + "cloudwatch_alarm_actions_alarm_state_configured", + "cloudwatch_log_metric_filter_unauthorized_api_calls", + "cloudwatch_log_metric_filter_security_group_changes", + "cloudwatch_log_metric_filter_root_usage", + "cloudwatch_log_metric_filter_authentication_failures" ] }, { @@ -1080,13 +1255,8 @@ } ], "Checks": [ - "dynamodb_tables_pitr_enabled", - "efs_have_backup_enabled", - "elbv2_deletion_protection", - "rds_instance_backup_enabled", - "rds_instance_multi_az", - "redshift_cluster_automated_snapshot", - "s3_bucket_object_versioning" + "ssmincidents_enabled_with_plans", + "drs_job_exist" ] }, { @@ -1102,9 +1272,9 @@ } ], "Checks": [ - "config_recorder_all_regions_enabled", - "ec2_instance_managed_by_ssm", - "ssm_managed_compliant_patching" + "inspector2_is_enabled", + "guardduty_is_enabled", + "inspector2_active_findings_exist" ] }, { @@ -1119,9 +1289,7 @@ "Service": "cloudtrail" } ], - "Checks": [ - "cloudtrail_multi_region_enabled" - ] + "Checks": [] }, { "Id": "pt_1", @@ -1136,13 +1304,22 @@ } ], "Checks": [ - "apigateway_restapi_logging_enabled", - "cloudtrail_multi_region_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "elbv2_logging_enabled", - "elb_logging_enabled", - "s3_bucket_server_access_logging_enabled", - "vpc_flow_logs_enabled" + "cloudtrail_kms_encryption_enabled", + "cloudtrail_log_file_validation_enabled", + "cloudtrail_logs_s3_bucket_access_logging_enabled", + "cloudtrail_multi_region_enabled_logging_management_events", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_threat_detection_privilege_escalation", + "cloudtrail_threat_detection_llm_jacking", + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", + "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes", + "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", + "cloudwatch_log_metric_filter_authentication_failures", + "cloudwatch_log_metric_filter_unauthorized_api_calls", + "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", + "vpc_flow_logs_enabled", + "config_recorder_all_regions_enabled", + "config_recorder_using_aws_service_role" ] }, { @@ -1157,19 +1334,7 @@ "Service": "aws" } ], - "Checks": [ - "ec2_ebs_public_snapshot", - "iam_aws_attached_policy_no_administrative_privileges", - "iam_customer_attached_policy_no_administrative_privileges", - "iam_inline_policy_no_administrative_privileges", - "iam_no_root_access_key", - "awslambda_function_url_public", - "rds_snapshots_public_access", - "redshift_cluster_public_access", - "s3_bucket_public_access", - "s3_bucket_policy_public_write_access", - "s3_account_level_public_access_blocks" - ] + "Checks": [] }, { "Id": "pt_4", @@ -1184,11 +1349,15 @@ } ], "Checks": [ - "awslambda_function_not_publicly_accessible", - "rds_instance_no_public_access", - "redshift_cluster_public_access", - "ec2_networkacl_allow_ingress_any_port", - "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22" + "cloudfront_distributions_https_enabled", + "cloudfront_distributions_origin_traffic_encrypted", + "vpc_flow_logs_enabled", + "ec2_securitygroup_allow_ingress_from_internet_to_all_ports", + "apigateway_restapi_public_with_authorizer", + "apigateway_restapi_authorizers_enabled", + "s3_bucket_secure_transport_policy", + "s3_bucket_kms_encryption", + "cloudtrail_kms_encryption_enabled" ] }, { @@ -1204,10 +1373,19 @@ } ], "Checks": [ - "elbv2_deletion_protection", - "rds_instance_backup_enabled", - "rds_instance_multi_az", - "s3_bucket_object_versioning" + "eks_cluster_deletion_protection_enabled", + "rds_cluster_deletion_protection", + "rds_instance_deletion_protection", + "dynamodb_table_deletion_protection_enabled", + "directconnect_connection_redundancy", + "vpc_endpoint_multi_az_enabled", + "elbv2_cross_zone_load_balancing_enabled", + "elb_cross_zone_load_balancing_enabled", + "autoscaling_group_capacity_rebalance_enabled", + "autoscaling_group_multiple_az", + "vpc_vpn_connection_tunnels_up", + "cloudfront_distributions_multiple_origin_failover_configured", + "s3_bucket_cross_region_replication" ] }, { @@ -1223,17 +1401,19 @@ } ], "Checks": [ - "apigateway_restapi_logging_enabled", - "cloudtrail_multi_region_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "elbv2_logging_enabled", - "elb_logging_enabled", - "redshift_cluster_audit_logging", - "s3_bucket_server_access_logging_enabled", - "ec2_securitygroup_default_restrict_traffic", "vpc_flow_logs_enabled", - "ec2_networkacl_allow_ingress_any_port", - "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22" + "cloudtrail_cloudwatch_logging_enabled", + "cloudtrail_multi_region_enabled_logging_management_events", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_threat_detection_privilege_escalation", + "cloudtrail_threat_detection_llm_jacking", + "vpc_endpoint_connections_trust_boundaries", + "vpc_endpoint_for_ec2_enabled", + "cloudwatch_log_metric_filter_security_group_changes", + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", + "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes", + "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", + "cloudwatch_log_metric_filter_unauthorized_api_calls" ] }, { @@ -1249,8 +1429,21 @@ } ], "Checks": [ - "guardduty_is_enabled", - "securityhub_enabled" + "cloudtrail_threat_detection_llm_jacking", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_threat_detection_privilege_escalation", + "cloudwatch_changes_to_network_gateways_alarm_configured", + "cloudwatch_changes_to_network_route_tables_alarm_configured", + "cloudwatch_changes_to_vpcs_alarm_configured", + "cloudwatch_log_metric_filter_authentication_failures", + "cloudwatch_log_metric_filter_unauthorized_api_calls", + "cloudwatch_log_metric_filter_root_usage", + "cloudwatch_log_metric_filter_policy_changes", + "cloudwatch_log_metric_filter_security_group_changes", + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", + "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", + "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes", + "cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk" ] }, { @@ -1266,16 +1459,16 @@ } ], "Checks": [ - "apigateway_restapi_logging_enabled", - "cloudtrail_multi_region_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", + "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", + "cloudwatch_changes_to_network_gateways_alarm_configured", + "cloudwatch_changes_to_network_route_tables_alarm_configured", + "cloudwatch_log_metric_filter_aws_organizations_changes", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_threat_detection_privilege_escalation", + "cloudtrail_threat_detection_llm_jacking", "cloudtrail_cloudwatch_logging_enabled", - "elbv2_logging_enabled", - "elb_logging_enabled", - "redshift_cluster_audit_logging", - "s3_bucket_server_access_logging_enabled", - "vpc_flow_logs_enabled" + "cloudtrail_multi_region_enabled_logging_management_events" ] }, { @@ -1290,17 +1483,7 @@ "Service": "aws" } ], - "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "elbv2_logging_enabled", - "elb_logging_enabled", - "guardduty_is_enabled", - "guardduty_no_high_severity_findings", - "s3_bucket_server_access_logging_enabled", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "ae_5", @@ -1314,12 +1497,7 @@ "Service": "aws" } ], - "Checks": [ - "cloudwatch_changes_to_network_acls_alarm_configured", - "cloudwatch_changes_to_network_gateways_alarm_configured", - "cloudwatch_changes_to_network_route_tables_alarm_configured", - "cloudwatch_changes_to_vpcs_alarm_configured" - ] + "Checks": [] }, { "Id": "cm_1", @@ -1334,16 +1512,24 @@ } ], "Checks": [ - "apigateway_restapi_logging_enabled", - "cloudtrail_multi_region_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "elbv2_logging_enabled", - "elb_logging_enabled", + "cloudtrail_multi_region_enabled_logging_management_events", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_threat_detection_privilege_escalation", + "cloudtrail_threat_detection_llm_jacking", + "cloudtrail_insights_exist", + "cloudtrail_cloudwatch_logging_enabled", + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", + "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", + "cloudwatch_log_metric_filter_authentication_failures", + "cloudwatch_log_metric_filter_unauthorized_api_calls", + "cloudwatch_changes_to_network_gateways_alarm_configured", + "cloudwatch_changes_to_network_route_tables_alarm_configured", + "cloudwatch_changes_to_vpcs_alarm_configured", + "cloudwatch_changes_to_network_acls_alarm_configured", + "vpc_flow_logs_enabled", "guardduty_is_enabled", - "s3_bucket_server_access_logging_enabled", - "securityhub_enabled", - "vpc_flow_logs_enabled" + "inspector2_is_enabled", + "accessanalyzer_enabled_without_findings" ] }, { @@ -1358,28 +1544,7 @@ "Service": "aws" } ], - "Checks": [ - "cloudtrail_cloudwatch_logging_enabled", - "cloudwatch_changes_to_network_acls_alarm_configured", - "cloudwatch_changes_to_network_gateways_alarm_configured", - "cloudwatch_changes_to_network_route_tables_alarm_configured", - "cloudwatch_changes_to_vpcs_alarm_configured", - "config_recorder_all_regions_enabled", - "ec2_instance_imdsv2_enabled", - "guardduty_is_enabled", - "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes", - "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", - "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", - "cloudwatch_log_metric_filter_authentication_failures", - "cloudwatch_log_metric_filter_sign_in_without_mfa", - "cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk", - "cloudwatch_log_metric_filter_policy_changes", - "cloudwatch_log_metric_filter_root_usage", - "cloudwatch_log_metric_filter_security_group_changes", - "cloudwatch_log_metric_filter_unauthorized_api_calls", - "rds_instance_enhanced_monitoring_enabled", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "cm_3", @@ -1394,12 +1559,14 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "guardduty_is_enabled", - "s3_bucket_server_access_logging_enabled", - "securityhub_enabled" + "cloudtrail_threat_detection_privilege_escalation", + "cloudtrail_threat_detection_llm_jacking", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_multi_region_enabled_logging_management_events", + "cloudtrail_cloudwatch_logging_enabled", + "cloudwatch_log_metric_filter_unauthorized_api_calls", + "cloudwatch_log_metric_filter_authentication_failures", + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled" ] }, { @@ -1415,8 +1582,8 @@ } ], "Checks": [ - "guardduty_is_enabled", - "securityhub_enabled" + "codebuild_project_source_repo_url_no_sensitive_credentials", + "codebuild_project_no_secrets_in_variables" ] }, { @@ -1431,18 +1598,7 @@ "Service": "aws" } ], - "Checks": [ - "cloudtrail_cloudwatch_logging_enabled", - "cloudwatch_changes_to_network_acls_alarm_configured", - "cloudwatch_changes_to_network_gateways_alarm_configured", - "cloudwatch_changes_to_network_route_tables_alarm_configured", - "cloudwatch_changes_to_vpcs_alarm_configured", - "ec2_instance_imdsv2_enabled", - "elbv2_waf_acl_attached", - "guardduty_is_enabled", - "guardduty_no_high_severity_findings", - "securityhub_enabled" - ] + "Checks": [] }, { "Id": "cm_6", @@ -1457,12 +1613,9 @@ } ], "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "guardduty_is_enabled", - "s3_bucket_server_access_logging_enabled", - "securityhub_enabled" + "cloudtrail_threat_detection_llm_jacking", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_threat_detection_privilege_escalation" ] }, { @@ -1478,16 +1631,26 @@ } ], "Checks": [ - "apigateway_restapi_logging_enabled", - "cloudtrail_multi_region_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "elbv2_logging_enabled", - "elb_logging_enabled", + "cloudtrail_cloudwatch_logging_enabled", + "cloudtrail_threat_detection_privilege_escalation", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_threat_detection_llm_jacking", + "cloudwatch_log_metric_filter_unauthorized_api_calls", + "cloudwatch_log_metric_filter_authentication_failures", + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", + "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", + "cloudwatch_log_metric_filter_security_group_changes", + "cloudwatch_log_metric_filter_root_usage", + "cloudwatch_log_metric_filter_aws_organizations_changes", + "cloudwatch_log_metric_filter_policy_changes", + "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes", + "cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk", "guardduty_is_enabled", - "s3_bucket_server_access_logging_enabled", - "securityhub_enabled", - "vpc_flow_logs_enabled" + "guardduty_ec2_malware_protection_enabled", + "guardduty_s3_protection_enabled", + "guardduty_rds_protection_enabled", + "guardduty_lambda_protection_enabled", + "guardduty_eks_runtime_monitoring_enabled" ] }, { @@ -1503,16 +1666,27 @@ } ], "Checks": [ - "cloudtrail_cloudwatch_logging_enabled", - "cloudwatch_changes_to_network_acls_alarm_configured", + "cloudtrail_threat_detection_llm_jacking", + "cloudtrail_threat_detection_enumeration", + "cloudtrail_threat_detection_privilege_escalation", + "cloudtrail_multi_region_enabled_logging_management_events", + "cloudtrail_insights_exist", + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", + "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", + "cloudwatch_log_metric_filter_authentication_failures", + "cloudwatch_log_metric_filter_unauthorized_api_calls", + "cloudwatch_alarm_actions_alarm_state_configured", + "cloudwatch_alarm_actions_enabled", + "cloudwatch_log_metric_filter_security_group_changes", + "cloudwatch_log_metric_filter_root_usage", "cloudwatch_changes_to_network_gateways_alarm_configured", "cloudwatch_changes_to_network_route_tables_alarm_configured", - "cloudwatch_changes_to_vpcs_alarm_configured", - "ec2_instance_imdsv2_enabled", - "elbv2_waf_acl_attached", - "guardduty_is_enabled", - "guardduty_no_high_severity_findings", - "securityhub_enabled" + "cloudwatch_changes_to_network_acls_alarm_configured", + "eventbridge_bus_cross_account_access", + "eventbridge_bus_exposed", + "route53_dangling_ip_subdomain_takeover", + "s3_bucket_event_notifications_enabled", + "sns_subscription_not_using_http_endpoints" ] }, { @@ -1527,9 +1701,7 @@ "Service": "ec2" } ], - "Checks": [ - "ec2_instance_imdsv2_enabled" - ] + "Checks": [] }, { "Id": "an_2", @@ -1543,9 +1715,7 @@ "Service": "guardduty" } ], - "Checks": [ - "guardduty_no_high_severity_findings" - ] + "Checks": [] }, { "Id": "mi_3", @@ -1559,9 +1729,7 @@ "Service": "guardduty" } ], - "Checks": [ - "guardduty_no_high_severity_findings" - ] + "Checks": [] }, { "Id": "rp_1", @@ -1576,13 +1744,7 @@ } ], "Checks": [ - "dynamodb_tables_pitr_enabled", - "efs_have_backup_enabled", - "elbv2_deletion_protection", - "rds_instance_backup_enabled", - "rds_instance_multi_az", - "redshift_cluster_automated_snapshot", - "s3_bucket_object_versioning" + "ssmincidents_enabled_with_plans" ] }, { @@ -1598,15 +1760,22 @@ } ], "Checks": [ - "dynamodb_tables_pitr_enabled", + "backup_vaults_exist", + "backup_vaults_encrypted", + "backup_recovery_point_encrypted", + "backup_reportplans_exist", + "backup_plans_exist", + "documentdb_cluster_backup_enabled", + "dynamodb_table_protected_by_backup_plan", + "ec2_ebs_volume_protected_by_backup_plan", "efs_have_backup_enabled", - "elbv2_deletion_protection", + "elasticache_redis_cluster_backup_enabled", + "fsx_file_system_copy_tags_to_backups_enabled", + "neptune_cluster_backup_enabled", + "rds_cluster_protected_by_backup_plan", "rds_instance_backup_enabled", - "rds_instance_multi_az", - "redshift_cluster_automated_snapshot", - "s3_bucket_object_versioning" + "rds_instance_protected_by_backup_plan" ] } ] } - From ee2d3ed052007967427262a6f45eaf12ea53bc18 Mon Sep 17 00:00:00 2001 From: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com> Date: Mon, 10 Nov 2025 09:19:10 +0100 Subject: [PATCH 2/3] feat: implement new design system variables across new components and add skeletons (#9193) --- .../components/risk-severity-chart.tsx | 35 ++- .../new-overview/components/status-chart.tsx | 52 ++++- .../new-overview/components/threat-score.tsx | 209 +++++++++--------- ui/app/(prowler)/new-overview/page.tsx | 33 +-- ui/components/graphs/donut-chart.tsx | 10 +- ui/components/graphs/horizontal-bar-chart.tsx | 37 ++-- ui/components/graphs/radial-chart.tsx | 26 +-- ui/components/graphs/shared/constants.ts | 11 +- .../shadcn/card/base-card/base-card.tsx | 36 --- ui/components/shadcn/card/card.tsx | 51 ++++- .../resource-stats-card-container.tsx | 55 ----- .../resource-stats-card-content.tsx | 20 +- .../resource-stats-card-divider.tsx | 59 ----- .../resource-stats-card-header.tsx | 27 +-- .../resource-stats-card.tsx | 16 +- ui/components/shadcn/index.ts | 4 +- ui/components/shadcn/skeleton/skeleton.tsx | 16 ++ ui/styles/globals.css | 61 ++--- 18 files changed, 348 insertions(+), 410 deletions(-) delete mode 100644 ui/components/shadcn/card/base-card/base-card.tsx delete mode 100644 ui/components/shadcn/card/resource-stats-card/resource-stats-card-container.tsx delete mode 100644 ui/components/shadcn/card/resource-stats-card/resource-stats-card-divider.tsx create mode 100644 ui/components/shadcn/skeleton/skeleton.tsx diff --git a/ui/app/(prowler)/new-overview/components/risk-severity-chart.tsx b/ui/app/(prowler)/new-overview/components/risk-severity-chart.tsx index b070a828ba..22f6fbb3b9 100644 --- a/ui/app/(prowler)/new-overview/components/risk-severity-chart.tsx +++ b/ui/app/(prowler)/new-overview/components/risk-severity-chart.tsx @@ -3,10 +3,11 @@ import { HorizontalBarChart } from "@/components/graphs/horizontal-bar-chart"; import { BarDataPoint } from "@/components/graphs/types"; import { - BaseCard, + Card, CardContent, CardHeader, CardTitle, + Skeleton, } from "@/components/shadcn"; import { calculatePercentage } from "@/lib/utils"; @@ -58,7 +59,10 @@ export const RiskSeverityChart = ({ ]; return ( - + Risk Severity @@ -66,6 +70,31 @@ export const RiskSeverityChart = ({ - + ); }; + +export function RiskSeverityChartSkeleton() { + return ( + + + + + + +
+ {/* 5 horizontal bar skeletons */} + {Array.from({ length: 5 }).map((_, index) => ( +
+ + +
+ ))} +
+
+
+ ); +} diff --git a/ui/app/(prowler)/new-overview/components/status-chart.tsx b/ui/app/(prowler)/new-overview/components/status-chart.tsx index 7e773f90e7..1efb406975 100644 --- a/ui/app/(prowler)/new-overview/components/status-chart.tsx +++ b/ui/app/(prowler)/new-overview/components/status-chart.tsx @@ -5,13 +5,13 @@ import { Bell, BellOff, ShieldCheck, TriangleAlert } from "lucide-react"; import { DonutChart } from "@/components/graphs/donut-chart"; import { DonutDataPoint } from "@/components/graphs/types"; import { - BaseCard, + Card, CardContent, CardHeader, CardTitle, CardVariant, ResourceStatsCard, - ResourceStatsCardContainer, + Skeleton, } from "@/components/shadcn"; import { calculatePercentage } from "@/lib/utils"; @@ -60,27 +60,30 @@ export const StatusChart = ({ { name: "Fail Findings", value: failFindingsData.total, - color: "#f43f5e", // Rose-500 + color: "var(--bg-fail-primary)", percentage: Number(failPercentage), change: Number(failChange), }, { name: "Pass Findings", value: passFindingsData.total, - color: "#4ade80", // Green-400 + color: "var(--bg-pass-primary)", percentage: Number(passPercentage), change: Number(passChange), }, ]; return ( - + Check Findings - -
+ +
- +
-
+
- + - + ); }; + +export function StatusChartSkeleton() { + return ( + + + + + + + {/* Circular skeleton for donut chart */} +
+ +
+ + {/* Bottom info box skeleton */} + +
+
+ ); +} diff --git a/ui/app/(prowler)/new-overview/components/threat-score.tsx b/ui/app/(prowler)/new-overview/components/threat-score.tsx index 511f6b9d42..db07e951b8 100644 --- a/ui/app/(prowler)/new-overview/components/threat-score.tsx +++ b/ui/app/(prowler)/new-overview/components/threat-score.tsx @@ -1,7 +1,6 @@ "use client"; import { MessageCircleWarning, ThumbsUp } from "lucide-react"; -import Link from "next/link"; import type { CriticalRequirement, @@ -9,50 +8,33 @@ import type { } from "@/actions/overview/types"; import { RadialChart } from "@/components/graphs/radial-chart"; import { - SEVERITY_COLORS, - STATUS_COLORS, -} from "@/components/graphs/shared/constants"; -import { - BaseCard, + Card, CardContent, CardHeader, CardTitle, + Skeleton, } from "@/components/shadcn"; const THREAT_LEVEL_CONFIG = { - CRITICAL: { + DANGER: { label: "Critical Risk", - color: "text-red-500", - chartColor: SEVERITY_COLORS.Critical, + color: "var(--bg-fail-primary)", + chartColor: "var(--bg-fail-primary)", minScore: 0, - maxScore: 20, + maxScore: 30, }, - HIGH: { - label: "High Risk", - color: "text-orange-500", - chartColor: SEVERITY_COLORS.High, - minScore: 21, - maxScore: 40, - }, - MODERATE: { - label: "Moderately Secure", - color: "text-yellow-500", - chartColor: SEVERITY_COLORS.Medium, - minScore: 41, + WARNING: { + label: "Moderate Risk", + color: "var(--bg-warning-primary)", + chartColor: "var(--bg-warning-primary)", + minScore: 31, maxScore: 60, }, - LOW: { - label: "Low Risk", - color: "text-blue-500", - chartColor: SEVERITY_COLORS.Low, + SUCCESS: { + label: "Secure", + color: "var(--bg-pass-primary)", + chartColor: "var(--bg-pass-primary)", minScore: 61, - maxScore: 80, - }, - SECURE: { - label: "Highly Secure", - color: "text-green-500", - chartColor: STATUS_COLORS.Success, - minScore: 81, maxScore: 100, }, } as const; @@ -74,7 +56,7 @@ function getThreatLevel(score: number): ThreatLevelKey { return key as ThreatLevelKey; } } - return "MODERATE"; + return "WARNING"; } // Convert section scores to tooltip data for the radial chart @@ -84,16 +66,13 @@ function convertSectionScoresToTooltipData( if (!sectionScores) return []; return Object.entries(sectionScores).map(([name, value]) => { - // Determine color based on score value - let color: string = SEVERITY_COLORS.Critical; - if (value >= 80) color = STATUS_COLORS.Success; - else if (value >= 60) color = SEVERITY_COLORS.Low; - else if (value >= 40) color = SEVERITY_COLORS.Medium; - else if (value >= 20) color = SEVERITY_COLORS.High; - // Round to nearest integer const roundedValue = Math.round(value); + // Determine color based on the same ranges as THREAT_LEVEL_CONFIG + const threatLevel = getThreatLevel(roundedValue); + const color = THREAT_LEVEL_CONFIG[threatLevel].chartColor; + return { name, value: roundedValue, color }; }); } @@ -122,22 +101,10 @@ export function ThreatScore({ sectionScores, criticalRequirements, }: ThreatScoreProps) { - if (score === null || score === undefined) { - return ( - - - Prowler Threat Score - - -

- No ThreatScore data available -

-
-
- ); - } + const hasData = score !== null && score !== undefined; + const displayScore = hasData ? score : 0; - const threatLevel = getThreatLevel(score); + const threatLevel = getThreatLevel(displayScore); const config = THREAT_LEVEL_CONFIG[threatLevel]; // Convert section scores to tooltip data @@ -147,20 +114,23 @@ export function ThreatScore({ const gaps = extractTopGaps(criticalRequirements, 2); return ( - + Prowler Threat Score - + {/* Radial Chart */} -
-
+
+
{/* Overlaid Text (centered) */} -
-

- {config.label} -

-
+ {hasData && ( +
+

+ {config.label} +

+
+ )}
- {/* Info Box */} -
-
- {/* Improvement Message */} - {scoreDelta !== undefined && - scoreDelta !== null && - scoreDelta !== 0 && ( -
- + {/* Info Box or Empty State */} + {hasData ? ( + +
+ {/* Improvement Message */} + {scoreDelta !== undefined && + scoreDelta !== null && + scoreDelta !== 0 && ( +
+ +

+ Threat score has{" "} + {scoreDelta > 0 ? "improved" : "decreased"} by{" "} + {Math.abs(scoreDelta)}% +

+
+ )} + + {/* Gaps Message */} + {gaps.length > 0 && ( +
+

- Threat score has {scoreDelta > 0 ? "improved" : "decreased"}{" "} - by {Math.abs(scoreDelta)}% + Major gaps include {gaps.slice(0, 2).join(", ")} + {gaps.length > 2 && ` & ${gaps.length - 2} more...`}

)} - - {/* Gaps Message */} - {gaps.length > 0 && ( -
- -

- Major gaps include {gaps.slice(0, 2).join(", ")} - {gaps.length > 2 && ` & ${gaps.length - 2} more...`} -

-
- )} - - {/* View Remediation Plan Button */} -
- - - View Remediation Plan - -
-
-
+ + ) : ( + +

+ Threat Score Data Unavailable +

+
+ )} - + + ); +} + +export function ThreatScoreSkeleton() { + return ( + + + + + + + {/* Circular skeleton for radial chart */} +
+ +
+ + {/* Bottom info box skeleton */} + +
+
); } diff --git a/ui/app/(prowler)/new-overview/page.tsx b/ui/app/(prowler)/new-overview/page.tsx index d45665be0e..954c668649 100644 --- a/ui/app/(prowler)/new-overview/page.tsx +++ b/ui/app/(prowler)/new-overview/page.tsx @@ -11,9 +11,12 @@ import { SearchParamsProps } from "@/types"; import { AccountsSelector } from "./components/accounts-selector"; import { ProviderTypeSelector } from "./components/provider-type-selector"; -import { RiskSeverityChart } from "./components/risk-severity-chart"; -import { StatusChart } from "./components/status-chart"; -import { ThreatScore } from "./components/threat-score"; +import { + RiskSeverityChart, + RiskSeverityChartSkeleton, +} from "./components/risk-severity-chart"; +import { StatusChart, StatusChartSkeleton } from "./components/status-chart"; +import { ThreatScore, ThreatScoreSkeleton } from "./components/threat-score"; const FILTER_PREFIX = "filter["; @@ -42,33 +45,15 @@ export default async function NewOverviewPage({
- -

Loading...

-
- } - > + }> - -

Loading...

-
- } - > + }> - -

Loading...

-
- } - > + }>
diff --git a/ui/components/graphs/donut-chart.tsx b/ui/components/graphs/donut-chart.tsx index 21fefd058b..e8c1c6e0da 100644 --- a/ui/components/graphs/donut-chart.tsx +++ b/ui/components/graphs/donut-chart.tsx @@ -30,7 +30,7 @@ const CustomTooltip = ({ active, payload }: any) => { const change = entry.payload?.change; return ( -
+
{/* Title with color chip */}
@@ -38,7 +38,7 @@ const CustomTooltip = ({ active, payload }: any) => { className="size-3 shrink-0 rounded" style={{ backgroundColor: color }} /> -

+

{percentage}% {name}

@@ -46,7 +46,7 @@ const CustomTooltip = ({ active, payload }: any) => { {/* Change percentage row */} {change !== undefined && (
-

+

{change > 0 ? "+" : ""} {change}% Since last scan

@@ -171,7 +171,7 @@ export function DonutChart({ { @@ -38,7 +39,7 @@ export function HorizontalBarChart({ data, title }: HorizontalBarChartProps) {

{title}

@@ -50,15 +51,15 @@ export function HorizontalBarChart({ data, title }: HorizontalBarChartProps) { const isHovered = !isEmpty && hoveredIndex === index; const isFaded = !isEmpty && hoveredIndex !== null && !isHovered; const barColor = isEmpty - ? CHART_COLORS.gridLine + ? "var(--bg-neutral-tertiary)" : item.color || getSeverityColorByName(item.name) || - CHART_COLORS.defaultColor; + "var(--bg-neutral-tertiary)"; return (
!isEmpty && setHoveredIndex(index)} onMouseLeave={() => !isEmpty && setHoveredIndex(null)} > @@ -67,18 +68,18 @@ export function HorizontalBarChart({ data, title }: HorizontalBarChartProps) { - {item.name} + {item.name === "Informational" ? "Info" : item.name}
{/* Bar - flexible */}
-
+
{(item.value > 0 || isEmpty) && (
+
{/* Title with color chip */}
@@ -101,8 +102,10 @@ export function HorizontalBarChart({ data, title }: HorizontalBarChartProps) { className="size-3 shrink-0 rounded" style={{ backgroundColor: barColor }} /> -

- {item.value.toLocaleString()} {item.name} Risk +

+ {item.value.toLocaleString()}{" "} + {item.name === "Informational" ? "Info" : item.name}{" "} + Risk

@@ -111,9 +114,9 @@ export function HorizontalBarChart({ data, title }: HorizontalBarChartProps) {
-

+

{item.newFindings} New Findings

@@ -122,7 +125,7 @@ export function HorizontalBarChart({ data, title }: HorizontalBarChartProps) { {/* Change percentage row */} {item.change !== undefined && (
-

+

{item.change > 0 ? "+" : ""} {item.change}% Since last scan

@@ -137,7 +140,7 @@ export function HorizontalBarChart({ data, title }: HorizontalBarChartProps) {
diff --git a/ui/components/graphs/radial-chart.tsx b/ui/components/graphs/radial-chart.tsx index 70b1c3a019..a80613bc59 100644 --- a/ui/components/graphs/radial-chart.tsx +++ b/ui/components/graphs/radial-chart.tsx @@ -8,8 +8,6 @@ import { Tooltip, } from "recharts"; -import { CHART_COLORS } from "./shared/constants"; - export interface TooltipItem { name: string; value: number; @@ -42,18 +40,18 @@ const CustomTooltip = ({ active, payload }: any) => { return null; return ( -
+
{tooltipItems.map((item: TooltipItem, index: number) => (
-

+

{item.name}

-
+

{item.value}% @@ -67,8 +65,8 @@ const CustomTooltip = ({ active, payload }: any) => { export function RadialChart({ percentage, - color = "var(--chart-success-color)", - backgroundColor = CHART_COLORS.tooltipBackground, + color = "var(--bg-pass-primary)", + backgroundColor = "var(--bg-neutral-tertiary)", height = 250, innerRadius = 60, outerRadius = 100, @@ -154,24 +152,18 @@ export function RadialChart({ const y = centerY - middleRadius * Math.sin(currentAngleRad); return ( - + ); })} {percentage}% diff --git a/ui/components/graphs/shared/constants.ts b/ui/components/graphs/shared/constants.ts index 4aadd4aac0..22fc946ff3 100644 --- a/ui/components/graphs/shared/constants.ts +++ b/ui/components/graphs/shared/constants.ts @@ -1,10 +1,9 @@ export const SEVERITY_COLORS = { - Informational: "var(--chart-info)", - Info: "var(--chart-info)", - Low: "var(--chart-warning)", - Medium: "var(--chart-warning-emphasis)", - High: "var(--chart-danger)", - Critical: "var(--chart-danger-emphasis)", + Informational: "var(--bg-data-info)", + Low: "var(--bg-data-low)", + Medium: "var(--bg-data-medium)", + High: "var(--bg-data-high)", + Critical: "var(--bg-data-critical)", } as const; export const PROVIDER_COLORS = { diff --git a/ui/components/shadcn/card/base-card/base-card.tsx b/ui/components/shadcn/card/base-card/base-card.tsx deleted file mode 100644 index 946e0cc184..0000000000 --- a/ui/components/shadcn/card/base-card/base-card.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { cva, type VariantProps } from "class-variance-authority"; - -import { cn } from "@/lib/utils"; - -import { Card } from "../card"; - -const baseCardVariants = cva("", { - variants: { - variant: { - default: - "border-slate-200 bg-white dark:border-zinc-900 dark:bg-stone-950", - }, - }, - defaultVariants: { - variant: "default", - }, -}); - -interface BaseCardProps - extends React.ComponentProps, - VariantProps {} - -const BaseCard = ({ className, variant, ...props }: BaseCardProps) => { - return ( - - ); -}; - -export { BaseCard }; diff --git a/ui/components/shadcn/card/card.tsx b/ui/components/shadcn/card/card.tsx index 62f4c66865..d57ddf0eb7 100644 --- a/ui/components/shadcn/card/card.tsx +++ b/ui/components/shadcn/card/card.tsx @@ -1,3 +1,5 @@ +import { cva, type VariantProps } from "class-variance-authority"; + import { cn } from "@/lib/utils"; export const CardVariant = { @@ -10,14 +12,44 @@ export const CardVariant = { export type CardVariant = (typeof CardVariant)[keyof typeof CardVariant]; -function Card({ className, ...props }: React.ComponentProps<"div">) { +const cardVariants = cva("flex flex-col gap-6 rounded-xl border", { + variants: { + variant: { + default: "", + base: "border-border-neutral-secondary bg-bg-neutral-secondary px-[18px] pt-3 pb-4", + inner: + "rounded-[12px] backdrop-blur-[46px] border-border-neutral-tertiary bg-bg-neutral-tertiary", + }, + padding: { + default: "", + sm: "px-3 py-2", + md: "px-4 py-3", + lg: "px-5 py-4", + none: "p-0", + }, + }, + compoundVariants: [ + { + variant: "inner", + padding: "default", + className: "px-4 py-3", // md padding by default for inner + }, + ], + defaultVariants: { + variant: "default", + padding: "default", + }, +}); + +interface CardProps + extends React.ComponentProps<"div">, + VariantProps {} + +function Card({ className, variant, padding, ...props }: CardProps) { return (

); @@ -28,7 +60,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
) { return (
); @@ -96,4 +125,6 @@ export { CardFooter, CardHeader, CardTitle, + cardVariants, }; +export type { CardProps }; diff --git a/ui/components/shadcn/card/resource-stats-card/resource-stats-card-container.tsx b/ui/components/shadcn/card/resource-stats-card/resource-stats-card-container.tsx deleted file mode 100644 index de27d052ed..0000000000 --- a/ui/components/shadcn/card/resource-stats-card/resource-stats-card-container.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { cva, type VariantProps } from "class-variance-authority"; - -import { cn } from "@/lib/utils"; - -const containerVariants = cva( - [ - "flex", - "rounded-[12px]", - "border", - "backdrop-blur-[46px]", - "border-slate-300", - "bg-[#F8FAFC80]", - "dark:border-[rgba(38,38,38,0.70)]", - "dark:bg-[rgba(23,23,23,0.50)]", - ], - { - variants: { - padding: { - sm: "px-3 py-2", - md: "px-[19px] py-[9px]", - lg: "px-6 py-3", - none: "p-0", - }, - }, - defaultVariants: { - padding: "md", - }, - }, -); - -export interface ResourceStatsCardContainerProps - extends React.HTMLAttributes, - VariantProps { - ref?: React.Ref; -} - -export const ResourceStatsCardContainer = ({ - className, - children, - padding, - ref, - ...props -}: ResourceStatsCardContainerProps) => { - return ( -
- {children} -
- ); -}; - -ResourceStatsCardContainer.displayName = "ResourceStatsCardContainer"; diff --git a/ui/components/shadcn/card/resource-stats-card/resource-stats-card-content.tsx b/ui/components/shadcn/card/resource-stats-card/resource-stats-card-content.tsx index e338c5e7b1..be3080143d 100644 --- a/ui/components/shadcn/card/resource-stats-card/resource-stats-card-content.tsx +++ b/ui/components/shadcn/card/resource-stats-card/resource-stats-card-content.tsx @@ -11,11 +11,11 @@ export interface StatItem { } const variantColors = { - default: "#868994", - fail: "#f54280", - pass: "#4ade80", - warning: "#fbbf24", - info: "#60a5fa", + default: "var(--bg-neutral-tertiary)", + fail: "var(--bg-fail-primary)", + pass: "var(--bg-pass-primary)", + warning: "var(--bg-warning-primary)", + info: "var(--bg-data-info)", } as const; type BadgeVariant = keyof typeof variantColors; @@ -26,8 +26,8 @@ const badgeVariants = cva( variants: { variant: { [CardVariant.default]: "bg-slate-100 dark:bg-[#535359]", - [CardVariant.fail]: "bg-red-100 dark:bg-[#432232]", - [CardVariant.pass]: "bg-green-100 dark:bg-[#204237]", + [CardVariant.fail]: "bg-bg-fail-secondary", + [CardVariant.pass]: "bg-bg-pass-secondary", [CardVariant.warning]: "bg-amber-100 dark:bg-[#3d3520]", [CardVariant.info]: "bg-blue-100 dark:bg-[#1e3a5f]", }, @@ -58,7 +58,7 @@ const badgeIconVariants = cva("", { }); const labelTextVariants = cva( - "leading-6 font-semibold text-slate-900 dark:text-zinc-300 whitespace-nowrap", + "leading-6 font-semibold text-text-neutral-secondary whitespace-nowrap", { variants: { size: { @@ -73,7 +73,7 @@ const labelTextVariants = cva( }, ); -const statIconVariants = cva("text-slate-600 dark:text-zinc-300", { +const statIconVariants = cva("text-text-neutral-secondary", { variants: { size: { sm: "h-2.5 w-2.5", @@ -87,7 +87,7 @@ const statIconVariants = cva("text-slate-600 dark:text-zinc-300", { }); const statLabelVariants = cva( - "leading-5 font-medium text-slate-700 dark:text-zinc-300", + "leading-5 font-medium text-text-neutral-secondary", { variants: { size: { diff --git a/ui/components/shadcn/card/resource-stats-card/resource-stats-card-divider.tsx b/ui/components/shadcn/card/resource-stats-card/resource-stats-card-divider.tsx deleted file mode 100644 index 3ec8d57ee4..0000000000 --- a/ui/components/shadcn/card/resource-stats-card/resource-stats-card-divider.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { cva, type VariantProps } from "class-variance-authority"; - -import { cn } from "@/lib/utils"; - -const dividerVariants = cva("flex items-center justify-center", { - variants: { - spacing: { - sm: "px-2", - md: "px-[23px]", - lg: "px-8", - }, - orientation: { - vertical: "h-full", - horizontal: "w-full", - }, - }, - defaultVariants: { - spacing: "md", - orientation: "vertical", - }, -}); - -const lineVariants = cva("bg-[rgba(39,39,42,1)]", { - variants: { - orientation: { - vertical: "h-full w-px", - horizontal: "w-full h-px", - }, - }, - defaultVariants: { - orientation: "vertical", - }, -}); - -export interface ResourceStatsCardDividerProps - extends React.HTMLAttributes, - VariantProps { - ref?: React.Ref; -} - -export const ResourceStatsCardDivider = ({ - className, - spacing, - orientation, - ref, - ...props -}: ResourceStatsCardDividerProps) => { - return ( -
-
-
- ); -}; - -ResourceStatsCardDivider.displayName = "ResourceStatsCardDivider"; diff --git a/ui/components/shadcn/card/resource-stats-card/resource-stats-card-header.tsx b/ui/components/shadcn/card/resource-stats-card/resource-stats-card-header.tsx index 2a4068e998..c324376a04 100644 --- a/ui/components/shadcn/card/resource-stats-card/resource-stats-card-header.tsx +++ b/ui/components/shadcn/card/resource-stats-card/resource-stats-card-header.tsx @@ -16,7 +16,7 @@ const headerVariants = cva("flex w-full items-center gap-1", { }, }); -const iconVariants = cva("text-zinc-300 dark:text-zinc-300", { +const iconVariants = cva("text-text-neutral-secondary", { variants: { size: { sm: "h-3.5 w-3.5", @@ -30,7 +30,7 @@ const iconVariants = cva("text-zinc-300 dark:text-zinc-300", { }); const titleVariants = cva( - "leading-7 font-semibold text-zinc-300 dark:text-zinc-300", + "leading-7 font-semibold text-text-neutral-secondary", { variants: { size: { @@ -45,21 +45,18 @@ const titleVariants = cva( }, ); -const countVariants = cva( - "leading-4 font-normal text-zinc-300 dark:text-zinc-300", - { - variants: { - size: { - sm: "text-[9px]", - md: "text-[10px]", - lg: "text-xs", - }, - }, - defaultVariants: { - size: "md", +const countVariants = cva("leading-4 font-normal text-text-neutral-secondary", { + variants: { + size: { + sm: "text-[9px]", + md: "text-[10px]", + lg: "text-xs", }, }, -); + defaultVariants: { + size: "md", + }, +}); export interface ResourceStatsCardHeaderProps extends React.HTMLAttributes, diff --git a/ui/components/shadcn/card/resource-stats-card/resource-stats-card.tsx b/ui/components/shadcn/card/resource-stats-card/resource-stats-card.tsx index 99d3e7867d..bb42f4bfff 100644 --- a/ui/components/shadcn/card/resource-stats-card/resource-stats-card.tsx +++ b/ui/components/shadcn/card/resource-stats-card/resource-stats-card.tsx @@ -3,17 +3,12 @@ import { LucideIcon } from "lucide-react"; import { cn } from "@/lib/utils"; -import { CardVariant } from "../card"; -import { ResourceStatsCardContainer } from "./resource-stats-card-container"; +import { Card, CardVariant } from "../card"; import type { StatItem } from "./resource-stats-card-content"; import { ResourceStatsCardContent } from "./resource-stats-card-content"; import { ResourceStatsCardHeader } from "./resource-stats-card-header"; export type { StatItem }; - -// Todo: when the design system is ready, we must use the colors from the design system (semantic colors) -// Variant styles using CVA for type safety and consistency -// Colors are exact HEX values from Figma design system const cardVariants = cva("", { variants: { variant: { @@ -109,7 +104,7 @@ export const ResourceStatsCard = ({ {header && } {emptyState ? (
-

+

{emptyState.message}

@@ -131,15 +126,16 @@ export const ResourceStatsCard = ({ // Otherwise, render with container return ( - {header && } {emptyState ? (
-

+

{emptyState.message}

@@ -155,7 +151,7 @@ export const ResourceStatsCard = ({ /> ) )} -
+ ); }; diff --git a/ui/components/shadcn/index.ts b/ui/components/shadcn/index.ts index 4bb244fb95..8991f1e34d 100644 --- a/ui/components/shadcn/index.ts +++ b/ui/components/shadcn/index.ts @@ -1,14 +1,12 @@ export * from "./badge/badge"; export * from "./button/button"; -export * from "./card/base-card/base-card"; export * from "./card/card"; export * from "./card/resource-stats-card/resource-stats-card"; -export * from "./card/resource-stats-card/resource-stats-card-container"; export * from "./card/resource-stats-card/resource-stats-card-content"; -export * from "./card/resource-stats-card/resource-stats-card-divider"; export * from "./card/resource-stats-card/resource-stats-card-header"; export * from "./dropdown/dropdown"; export * from "./select/select"; export * from "./separator/separator"; +export * from "./skeleton/skeleton"; export * from "./tabs/generic-tabs"; export * from "./tabs/tabs"; diff --git a/ui/components/shadcn/skeleton/skeleton.tsx b/ui/components/shadcn/skeleton/skeleton.tsx new file mode 100644 index 0000000000..34971c0554 --- /dev/null +++ b/ui/components/shadcn/skeleton/skeleton.tsx @@ -0,0 +1,16 @@ +import { cn } from "@/lib/utils"; + +function Skeleton({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +export { Skeleton }; diff --git a/ui/styles/globals.css b/ui/styles/globals.css index 50272c81a4..d6dfe3ee05 100644 --- a/ui/styles/globals.css +++ b/ui/styles/globals.css @@ -6,11 +6,11 @@ /* ===== LIGHT THEME (ROOT) ===== */ :root { /* ===== LEGACY VARIABLES (CHART COLORS) ===== */ - --chart-info: #3C8DFF; - --chart-warning: #FDFBD4; - --chart-warning-emphasis: #FEC94D; - --chart-danger: #F77852; - --chart-danger-emphasis: #FF006A; + --chart-info: #3c8dff; + --chart-warning: #fdfbd4; + --chart-warning-emphasis: #fec94d; + --chart-danger: #f77852; + --chart-danger-emphasis: #ff006a; --chart-success-color: #16a34a; --chart-fail: #dc2626; --chart-radar-primary: #9d174d; @@ -69,36 +69,40 @@ /* Text Colors */ --text-neutral-primary: var(--color-slate-950); - --text-neutral-secondary: var(--color-zinc-700); + --text-neutral-secondary: var(--color-zinc-800); --text-neutral-tertiary: var(--color-zinc-500); --text-error-primary: var(--color-red-600); /* Background Colors */ - --bg-neutral-primary: #FDFDFD; + --bg-neutral-primary: #fdfdfd; --bg-neutral-secondary: var(--color-white); - --bg-neutral-tertiary: #FBFDFD; + --bg-neutral-tertiary: #fbfdfd; --bg-tag-primary: var(--color-slate-50); --bg-pass-primary: var(--color-emerald-400); --bg-pass-secondary: var(--color-emerald-50); + --bg-warning-primary: var(--color-orange-500); --bg-fail-primary: var(--color-rose-500); --bg-fail-secondary: var(--color-rose-50); /* Severity Colors */ - --bg-data-critical: #FF006A; - --bg-data-high: #F77852; - --bg-data-medium: #FDD34F; - --bg-data-low: #F5F3CE; - --bg-data-info: #3C8DFF; + --bg-data-critical: #ff006a; + --bg-data-high: #f77852; + --bg-data-medium: #fdd34f; + --bg-data-low: #f5f3ce; + --bg-data-info: #3c8dff; + + /* Chart Dots */ + --chart-dots: var(--color-neutral-200); } /* ===== DARK THEME ===== */ .dark { /* ===== LEGACY VARIABLES (CHART COLORS) ===== */ - --chart-info: #3C8DFF; - --chart-warning: #FDFBD4; - --chart-warning-emphasis: #FEC94D; - --chart-danger: #F77852; - --chart-danger-emphasis: #FF006A; + --chart-info: #3c8dff; + --chart-warning: #fdfbd4; + --chart-warning-emphasis: #fec94d; + --chart-danger: #f77852; + --chart-danger-emphasis: #ff006a; --chart-success-color: #86da26; --chart-fail: #db2b49; --chart-radar-primary: #b51c80; @@ -157,26 +161,30 @@ /* Text Colors */ --text-neutral-primary: var(--color-zinc-100); - --text-neutral-secondary: var(--color-zinc-400); + --text-neutral-secondary: var(--color-zinc-300); --text-neutral-tertiary: var(--color-zinc-500); --text-error-primary: var(--color-rose-300); /* Background Colors */ --bg-neutral-primary: var(--color-zinc-950); - --bg-neutral-secondary: var(--color-slate-950); + --bg-neutral-secondary: var(--color-stone-950); --bg-neutral-tertiary: #121110; --bg-tag-primary: var(--color-slate-950); + --bg-warning-primary: var(--color-orange-400); --bg-pass-primary: var(--color-green-400); --bg-pass-secondary: var(--color-emerald-900); --bg-fail-primary: var(--color-rose-500); --bg-fail-secondary: #432232; /* Severity Colors */ - --bg-data-critical: #FF006A; - --bg-data-high: #F77852; - --bg-data-medium: #FEC94D; - --bg-data-low: #FDFBD4; - --bg-data-info: #3C8DFF; + --bg-data-critical: #ff006a; + --bg-data-high: #f77852; + --bg-data-medium: #fec94d; + --bg-data-low: #fdfbd4; + --bg-data-info: #3c8dff; + + /* Chart Dots */ + --chart-dots: var(--text-neutral-primary); } /* ===== TAILWIND THEME MAPPINGS ===== */ @@ -248,6 +256,7 @@ --color-bg-tag: var(--bg-tag-primary); --color-bg-pass: var(--bg-pass-primary); --color-bg-pass-secondary: var(--bg-pass-secondary); + --color-bg-warning: var(--bg-warning-primary); --color-bg-fail: var(--bg-fail-primary); --color-bg-fail-secondary: var(--bg-fail-secondary); } @@ -357,4 +366,4 @@ body { @apply bg-background text-foreground; } -} \ No newline at end of file +} From ef4e28da03ebae1ba06cda9123aa3e5216bd817c Mon Sep 17 00:00:00 2001 From: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> Date: Mon, 10 Nov 2025 11:23:56 +0100 Subject: [PATCH 3/3] fix(m365_powershell): teams connection with `--sp-env-auth` and enhanced timeouts error logging (#9191) --- prowler/CHANGELOG.md | 1 + .../m365/lib/powershell/m365_powershell.py | 41 +++--- .../lib/powershell/m365_powershell_test.py | 118 ++++++++---------- 3 files changed, 66 insertions(+), 94 deletions(-) diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 7ee85b6607..286322a155 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -49,6 +49,7 @@ All notable changes to the **Prowler SDK** are documented in this file. ### Fixed - Check `check_name` has no `resource_name` error for GCP provider [(#9169)](https://github.com/prowler-cloud/prowler/pull/9169) - Depth Truncation and parsing error in PowerShell queries [(#9181)](https://github.com/prowler-cloud/prowler/pull/9181) +- Fix M365 Teams `--sp-env-auth` connection error and enhanced timeout logging [(#9191)](https://github.com/prowler-cloud/prowler/pull/9191) --- diff --git a/prowler/providers/m365/lib/powershell/m365_powershell.py b/prowler/providers/m365/lib/powershell/m365_powershell.py index e3d729275f..797fd667ce 100644 --- a/prowler/providers/m365/lib/powershell/m365_powershell.py +++ b/prowler/providers/m365/lib/powershell/m365_powershell.py @@ -1,5 +1,4 @@ import os -from typing import Optional from prowler.lib.logger import logger from prowler.lib.powershell.powershell import PowerShellSession @@ -7,12 +6,11 @@ from prowler.providers.m365.exceptions.exceptions import ( M365CertificateCreationError, M365GraphConnectionError, ) -from prowler.providers.m365.lib.jwt.jwt_decoder import decode_jwt, decode_msal_token +from prowler.providers.m365.lib.jwt.jwt_decoder import decode_msal_token from prowler.providers.m365.models import M365Credentials, M365IdentityInfo class M365PowerShell(PowerShellSession): - CONNECT_TIMEOUT = 15 """ Microsoft 365 specific PowerShell session management implementation. @@ -125,9 +123,7 @@ class M365PowerShell(PowerShellSession): '$graphToken = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/token" -Method POST -Body $graphtokenBody | Select-Object -ExpandProperty Access_Token' ) - def _execute_connect_command( - self, command: str, timeout: Optional[int] = None - ) -> str: + def execute_connect(self, command: str) -> str: """ Execute a PowerShell connect command ensuring empty responses surface as timeouts. @@ -138,9 +134,9 @@ class M365PowerShell(PowerShellSession): Returns: str: Command output or 'Timeout' if the command produced no output. """ - effective_timeout = timeout or self.CONNECT_TIMEOUT - result = self.execute(command, timeout=effective_timeout) - return result or "Timeout" + connect_timeout = 15 + result = self.execute(command, timeout=connect_timeout) + return result or "'execute_connect' command timeout reached" def test_credentials(self, credentials: M365Credentials) -> bool: """ @@ -207,7 +203,7 @@ class M365PowerShell(PowerShellSession): def test_graph_certificate_connection(self) -> bool: """Test Microsoft Graph API connection using certificate and raise exception if it fails.""" - result = self._execute_connect_command( + result = self.execute_connect( "Connect-Graph -Certificate $certificate -AppId $clientID -TenantId $tenantID" ) if "Welcome to Microsoft Graph!" not in result: @@ -221,18 +217,13 @@ class M365PowerShell(PowerShellSession): self.execute( '$teamstokenBody = @{ Grant_Type = "client_credentials"; Scope = "48ac35b8-9aa8-4d74-927d-1f4a14a0b239/.default"; Client_Id = $clientID; Client_Secret = $clientSecret }' ) - self.execute( + result = self.execute( '$teamsToken = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/token" -Method POST -Body $teamstokenBody | Select-Object -ExpandProperty Access_Token' ) - permissions = decode_jwt(self.execute("Write-Output $teamsToken")).get( - "roles", [] - ) - if "application_access" not in permissions: - logger.error( - "Microsoft Teams connection failed: Please check your permissions and try again." - ) + if result != "": + logger.error(f"Microsoft Teams connection failed: {result}") return False - self._execute_connect_command( + self.execute_connect( 'Connect-MicrosoftTeams -AccessTokens @("$graphToken","$teamsToken")' ) return True @@ -244,7 +235,7 @@ class M365PowerShell(PowerShellSession): def test_teams_certificate_connection(self) -> bool: """Test Microsoft Teams API connection using certificate and raise exception if it fails.""" - result = self._execute_connect_command( + result = self.execute_connect( "Connect-MicrosoftTeams -Certificate $certificate -ApplicationId $clientID -TenantId $tenantID" ) if self.tenant_identity.identity_id not in result: @@ -268,9 +259,8 @@ class M365PowerShell(PowerShellSession): "Exchange Online connection failed: Please check your permissions and try again." ) return False - self._execute_connect_command( - 'Connect-ExchangeOnline -AccessToken $exchangeToken.AccessToken -Organization "$tenantID"', - timeout=self.CONNECT_TIMEOUT, + self.execute_connect( + 'Connect-ExchangeOnline -AccessToken $exchangeToken.AccessToken -Organization "$tenantID"' ) return True except Exception as e: @@ -281,9 +271,8 @@ class M365PowerShell(PowerShellSession): def test_exchange_certificate_connection(self) -> bool: """Test Exchange Online API connection using certificate and raise exception if it fails.""" - result = self._execute_connect_command( - "Connect-ExchangeOnline -Certificate $certificate -AppId $clientID -Organization $tenantDomain", - timeout=self.CONNECT_TIMEOUT, + result = self.execute_connect( + "Connect-ExchangeOnline -Certificate $certificate -AppId $clientID -Organization $tenantDomain" ) if "https://aka.ms/exov3-module" not in result: logger.error(f"Exchange Online Certificate connection failed: {result}") diff --git a/tests/providers/m365/lib/powershell/m365_powershell_test.py b/tests/providers/m365/lib/powershell/m365_powershell_test.py index 8d37b6c095..28b09768c3 100644 --- a/tests/providers/m365/lib/powershell/m365_powershell_test.py +++ b/tests/providers/m365/lib/powershell/m365_powershell_test.py @@ -547,8 +547,7 @@ class Testm365PowerShell: session.close() @patch("subprocess.Popen") - @patch("prowler.providers.m365.lib.powershell.m365_powershell.decode_jwt") - def test_test_teams_connection_success(self, mock_decode_jwt, mock_popen): + def test_test_teams_connection_success(self, mock_popen): """Test test_teams_connection when token is valid""" mock_process = MagicMock() mock_popen.return_value = mock_process @@ -567,30 +566,20 @@ class Testm365PowerShell: ) session = M365PowerShell(credentials, identity) - # Mock execute to return valid responses - def mock_execute(command, *args, **kwargs): - if "Write-Output $teamsToken" in command: - return "valid_teams_token" - return None - - session.execute = MagicMock(side_effect=mock_execute) - # Mock JWT decode to return proper permissions - mock_decode_jwt.return_value = {"roles": ["application_access"]} + session.execute = MagicMock(side_effect=[None, ""]) + session.execute_connect = MagicMock(return_value="") result = session.test_teams_connection() assert result is True - # Verify all expected PowerShell commands were called - # 4 calls: teamstokenBody, teamsToken, Write-Output $teamsToken, Connect-MicrosoftTeams - assert session.execute.call_count == 4 - mock_decode_jwt.assert_called_once_with("valid_teams_token") + assert session.execute.call_count == 2 + session.execute_connect.assert_called_once_with( + 'Connect-MicrosoftTeams -AccessTokens @("$graphToken","$teamsToken")' + ) session.close() @patch("subprocess.Popen") - @patch("prowler.providers.m365.lib.powershell.m365_powershell.decode_jwt") - def test_test_teams_connection_missing_permissions( - self, mock_decode_jwt, mock_popen - ): + def test_test_teams_connection_missing_permissions(self, mock_popen): """Test test_teams_connection when token lacks required permissions""" mock_process = MagicMock() mock_popen.return_value = mock_process @@ -609,23 +598,17 @@ class Testm365PowerShell: ) session = M365PowerShell(credentials, identity) - # Mock execute to return valid token but decode returns no permissions - def mock_execute(command, *args, **kwargs): - if "Write-Output $teamsToken" in command: - return "valid_teams_token" - return None - - session.execute = MagicMock(side_effect=mock_execute) - # Mock JWT decode to return missing required permission - mock_decode_jwt.return_value = {"roles": ["other_permission"]} + session.execute = MagicMock(side_effect=[None, "Permission denied"]) + session.execute_connect = MagicMock() with patch("prowler.lib.logger.logger.error") as mock_error: result = session.test_teams_connection() assert result is False mock_error.assert_called_once_with( - "Microsoft Teams connection failed: Please check your permissions and try again." + "Microsoft Teams connection failed: Permission denied" ) + session.execute_connect.assert_not_called() session.close() @patch("subprocess.Popen") @@ -688,15 +671,17 @@ class Testm365PowerShell: return None session.execute = MagicMock(side_effect=mock_execute) + session.execute_connect = MagicMock(return_value=None) # Mock MSAL token decode to return proper permissions mock_decode_msal_token.return_value = {"roles": ["Exchange.ManageAsApp"]} result = session.test_exchange_connection() assert result is True - # Verify all expected PowerShell commands were called - # 4 calls: SecureSecret, exchangeToken, Write-Output $exchangeToken, Connect-ExchangeOnline - assert session.execute.call_count == 4 + assert session.execute.call_count == 3 + session.execute_connect.assert_called_once_with( + 'Connect-ExchangeOnline -AccessToken $exchangeToken.AccessToken -Organization "$tenantID"' + ) mock_decode_msal_token.assert_called_once_with("valid_exchange_token") session.close() @@ -730,6 +715,7 @@ class Testm365PowerShell: return None session.execute = MagicMock(side_effect=mock_execute) + session.execute_connect = MagicMock(return_value=None) # Mock MSAL token decode to return missing required permission mock_decode_msal_token.return_value = {"roles": ["other_permission"]} @@ -737,6 +723,7 @@ class Testm365PowerShell: result = session.test_exchange_connection() assert result is False + session.execute_connect.assert_not_called() mock_error.assert_called_once_with( "Exchange Online connection failed: Please check your permissions and try again." ) @@ -781,7 +768,7 @@ class Testm365PowerShell: mock_popen.return_value = mock_process credentials = M365Credentials() - identity = M365IdentityInfo() + identity = M365IdentityInfo(identity_id="expected-id") session = M365PowerShell(credentials, identity) # Test with clean base64 content @@ -924,20 +911,18 @@ class Testm365PowerShell: mock_popen.return_value = mock_process credentials = M365Credentials() - identity = M365IdentityInfo() + identity = M365IdentityInfo(identity_id="expected-id") session = M365PowerShell(credentials, identity) - # Mock successful Exchange connection - session.execute = MagicMock( + session.execute_connect = MagicMock( return_value="Connected successfully https://aka.ms/exov3-module" ) result = session.test_exchange_certificate_connection() assert result is True - session.execute.assert_called_once_with( - "Connect-ExchangeOnline -Certificate $certificate -AppId $clientID -Organization $tenantDomain", - timeout=M365PowerShell.CONNECT_TIMEOUT, + session.execute_connect.assert_called_once_with( + "Connect-ExchangeOnline -Certificate $certificate -AppId $clientID -Organization $tenantDomain" ) session.close() @@ -949,20 +934,23 @@ class Testm365PowerShell: mock_popen.return_value = mock_process credentials = M365Credentials() - identity = M365IdentityInfo() + identity = M365IdentityInfo(identity_id="expected-id") session = M365PowerShell(credentials, identity) - # Mock failed Exchange connection - session.execute = MagicMock( + session.execute_connect = MagicMock( return_value="Connection failed: Authentication error" ) - result = session.test_exchange_certificate_connection() + with patch("prowler.lib.logger.logger.error") as mock_error: + result = session.test_exchange_certificate_connection() + assert result is False - session.execute.assert_called_once_with( - "Connect-ExchangeOnline -Certificate $certificate -AppId $clientID -Organization $tenantDomain", - timeout=M365PowerShell.CONNECT_TIMEOUT, + session.execute_connect.assert_called_once_with( + "Connect-ExchangeOnline -Certificate $certificate -AppId $clientID -Organization $tenantDomain" + ) + mock_error.assert_called_once_with( + "Exchange Online Certificate connection failed: Connection failed: Authentication error" ) session.close() @@ -981,20 +969,15 @@ class Testm365PowerShell: session = M365PowerShell(credentials, identity) # Mock successful Teams connection - the method returns bool - def mock_execute_side_effect(command, *_, **__): - if "Connect-MicrosoftTeams" in command: - # Return result that contains the identity_id for success - return "Connected successfully test_identity_id" - return "" - - session.execute = MagicMock(side_effect=mock_execute_side_effect) + session.execute_connect = MagicMock( + return_value="Connected successfully test_identity_id" + ) result = session.test_teams_certificate_connection() assert result is True - session.execute.assert_called_once_with( - "Connect-MicrosoftTeams -Certificate $certificate -ApplicationId $clientID -TenantId $tenantID", - timeout=M365PowerShell.CONNECT_TIMEOUT, + session.execute_connect.assert_called_once_with( + "Connect-MicrosoftTeams -Certificate $certificate -ApplicationId $clientID -TenantId $tenantID" ) session.close() @@ -1006,22 +989,21 @@ class Testm365PowerShell: mock_popen.return_value = mock_process credentials = M365Credentials() - identity = M365IdentityInfo() + identity = M365IdentityInfo(identity_id="expected-id") session = M365PowerShell(credentials, identity) - # Mock failed Teams connection - def mock_execute_side_effect(command, **kwargs): - if "Connect-MicrosoftTeams" in command: - raise Exception("Connection failed: Authentication error") - return "" + session.execute_connect = MagicMock(return_value="Connection failed") - session.execute = MagicMock(side_effect=mock_execute_side_effect) + with patch("prowler.lib.logger.logger.error") as mock_error: + result = session.test_teams_certificate_connection() - # Should raise exception on connection failure - with pytest.raises(Exception) as exc_info: - session.test_teams_certificate_connection() - - assert "Connection failed: Authentication error" in str(exc_info.value) + assert result is False + session.execute_connect.assert_called_once_with( + "Connect-MicrosoftTeams -Certificate $certificate -ApplicationId $clientID -TenantId $tenantID" + ) + mock_error.assert_called_once_with( + "Microsoft Teams Certificate connection failed: Connection failed" + ) session.close()