From daee5fb4d24a6349ebef565d2edce94756932917 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:46:19 +0100 Subject: [PATCH 01/55] chore(deps): bump microsoft-kiota-abstractions from 1.6.0 to 1.6.2 (#5720) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 16 ++++++++-------- pyproject.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3784112da4..3698c79115 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2641,19 +2641,19 @@ files = [ [[package]] name = "microsoft-kiota-abstractions" -version = "1.6.0" +version = "1.6.2" description = "Core abstractions for kiota generated libraries in Python" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "microsoft_kiota_abstractions-1.6.0-py3-none-any.whl", hash = "sha256:ead1846da7ced9a7039bee14b0d013250ca0bc2b5790d2e0b5c2f27c5bcf8182"}, - {file = "microsoft_kiota_abstractions-1.6.0.tar.gz", hash = "sha256:4cf8eb3f78a88619e8af6d6073ae6d15bcd075a086f86074585626ec5b8f4a08"}, + {file = "microsoft_kiota_abstractions-1.6.2-py3-none-any.whl", hash = "sha256:8c2c777748e80f17dba3809b5d149585d9918198f0f94125e87432f7165ba80e"}, + {file = "microsoft_kiota_abstractions-1.6.2.tar.gz", hash = "sha256:dec30f0fb427a051003e94b5c6fcf266f4702ecbd9d6961e3966124b9cbe41bf"}, ] [package.dependencies] opentelemetry-api = ">=1.27.0" opentelemetry-sdk = ">=1.27.0" -std-uritemplate = ">=0.0.38,<2.0.0" +std-uritemplate = ">=2.0.0" [[package]] name = "microsoft-kiota-authentication-azure" @@ -5205,13 +5205,13 @@ catalogue = ">=2.0.3,<2.1.0" [[package]] name = "std-uritemplate" -version = "1.0.6" +version = "2.0.0" description = "std-uritemplate implementation for Python" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "std_uritemplate-1.0.6-py3-none-any.whl", hash = "sha256:52f575b088721e6e698303b90eb8de04c2820afa946ee9f923a19dd7c21c7f23"}, - {file = "std_uritemplate-1.0.6.tar.gz", hash = "sha256:9bed621204a9dbb47f89ea958c97c463d15f28bccbc801aab8c9b0d75719ec62"}, + {file = "std_uritemplate-2.0.0-py3-none-any.whl", hash = "sha256:b69a80b707b93095075066005155eebd9172ec1ac7601992e2f9e4ec380f5a7b"}, + {file = "std_uritemplate-2.0.0.tar.gz", hash = "sha256:be88c8a4a89fee6768237f594c537d9347d96d7e68b8ad12200a3f11a0f6398f"}, ] [[package]] @@ -5852,4 +5852,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "e7c299d2efe2a668adecafb0111bc2b1974d7f15203da6fe21ebdcfe28f42420" +content-hash = "3d245d541604a9c5629b8d8b822e41907cebff85a228d1b3b0b1fdfe61c4aa08" diff --git a/pyproject.toml b/pyproject.toml index 4e27cfae3b..122cb8d94d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ google-api-python-client = "2.151.0" google-auth-httplib2 = ">=0.1,<0.3" jsonschema = "4.23.0" kubernetes = "31.0.0" -microsoft-kiota-abstractions = "1.6.0" +microsoft-kiota-abstractions = "1.6.2" msgraph-sdk = "1.11.0" numpy = "2.0.2" pandas = "2.2.3" From 2ac716d6db03e25389f675d5ae10dff73aae2fde Mon Sep 17 00:00:00 2001 From: Prowler Bot Date: Tue, 12 Nov 2024 15:25:12 +0100 Subject: [PATCH 02/55] chore(regions_update): Changes in regions for AWS services (#5732) Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com> --- prowler/providers/aws/aws_regions_by_service.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/prowler/providers/aws/aws_regions_by_service.json b/prowler/providers/aws/aws_regions_by_service.json index faee7f8821..fe8c5f2342 100644 --- a/prowler/providers/aws/aws_regions_by_service.json +++ b/prowler/providers/aws/aws_regions_by_service.json @@ -5855,6 +5855,7 @@ "ap-southeast-2", "ap-southeast-3", "ap-southeast-4", + "ap-southeast-5", "ca-central-1", "ca-west-1", "eu-central-1", @@ -10684,7 +10685,10 @@ "us-east-2", "us-west-2" ], - "aws-cn": [], + "aws-cn": [ + "cn-north-1", + "cn-northwest-1" + ], "aws-us-gov": [] } }, From 77c798679730ff9e3188fc96946cb7f2e2393389 Mon Sep 17 00:00:00 2001 From: sansns-aws <107269923+sansns@users.noreply.github.com> Date: Tue, 12 Nov 2024 09:26:52 -0500 Subject: [PATCH 03/55] feat(aws): Update check metadata with `logging` category (#5639) --- .../acm_certificates_transparency_logs_enabled.metadata.json | 4 +++- .../apigateway_restapi_logging_enabled.metadata.json | 3 ++- .../apigatewayv2_api_access_logging_enabled.metadata.json | 4 +++- ...e_api_operations_cloudtrail_logging_enabled.metadata.json | 3 ++- .../cloudfront_distributions_logging_enabled.metadata.json | 3 ++- .../cloudtrail_cloudwatch_logging_enabled.metadata.json | 3 ++- ...oryservice_directory_log_forwarding_enabled.metadata.json | 4 +++- ...eks_control_plane_logging_all_types_enabled.metadata.json | 3 ++- .../elb_logging_enabled/elb_logging_enabled.metadata.json | 3 ++- .../elbv2_logging_enabled.metadata.json | 5 ++++- .../glue_etl_jobs_logging_enabled.metadata.json | 4 +++- .../mq_broker_logging_enabled.metadata.json | 4 +++- ...neptune_cluster_integration_cloudwatch_logs.metadata.json | 4 +++- ...earch_service_domains_audit_logging_enabled.metadata.json | 3 ++- ..._service_domains_cloudwatch_logging_enabled.metadata.json | 3 ++- .../rds_cluster_integration_cloudwatch_logs.metadata.json | 4 +++- .../rds_instance_integration_cloudwatch_logs.metadata.json | 4 +++- .../redshift_cluster_audit_logging.metadata.json | 3 ++- .../vpc_flow_logs_enabled.metadata.json | 3 ++- 19 files changed, 48 insertions(+), 19 deletions(-) diff --git a/prowler/providers/aws/services/acm/acm_certificates_transparency_logs_enabled/acm_certificates_transparency_logs_enabled.metadata.json b/prowler/providers/aws/services/acm/acm_certificates_transparency_logs_enabled/acm_certificates_transparency_logs_enabled.metadata.json index a3d004f6f5..758806c2a9 100644 --- a/prowler/providers/aws/services/acm/acm_certificates_transparency_logs_enabled/acm_certificates_transparency_logs_enabled.metadata.json +++ b/prowler/providers/aws/services/acm/acm_certificates_transparency_logs_enabled/acm_certificates_transparency_logs_enabled.metadata.json @@ -25,7 +25,9 @@ "Url": "https://aws.amazon.com/blogs/security/how-to-get-ready-for-certificate-transparency/" } }, - "Categories": [], + "Categories": [ + "logging" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/apigateway/apigateway_restapi_logging_enabled/apigateway_restapi_logging_enabled.metadata.json b/prowler/providers/aws/services/apigateway/apigateway_restapi_logging_enabled/apigateway_restapi_logging_enabled.metadata.json index 1d79e8c083..7edad6be41 100644 --- a/prowler/providers/aws/services/apigateway/apigateway_restapi_logging_enabled/apigateway_restapi_logging_enabled.metadata.json +++ b/prowler/providers/aws/services/apigateway/apigateway_restapi_logging_enabled/apigateway_restapi_logging_enabled.metadata.json @@ -29,7 +29,8 @@ } }, "Categories": [ - "forensics-ready" + "forensics-ready", + "logging" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/aws/services/apigatewayv2/apigatewayv2_api_access_logging_enabled/apigatewayv2_api_access_logging_enabled.metadata.json b/prowler/providers/aws/services/apigatewayv2/apigatewayv2_api_access_logging_enabled/apigatewayv2_api_access_logging_enabled.metadata.json index 285e4f149b..f2000ac300 100644 --- a/prowler/providers/aws/services/apigatewayv2/apigatewayv2_api_access_logging_enabled/apigatewayv2_api_access_logging_enabled.metadata.json +++ b/prowler/providers/aws/services/apigatewayv2/apigatewayv2_api_access_logging_enabled/apigatewayv2_api_access_logging_enabled.metadata.json @@ -28,7 +28,9 @@ "Url": "https://docs.aws.amazon.com/apigateway/latest/developerguide/security-monitoring.html" } }, - "Categories": [], + "Categories": [ + "logging" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.metadata.json b/prowler/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.metadata.json index d214d90424..1782488f9c 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.metadata.json +++ b/prowler/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.metadata.json @@ -24,7 +24,8 @@ } }, "Categories": [ - "forensics-ready" + "forensics-ready", + "logging" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled.metadata.json b/prowler/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled.metadata.json index c8e52e9f47..d71816dffb 100644 --- a/prowler/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled.metadata.json +++ b/prowler/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled.metadata.json @@ -24,7 +24,8 @@ } }, "Categories": [ - "forensics-ready" + "forensics-ready", + "logging" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled.metadata.json b/prowler/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled.metadata.json index 046ed10af7..9f9339038f 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled.metadata.json +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled.metadata.json @@ -28,7 +28,8 @@ } }, "Categories": [ - "forensics-ready" + "forensics-ready", + "logging" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/aws/services/directoryservice/directoryservice_directory_log_forwarding_enabled/directoryservice_directory_log_forwarding_enabled.metadata.json b/prowler/providers/aws/services/directoryservice/directoryservice_directory_log_forwarding_enabled/directoryservice_directory_log_forwarding_enabled.metadata.json index f0fc8b8cf1..153e8d3600 100644 --- a/prowler/providers/aws/services/directoryservice/directoryservice_directory_log_forwarding_enabled/directoryservice_directory_log_forwarding_enabled.metadata.json +++ b/prowler/providers/aws/services/directoryservice/directoryservice_directory_log_forwarding_enabled/directoryservice_directory_log_forwarding_enabled.metadata.json @@ -23,7 +23,9 @@ "Url": "https://docs.aws.amazon.com/directoryservice/latest/admin-guide/incident-response.html" } }, - "Categories": [], + "Categories": [ + "logging" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/eks/eks_control_plane_logging_all_types_enabled/eks_control_plane_logging_all_types_enabled.metadata.json b/prowler/providers/aws/services/eks/eks_control_plane_logging_all_types_enabled/eks_control_plane_logging_all_types_enabled.metadata.json index 3c5e4036ed..23fa32f4d3 100644 --- a/prowler/providers/aws/services/eks/eks_control_plane_logging_all_types_enabled/eks_control_plane_logging_all_types_enabled.metadata.json +++ b/prowler/providers/aws/services/eks/eks_control_plane_logging_all_types_enabled/eks_control_plane_logging_all_types_enabled.metadata.json @@ -26,7 +26,8 @@ } }, "Categories": [ - "forensics-ready" + "forensics-ready", + "logging" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/aws/services/elb/elb_logging_enabled/elb_logging_enabled.metadata.json b/prowler/providers/aws/services/elb/elb_logging_enabled/elb_logging_enabled.metadata.json index 9483ccf127..31c049b150 100644 --- a/prowler/providers/aws/services/elb/elb_logging_enabled/elb_logging_enabled.metadata.json +++ b/prowler/providers/aws/services/elb/elb_logging_enabled/elb_logging_enabled.metadata.json @@ -26,7 +26,8 @@ } }, "Categories": [ - "forensics-ready" + "forensics-ready", + "logging" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/aws/services/elbv2/elbv2_logging_enabled/elbv2_logging_enabled.metadata.json b/prowler/providers/aws/services/elbv2/elbv2_logging_enabled/elbv2_logging_enabled.metadata.json index 3117bbb6c5..b335f1d193 100644 --- a/prowler/providers/aws/services/elbv2/elbv2_logging_enabled/elbv2_logging_enabled.metadata.json +++ b/prowler/providers/aws/services/elbv2/elbv2_logging_enabled/elbv2_logging_enabled.metadata.json @@ -25,7 +25,10 @@ "Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html" } }, - "Categories": [], + "Categories": [ + "forensics-ready", + "logging" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/glue/glue_etl_jobs_logging_enabled/glue_etl_jobs_logging_enabled.metadata.json b/prowler/providers/aws/services/glue/glue_etl_jobs_logging_enabled/glue_etl_jobs_logging_enabled.metadata.json index 0a50c8b1bf..eeb0556e8d 100644 --- a/prowler/providers/aws/services/glue/glue_etl_jobs_logging_enabled/glue_etl_jobs_logging_enabled.metadata.json +++ b/prowler/providers/aws/services/glue/glue_etl_jobs_logging_enabled/glue_etl_jobs_logging_enabled.metadata.json @@ -25,7 +25,9 @@ "Url": "https://docs.aws.amazon.com/glue/latest/dg/monitor-continuous-logging-enable.html" } }, - "Categories": [], + "Categories": [ + "logging" + ], "DependsOn": [], "RelatedTo": [], "Notes": "This check is being removed since logs for all AWS Glue jobs are now always sent to Amazon CloudWatch." diff --git a/prowler/providers/aws/services/mq/mq_broker_logging_enabled/mq_broker_logging_enabled.metadata.json b/prowler/providers/aws/services/mq/mq_broker_logging_enabled/mq_broker_logging_enabled.metadata.json index 60804d10f2..d73f495caa 100644 --- a/prowler/providers/aws/services/mq/mq_broker_logging_enabled/mq_broker_logging_enabled.metadata.json +++ b/prowler/providers/aws/services/mq/mq_broker_logging_enabled/mq_broker_logging_enabled.metadata.json @@ -25,7 +25,9 @@ "Url": "https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/configure-logging-monitoring-activemq.html" } }, - "Categories": [], + "Categories": [ + "logging" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/neptune/neptune_cluster_integration_cloudwatch_logs/neptune_cluster_integration_cloudwatch_logs.metadata.json b/prowler/providers/aws/services/neptune/neptune_cluster_integration_cloudwatch_logs/neptune_cluster_integration_cloudwatch_logs.metadata.json index 12f90b666e..c554ac4af6 100644 --- a/prowler/providers/aws/services/neptune/neptune_cluster_integration_cloudwatch_logs/neptune_cluster_integration_cloudwatch_logs.metadata.json +++ b/prowler/providers/aws/services/neptune/neptune_cluster_integration_cloudwatch_logs/neptune_cluster_integration_cloudwatch_logs.metadata.json @@ -25,7 +25,9 @@ "Url": "https://docs.aws.amazon.com/neptune/latest/userguide/cloudwatch-logs.html" } }, - "Categories": [], + "Categories": [ + "logging" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/opensearch/opensearch_service_domains_audit_logging_enabled/opensearch_service_domains_audit_logging_enabled.metadata.json b/prowler/providers/aws/services/opensearch/opensearch_service_domains_audit_logging_enabled/opensearch_service_domains_audit_logging_enabled.metadata.json index 91fba0e956..618341c4e8 100644 --- a/prowler/providers/aws/services/opensearch/opensearch_service_domains_audit_logging_enabled/opensearch_service_domains_audit_logging_enabled.metadata.json +++ b/prowler/providers/aws/services/opensearch/opensearch_service_domains_audit_logging_enabled/opensearch_service_domains_audit_logging_enabled.metadata.json @@ -27,7 +27,8 @@ } }, "Categories": [ - "forensics-ready" + "forensics-ready", + "logging" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/aws/services/opensearch/opensearch_service_domains_cloudwatch_logging_enabled/opensearch_service_domains_cloudwatch_logging_enabled.metadata.json b/prowler/providers/aws/services/opensearch/opensearch_service_domains_cloudwatch_logging_enabled/opensearch_service_domains_cloudwatch_logging_enabled.metadata.json index fe9f312a34..bb621ad5de 100644 --- a/prowler/providers/aws/services/opensearch/opensearch_service_domains_cloudwatch_logging_enabled/opensearch_service_domains_cloudwatch_logging_enabled.metadata.json +++ b/prowler/providers/aws/services/opensearch/opensearch_service_domains_cloudwatch_logging_enabled/opensearch_service_domains_cloudwatch_logging_enabled.metadata.json @@ -27,7 +27,8 @@ } }, "Categories": [ - "forensics-ready" + "forensics-ready", + "logging" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/aws/services/rds/rds_cluster_integration_cloudwatch_logs/rds_cluster_integration_cloudwatch_logs.metadata.json b/prowler/providers/aws/services/rds/rds_cluster_integration_cloudwatch_logs/rds_cluster_integration_cloudwatch_logs.metadata.json index dc0feb7358..8f207bd32c 100644 --- a/prowler/providers/aws/services/rds/rds_cluster_integration_cloudwatch_logs/rds_cluster_integration_cloudwatch_logs.metadata.json +++ b/prowler/providers/aws/services/rds/rds_cluster_integration_cloudwatch_logs/rds_cluster_integration_cloudwatch_logs.metadata.json @@ -23,7 +23,9 @@ "Url": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/publishing_cloudwatchlogs.html" } }, - "Categories": [], + "Categories": [ + "logging" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/rds/rds_instance_integration_cloudwatch_logs/rds_instance_integration_cloudwatch_logs.metadata.json b/prowler/providers/aws/services/rds/rds_instance_integration_cloudwatch_logs/rds_instance_integration_cloudwatch_logs.metadata.json index 7125e64eef..ed83e489d4 100644 --- a/prowler/providers/aws/services/rds/rds_instance_integration_cloudwatch_logs/rds_instance_integration_cloudwatch_logs.metadata.json +++ b/prowler/providers/aws/services/rds/rds_instance_integration_cloudwatch_logs/rds_instance_integration_cloudwatch_logs.metadata.json @@ -23,7 +23,9 @@ "Url": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/publishing_cloudwatchlogs.html" } }, - "Categories": [], + "Categories": [ + "logging" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/redshift/redshift_cluster_audit_logging/redshift_cluster_audit_logging.metadata.json b/prowler/providers/aws/services/redshift/redshift_cluster_audit_logging/redshift_cluster_audit_logging.metadata.json index b1b1934033..863998afca 100644 --- a/prowler/providers/aws/services/redshift/redshift_cluster_audit_logging/redshift_cluster_audit_logging.metadata.json +++ b/prowler/providers/aws/services/redshift/redshift_cluster_audit_logging/redshift_cluster_audit_logging.metadata.json @@ -24,7 +24,8 @@ } }, "Categories": [ - "forensics-ready" + "forensics-ready", + "logging" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/aws/services/vpc/vpc_flow_logs_enabled/vpc_flow_logs_enabled.metadata.json b/prowler/providers/aws/services/vpc/vpc_flow_logs_enabled/vpc_flow_logs_enabled.metadata.json index af5a837946..80816cb495 100644 --- a/prowler/providers/aws/services/vpc/vpc_flow_logs_enabled/vpc_flow_logs_enabled.metadata.json +++ b/prowler/providers/aws/services/vpc/vpc_flow_logs_enabled/vpc_flow_logs_enabled.metadata.json @@ -26,7 +26,8 @@ } }, "Categories": [ - "forensics-ready" + "forensics-ready", + "logging" ], "DependsOn": [], "RelatedTo": [], From f3cfacae9a1b5d893824744777e692717ea1f0c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:26:10 -0500 Subject: [PATCH 04/55] chore(deps): bump msgraph-sdk from 1.11.0 to 1.12.0 (#5722) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3698c79115..d3cf398a3f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3007,13 +3007,13 @@ dev = ["bumpver", "isort", "mypy", "pylint", "pytest", "yapf"] [[package]] name = "msgraph-sdk" -version = "1.11.0" +version = "1.12.0" description = "The Microsoft Graph Python SDK" optional = false python-versions = ">=3.8" files = [ - {file = "msgraph_sdk-1.11.0-py3-none-any.whl", hash = "sha256:91e5243005298cec19c6c4712647dcb8bc66e54f02cd1d6b0c93541bd50ae0be"}, - {file = "msgraph_sdk-1.11.0.tar.gz", hash = "sha256:636d73e48e9cb5fddefe110669c17d47059228e4855db6a55cafad5ff02af8a2"}, + {file = "msgraph_sdk-1.12.0-py3-none-any.whl", hash = "sha256:ac298b546b240391b0e407379d039db32862a56d6fe15cf7c5f7e77631fc6771"}, + {file = "msgraph_sdk-1.12.0.tar.gz", hash = "sha256:fbb5a8a9f6eed89b496f207eb35b6b4cfc7fefa75608aeef07477a3b2276d4fa"}, ] [package.dependencies] @@ -5852,4 +5852,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "3d245d541604a9c5629b8d8b822e41907cebff85a228d1b3b0b1fdfe61c4aa08" +content-hash = "55969ea0b1ad720bdc01d3cdd20bbee64531a260e74f10aaec230477bd9faa33" diff --git a/pyproject.toml b/pyproject.toml index 122cb8d94d..859d2892d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,7 +59,7 @@ google-auth-httplib2 = ">=0.1,<0.3" jsonschema = "4.23.0" kubernetes = "31.0.0" microsoft-kiota-abstractions = "1.6.2" -msgraph-sdk = "1.11.0" +msgraph-sdk = "1.12.0" numpy = "2.0.2" pandas = "2.2.3" presidio-analyzer = "2.2.355" From c434181dfd2779b8e8ef00c24e6732241a8106c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 11:43:21 -0500 Subject: [PATCH 05/55] chore(deps): bump azure-mgmt-containerservice from 32.1.0 to 33.0.0 (#5706) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index d3cf398a3f..5d3a666de5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -432,13 +432,13 @@ isodate = ">=0.6.1,<1.0.0" [[package]] name = "azure-mgmt-containerservice" -version = "32.1.0" +version = "33.0.0" description = "Microsoft Azure Container Service Management Client Library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "azure_mgmt_containerservice-32.1.0-py3-none-any.whl", hash = "sha256:8911e7bd0ca2162c6baad3ba57fbe8517fd7d697c4e54883f3c8fed43de61ac0"}, - {file = "azure_mgmt_containerservice-32.1.0.tar.gz", hash = "sha256:af1392d4c4f5174b5bb7e3b28e92137d298f3add54e402772df2afccf1263ba0"}, + {file = "azure_mgmt_containerservice-33.0.0-py3-none-any.whl", hash = "sha256:5b36cb25075c9fc3070135b5a45debae9eca3183b00f8c21faddb37b2daf4a60"}, + {file = "azure_mgmt_containerservice-33.0.0.tar.gz", hash = "sha256:868583dcdb8a4905de03a84a9b7903d76a1cb59acd9c3736f02bc743b5047c9e"}, ] [package.dependencies] @@ -5852,4 +5852,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "55969ea0b1ad720bdc01d3cdd20bbee64531a260e74f10aaec230477bd9faa33" +content-hash = "b69006f39bcc5ff5ea4adfbceddf680bffa7c4c2b986116c2cad83c239d34fab" diff --git a/pyproject.toml b/pyproject.toml index 859d2892d5..1fdddf473a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ azure-mgmt-applicationinsights = "4.0.0" azure-mgmt-authorization = "4.0.0" azure-mgmt-compute = "33.0.0" azure-mgmt-containerregistry = "10.3.0" -azure-mgmt-containerservice = "32.1.0" +azure-mgmt-containerservice = "33.0.0" azure-mgmt-cosmosdb = "9.6.0" azure-mgmt-keyvault = "10.3.1" azure-mgmt-monitor = "6.0.2" From f1334190d85a24aed39c3c683f931b070059d646 Mon Sep 17 00:00:00 2001 From: Sergio Garcia <38561120+MrCloudSec@users.noreply.github.com> Date: Tue, 12 Nov 2024 12:50:47 -0500 Subject: [PATCH 06/55] fix(aws): remove `cloudwatch_log_group_no_critical_pii_in_logs` check (#5736) --- Dockerfile | 2 +- docs/tutorials/configuration_file.md | 2 - poetry.lock | 680 +----------------- prowler/config/config.yaml | 25 - .../__init__.py | 0 ...roup_no_critical_pii_in_logs.metadata.json | 32 - ...watch_log_group_no_critical_pii_in_logs.py | 147 ---- .../services/cloudwatch/cloudwatch_service.py | 2 - pyproject.toml | 1 - tests/config/config_test.py | 20 - tests/config/fixtures/config.yaml | 25 - tests/config/fixtures/config_old.yaml | 25 - ..._log_group_no_critical_pii_in_logs_test.py | 206 ------ 13 files changed, 3 insertions(+), 1164 deletions(-) delete mode 100644 prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/__init__.py delete mode 100644 prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/cloudwatch_log_group_no_critical_pii_in_logs.metadata.json delete mode 100644 prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/cloudwatch_log_group_no_critical_pii_in_logs.py delete mode 100644 tests/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/cloudwatch_log_group_no_critical_pii_in_logs_test.py diff --git a/Dockerfile b/Dockerfile index 1daf59df51..613bd790eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ LABEL maintainer="https://github.com/prowler-cloud/prowler" # Update system dependencies and install essential tools #hadolint ignore=DL3018 -RUN apk --no-cache upgrade && apk --no-cache add curl git g++ +RUN apk --no-cache upgrade && apk --no-cache add curl git # Create non-root user RUN mkdir -p /home/prowler && \ diff --git a/docs/tutorials/configuration_file.md b/docs/tutorials/configuration_file.md index 2f47cc497e..d4ef971d41 100644 --- a/docs/tutorials/configuration_file.md +++ b/docs/tutorials/configuration_file.md @@ -30,8 +30,6 @@ The following list includes all the AWS checks with configurable variables that | `cloudtrail_threat_detection_privilege_escalation` | `threat_detection_privilege_escalation_entropy` | Integer | | `cloudtrail_threat_detection_privilege_escalation` | `threat_detection_privilege_escalation_minutes` | Integer | | `cloudwatch_log_group_no_secrets_in_logs` | `secrets_ignore_patterns` | List of Strings | -| `cloudwatch_log_group_no_critical_pii_in_logs` | `critical_pii_entities` | List of Strings | -| `cloudwatch_log_group_no_critical_pii_in_logs` | `pii_language` | String | | `cloudwatch_log_group_retention_policy_specific_days_enabled` | `log_group_retention_days` | Integer | | `codebuild_project_no_secrets_in_variables` | `excluded_sensitive_environment_variables` | List of Strings | | `codebuild_project_no_secrets_in_variables` | `secrets_ignore_patterns` | List of Strings | diff --git a/poetry.lock b/poetry.lock index 5d3a666de5..8c7ad6d0ba 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "about-time" @@ -757,47 +757,6 @@ files = [ {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, ] -[[package]] -name = "blis" -version = "1.0.1" -description = "The Blis BLAS-like linear algebra library, as a self-contained C-extension." -optional = false -python-versions = "*" -files = [ - {file = "blis-1.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:232f68f78fd9ab2f8bf01424cd6a265edd97e14e26186ef48eca1b6f212e928f"}, - {file = "blis-1.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90a796d8311b6439d63c91ed09163be995173a1ca5bedc510c1b45cbdbb7886c"}, - {file = "blis-1.0.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:388b0b0c55df884eb5853292d53c7c3daaa009b3d991e7a95413e46e82e88058"}, - {file = "blis-1.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c89149eed90c3b76f50c88d8d68661ca07d8b4bfaa517eedf5b77ddf640bb1"}, - {file = "blis-1.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f23cff163c0a256b580c33364f1598a4f49f532074c673a734d9b02a101adce1"}, - {file = "blis-1.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8d4d403d4b4979d5b1e6f12637ed25fb3bd4e260440997d9ba9e752f9e9c91cb"}, - {file = "blis-1.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:a6440bed28f405ab81d1d57b3fb96b7ea8b5b1f3d3a0d29860b0a814fe4ece27"}, - {file = "blis-1.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5076b33c04b864709d0d22c40e9f57dc06ee9c2bd2f7ab72b10ffa74a470d9e6"}, - {file = "blis-1.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6b1f791fa3eeb267b97f5877a6bdcc09c868603b84422acf7fd138ec9b96c3c"}, - {file = "blis-1.0.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c249649c0f7c06b2368a9d5c6b12209255981e96304c6140a3beffaff6cae62"}, - {file = "blis-1.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:632580f1d3ff44fb36e8a21a9457f23aeaff5d35d108bd2ef0393b9f6d85de93"}, - {file = "blis-1.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:64393297304712818020734fa75735f4543243eefc44858ef3c99375d5bb029a"}, - {file = "blis-1.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d3b3293c795007dbf4ba8ceaf3184a6bf510ca3252d0229607792f52e8702bb2"}, - {file = "blis-1.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:60e1b03663bee7a37b4b3131b4179d283868ccb10a3260fed01dd980866bc49f"}, - {file = "blis-1.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e031b6cf0dcc026d697a860bf53bc02b5f26ddb5a3ecd23933c51cf22803825b"}, - {file = "blis-1.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f860c1723928c40d4920a05130d21a600dcb5fbf07aa1fe8f4bdff2c4a5238a5"}, - {file = "blis-1.0.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:399196506829c278836028511f351f32f2c992263e91ef876c6bc41dc2483d3d"}, - {file = "blis-1.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce20590af2c6ff02d66ffed4148ea8ea1e3f772febb8471e3e58614e71d428c1"}, - {file = "blis-1.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e6ad60d9cd81523429f46109b31e3c4bbdd0dc28a328d6dbdcdff8447a53a61e"}, - {file = "blis-1.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb553b233fc815957c5bbb5d2fc2f6d2b199c123ec15c5000db935662849e543"}, - {file = "blis-1.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:376188493f590c4310ca534b687ef96c21c8224eb1ef4a0420703eebe175d6fa"}, - {file = "blis-1.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:136eae35dd9fd6c1923b95d5623aa174abd43d5b764bed79fd37bf6ad40282e7"}, - {file = "blis-1.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:16b5a418c55825d0d7912f63befee865da7522ce8f388b8ef76224050c5f8386"}, - {file = "blis-1.0.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:806ab0838efa5e434e6fcdce86542601d0263256d483623bc86e91728a645de4"}, - {file = "blis-1.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33ad60cde863a7cf8e69fcf0c3b965ab0f804f0332adb35552788bb76660c97f"}, - {file = "blis-1.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:40459f37f430de0c6059631cc9f7588c3bccb6ade074571015ea09856b91cb69"}, - {file = "blis-1.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f0977eaba2d3f64df649201aa281cf3216e7bec18929a7374c3f3f36100db029"}, - {file = "blis-1.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:5b4c8298c49b25058462731139b711e35e73623280cee03c3b0804cbdee50c0d"}, - {file = "blis-1.0.1.tar.gz", hash = "sha256:91739cd850ca8100dcddbd8ad66942cab20c9473cdea9a35b165b11d7b8d91e4"}, -] - -[package.dependencies] -numpy = ">=2.0.0,<3.0.0" - [[package]] name = "boto3" version = "1.35.57" @@ -850,17 +809,6 @@ files = [ {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, ] -[[package]] -name = "catalogue" -version = "2.0.10" -description = "Super lightweight function registries for your library" -optional = false -python-versions = ">=3.6" -files = [ - {file = "catalogue-2.0.10-py3-none-any.whl", hash = "sha256:58c2de0020aa90f4a2da7dfad161bf7b3b054c86a5f09fcedc0b2b740c109a9f"}, - {file = "catalogue-2.0.10.tar.gz", hash = "sha256:4f56daa940913d3f09d589c191c74e5a6d51762b3a9e37dd53b7437afd6cda15"}, -] - [[package]] name = "certifi" version = "2024.8.30" @@ -1122,26 +1070,6 @@ click = ">=4.0" [package.extras] dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] -[[package]] -name = "cloudpathlib" -version = "0.20.0" -description = "pathlib-style classes for cloud storage services." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cloudpathlib-0.20.0-py3-none-any.whl", hash = "sha256:7af3bcefbf73392ae7f31c08b3660ec31607f8c01b7f6262d4d73469a845f641"}, - {file = "cloudpathlib-0.20.0.tar.gz", hash = "sha256:f6ef7ca409a510f7ba4639ba50ab3fc5b6dee82d6dff0d7f5715fd0c9ab35891"}, -] - -[package.dependencies] -typing_extensions = {version = ">4", markers = "python_version < \"3.11\""} - -[package.extras] -all = ["cloudpathlib[azure]", "cloudpathlib[gs]", "cloudpathlib[s3]"] -azure = ["azure-storage-blob (>=12)", "azure-storage-file-datalake (>=12)"] -gs = ["google-cloud-storage"] -s3 = ["boto3 (>=1.34.0)"] - [[package]] name = "colorama" version = "0.4.6" @@ -1153,21 +1081,6 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "confection" -version = "0.1.5" -description = "The sweetest config system for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "confection-0.1.5-py3-none-any.whl", hash = "sha256:e29d3c3f8eac06b3f77eb9dfb4bf2fc6bcc9622a98ca00a698e3d019c6430b14"}, - {file = "confection-0.1.5.tar.gz", hash = "sha256:8e72dd3ca6bd4f48913cd220f10b8275978e740411654b6e8ca6d7008c590f0e"}, -] - -[package.dependencies] -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<3.0.0" -srsly = ">=2.4.0,<3.0.0" - [[package]] name = "coverage" version = "7.6.4" @@ -1294,48 +1207,6 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] -[[package]] -name = "cymem" -version = "2.0.8" -description = "Manage calls to calloc/free through Cython" -optional = false -python-versions = "*" -files = [ - {file = "cymem-2.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:77b5d3a73c41a394efd5913ab7e48512054cd2dabb9582d489535456641c7666"}, - {file = "cymem-2.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bd33da892fb560ba85ea14b1528c381ff474048e861accc3366c8b491035a378"}, - {file = "cymem-2.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29a551eda23eebd6d076b855f77a5ed14a1d1cae5946f7b3cb5de502e21b39b0"}, - {file = "cymem-2.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8260445652ae5ab19fff6851f32969a7b774f309162e83367dd0f69aac5dbf7"}, - {file = "cymem-2.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:a63a2bef4c7e0aec7c9908bca0a503bf91ac7ec18d41dd50dc7dff5d994e4387"}, - {file = "cymem-2.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b84b780d52cb2db53d4494fe0083c4c5ee1f7b5380ceaea5b824569009ee5bd"}, - {file = "cymem-2.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d5f83dc3cb5a39f0e32653cceb7c8ce0183d82f1162ca418356f4a8ed9e203e"}, - {file = "cymem-2.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ac218cf8a43a761dc6b2f14ae8d183aca2bbb85b60fe316fd6613693b2a7914"}, - {file = "cymem-2.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c993589d1811ec665d37437d5677b8757f53afadd927bf8516ac8ce2d3a50c"}, - {file = "cymem-2.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:ab3cf20e0eabee9b6025ceb0245dadd534a96710d43fb7a91a35e0b9e672ee44"}, - {file = "cymem-2.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cb51fddf1b920abb1f2742d1d385469bc7b4b8083e1cfa60255e19bc0900ccb5"}, - {file = "cymem-2.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9235957f8c6bc2574a6a506a1687164ad629d0b4451ded89d49ebfc61b52660c"}, - {file = "cymem-2.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2cc38930ff5409f8d61f69a01e39ecb185c175785a1c9bec13bcd3ac8a614ba"}, - {file = "cymem-2.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bf49e3ea2c441f7b7848d5c61b50803e8cbd49541a70bb41ad22fce76d87603"}, - {file = "cymem-2.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:ecd12e3bacf3eed5486e4cd8ede3c12da66ee0e0a9d0ae046962bc2bb503acef"}, - {file = "cymem-2.0.8-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:167d8019db3b40308aabf8183fd3fbbc256323b645e0cbf2035301058c439cd0"}, - {file = "cymem-2.0.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17cd2c2791c8f6b52f269a756ba7463f75bf7265785388a2592623b84bb02bf8"}, - {file = "cymem-2.0.8-cp36-cp36m-win_amd64.whl", hash = "sha256:6204f0a3307bf45d109bf698ba37997ce765f21e359284328e4306c7500fcde8"}, - {file = "cymem-2.0.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9c05db55ea338648f8e5f51dd596568c7f62c5ae32bf3fa5b1460117910ebae"}, - {file = "cymem-2.0.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ce641f7ba0489bd1b42a4335a36f38c8507daffc29a512681afaba94a0257d2"}, - {file = "cymem-2.0.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6b83a5972a64f62796118da79dfeed71f4e1e770b2b7455e889c909504c2358"}, - {file = "cymem-2.0.8-cp37-cp37m-win_amd64.whl", hash = "sha256:ada6eb022e4a0f4f11e6356a5d804ceaa917174e6cf33c0b3e371dbea4dd2601"}, - {file = "cymem-2.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e593cd57e2e19eb50c7ddaf7e230b73c890227834425b9dadcd4a86834ef2ab"}, - {file = "cymem-2.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d513f0d5c6d76facdc605e42aa42c8d50bb7dedca3144ec2b47526381764deb0"}, - {file = "cymem-2.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e370dd54359101b125bfb191aca0542718077b4edb90ccccba1a28116640fed"}, - {file = "cymem-2.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84f8c58cde71b8fc7024883031a4eec66c0a9a4d36b7850c3065493652695156"}, - {file = "cymem-2.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:6a6edddb30dd000a27987fcbc6f3c23b7fe1d74f539656952cb086288c0e4e29"}, - {file = "cymem-2.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b896c83c08dadafe8102a521f83b7369a9c5cc3e7768eca35875764f56703f4c"}, - {file = "cymem-2.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a4f8f2bfee34f6f38b206997727d29976666c89843c071a968add7d61a1e8024"}, - {file = "cymem-2.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7372e2820fa66fd47d3b135f3eb574ab015f90780c3a21cfd4809b54f23a4723"}, - {file = "cymem-2.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4e57bee56d35b90fc2cba93e75b2ce76feaca05251936e28a96cf812a1f5dda"}, - {file = "cymem-2.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ceeab3ce2a92c7f3b2d90854efb32cb203e78cb24c836a5a9a2cac221930303b"}, - {file = "cymem-2.0.8.tar.gz", hash = "sha256:8fb09d222e21dcf1c7e907dc85cf74501d4cea6c4ed4ac6c9e016f98fb59cbbf"}, -] - [[package]] name = "dash" version = "2.18.2" @@ -2302,42 +2173,6 @@ websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.dev0 || >=0.43.dev0" [package.extras] adal = ["adal (>=1.0.2)"] -[[package]] -name = "langcodes" -version = "3.4.1" -description = "Tools for labeling human languages with IETF language tags" -optional = false -python-versions = ">=3.8" -files = [ - {file = "langcodes-3.4.1-py3-none-any.whl", hash = "sha256:68f686fc3d358f222674ecf697ddcee3ace3c2fe325083ecad2543fd28a20e77"}, - {file = "langcodes-3.4.1.tar.gz", hash = "sha256:a24879fed238013ac3af2424b9d1124e38b4a38b2044fd297c8ff38e5912e718"}, -] - -[package.dependencies] -language-data = ">=1.2" - -[package.extras] -build = ["build", "twine"] -test = ["pytest", "pytest-cov"] - -[[package]] -name = "language-data" -version = "1.2.0" -description = "Supplementary data about languages used by the langcodes module" -optional = false -python-versions = "*" -files = [ - {file = "language_data-1.2.0-py3-none-any.whl", hash = "sha256:77d5cab917f91ee0b2f1aa7018443e911cf8985ef734ca2ba3940770f6a3816b"}, - {file = "language_data-1.2.0.tar.gz", hash = "sha256:82a86050bbd677bfde87d97885b17566cfe75dad3ac4f5ce44b52c28f752e773"}, -] - -[package.dependencies] -marisa-trie = ">=0.7.7" - -[package.extras] -build = ["build", "twine"] -test = ["pytest", "pytest-cov"] - [[package]] name = "lazy-object-proxy" version = "1.10.0" @@ -2384,97 +2219,6 @@ files = [ {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"}, ] -[[package]] -name = "marisa-trie" -version = "1.2.1" -description = "Static memory-efficient and fast Trie-like structures for Python." -optional = false -python-versions = ">=3.7" -files = [ - {file = "marisa_trie-1.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a2eb41d2f9114d8b7bd66772c237111e00d2bae2260824560eaa0a1e291ce9e8"}, - {file = "marisa_trie-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9e956e6a46f604b17d570901e66f5214fb6f658c21e5e7665deace236793cef6"}, - {file = "marisa_trie-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bd45142501300e7538b2e544905580918b67b1c82abed1275fe4c682c95635fa"}, - {file = "marisa_trie-1.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8443d116c612cfd1961fbf76769faf0561a46d8e317315dd13f9d9639ad500c"}, - {file = "marisa_trie-1.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:875a6248e60fbb48d947b574ffa4170f34981f9e579bde960d0f9a49ea393ecc"}, - {file = "marisa_trie-1.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:746a7c60a17fccd3cfcfd4326926f02ea4fcdfc25d513411a0c4fc8e4a1ca51f"}, - {file = "marisa_trie-1.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e70869737cc0e5bd903f620667da6c330d6737048d1f44db792a6af68a1d35be"}, - {file = "marisa_trie-1.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06b099dd743676dbcd8abd8465ceac8f6d97d8bfaabe2c83b965495523b4cef2"}, - {file = "marisa_trie-1.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d2a82eb21afdaf22b50d9b996472305c05ca67fc4ff5a026a220320c9c961db6"}, - {file = "marisa_trie-1.2.1-cp310-cp310-win32.whl", hash = "sha256:8951e7ce5d3167fbd085703b4cbb3f47948ed66826bef9a2173c379508776cf5"}, - {file = "marisa_trie-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:5685a14b3099b1422c4f59fa38b0bf4b5342ee6cc38ae57df9666a0b28eeaad3"}, - {file = "marisa_trie-1.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed3fb4ed7f2084597e862bcd56c56c5529e773729a426c083238682dba540e98"}, - {file = "marisa_trie-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fe69fb9ffb2767746181f7b3b29bbd3454d1d24717b5958e030494f3d3cddf3"}, - {file = "marisa_trie-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4728ed3ae372d1ea2cdbd5eaa27b8f20a10e415d1f9d153314831e67d963f281"}, - {file = "marisa_trie-1.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8cf4f25cf895692b232f49aa5397af6aba78bb679fb917a05fce8d3cb1ee446d"}, - {file = "marisa_trie-1.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cca7f96236ffdbf49be4b2e42c132e3df05968ac424544034767650913524de"}, - {file = "marisa_trie-1.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7eb20bf0e8b55a58d2a9b518aabc4c18278787bdba476c551dd1c1ed109e509"}, - {file = "marisa_trie-1.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b1ec93f0d1ee6d7ab680a6d8ea1a08bf264636358e92692072170032dda652ba"}, - {file = "marisa_trie-1.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e2699255d7ac610dee26d4ae7bda5951d05c7d9123a22e1f7c6a6f1964e0a4e4"}, - {file = "marisa_trie-1.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c484410911182457a8a1a0249d0c09c01e2071b78a0a8538cd5f7fa45589b13a"}, - {file = "marisa_trie-1.2.1-cp311-cp311-win32.whl", hash = "sha256:ad548117744b2bcf0e3d97374608be0a92d18c2af13d98b728d37cd06248e571"}, - {file = "marisa_trie-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:436f62d27714970b9cdd3b3c41bdad046f260e62ebb0daa38125ef70536fc73b"}, - {file = "marisa_trie-1.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:638506eacf20ca503fff72221a7e66a6eadbf28d6a4a6f949fcf5b1701bb05ec"}, - {file = "marisa_trie-1.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de1665eaafefa48a308e4753786519888021740501a15461c77bdfd57638e6b4"}, - {file = "marisa_trie-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f713af9b8aa66a34cd3a78c7d150a560a75734713abe818a69021fd269e927fa"}, - {file = "marisa_trie-1.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2a7d00f53f4945320b551bccb826b3fb26948bde1a10d50bb9802fabb611b10"}, - {file = "marisa_trie-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98042040d1d6085792e8d0f74004fc0f5f9ca6091c298f593dd81a22a4643854"}, - {file = "marisa_trie-1.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6532615111eec2c79e711965ece0bc95adac1ff547a7fff5ffca525463116deb"}, - {file = "marisa_trie-1.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:20948e40ab2038e62b7000ca6b4a913bc16c91a2c2e6da501bd1f917eeb28d51"}, - {file = "marisa_trie-1.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66b23e5b35dd547f85bf98db7c749bc0ffc57916ade2534a6bbc32db9a4abc44"}, - {file = "marisa_trie-1.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6704adf0247d2dda42e876b793be40775dff46624309ad99bc7537098bee106d"}, - {file = "marisa_trie-1.2.1-cp312-cp312-win32.whl", hash = "sha256:3ad356442c2fea4c2a6f514738ddf213d23930f942299a2b2c05df464a00848a"}, - {file = "marisa_trie-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:f2806f75817392cedcacb24ac5d80b0350dde8d3861d67d045c1d9b109764114"}, - {file = "marisa_trie-1.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:b5ea16e69bfda0ac028c921b58de1a4aaf83d43934892977368579cd3c0a2554"}, - {file = "marisa_trie-1.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9f627f4e41be710b6cb6ed54b0128b229ac9d50e2054d9cde3af0fef277c23cf"}, - {file = "marisa_trie-1.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5e649f3dc8ab5476732094f2828cc90cac3be7c79bc0c8318b6fda0c1d248db4"}, - {file = "marisa_trie-1.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46e528ee71808c961baf8c3ce1c46a8337ec7a96cc55389d11baafe5b632f8e9"}, - {file = "marisa_trie-1.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36aa4401a1180615f74d575571a6550081d84fc6461e9aefc0bb7b2427af098e"}, - {file = "marisa_trie-1.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce59bcd2cda9bb52b0e90cc7f36413cd86c3d0ce7224143447424aafb9f4aa48"}, - {file = "marisa_trie-1.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f4cd800704a5fc57e53c39c3a6b0c9b1519ebdbcb644ede3ee67a06eb542697d"}, - {file = "marisa_trie-1.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2428b495003c189695fb91ceeb499f9fcced3a2dce853e17fa475519433c67ff"}, - {file = "marisa_trie-1.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:735c363d9aaac82eaf516a28f7c6b95084c2e176d8231c87328dc80e112a9afa"}, - {file = "marisa_trie-1.2.1-cp313-cp313-win32.whl", hash = "sha256:eba6ca45500ca1a042466a0684aacc9838e7f20fe2605521ee19f2853062798f"}, - {file = "marisa_trie-1.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:aa7cd17e1c690ce96c538b2f4aae003d9a498e65067dd433c52dd069009951d4"}, - {file = "marisa_trie-1.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5e43891a37b0d7f618819fea14bd951289a0a8e3dd0da50c596139ca83ebb9b1"}, - {file = "marisa_trie-1.2.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6946100a43f933fad6bc458c502a59926d80b321d5ac1ed2ff9c56605360496f"}, - {file = "marisa_trie-1.2.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4177dc0bd1374e82be9b2ba4d0c2733b0a85b9d154ceeea83a5bee8c1e62fbf"}, - {file = "marisa_trie-1.2.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f35c2603a6be168088ed1db6ad1704b078aa8f39974c60888fbbced95dcadad4"}, - {file = "marisa_trie-1.2.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d659fda873d8dcb2c14c2c331de1dee21f5a902d7f2de7978b62c6431a8850ef"}, - {file = "marisa_trie-1.2.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:b0ef26733d3c836be79e812071e1a431ce1f807955a27a981ebb7993d95f842b"}, - {file = "marisa_trie-1.2.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:536ea19ce6a2ce61c57fed4123ecd10d18d77a0db45cd2741afff2b8b68f15b3"}, - {file = "marisa_trie-1.2.1-cp37-cp37m-win32.whl", hash = "sha256:0ee6cf6a16d9c3d1c94e21c8e63c93d8b34bede170ca4e937e16e1c0700d399f"}, - {file = "marisa_trie-1.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7e7b1786e852e014d03e5f32dbd991f9a9eb223dd3fa9a2564108b807e4b7e1c"}, - {file = "marisa_trie-1.2.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:952af3a5859c3b20b15a00748c36e9eb8316eb2c70bd353ae1646da216322908"}, - {file = "marisa_trie-1.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24a81aa7566e4ec96fc4d934581fe26d62eac47fc02b35fa443a0bb718b471e8"}, - {file = "marisa_trie-1.2.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9c9b32b14651a6dcf9e8857d2df5d29d322a1ea8c0be5c8ffb88f9841c4ec62b"}, - {file = "marisa_trie-1.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ac170d20b97beb75059ba65d1ccad6b434d777c8992ab41ffabdade3b06dd74"}, - {file = "marisa_trie-1.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da4e4facb79614cc4653cfd859f398e4db4ca9ab26270ff12610e50ed7f1f6c6"}, - {file = "marisa_trie-1.2.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25688f34cac3bec01b4f655ffdd6c599a01f0bd596b4a79cf56c6f01a7df3560"}, - {file = "marisa_trie-1.2.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:1db3213b451bf058d558f6e619bceff09d1d130214448a207c55e1526e2773a1"}, - {file = "marisa_trie-1.2.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d5648c6dcc5dc9200297fb779b1663b8a4467bda034a3c69bd9c32d8afb33b1d"}, - {file = "marisa_trie-1.2.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5bd39a4e1cc839a88acca2889d17ebc3f202a5039cd6059a13148ce75c8a6244"}, - {file = "marisa_trie-1.2.1-cp38-cp38-win32.whl", hash = "sha256:594f98491a96c7f1ffe13ce292cef1b4e63c028f0707effdea0f113364c1ae6c"}, - {file = "marisa_trie-1.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:5fe5a286f997848a410eebe1c28657506adaeb405220ee1e16cfcfd10deb37f2"}, - {file = "marisa_trie-1.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c0fe2ace0cb1806badbd1c551a8ec2f8d4cf97bf044313c082ef1acfe631ddca"}, - {file = "marisa_trie-1.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:67f0c2ec82c20a02c16fc9ba81dee2586ef20270127c470cb1054767aa8ba310"}, - {file = "marisa_trie-1.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a3c98613180cf1730e221933ff74b454008161b1a82597e41054127719964188"}, - {file = "marisa_trie-1.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:429858a0452a7bedcf67bc7bb34383d00f666c980cb75a31bcd31285fbdd4403"}, - {file = "marisa_trie-1.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2eacb84446543082ec50f2fb563f1a94c96804d4057b7da8ed815958d0cdfbe"}, - {file = "marisa_trie-1.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:852d7bcf14b0c63404de26e7c4c8d5d65ecaeca935e93794331bc4e2f213660b"}, - {file = "marisa_trie-1.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e58788004adda24c401d1751331618ed20c507ffc23bfd28d7c0661a1cf0ad16"}, - {file = "marisa_trie-1.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aefe0973cc4698e0907289dc0517ab0c7cdb13d588201932ff567d08a50b0e2e"}, - {file = "marisa_trie-1.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6c50c861faad0a5c091bd763e0729f958c316e678dfa065d3984fbb9e4eacbcd"}, - {file = "marisa_trie-1.2.1-cp39-cp39-win32.whl", hash = "sha256:b1ce340da608530500ab4f963f12d6bfc8d8680900919a60dbdc9b78c02060a4"}, - {file = "marisa_trie-1.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:ce37d8ca462bb64cc13f529b9ed92f7b21fe8d1f1679b62e29f9cb7d0e888b49"}, - {file = "marisa_trie-1.2.1.tar.gz", hash = "sha256:3a27c408e2aefc03e0f1d25b2ff2afb85aac3568f6fa2ae2a53b57a2e87ce29d"}, -] - -[package.dependencies] -setuptools = "*" - -[package.extras] -test = ["hypothesis", "pytest", "readme-renderer"] - [[package]] name = "markdown" version = "3.7" @@ -3169,48 +2913,6 @@ files = [ [package.extras] dev = ["build", "pytest", "pytest-cov", "twine"] -[[package]] -name = "murmurhash" -version = "1.0.10" -description = "Cython bindings for MurmurHash" -optional = false -python-versions = ">=3.6" -files = [ - {file = "murmurhash-1.0.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3e90eef568adca5e17a91f96975e9a782ace3a617bbb3f8c8c2d917096e9bfeb"}, - {file = "murmurhash-1.0.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f8ecb00cc1ab57e4b065f9fb3ea923b55160c402d959c69a0b6dbbe8bc73efc3"}, - {file = "murmurhash-1.0.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3310101004d9e2e0530c2fed30174448d998ffd1b50dcbfb7677e95db101aa4b"}, - {file = "murmurhash-1.0.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65401a6f1778676253cbf89c1f45a8a7feb7d73038e483925df7d5943c08ed9"}, - {file = "murmurhash-1.0.10-cp310-cp310-win_amd64.whl", hash = "sha256:f23f2dfc7174de2cdc5007c0771ab8376a2a3f48247f32cac4a5563e40c6adcc"}, - {file = "murmurhash-1.0.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90ed37ee2cace9381b83d56068334f77e3e30bc521169a1f886a2a2800e965d6"}, - {file = "murmurhash-1.0.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:22e9926fdbec9d24ced9b0a42f0fee68c730438be3cfb00c2499fd495caec226"}, - {file = "murmurhash-1.0.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54bfbfd68baa99717239b8844600db627f336a08b1caf4df89762999f681cdd1"}, - {file = "murmurhash-1.0.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b9d200a09d48ef67f6840b77c14f151f2b6c48fd69661eb75c7276ebdb146c"}, - {file = "murmurhash-1.0.10-cp311-cp311-win_amd64.whl", hash = "sha256:e5d7cfe392c0a28129226271008e61e77bf307afc24abf34f386771daa7b28b0"}, - {file = "murmurhash-1.0.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:96f0a070344d4802ea76a160e0d4c88b7dc10454d2426f48814482ba60b38b9e"}, - {file = "murmurhash-1.0.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9f61862060d677c84556610ac0300a0776cb13cb3155f5075ed97e80f86e55d9"}, - {file = "murmurhash-1.0.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3b6d2d877d8881a08be66d906856d05944be0faf22b9a0390338bcf45299989"}, - {file = "murmurhash-1.0.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f54b0031d8696fed17ed6e9628f339cdea0ba2367ca051e18ff59193f52687"}, - {file = "murmurhash-1.0.10-cp312-cp312-win_amd64.whl", hash = "sha256:97e09d675de2359e586f09de1d0de1ab39f9911edffc65c9255fb5e04f7c1f85"}, - {file = "murmurhash-1.0.10-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b64e5332932993fef598e78d633b1ba664789ab73032ed511f3dc615a631a1a"}, - {file = "murmurhash-1.0.10-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e2a38437a8497e082408aa015c6d90554b9e00c2c221fdfa79728a2d99a739e"}, - {file = "murmurhash-1.0.10-cp36-cp36m-win_amd64.whl", hash = "sha256:55f4e4f9291a53c36070330950b472d72ba7d331e4ce3ce1ab349a4f458f7bc4"}, - {file = "murmurhash-1.0.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:16ef9f0855952493fe08929d23865425906a8c0c40607ac8a949a378652ba6a9"}, - {file = "murmurhash-1.0.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cc3351ae92b89c2fcdc6e41ac6f17176dbd9b3554c96109fd0713695d8663e7"}, - {file = "murmurhash-1.0.10-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6559fef7c2e7349a42a63549067709b656d6d1580752bd76be1541d8b2d65718"}, - {file = "murmurhash-1.0.10-cp37-cp37m-win_amd64.whl", hash = "sha256:8bf49e3bb33febb7057ae3a5d284ef81243a1e55eaa62bdcd79007cddbdc0461"}, - {file = "murmurhash-1.0.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f1605fde07030516eb63d77a598dd164fb9bf217fd937dbac588fe7e47a28c40"}, - {file = "murmurhash-1.0.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4904f7e68674a64eb2b08823c72015a5e14653e0b4b109ea00c652a005a59bad"}, - {file = "murmurhash-1.0.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0438f0cb44cf1cd26251f72c1428213c4197d40a4e3f48b1efc3aea12ce18517"}, - {file = "murmurhash-1.0.10-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db1171a3f9a10571931764cdbfaa5371f4cf5c23c680639762125cb075b833a5"}, - {file = "murmurhash-1.0.10-cp38-cp38-win_amd64.whl", hash = "sha256:1c9fbcd7646ad8ba67b895f71d361d232c6765754370ecea473dd97d77afe99f"}, - {file = "murmurhash-1.0.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7024ab3498434f22f8e642ae31448322ad8228c65c8d9e5dc2d563d57c14c9b8"}, - {file = "murmurhash-1.0.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a99dedfb7f0cc5a4cd76eb409ee98d3d50eba024f934e705914f6f4d765aef2c"}, - {file = "murmurhash-1.0.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b580b8503647de5dd7972746b7613ea586270f17ac92a44872a9b1b52c36d68"}, - {file = "murmurhash-1.0.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75840212bf75eb1352c946c3cf1622dacddd6d6bdda34368237d1eb3568f23a"}, - {file = "murmurhash-1.0.10-cp39-cp39-win_amd64.whl", hash = "sha256:a4209962b9f85de397c3203ea4b3a554da01ae9fd220fdab38757d4e9eba8d1a"}, - {file = "murmurhash-1.0.10.tar.gz", hash = "sha256:5282aab1317804c6ebd6dd7f69f15ba9075aee671c44a34be2bde0f1b11ef88a"}, -] - [[package]] name = "mypy-extensions" version = "1.0.0" @@ -3644,17 +3346,6 @@ tzdata = ">=2020.1" [package.extras] test = ["time-machine (>=2.6.0)"] -[[package]] -name = "phonenumbers" -version = "8.13.49" -description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers." -optional = false -python-versions = "*" -files = [ - {file = "phonenumbers-8.13.49-py2.py3-none-any.whl", hash = "sha256:e17140955ab3d8f9580727372ea64c5ada5327932d6021ef6fd203c3db8c8139"}, - {file = "phonenumbers-8.13.49.tar.gz", hash = "sha256:e608ccb61f0bd42e6db1d2c421f7c22186b88f494870bf40aa31d1a2718ab0ae"}, -] - [[package]] name = "platformdirs" version = "4.3.6" @@ -3731,75 +3422,6 @@ docs = ["sphinx (>=1.7.1)"] redis = ["redis"] tests = ["pytest (>=5.4.1)", "pytest-cov (>=2.8.1)", "pytest-mypy (>=0.8.0)", "pytest-timeout (>=2.1.0)", "redis", "sphinx (>=6.0.0)", "types-redis"] -[[package]] -name = "preshed" -version = "3.0.9" -description = "Cython hash table that trusts the keys are pre-hashed" -optional = false -python-versions = ">=3.6" -files = [ - {file = "preshed-3.0.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f96ef4caf9847b2bb9868574dcbe2496f974e41c2b83d6621c24fb4c3fc57e3"}, - {file = "preshed-3.0.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a61302cf8bd30568631adcdaf9e6b21d40491bd89ba8ebf67324f98b6c2a2c05"}, - {file = "preshed-3.0.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99499e8a58f58949d3f591295a97bca4e197066049c96f5d34944dd21a497193"}, - {file = "preshed-3.0.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea6b6566997dc3acd8c6ee11a89539ac85c77275b4dcefb2dc746d11053a5af8"}, - {file = "preshed-3.0.9-cp310-cp310-win_amd64.whl", hash = "sha256:bfd523085a84b1338ff18f61538e1cfcdedc4b9e76002589a301c364d19a2e36"}, - {file = "preshed-3.0.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7c2364da27f2875524ce1ca754dc071515a9ad26eb5def4c7e69129a13c9a59"}, - {file = "preshed-3.0.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:182138033c0730c683a6d97e567ceb8a3e83f3bff5704f300d582238dbd384b3"}, - {file = "preshed-3.0.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:345a10be3b86bcc6c0591d343a6dc2bfd86aa6838c30ced4256dfcfa836c3a64"}, - {file = "preshed-3.0.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51d0192274aa061699b284f9fd08416065348edbafd64840c3889617ee1609de"}, - {file = "preshed-3.0.9-cp311-cp311-win_amd64.whl", hash = "sha256:96b857d7a62cbccc3845ac8c41fd23addf052821be4eb987f2eb0da3d8745aa1"}, - {file = "preshed-3.0.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4fe6720012c62e6d550d6a5c1c7ad88cacef8388d186dad4bafea4140d9d198"}, - {file = "preshed-3.0.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e04f05758875be9751e483bd3c519c22b00d3b07f5a64441ec328bb9e3c03700"}, - {file = "preshed-3.0.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a55091d0e395f1fdb62ab43401bb9f8b46c7d7794d5b071813c29dc1ab22fd0"}, - {file = "preshed-3.0.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7de8f5138bcac7870424e09684dc3dd33c8e30e81b269f6c9ede3d8c7bb8e257"}, - {file = "preshed-3.0.9-cp312-cp312-win_amd64.whl", hash = "sha256:24229c77364628743bc29c5620c5d6607ed104f0e02ae31f8a030f99a78a5ceb"}, - {file = "preshed-3.0.9-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b73b0f7ecc58095ebbc6ca26ec806008ef780190fe685ce471b550e7eef58dc2"}, - {file = "preshed-3.0.9-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cb90ecd5bec71c21d95962db1a7922364d6db2abe284a8c4b196df8bbcc871e"}, - {file = "preshed-3.0.9-cp36-cp36m-win_amd64.whl", hash = "sha256:e304a0a8c9d625b70ba850c59d4e67082a6be9c16c4517b97850a17a282ebee6"}, - {file = "preshed-3.0.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1fa6d3d5529b08296ff9b7b4da1485c080311fd8744bbf3a86019ff88007b382"}, - {file = "preshed-3.0.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1e5173809d85edd420fc79563b286b88b4049746b797845ba672cf9435c0e7"}, - {file = "preshed-3.0.9-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fe81eb21c7d99e8b9a802cc313b998c5f791bda592903c732b607f78a6b7dc4"}, - {file = "preshed-3.0.9-cp37-cp37m-win_amd64.whl", hash = "sha256:78590a4a952747c3766e605ce8b747741005bdb1a5aa691a18aae67b09ece0e6"}, - {file = "preshed-3.0.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3452b64d97ce630e200c415073040aa494ceec6b7038f7a2a3400cbd7858e952"}, - {file = "preshed-3.0.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ac970d97b905e9e817ec13d31befd5b07c9cfec046de73b551d11a6375834b79"}, - {file = "preshed-3.0.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eebaa96ece6641cd981491cba995b68c249e0b6877c84af74971eacf8990aa19"}, - {file = "preshed-3.0.9-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d473c5f6856e07a88d41fe00bb6c206ecf7b34c381d30de0b818ba2ebaf9406"}, - {file = "preshed-3.0.9-cp38-cp38-win_amd64.whl", hash = "sha256:0de63a560f10107a3f0a9e252cc3183b8fdedcb5f81a86938fd9f1dcf8a64adf"}, - {file = "preshed-3.0.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3a9ad9f738084e048a7c94c90f40f727217387115b2c9a95c77f0ce943879fcd"}, - {file = "preshed-3.0.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a671dfa30b67baa09391faf90408b69c8a9a7f81cb9d83d16c39a182355fbfce"}, - {file = "preshed-3.0.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23906d114fc97c17c5f8433342495d7562e96ecfd871289c2bb2ed9a9df57c3f"}, - {file = "preshed-3.0.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:778cf71f82cedd2719b256f3980d556d6fb56ec552334ba79b49d16e26e854a0"}, - {file = "preshed-3.0.9-cp39-cp39-win_amd64.whl", hash = "sha256:a6e579439b329eb93f32219ff27cb358b55fbb52a4862c31a915a098c8a22ac2"}, - {file = "preshed-3.0.9.tar.gz", hash = "sha256:721863c5244ffcd2651ad0928951a2c7c77b102f4e11a251ad85d37ee7621660"}, -] - -[package.dependencies] -cymem = ">=2.0.2,<2.1.0" -murmurhash = ">=0.28.0,<1.1.0" - -[[package]] -name = "presidio-analyzer" -version = "2.2.355" -description = "Presidio Analyzer package" -optional = false -python-versions = "<4.0,>=3.8" -files = [ - {file = "presidio_analyzer-2.2.355-py3-none-any.whl", hash = "sha256:c4c5bc6d82e4f94059fd554c31365fc5d29afe51763391b6ecc33b628bdb5858"}, -] - -[package.dependencies] -phonenumbers = ">=8.12,<9.0.0" -pyyaml = "*" -regex = "*" -spacy = ">=3.4.4,<4.0.0" -tldextract = "*" - -[package.extras] -azure-ai-language = ["azure-ai-textanalytics", "azure-core"] -server = ["flask (>=1.1)"] -stanza = ["spacy_stanza", "stanza"] -transformers = ["spacy_huggingface_pipelines"] - [[package]] name = "propcache" version = "0.2.0" @@ -5010,31 +4632,6 @@ files = [ [package.extras] optional = ["SQLAlchemy (>=1.4,<3)", "aiodns (>1.0)", "aiohttp (>=3.7.3,<4)", "boto3 (<=2)", "websocket-client (>=1,<2)", "websockets (>=9.1,<14)"] -[[package]] -name = "smart-open" -version = "7.0.5" -description = "Utils for streaming large files (S3, HDFS, GCS, Azure Blob Storage, gzip, bz2...)" -optional = false -python-versions = "<4.0,>=3.7" -files = [ - {file = "smart_open-7.0.5-py3-none-any.whl", hash = "sha256:8523ed805c12dff3eaa50e9c903a6cb0ae78800626631c5fe7ea073439847b89"}, - {file = "smart_open-7.0.5.tar.gz", hash = "sha256:d3672003b1dbc85e2013e4983b88eb9a5ccfd389b0d4e5015f39a9ee5620ec18"}, -] - -[package.dependencies] -wrapt = "*" - -[package.extras] -all = ["azure-common", "azure-core", "azure-storage-blob", "boto3", "google-cloud-storage (>=2.6.0)", "paramiko", "requests", "zstandard"] -azure = ["azure-common", "azure-core", "azure-storage-blob"] -gcs = ["google-cloud-storage (>=2.6.0)"] -http = ["requests"] -s3 = ["boto3"] -ssh = ["paramiko"] -test = ["awscli", "azure-common", "azure-core", "azure-storage-blob", "boto3", "google-cloud-storage (>=2.6.0)", "moto[server]", "numpy", "paramiko", "pyopenssl", "pytest", "pytest-benchmark", "pytest-rerunfailures", "requests", "responses", "zstandard"] -webhdfs = ["requests"] -zst = ["zstandard"] - [[package]] name = "smmap" version = "5.0.1" @@ -5057,152 +4654,6 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] -[[package]] -name = "spacy" -version = "3.8.2" -description = "Industrial-strength Natural Language Processing (NLP) in Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "spacy-3.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:795e74493036dda0a576093b797a4fdc1aaa83d66f6c9af0e5b6b1c640dc2222"}, - {file = "spacy-3.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d432e78fe2a7424aba9a741db07ce58487d3b74fae4e20a779142828e61bc402"}, - {file = "spacy-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536a8ba17359540de502285934bf357805d978128d7bd5e84ba53d28b32a0ffb"}, - {file = "spacy-3.8.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6a0d0d39baa1cb9f5bb82874cbe1067bf494f76277a383f1f7b29f7a855d41a9"}, - {file = "spacy-3.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:9dcfcfda558b3e47946b2041c7a4203b78e542d0de20997a7c0a6d11b58b2522"}, - {file = "spacy-3.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e5d48918028cff6d69d9915dad64f0e32ebd5f1e4f1fa81a2e17e56a6f61e05"}, - {file = "spacy-3.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:455f845b88ed795d7e595070ee84b65b3ea382357811e09fc744789a20b7b5f7"}, - {file = "spacy-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05d8a4cbfdb90049053564790a0d12fa790c9471580cb6a1f8bdc2b6e74703dd"}, - {file = "spacy-3.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e3c3e67f786f1410d08420ffcaba0f80dc58387ab6172dcdac1a73353d3a85c7"}, - {file = "spacy-3.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:cfe0c4558f635c67677e36d9a315f51d51f824870589c4846c95e880042a2ceb"}, - {file = "spacy-3.8.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0ce56f3c46dd4cebb5aaa3a40966e813b7fc6a540d547788a7d00cca10cd60a9"}, - {file = "spacy-3.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09faa873cf23d5136b1c1ce6378054a885f650fda96e1655a3ab49e2e7fdd15b"}, - {file = "spacy-3.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e992a11de9b727c61288541945c0ffc37ed998aca76bfd557937c2c195d7d4"}, - {file = "spacy-3.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be962a8188fb20d6c2065e1e865d1799ebbf544c1af67ab8c75cb279bf5448c7"}, - {file = "spacy-3.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:04546c5f5ed607387d4e9ecf57614e90c5784866a10a3c6dbe5b06e9b18a2f29"}, - {file = "spacy-3.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7c5fb8b804ebf1c2791b384d61391e9d0227bcfdecd6c861291690813b8a6eb1"}, - {file = "spacy-3.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3647233b2454e8e7bae94232563c9bff849db9e26bf61ef51122ef723de009fe"}, - {file = "spacy-3.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca20e2d9b4aeaedd7068d6419762d66cfad82bc8b1e63e36714601686d67f163"}, - {file = "spacy-3.8.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:be3aa3e7d456627acbcb7f585156ee463c01d006a07aeb20b43a8543a02cd047"}, - {file = "spacy-3.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:54c63d31ef410ebb5b0fd72729afaf50f876bf2bc29f73c6c5fc3676ae4158a1"}, - {file = "spacy-3.8.2.tar.gz", hash = "sha256:4b37ebd25ada4059b0dc9e0893e70dde5df83485329a068ef04580e70892a65d"}, -] - -[package.dependencies] -catalogue = ">=2.0.6,<2.1.0" -cymem = ">=2.0.2,<2.1.0" -jinja2 = "*" -langcodes = ">=3.2.0,<4.0.0" -murmurhash = ">=0.28.0,<1.1.0" -numpy = {version = ">=1.19.0", markers = "python_version >= \"3.9\""} -packaging = ">=20.0" -preshed = ">=3.0.2,<3.1.0" -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<3.0.0" -requests = ">=2.13.0,<3.0.0" -setuptools = "*" -spacy-legacy = ">=3.0.11,<3.1.0" -spacy-loggers = ">=1.0.0,<2.0.0" -srsly = ">=2.4.3,<3.0.0" -thinc = ">=8.3.0,<8.4.0" -tqdm = ">=4.38.0,<5.0.0" -typer = ">=0.3.0,<1.0.0" -wasabi = ">=0.9.1,<1.2.0" -weasel = ">=0.1.0,<0.5.0" - -[package.extras] -apple = ["thinc-apple-ops (>=1.0.0,<2.0.0)"] -cuda = ["cupy (>=5.0.0b4,<13.0.0)"] -cuda-autodetect = ["cupy-wheel (>=11.0.0,<13.0.0)"] -cuda100 = ["cupy-cuda100 (>=5.0.0b4,<13.0.0)"] -cuda101 = ["cupy-cuda101 (>=5.0.0b4,<13.0.0)"] -cuda102 = ["cupy-cuda102 (>=5.0.0b4,<13.0.0)"] -cuda110 = ["cupy-cuda110 (>=5.0.0b4,<13.0.0)"] -cuda111 = ["cupy-cuda111 (>=5.0.0b4,<13.0.0)"] -cuda112 = ["cupy-cuda112 (>=5.0.0b4,<13.0.0)"] -cuda113 = ["cupy-cuda113 (>=5.0.0b4,<13.0.0)"] -cuda114 = ["cupy-cuda114 (>=5.0.0b4,<13.0.0)"] -cuda115 = ["cupy-cuda115 (>=5.0.0b4,<13.0.0)"] -cuda116 = ["cupy-cuda116 (>=5.0.0b4,<13.0.0)"] -cuda117 = ["cupy-cuda117 (>=5.0.0b4,<13.0.0)"] -cuda11x = ["cupy-cuda11x (>=11.0.0,<13.0.0)"] -cuda12x = ["cupy-cuda12x (>=11.5.0,<13.0.0)"] -cuda80 = ["cupy-cuda80 (>=5.0.0b4,<13.0.0)"] -cuda90 = ["cupy-cuda90 (>=5.0.0b4,<13.0.0)"] -cuda91 = ["cupy-cuda91 (>=5.0.0b4,<13.0.0)"] -cuda92 = ["cupy-cuda92 (>=5.0.0b4,<13.0.0)"] -ja = ["sudachidict-core (>=20211220)", "sudachipy (>=0.5.2,!=0.6.1)"] -ko = ["natto-py (>=0.9.0)"] -lookups = ["spacy-lookups-data (>=1.0.3,<1.1.0)"] -th = ["pythainlp (>=2.0)"] -transformers = ["spacy-transformers (>=1.1.2,<1.4.0)"] - -[[package]] -name = "spacy-legacy" -version = "3.0.12" -description = "Legacy registered functions for spaCy backwards compatibility" -optional = false -python-versions = ">=3.6" -files = [ - {file = "spacy-legacy-3.0.12.tar.gz", hash = "sha256:b37d6e0c9b6e1d7ca1cf5bc7152ab64a4c4671f59c85adaf7a3fcb870357a774"}, - {file = "spacy_legacy-3.0.12-py2.py3-none-any.whl", hash = "sha256:476e3bd0d05f8c339ed60f40986c07387c0a71479245d6d0f4298dbd52cda55f"}, -] - -[[package]] -name = "spacy-loggers" -version = "1.0.5" -description = "Logging utilities for SpaCy" -optional = false -python-versions = ">=3.6" -files = [ - {file = "spacy-loggers-1.0.5.tar.gz", hash = "sha256:d60b0bdbf915a60e516cc2e653baeff946f0cfc461b452d11a4d5458c6fe5f24"}, - {file = "spacy_loggers-1.0.5-py3-none-any.whl", hash = "sha256:196284c9c446cc0cdb944005384270d775fdeaf4f494d8e269466cfa497ef645"}, -] - -[[package]] -name = "srsly" -version = "2.4.8" -description = "Modern high-performance serialization utilities for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "srsly-2.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:17f3bcb418bb4cf443ed3d4dcb210e491bd9c1b7b0185e6ab10b6af3271e63b2"}, - {file = "srsly-2.4.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0b070a58e21ab0e878fd949f932385abb4c53dd0acb6d3a7ee75d95d447bc609"}, - {file = "srsly-2.4.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98286d20014ed2067ad02b0be1e17c7e522255b188346e79ff266af51a54eb33"}, - {file = "srsly-2.4.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18685084e2e0cc47c25158cbbf3e44690e494ef77d6418c2aae0598c893f35b0"}, - {file = "srsly-2.4.8-cp310-cp310-win_amd64.whl", hash = "sha256:980a179cbf4eb5bc56f7507e53f76720d031bcf0cef52cd53c815720eb2fc30c"}, - {file = "srsly-2.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5472ed9f581e10c32e79424c996cf54c46c42237759f4224806a0cd4bb770993"}, - {file = "srsly-2.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:50f10afe9230072c5aad9f6636115ea99b32c102f4c61e8236d8642c73ec7a13"}, - {file = "srsly-2.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c994a89ba247a4d4f63ef9fdefb93aa3e1f98740e4800d5351ebd56992ac75e3"}, - {file = "srsly-2.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7ed4a0c20fa54d90032be32f9c656b6d75445168da78d14fe9080a0c208ad"}, - {file = "srsly-2.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:7a919236a090fb93081fbd1cec030f675910f3863825b34a9afbcae71f643127"}, - {file = "srsly-2.4.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7583c03d114b4478b7a357a1915305163e9eac2dfe080da900555c975cca2a11"}, - {file = "srsly-2.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:94ccdd2f6db824c31266aaf93e0f31c1c43b8bc531cd2b3a1d924e3c26a4f294"}, - {file = "srsly-2.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db72d2974f91aee652d606c7def98744ca6b899bd7dd3009fd75ebe0b5a51034"}, - {file = "srsly-2.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a60c905fd2c15e848ce1fc315fd34d8a9cc72c1dee022a0d8f4c62991131307"}, - {file = "srsly-2.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:e0b8d5722057000694edf105b8f492e7eb2f3aa6247a5f0c9170d1e0d074151c"}, - {file = "srsly-2.4.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:196b4261f9d6372d1d3d16d1216b90c7e370b4141471322777b7b3c39afd1210"}, - {file = "srsly-2.4.8-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4750017e6d78590b02b12653e97edd25aefa4734281386cc27501d59b7481e4e"}, - {file = "srsly-2.4.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa034cd582ba9e4a120c8f19efa263fcad0f10fc481e73fb8c0d603085f941c4"}, - {file = "srsly-2.4.8-cp36-cp36m-win_amd64.whl", hash = "sha256:5a78ab9e9d177ee8731e950feb48c57380036d462b49e3fb61a67ce529ff5f60"}, - {file = "srsly-2.4.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:087e36439af517e259843df93eb34bb9e2d2881c34fa0f541589bcfbc757be97"}, - {file = "srsly-2.4.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad141d8a130cb085a0ed3a6638b643e2b591cb98a4591996780597a632acfe20"}, - {file = "srsly-2.4.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24d05367b2571c0d08d00459636b951e3ca2a1e9216318c157331f09c33489d3"}, - {file = "srsly-2.4.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3fd661a1c4848deea2849b78f432a70c75d10968e902ca83c07c89c9b7050ab8"}, - {file = "srsly-2.4.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec37233fe39af97b00bf20dc2ceda04d39b9ea19ce0ee605e16ece9785e11f65"}, - {file = "srsly-2.4.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d2fd4bc081f1d6a6063396b6d97b00d98e86d9d3a3ac2949dba574a84e148080"}, - {file = "srsly-2.4.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7347cff1eb4ef3fc335d9d4acc89588051b2df43799e5d944696ef43da79c873"}, - {file = "srsly-2.4.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9dc1da5cc94d77056b91ba38365c72ae08556b6345bef06257c7e9eccabafe"}, - {file = "srsly-2.4.8-cp38-cp38-win_amd64.whl", hash = "sha256:dc0bf7b6f23c9ecb49ec0924dc645620276b41e160e9b283ed44ca004c060d79"}, - {file = "srsly-2.4.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ff8df21d00d73c371bead542cefef365ee87ca3a5660de292444021ff84e3b8c"}, - {file = "srsly-2.4.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ac3e340e65a9fe265105705586aa56054dc3902789fcb9a8f860a218d6c0a00"}, - {file = "srsly-2.4.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06d1733f4275eff4448e96521cc7dcd8fdabd68ba9b54ca012dcfa2690db2644"}, - {file = "srsly-2.4.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be5b751ad88fdb58fb73871d456248c88204f213aaa3c9aab49b6a1802b3fa8d"}, - {file = "srsly-2.4.8-cp39-cp39-win_amd64.whl", hash = "sha256:822a38b8cf112348f3accbc73274a94b7bf82515cb14a85ba586d126a5a72851"}, - {file = "srsly-2.4.8.tar.gz", hash = "sha256:b24d95a65009c2447e0b49cda043ac53fecf4f09e358d87a57446458f91b8a91"}, -] - -[package.dependencies] -catalogue = ">=2.0.3,<2.1.0" - [[package]] name = "std-uritemplate" version = "2.0.0" @@ -5274,76 +4725,6 @@ files = [ doc = ["reno", "sphinx"] test = ["pytest", "tornado (>=4.5)", "typeguard"] -[[package]] -name = "thinc" -version = "8.3.2" -description = "A refreshing functional take on deep learning, compatible with your favorite libraries" -optional = false -python-versions = ">=3.6" -files = [ - {file = "thinc-8.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6af5b1b57fb1874079f7e84cd99c983c3dcb234a55845d8585d7e066b09755fb"}, - {file = "thinc-8.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f8b753b63714d38f36e951241466c650afe3177b0c8b220e180ebf4888f09f5e"}, - {file = "thinc-8.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d471e1261e5e650f93cfae9880928c2ee68ad0426656f02da4490dd24716a93b"}, - {file = "thinc-8.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8b46786063787a60a0d732a5d43d0196f632d3d35780c8fe1232d1378b1b5980"}, - {file = "thinc-8.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:f96c274c4119c92fb8fd8a708381080d47ad92994ef3041c791ed6d4b5c27761"}, - {file = "thinc-8.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:12e998780f40d36d4d5f3b760ef60ac60637643f2965ebe1948801ba44261a03"}, - {file = "thinc-8.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:54a5411daaca1718a73982767b714c1d0a5e142de73c916367baf1c13d79e8f0"}, - {file = "thinc-8.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed88275031dcaadd85d3deeb8eb12d1ec0ee6b4679e24cc893c81a30409ac4ee"}, - {file = "thinc-8.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ef0868e55108f05300f4508e6896ae4e9492f3415220e3da65579f693225816e"}, - {file = "thinc-8.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:813942d59881c4e4165ce95fef37ba30ce3366dac43289697d13a952a8208854"}, - {file = "thinc-8.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bce8ca6a62ab82f4595210ba7f18bbdb6e33561277c59060f2f04bdb93ac4fbc"}, - {file = "thinc-8.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b014a282e9ea6330a678b472d74f479c7a38168cbf570bdc740e50d960dd78a1"}, - {file = "thinc-8.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a80384c228ac6bbaf4ab7f7c9ca4a53c6053f2fb37b2b50c4730b9057f07e9fd"}, - {file = "thinc-8.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ae309b0788478984eafeac4e3c33a2de84a6ea251fd1e3528d8018d4b4347247"}, - {file = "thinc-8.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:fe8dac2749db23f8ebf09d7a7f29e1b99d67e7d7b183e106aa2b6c9b570f3015"}, - {file = "thinc-8.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e4b1e4149a3bfdeb308cee4b53b07d234e5b35495a7f35241b80acf7cb4a33d3"}, - {file = "thinc-8.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a6b507b1fecd1771fc448aa27dc42d024e5799d10f1ddad6abc6353ae72ef540"}, - {file = "thinc-8.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4edb20939c3a157beb386aee221a5e1bbfb7ffb90d63d22c80047ca0fa4d026d"}, - {file = "thinc-8.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d701de93d6d6bb029d24088d7f8cb8200f486658fd08dd859767b5eda6eba268"}, - {file = "thinc-8.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:2977c4811b7612984ded795dce182419b9f3058a1a55c191f75024ec2f4cb218"}, - {file = "thinc-8.3.2.tar.gz", hash = "sha256:3e8ef69eac89a601e11d47fc9e43d26ffe7ef682dcf667c94ff35ff690549aeb"}, -] - -[package.dependencies] -blis = ">=1.0.0,<1.1.0" -catalogue = ">=2.0.4,<2.1.0" -confection = ">=0.0.1,<1.0.0" -cymem = ">=2.0.2,<2.1.0" -murmurhash = ">=1.0.2,<1.1.0" -numpy = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3.9\""} -packaging = ">=20.0" -preshed = ">=3.0.2,<3.1.0" -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<3.0.0" -setuptools = "*" -srsly = ">=2.4.0,<3.0.0" -wasabi = ">=0.8.1,<1.2.0" - -[package.extras] -apple = ["thinc-apple-ops (>=1.0.0,<2.0.0)"] -cuda = ["cupy (>=5.0.0b4)"] -cuda-autodetect = ["cupy-wheel (>=11.0.0)"] -cuda100 = ["cupy-cuda100 (>=5.0.0b4)"] -cuda101 = ["cupy-cuda101 (>=5.0.0b4)"] -cuda102 = ["cupy-cuda102 (>=5.0.0b4)"] -cuda110 = ["cupy-cuda110 (>=5.0.0b4)"] -cuda111 = ["cupy-cuda111 (>=5.0.0b4)"] -cuda112 = ["cupy-cuda112 (>=5.0.0b4)"] -cuda113 = ["cupy-cuda113 (>=5.0.0b4)"] -cuda114 = ["cupy-cuda114 (>=5.0.0b4)"] -cuda115 = ["cupy-cuda115 (>=5.0.0b4)"] -cuda116 = ["cupy-cuda116 (>=5.0.0b4)"] -cuda117 = ["cupy-cuda117 (>=5.0.0b4)"] -cuda11x = ["cupy-cuda11x (>=11.0.0)"] -cuda12x = ["cupy-cuda12x (>=11.5.0)"] -cuda80 = ["cupy-cuda80 (>=5.0.0b4)"] -cuda90 = ["cupy-cuda90 (>=5.0.0b4)"] -cuda91 = ["cupy-cuda91 (>=5.0.0b4)"] -cuda92 = ["cupy-cuda92 (>=5.0.0b4)"] -datasets = ["ml-datasets (>=0.2.0,<0.3.0)"] -mxnet = ["mxnet (>=1.5.1,<1.6.0)"] -tensorflow = ["tensorflow (>=2.0.0,<2.6.0)"] -torch = ["torch (>=1.6.0)"] - [[package]] name = "tldextract" version = "5.1.3" @@ -5387,27 +4768,6 @@ files = [ {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, ] -[[package]] -name = "tqdm" -version = "4.67.0" -description = "Fast, Extensible Progress Meter" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tqdm-4.67.0-py3-none-any.whl", hash = "sha256:0cd8af9d56911acab92182e88d763100d4788bdf421d251616040cc4d44863be"}, - {file = "tqdm-4.67.0.tar.gz", hash = "sha256:fe5a6f95e6fe0b9755e9469b77b9c3cf850048224ecaa8293d7d2d31f97d869a"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] -discord = ["requests"] -notebook = ["ipywidgets (>=6)"] -slack = ["slack-sdk"] -telegram = ["requests"] - [[package]] name = "typer" version = "0.12.5" @@ -5522,20 +4882,6 @@ files = [ [package.dependencies] tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -[[package]] -name = "wasabi" -version = "1.1.3" -description = "A lightweight console printing and formatting toolkit" -optional = false -python-versions = ">=3.6" -files = [ - {file = "wasabi-1.1.3-py3-none-any.whl", hash = "sha256:f76e16e8f7e79f8c4c8be49b4024ac725713ab10cd7f19350ad18a8e3f71728c"}, - {file = "wasabi-1.1.3.tar.gz", hash = "sha256:4bb3008f003809db0c3e28b4daf20906ea871a2bb43f9914197d540f4f2e0878"}, -] - -[package.dependencies] -colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\" and python_version >= \"3.7\""} - [[package]] name = "watchdog" version = "6.0.0" @@ -5578,28 +4924,6 @@ files = [ [package.extras] watchmedo = ["PyYAML (>=3.10)"] -[[package]] -name = "weasel" -version = "0.4.1" -description = "Weasel: A small and easy workflow system" -optional = false -python-versions = ">=3.7" -files = [ - {file = "weasel-0.4.1-py3-none-any.whl", hash = "sha256:24140a090ea1ac512a2b2f479cc64192fd1d527a7f3627671268d08ed5ac418c"}, - {file = "weasel-0.4.1.tar.gz", hash = "sha256:aabc210f072e13f6744e5c3a28037f93702433405cd35673f7c6279147085aa9"}, -] - -[package.dependencies] -cloudpathlib = ">=0.7.0,<1.0.0" -confection = ">=0.0.4,<0.2.0" -packaging = ">=20.0" -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<3.0.0" -requests = ">=2.13.0,<3.0.0" -smart-open = ">=5.2.1,<8.0.0" -srsly = ">=2.4.3,<3.0.0" -typer = ">=0.3.0,<1.0.0" -wasabi = ">=0.9.1,<1.2.0" - [[package]] name = "websocket-client" version = "1.8.0" @@ -5852,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "b69006f39bcc5ff5ea4adfbceddf680bffa7c4c2b986116c2cad83c239d34fab" +content-hash = "cef6d708932a76766d79017a61af8e74cdce0ead795b286be27fb1af83068db5" diff --git a/prowler/config/config.yaml b/prowler/config/config.yaml index 2c528a5206..42e7afa6b9 100644 --- a/prowler/config/config.yaml +++ b/prowler/config/config.yaml @@ -72,31 +72,6 @@ aws: # AWS Cloudwatch Configuration # aws.cloudwatch_log_group_retention_policy_specific_days_enabled --> by default is 365 days log_group_retention_days: 365 - # aws.cloudwatch_log_group_no_critical_pii_in_logs --> see all available entities in https://microsoft.github.io/presidio/supported_entities/ - critical_pii_entities : [ - "CREDIT_CARD", # Credit card numbers are highly sensitive financial information. - "CRYPTO", # Crypto wallet numbers (e.g., Bitcoin addresses) can give access to cryptocurrency. - "IBAN_CODE", # International Bank Account Numbers are critical financial information. - "US_BANK_NUMBER", # US bank account numbers are sensitive and should be protected. - "US_SSN", # US Social Security Numbers are critical PII used for identity verification. - "US_PASSPORT", # US passport numbers can be used for identity theft. - "US_ITIN", # US Individual Taxpayer Identification Numbers are sensitive personal identifiers. - #"UK_NHS", # UK NHS numbers can be used to access medical records and other private information. - #"ES_NIF", # Spanish NIF (Personal tax ID) is critical for identification and tax purposes. - #"ES_NIE", # Spanish NIE (Foreigners ID card) is a critical identifier for foreign residents. - #"IT_FISCAL_CODE", # Italian personal identification code is sensitive PII for tax and legal purposes. - #"IT_PASSPORT", # Italian passport numbers are critical PII. - #"IT_IDENTITY_CARD", # Italian identity card numbers are critical for personal identification. - #"PL_PESEL", # Polish PESEL numbers are sensitive personal identifiers. - #"SG_NRIC_FIN", # Singapore National Registration Identification Card is critical PII. - #"AU_ABN", # Australian Business Numbers are critical for business identification. - #"AU_TFN", # Australian Tax File Numbers are sensitive and used for taxation purposes. - #"AU_MEDICARE", # Australian Medicare numbers are sensitive medical identifiers. - #"IN_PAN", # Indian Permanent Account Numbers are critical for tax purposes and identity. - #"IN_AADHAAR", # Indian Aadhaar numbers are highly sensitive and serve as a universal identity number. - #"FI_PERSONAL_IDENTITY_CODE" # Finnish Personal Identity Code is sensitive PII for personal identification. - ] - pii_language: "en" # Language for recognizing PII entities # AWS AppStream Session Configuration # aws.appstream_fleet_session_idle_disconnect_timeout diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/__init__.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/cloudwatch_log_group_no_critical_pii_in_logs.metadata.json b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/cloudwatch_log_group_no_critical_pii_in_logs.metadata.json deleted file mode 100644 index 813b9cacbe..0000000000 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/cloudwatch_log_group_no_critical_pii_in_logs.metadata.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "Provider": "aws", - "CheckID": "cloudwatch_log_group_no_critical_pii_in_logs", - "CheckTitle": "Check if secrets exists in CloudWatch logs.", - "CheckType": [], - "ServiceName": "cloudwatch", - "SubServiceName": "", - "ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:log-group/resource-id", - "Severity": "medium", - "ResourceType": "Other", - "Description": "Check if secrets exists in CloudWatch logs", - "Risk": "Storing sensitive data in CloudWatch logs could allow an attacker with read-only access to escalate their privileges or gain unauthorised access to systems.", - "RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html", - "Remediation": { - "Code": { - "CLI": "", - "NativeIaC": "", - "Other": "", - "Terraform": "" - }, - "Recommendation": { - "Text": "It is recommended that sensitive information is not logged to CloudWatch logs. Alternatively, sensitive data may be masked using a protection policy", - "Url": "https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/mask-sensitive-log-data.html" - } - }, - "Categories": [ - "secrets" - ], - "DependsOn": [], - "RelatedTo": [], - "Notes": "" -} diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/cloudwatch_log_group_no_critical_pii_in_logs.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/cloudwatch_log_group_no_critical_pii_in_logs.py deleted file mode 100644 index 2572a33702..0000000000 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/cloudwatch_log_group_no_critical_pii_in_logs.py +++ /dev/null @@ -1,147 +0,0 @@ -from json import dumps -from typing import Set - -from presidio_analyzer import AnalyzerEngine - -from prowler.lib.check.models import Check, Check_Report_AWS -from prowler.providers.aws.services.cloudwatch.cloudwatch_service import ( - convert_to_cloudwatch_timestamp_format, -) -from prowler.providers.aws.services.cloudwatch.logs_client import logs_client - - -class cloudwatch_log_group_no_critical_pii_in_logs(Check): - def execute(self): - findings = [] - - # Initialize the PII Analyzer engine - analyzer = AnalyzerEngine() - - if logs_client.log_groups: - critical_pii_entities = logs_client.audit_config.get( - "critical_pii_entities", - [ - "CREDIT_CARD", - "EMAIL_ADDRESS", - "PHONE_NUMBER", - "US_SSN", - "US_BANK_NUMBER", - "IBAN_CODE", - "US_PASSPORT", - ], - ) - pii_language = logs_client.audit_config.get("pii_language", "en") - for log_group in logs_client.log_groups.values(): - report = Check_Report_AWS(self.metadata()) - report.status = "PASS" - report.status_extended = ( - f"No critical PII found in {log_group.name} log group." - ) - report.region = log_group.region - report.resource_id = log_group.name - report.resource_arn = log_group.arn - report.resource_tags = log_group.tags - log_group_pii = [] - - if log_group.log_streams: - for log_stream_name in log_group.log_streams: - log_stream_pii = {} - log_stream_events = [ - dumps(event["message"]) - for event in log_group.log_streams[log_stream_name] - ] - - # Process log data in manageable chunks since the limit of Presidio Analyzer is 100,000 characters - MAX_CHUNK_SIZE = 100000 - for i in range(0, len(log_stream_events)): - chunk = log_stream_events[i] - - # Split if chunk exceeds max allowed size for analyzer - if len(chunk) > MAX_CHUNK_SIZE: - split_chunks = [ - chunk[j : j + MAX_CHUNK_SIZE] - for j in range(0, len(chunk), MAX_CHUNK_SIZE) - ] - else: - split_chunks = [chunk] - - for split_chunk in split_chunks: - # PII detection for each split chunk - pii_detection_result = analyzer.analyze( - text=split_chunk, - entities=critical_pii_entities, - score_threshold=1, - language=pii_language, - ) - - # Track cumulative character count to map PII to log event - cumulative_char_count = 0 - for j, log_event in enumerate( - log_stream_events[i : i + len(split_chunks)] - ): - log_event_length = len(log_event) - for pii in pii_detection_result: - # Check if PII start position falls within this log event - if ( - cumulative_char_count - <= pii.start - < cumulative_char_count + log_event_length - ): - flagged_event = log_group.log_streams[ - log_stream_name - ][j] - cloudwatch_timestamp = ( - convert_to_cloudwatch_timestamp_format( - flagged_event["timestamp"] - ) - ) - if ( - cloudwatch_timestamp - not in log_stream_pii - ): - log_stream_pii[cloudwatch_timestamp] = ( - SecretsDict() - ) - - # Add the detected PII entity to log_stream_pii - log_stream_pii[ - cloudwatch_timestamp - ].add_secret( - pii.start - cumulative_char_count, - pii.entity_type, - ) - cumulative_char_count += ( - log_event_length + 1 - ) # +1 to account for '\n' - - if log_stream_pii: - pii_string = "; ".join( - [ - f"at {timestamp} - {str(log_stream_pii[timestamp])}" - for timestamp in log_stream_pii - ] - ) - log_group_pii.append( - f"in log stream {log_stream_name} {pii_string}" - ) - if log_group_pii: - pii_string = "; ".join(log_group_pii) - report.status = "FAIL" - report.status_extended = f"Potential critical PII found in log group {log_group.name} {pii_string}." - findings.append(report) - return findings - - -class SecretsDict(dict[int, Set[str]]): - """Dictionary to track unique PII types on each line.""" - - def add_secret(self, line_number: int, pii_type: str) -> None: - """Add a PII type to a specific line number, ensuring no duplicates.""" - self.setdefault(line_number, set()).add(pii_type) - - def __str__(self) -> str: - """Generate a formatted string representation of the dictionary.""" - return ", ".join( - f"{', '.join(sorted(pii_types))} on line {line_number}" - for line_number, pii_types in sorted(self.items()) - ) diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py index 793185f10f..ac05ebae10 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py @@ -93,8 +93,6 @@ class Logs(AWSService): if ( "cloudwatch_log_group_no_secrets_in_logs" in provider.audit_metadata.expected_checks - or "cloudwatch_log_group_no_critical_pii_in_logs" - in provider.audit_metadata.expected_checks ): self.events_per_log_group_threshold = ( 1000 # The threshold for number of events to return per log group. diff --git a/pyproject.toml b/pyproject.toml index 1fdddf473a..a1f09abf28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,6 @@ microsoft-kiota-abstractions = "1.6.2" msgraph-sdk = "1.12.0" numpy = "2.0.2" pandas = "2.2.3" -presidio-analyzer = "2.2.355" py-ocsf-models = "0.2.0" pydantic = "1.10.18" python = ">=3.9,<3.13" diff --git a/tests/config/config_test.py b/tests/config/config_test.py index 4eb443e03b..685d304a34 100644 --- a/tests/config/config_test.py +++ b/tests/config/config_test.py @@ -33,16 +33,6 @@ old_config_aws = { "ec2_allowed_instance_owners": ["amazon-elb"], "trusted_account_ids": [], "log_group_retention_days": 365, - "critical_pii_entities": [ - "CREDIT_CARD", # Credit card numbers are highly sensitive financial information. - "CRYPTO", # Crypto wallet numbers (e.g., Bitcoin addresses) can give access to cryptocurrency. - "IBAN_CODE", # International Bank Account Numbers are critical financial information. - "US_BANK_NUMBER", # US bank account numbers are sensitive and should be protected. - "US_SSN", # US Social Security Numbers are critical PII used for identity verification. - "US_PASSPORT", # US passport numbers can be used for identity theft. - "US_ITIN", # US Individual Taxpayer Identification Numbers are sensitive personal identifiers. - ], - "pii_language": "en", # Language for recognizing PII entities "max_idle_disconnect_timeout_in_seconds": 600, "max_disconnect_timeout_in_seconds": 300, "max_session_duration_seconds": 36000, @@ -107,16 +97,6 @@ config_aws = { "fargate_windows_latest_version": "1.0.0", "trusted_account_ids": [], "log_group_retention_days": 365, - "critical_pii_entities": [ - "CREDIT_CARD", # Credit card numbers are highly sensitive financial information. - "CRYPTO", # Crypto wallet numbers (e.g., Bitcoin addresses) can give access to cryptocurrency. - "IBAN_CODE", # International Bank Account Numbers are critical financial information. - "US_BANK_NUMBER", # US bank account numbers are sensitive and should be protected. - "US_SSN", # US Social Security Numbers are critical PII used for identity verification. - "US_PASSPORT", # US passport numbers can be used for identity theft. - "US_ITIN", # US Individual Taxpayer Identification Numbers are sensitive personal identifiers. - ], - "pii_language": "en", # Language for recognizing PII entities "max_idle_disconnect_timeout_in_seconds": 600, "max_disconnect_timeout_in_seconds": 300, "max_session_duration_seconds": 36000, diff --git a/tests/config/fixtures/config.yaml b/tests/config/fixtures/config.yaml index 630c723de5..d769b83404 100644 --- a/tests/config/fixtures/config.yaml +++ b/tests/config/fixtures/config.yaml @@ -72,31 +72,6 @@ aws: # AWS Cloudwatch Configuration # aws.cloudwatch_log_group_retention_policy_specific_days_enabled --> by default is 365 days log_group_retention_days: 365 - # aws.cloudwatch_log_group_no_critical_pii_in_logs --> see all available entities in https://microsoft.github.io/presidio/supported_entities/ - critical_pii_entities : [ - "CREDIT_CARD", # Credit card numbers are highly sensitive financial information. - "CRYPTO", # Crypto wallet numbers (e.g., Bitcoin addresses) can give access to cryptocurrency. - "IBAN_CODE", # International Bank Account Numbers are critical financial information. - "US_BANK_NUMBER", # US bank account numbers are sensitive and should be protected. - "US_SSN", # US Social Security Numbers are critical PII used for identity verification. - "US_PASSPORT", # US passport numbers can be used for identity theft. - "US_ITIN", # US Individual Taxpayer Identification Numbers are sensitive personal identifiers. - #"UK_NHS", # UK NHS numbers can be used to access medical records and other private information. - #"ES_NIF", # Spanish NIF (Personal tax ID) is critical for identification and tax purposes. - #"ES_NIE", # Spanish NIE (Foreigners ID card) is a critical identifier for foreign residents. - #"IT_FISCAL_CODE", # Italian personal identification code is sensitive PII for tax and legal purposes. - #"IT_PASSPORT", # Italian passport numbers are critical PII. - #"IT_IDENTITY_CARD", # Italian identity card numbers are critical for personal identification. - #"PL_PESEL", # Polish PESEL numbers are sensitive personal identifiers. - #"SG_NRIC_FIN", # Singapore National Registration Identification Card is critical PII. - #"AU_ABN", # Australian Business Numbers are critical for business identification. - #"AU_TFN", # Australian Tax File Numbers are sensitive and used for taxation purposes. - #"AU_MEDICARE", # Australian Medicare numbers are sensitive medical identifiers. - #"IN_PAN", # Indian Permanent Account Numbers are critical for tax purposes and identity. - #"IN_AADHAAR", # Indian Aadhaar numbers are highly sensitive and serve as a universal identity number. - #"FI_PERSONAL_IDENTITY_CODE" # Finnish Personal Identity Code is sensitive PII for personal identification. - ] - pii_language: "en" # Language for recognizing PII entities # AWS AppStream Session Configuration # aws.appstream_fleet_session_idle_disconnect_timeout diff --git a/tests/config/fixtures/config_old.yaml b/tests/config/fixtures/config_old.yaml index 87243c4d41..cbd3bf4fa0 100644 --- a/tests/config/fixtures/config_old.yaml +++ b/tests/config/fixtures/config_old.yaml @@ -28,31 +28,6 @@ trusted_account_ids: [] # AWS Cloudwatch Configuration # aws.cloudwatch_log_group_retention_policy_specific_days_enabled --> by default is 365 days log_group_retention_days: 365 -# aws.cloudwatch_log_group_no_critical_pii_in_logs --> see all available entities in https://microsoft.github.io/presidio/supported_entities/ -critical_pii_entities : [ - "CREDIT_CARD", # Credit card numbers are highly sensitive financial information. - "CRYPTO", # Crypto wallet numbers (e.g., Bitcoin addresses) can give access to cryptocurrency. - "IBAN_CODE", # International Bank Account Numbers are critical financial information. - "US_BANK_NUMBER", # US bank account numbers are sensitive and should be protected. - "US_SSN", # US Social Security Numbers are critical PII used for identity verification. - "US_PASSPORT", # US passport numbers can be used for identity theft. - "US_ITIN", # US Individual Taxpayer Identification Numbers are sensitive personal identifiers. - #"UK_NHS", # UK NHS numbers can be used to access medical records and other private information. - #"ES_NIF", # Spanish NIF (Personal tax ID) is critical for identification and tax purposes. - #"ES_NIE", # Spanish NIE (Foreigners ID card) is a critical identifier for foreign residents. - #"IT_FISCAL_CODE", # Italian personal identification code is sensitive PII for tax and legal purposes. - #"IT_PASSPORT", # Italian passport numbers are critical PII. - #"IT_IDENTITY_CARD", # Italian identity card numbers are critical for personal identification. - #"PL_PESEL", # Polish PESEL numbers are sensitive personal identifiers. - #"SG_NRIC_FIN", # Singapore National Registration Identification Card is critical PII. - #"AU_ABN", # Australian Business Numbers are critical for business identification. - #"AU_TFN", # Australian Tax File Numbers are sensitive and used for taxation purposes. - #"AU_MEDICARE", # Australian Medicare numbers are sensitive medical identifiers. - #"IN_PAN", # Indian Permanent Account Numbers are critical for tax purposes and identity. - #"IN_AADHAAR", # Indian Aadhaar numbers are highly sensitive and serve as a universal identity number. - #"FI_PERSONAL_IDENTITY_CODE" # Finnish Personal Identity Code is sensitive PII for personal identification. -] -pii_language: "en" # Language for recognizing PII entities # AWS AppStream Session Configuration # aws.appstream_fleet_session_idle_disconnect_timeout diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/cloudwatch_log_group_no_critical_pii_in_logs_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/cloudwatch_log_group_no_critical_pii_in_logs_test.py deleted file mode 100644 index 1217f18308..0000000000 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_no_critical_pii_in_logs/cloudwatch_log_group_no_critical_pii_in_logs_test.py +++ /dev/null @@ -1,206 +0,0 @@ -from re import search -from unittest import mock - -from boto3 import client -from moto import mock_aws -from moto.core.utils import unix_time_millis - -from tests.providers.aws.utils import ( - AWS_REGION_EU_WEST_1, - AWS_REGION_US_EAST_1, - set_mocked_aws_provider, -) - - -class Test_cloudwatch_log_group_no_critical_pii_in_logs: - def test_cloudwatch_no_log_groups(self): - from prowler.providers.aws.services.cloudwatch.cloudwatch_service import Logs - - aws_provider = set_mocked_aws_provider( - [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1] - ) - - from prowler.providers.common.models import Audit_Metadata - - aws_provider.audit_metadata = Audit_Metadata( - services_scanned=0, - # We need to set this check to call _describe_log_groups - expected_checks=["cloudwatch_log_group_no_critical_pii_in_logs"], - completed_checks=0, - audit_progress=0, - ) - - with mock.patch( - "prowler.providers.common.provider.Provider.get_global_provider", - return_value=aws_provider, - ), mock.patch( - "prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_critical_pii_in_logs.cloudwatch_log_group_no_critical_pii_in_logs.logs_client", - new=Logs(aws_provider), - ): - # Test Check - from prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_critical_pii_in_logs.cloudwatch_log_group_no_critical_pii_in_logs import ( - cloudwatch_log_group_no_critical_pii_in_logs, - ) - - check = cloudwatch_log_group_no_critical_pii_in_logs() - result = check.execute() - - assert len(result) == 0 - - @mock_aws - def test_cloudwatch_log_group_without_pii(self): - # Generate Logs Client - logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) - # Request Logs group - logs_client.create_log_group(logGroupName="test", tags={"test": "test"}) - logs_client.create_log_stream(logGroupName="test", logStreamName="test stream") - logs_client.put_log_events( - logGroupName="test", - logStreamName="test stream", - logEvents=[ - { - "timestamp": int(unix_time_millis()), - "message": "non sensitive message", - } - ], - ) - from prowler.providers.aws.services.cloudwatch.cloudwatch_service import Logs - - aws_provider = set_mocked_aws_provider( - [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1] - ) - - from prowler.providers.common.models import Audit_Metadata - - aws_provider.audit_metadata = Audit_Metadata( - services_scanned=0, - # We need to set this check to call _describe_log_groups - expected_checks=["cloudwatch_log_group_no_critical_pii_in_logs"], - completed_checks=0, - audit_progress=0, - ) - - with mock.patch( - "prowler.providers.common.provider.Provider.get_global_provider", - return_value=aws_provider, - ), mock.patch( - "prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_critical_pii_in_logs.cloudwatch_log_group_no_critical_pii_in_logs.logs_client", - new=Logs(aws_provider), - ): - # Test Check - from prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_critical_pii_in_logs.cloudwatch_log_group_no_critical_pii_in_logs import ( - cloudwatch_log_group_no_critical_pii_in_logs, - ) - - check = cloudwatch_log_group_no_critical_pii_in_logs() - result = check.execute() - - assert len(result) == 1 - assert result[0].status == "PASS" - assert ( - result[0].status_extended == "No critical PII found in test log group." - ) - assert result[0].resource_id == "test" - assert ( - result[0].resource_arn - == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:123456789012:log-group:test" - ) - assert result[0].region == AWS_REGION_US_EAST_1 - assert result[0].resource_tags == [{"test": "test"}] - - @mock_aws - def test_cloudwatch_log_group_with_pii(self): - # Generate Logs Client - logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) - # Request Logs group - logs_client.create_log_group(logGroupName="test", tags={"test": "test"}) - logs_client.create_log_stream(logGroupName="test", logStreamName="test stream") - logs_client.put_log_events( - logGroupName="test", - logStreamName="test stream", - logEvents=[ - { - "timestamp": int(unix_time_millis()), - "message": "Credit Card Number = 374752619478856", - } - ], - ) - from prowler.providers.aws.services.cloudwatch.cloudwatch_service import Logs - - aws_provider = set_mocked_aws_provider( - [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1] - ) - - from prowler.providers.common.models import Audit_Metadata - - aws_provider.audit_metadata = Audit_Metadata( - services_scanned=0, - # We need to set this check to call _describe_log_groups - expected_checks=["cloudwatch_log_group_no_critical_pii_in_logs"], - completed_checks=0, - audit_progress=0, - ) - - with mock.patch( - "prowler.providers.common.provider.Provider.get_global_provider", - return_value=aws_provider, - ), mock.patch( - "prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_critical_pii_in_logs.cloudwatch_log_group_no_critical_pii_in_logs.logs_client", - new=Logs(aws_provider), - ): - # Test Check - from prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_critical_pii_in_logs.cloudwatch_log_group_no_critical_pii_in_logs import ( - cloudwatch_log_group_no_critical_pii_in_logs, - ) - - check = cloudwatch_log_group_no_critical_pii_in_logs() - result = check.execute() - - assert len(result) == 1 - assert result[0].status == "FAIL" - assert search( - "Potential critical PII found in log group", result[0].status_extended - ) - assert result[0].resource_id == "test" - assert ( - result[0].resource_arn - == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:123456789012:log-group:test" - ) - assert result[0].region == AWS_REGION_US_EAST_1 - assert result[0].resource_tags == [{"test": "test"}] - - @mock_aws - def test_access_denied(self): - from prowler.providers.aws.services.cloudwatch.cloudwatch_service import Logs - - aws_provider = set_mocked_aws_provider( - [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1] - ) - - from prowler.providers.common.models import Audit_Metadata - - aws_provider.audit_metadata = Audit_Metadata( - services_scanned=0, - # We need to set this check to call _describe_log_groups - expected_checks=["cloudwatch_log_group_no_critical_pii_in_logs"], - completed_checks=0, - audit_progress=0, - ) - - with mock.patch( - "prowler.providers.common.provider.Provider.get_global_provider", - return_value=aws_provider, - ), mock.patch( - "prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_critical_pii_in_logs.cloudwatch_log_group_no_critical_pii_in_logs.logs_client", - new=Logs(aws_provider), - ) as logs_client: - # Test Check - from prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_critical_pii_in_logs.cloudwatch_log_group_no_critical_pii_in_logs import ( - cloudwatch_log_group_no_critical_pii_in_logs, - ) - - logs_client.log_groups = None - check = cloudwatch_log_group_no_critical_pii_in_logs() - result = check.execute() - - assert len(result) == 0 From 9d65b49cb43b337b550800853ded9b1da2cb287a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:37:51 -0500 Subject: [PATCH 07/55] chore(deps): bump azure-mgmt-network from 27.0.0 to 28.0.0 (#5703) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Rubén De la Torre Vico Co-authored-by: Sergio Garcia <38561120+MrCloudSec@users.noreply.github.com> Co-authored-by: Sergio --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- .../network_flow_log_captured_sent_test.py | 2 +- .../network_flow_log_more_than_90_days_test.py | 2 +- .../network_http_internet_access_restricted_test.py | 2 +- .../network_rdp_internet_access_restricted_test.py | 2 +- .../network_ssh_internet_access_restricted_test.py | 2 +- .../network_udp_internet_access_restricted_test.py | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8c7ad6d0ba..3fc6af59ad 100644 --- a/poetry.lock +++ b/poetry.lock @@ -513,13 +513,13 @@ isodate = ">=0.6.1,<1.0.0" [[package]] name = "azure-mgmt-network" -version = "27.0.0" +version = "28.0.0" description = "Microsoft Azure Network Management Client Library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "azure_mgmt_network-27.0.0-py3-none-any.whl", hash = "sha256:67295fda351fdae79d14591487009e911346adea9578bb1b04636a15e3472598"}, - {file = "azure_mgmt_network-27.0.0.tar.gz", hash = "sha256:5c1c61d8bb13ad40f788a26fd7569c1d9d60eb2e4cb19c2a1b5d9c02ae862316"}, + {file = "azure_mgmt_network-28.0.0-py3-none-any.whl", hash = "sha256:2ee23c1f2ba75752187bd7f4c3e94ad172282cbf8153694feadc7886ef88493c"}, + {file = "azure_mgmt_network-28.0.0.tar.gz", hash = "sha256:40356d348ef4838324f19a41cd80340b4f8dd4ac2f0a18a4cbd5cc95ef2974f3"}, ] [package.dependencies] @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "cef6d708932a76766d79017a61af8e74cdce0ead795b286be27fb1af83068db5" +content-hash = "6e29516fe339f665008550f771ec72aae633c306d280d0d1c3d63ecb2f7b329a" diff --git a/pyproject.toml b/pyproject.toml index a1f09abf28..6e76e7e214 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ azure-mgmt-containerservice = "33.0.0" azure-mgmt-cosmosdb = "9.6.0" azure-mgmt-keyvault = "10.3.1" azure-mgmt-monitor = "6.0.2" -azure-mgmt-network = "27.0.0" +azure-mgmt-network = "28.0.0" azure-mgmt-rdbms = "10.1.0" azure-mgmt-resource = "23.2.0" azure-mgmt-security = "7.0.0" diff --git a/tests/providers/azure/services/network/network_flow_log_captured_sent/network_flow_log_captured_sent_test.py b/tests/providers/azure/services/network/network_flow_log_captured_sent/network_flow_log_captured_sent_test.py index e33d6af3ca..5506fa7740 100644 --- a/tests/providers/azure/services/network/network_flow_log_captured_sent/network_flow_log_captured_sent_test.py +++ b/tests/providers/azure/services/network/network_flow_log_captured_sent/network_flow_log_captured_sent_test.py @@ -1,7 +1,7 @@ from unittest import mock from uuid import uuid4 -from azure.mgmt.network.models._models import FlowLog, RetentionPolicyParameters +from azure.mgmt.network.models import FlowLog, RetentionPolicyParameters from prowler.providers.azure.services.network.network_service import NetworkWatcher from tests.providers.azure.azure_fixtures import AZURE_SUBSCRIPTION_ID diff --git a/tests/providers/azure/services/network/network_flow_log_more_than_90_days/network_flow_log_more_than_90_days_test.py b/tests/providers/azure/services/network/network_flow_log_more_than_90_days/network_flow_log_more_than_90_days_test.py index 48ba89e9c2..c27b02de79 100644 --- a/tests/providers/azure/services/network/network_flow_log_more_than_90_days/network_flow_log_more_than_90_days_test.py +++ b/tests/providers/azure/services/network/network_flow_log_more_than_90_days/network_flow_log_more_than_90_days_test.py @@ -1,7 +1,7 @@ from unittest import mock from uuid import uuid4 -from azure.mgmt.network.models._models import FlowLog, RetentionPolicyParameters +from azure.mgmt.network.models import FlowLog, RetentionPolicyParameters from prowler.providers.azure.services.network.network_service import NetworkWatcher from tests.providers.azure.azure_fixtures import ( diff --git a/tests/providers/azure/services/network/network_http_internet_access_restricted/network_http_internet_access_restricted_test.py b/tests/providers/azure/services/network/network_http_internet_access_restricted/network_http_internet_access_restricted_test.py index f35ba9bd1e..082e96e1b9 100644 --- a/tests/providers/azure/services/network/network_http_internet_access_restricted/network_http_internet_access_restricted_test.py +++ b/tests/providers/azure/services/network/network_http_internet_access_restricted/network_http_internet_access_restricted_test.py @@ -1,7 +1,7 @@ from unittest import mock from uuid import uuid4 -from azure.mgmt.network.models._models import SecurityRule +from azure.mgmt.network.models import SecurityRule from prowler.providers.azure.services.network.network_service import SecurityGroup from tests.providers.azure.azure_fixtures import ( diff --git a/tests/providers/azure/services/network/network_rdp_internet_access_restricted/network_rdp_internet_access_restricted_test.py b/tests/providers/azure/services/network/network_rdp_internet_access_restricted/network_rdp_internet_access_restricted_test.py index 7dabf2cc06..ce03204158 100644 --- a/tests/providers/azure/services/network/network_rdp_internet_access_restricted/network_rdp_internet_access_restricted_test.py +++ b/tests/providers/azure/services/network/network_rdp_internet_access_restricted/network_rdp_internet_access_restricted_test.py @@ -1,7 +1,7 @@ from unittest import mock from uuid import uuid4 -from azure.mgmt.network.models._models import SecurityRule +from azure.mgmt.network.models import SecurityRule from prowler.providers.azure.services.network.network_service import SecurityGroup from tests.providers.azure.azure_fixtures import ( diff --git a/tests/providers/azure/services/network/network_ssh_internet_access_restricted/network_ssh_internet_access_restricted_test.py b/tests/providers/azure/services/network/network_ssh_internet_access_restricted/network_ssh_internet_access_restricted_test.py index bb904a887b..5070e7c29b 100644 --- a/tests/providers/azure/services/network/network_ssh_internet_access_restricted/network_ssh_internet_access_restricted_test.py +++ b/tests/providers/azure/services/network/network_ssh_internet_access_restricted/network_ssh_internet_access_restricted_test.py @@ -1,7 +1,7 @@ from unittest import mock from uuid import uuid4 -from azure.mgmt.network.models._models import SecurityRule +from azure.mgmt.network.models import SecurityRule from prowler.providers.azure.services.network.network_service import SecurityGroup from tests.providers.azure.azure_fixtures import ( diff --git a/tests/providers/azure/services/network/network_udp_internet_access_restricted/network_udp_internet_access_restricted_test.py b/tests/providers/azure/services/network/network_udp_internet_access_restricted/network_udp_internet_access_restricted_test.py index 5fa1d0ba64..f6c7a68f13 100644 --- a/tests/providers/azure/services/network/network_udp_internet_access_restricted/network_udp_internet_access_restricted_test.py +++ b/tests/providers/azure/services/network/network_udp_internet_access_restricted/network_udp_internet_access_restricted_test.py @@ -1,7 +1,7 @@ from unittest import mock from uuid import uuid4 -from azure.mgmt.network.models._models import SecurityRule +from azure.mgmt.network.models import SecurityRule from prowler.providers.azure.services.network.network_service import SecurityGroup from tests.providers.azure.azure_fixtures import ( From b8b60e6bc53edb5db9f2f090b7b149390bd7d501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Tue, 12 Nov 2024 21:00:09 +0100 Subject: [PATCH 08/55] feat(prowler-check-kreator): `ProwlerChecKreator` first version (#5099) Co-authored-by: Sergio --- docs/tutorials/prowler-check-kreator.md | 36 +++ mkdocs.yml | 1 + util/__init__.py | 0 util/prowler_check_kreator/__init__.py | 0 .../lib/llms/__init__.py | 0 util/prowler_check_kreator/lib/llms/gemini.py | 239 ++++++++++++++ .../lib/metadata_types.py | 246 ++++++++++++++ util/prowler_check_kreator/lib/templates.py | 132 ++++++++ .../prowler_check_kreator.py | 304 ++++++++++++++++++ 9 files changed, 958 insertions(+) create mode 100644 docs/tutorials/prowler-check-kreator.md create mode 100644 util/__init__.py create mode 100644 util/prowler_check_kreator/__init__.py create mode 100644 util/prowler_check_kreator/lib/llms/__init__.py create mode 100644 util/prowler_check_kreator/lib/llms/gemini.py create mode 100644 util/prowler_check_kreator/lib/metadata_types.py create mode 100644 util/prowler_check_kreator/lib/templates.py create mode 100644 util/prowler_check_kreator/prowler_check_kreator.py diff --git a/docs/tutorials/prowler-check-kreator.md b/docs/tutorials/prowler-check-kreator.md new file mode 100644 index 0000000000..c1a53c1235 --- /dev/null +++ b/docs/tutorials/prowler-check-kreator.md @@ -0,0 +1,36 @@ + +# Prowler Check Kreator + +???+ note + Currently, this tool is only available for creating checks for the AWS provider. + +**Prowler Check Kreator** is a utility designed to streamline the creation of new checks for Prowler. This tool generates all necessary files required to add a new check to the Prowler repository. Specifically, it creates: + +- A dedicated folder for the check. +- The main check script. +- A metadata file with essential details. +- A folder and file structure for testing the check. + +## Usage + +To use the tool, execute the main script with the following command: + +```bash +python util/prowler_check_kreator/prowler_check_kreator.py +``` +Parameters: + +- ``: Currently only AWS is supported. +- ``: The name you wish to assign to the new check. + +## AI integration + +This tool optionally integrates AI to assist in generating the check code and metadata file content. When AI assistance is chosen, the tool uses [Gemini](https://gemini.google.com/) to produce preliminary code and metadata. + +???+ note + For this feature to work, you must have the library `google-generativeai` installed in your Python environment. + +???+ warning + AI-generated code and metadata might contain errors or require adjustments to align with specific Prowler requirements. Carefully review all AI-generated content before committing. + +To enable AI assistance, simply confirm when prompted by the tool. Additionally, ensure that the `GEMINI_API_KEY` environment variable is set with a valid Gemini API key. For instructions on obtaining your API key, refer to the [Gemini documentation](https://ai.google.dev/gemini-api/docs/api-key). diff --git a/mkdocs.yml b/mkdocs.yml index bc9ce5604b..170a9c3d9d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -65,6 +65,7 @@ nav: - Pentesting: tutorials/pentesting.md - Parallel Execution: tutorials/parallel-execution.md - Developer Guide: developer-guide/introduction.md + - Prowler Check Kreator: tutorials/prowler-check-kreator.md - AWS: - Authentication: tutorials/aws/authentication.md - Assume Role: tutorials/aws/role-assumption.md diff --git a/util/__init__.py b/util/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/util/prowler_check_kreator/__init__.py b/util/prowler_check_kreator/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/util/prowler_check_kreator/lib/llms/__init__.py b/util/prowler_check_kreator/lib/llms/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/util/prowler_check_kreator/lib/llms/gemini.py b/util/prowler_check_kreator/lib/llms/gemini.py new file mode 100644 index 0000000000..42e47c8c38 --- /dev/null +++ b/util/prowler_check_kreator/lib/llms/gemini.py @@ -0,0 +1,239 @@ +import json +import os + +import google.generativeai as genai + +from util.prowler_check_kreator.lib.metadata_types import ( + get_metadata_placeholder_resource_type, + get_metadata_valid_check_type, + get_metadata_valid_resource_type, +) + + +class Gemini: + def __init__(self, model: str = "gemini-1.5-flash"): + if os.getenv("GEMINI_API_KEY"): + self.api_key = os.getenv("GEMINI_API_KEY") + else: + self.api_key = input( + "GEMINI_API_KEY is not set, please enter the API key: " + ) + if not self.api_key: + raise Exception("GEMINI_API_KEY is required") + + if model not in ["gemini-1.5-flash", "gemini-1.5-pro", "gemini-1.0-pro"]: + raise Exception("Invalid Gemini AI model") + + self.model_name = model + self.generation_config = { + "temperature": 0, + "top_p": 1, + "top_k": 1, + } + self.safety_settings = [ + { + "category": "HARM_CATEGORY_HARASSMENT", + "threshold": "BLOCK_MEDIUM_AND_ABOVE", + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "threshold": "BLOCK_MEDIUM_AND_ABOVE", + }, + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "threshold": "BLOCK_MEDIUM_AND_ABOVE", + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "threshold": "BLOCK_MEDIUM_AND_ABOVE", + }, + ] + self._configure_genai() + + def _configure_genai(self): + """Configure the Gemini AI model.""" + try: + genai.configure(api_key=self.api_key) + except Exception as e: + raise Exception(f"Error configuring Gemini AI: {e}") + + def _generate_content(self, prompt_parts: list) -> str: + """Generate content using Gemini AI based on provided prompts.""" + try: + model = genai.GenerativeModel( + model_name=self.model_name, + generation_config=self.generation_config, + safety_settings=self.safety_settings, + ) + response = model.generate_content(prompt_parts) + if response: + return response.text + else: + raise Exception("Error generating content with Gemini AI") + except Exception as e: + raise Exception(f"Error generating content with Gemini AI: {e}") + + def _prepare_check_prompt(self, check_name: str, context: str) -> list: + """Prepare the prompt for generating the check.""" + + prompt_parts = [ + "You are a AWS cybersecurity engineer working in Prowler, an open-source Cloud Security tool to audit Cloud environments in an automated way.", + f"Your task is to create a new security check called '{check_name}' based on the following context:\n{context}", + "TA check is a Python class that inherits from the Check class and has only one method called execute.\n", + "The execute method must return a list of Check_Report_AWS objects.\n", + "Limit to Python code only.\n", + "Use the following check as inspiration about the format to create the new check:\n", + "ec2_instance_port_ssh_exposed_to_internet:", + "from prowler.lib.check.models import Check, Check_Report_AWS\nfrom prowler.providers.aws.services.ec2.ec2_client import ec2_client\nfrom prowler.providers.aws.services.ec2.lib.instance import get_instance_public_status\nfrom prowler.providers.aws.services.ec2.lib.security_groups import check_security_group\nfrom prowler.providers.aws.services.vpc.vpc_client import vpc_client\n\n\nclass ec2_instance_port_ssh_exposed_to_internet(Check):\n\t# EC2 Instances with SSH port 22 open to the Internet will be flagged as FAIL with a severity of medium if the instance has no public IP, high if the instance has a public IP but is in a private subnet, and critical if the instance has a public IP and is in a public subnet.\n\tdef execute(self):\n\t\tfindings = []\n\t\tcheck_ports = [22]\n\t\tfor instance in ec2_client.instances:\n\t\t\treport = Check_Report_AWS(self.metadata())\n\t\t\treport.region = instance.region\n\t\t\treport.status = 'PASS'\n\t\t\treport.status_extended = f'Instance {instance.id} does not have SSH port 22 open to the Internet.'\n\t\t\treport.resource_id = instance.id\n\t\t\treport.resource_arn = instance.arn\n\t\t\treport.resource_tags = instance.tags\n\t\t\tis_open_port = False\n\t\t\tif instance.security_groups:\n\t\t\t\tfor sg in ec2_client.security_groups.values():\n\t\t\t\t\tif sg.id in instance.security_groups:\n\t\t\t\t\t\tfor ingress_rule in sg.ingress_rules:\n\t\t\t\t\t\t\tif check_security_group(\n\t\t\t\t\t\t\t\tingress_rule, 'tcp', check_ports, any_address=True\n\t\t\t\t\t\t\t):\n\t\t\t\t\t\t\t\t# The port is open, now check if the instance is in a public subnet with a public IP\n\t\t\t\t\t\t\t\treport.status = 'FAIL'\n\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\treport.status_extended,\n\t\t\t\t\t\t\t\t\treport.check_metadata.Severity,\n\t\t\t\t\t\t\t\t) = get_instance_public_status(\n\t\t\t\t\t\t\t\t\tvpc_client.vpc_subnets, instance, 'SSH'\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tis_open_port = True\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tif is_open_port:\n\t\t\t\t\t\t\tbreak\n\t\t\tfindings.append(report)\n\t\treturn findings\n", + "s3_bucket_default_encryption:", + "from prowler.lib.check.models import Check, Check_Report_AWS\nfrom prowler.providers.aws.services.s3.s3_client import s3_client\n\n\nclass s3_bucket_default_encryption(Check):\n\tdef execute(self):\n\t\tfindings = []\n\t\tfor arn, bucket in s3_client.buckets.items():\n\t\t\treport = Check_Report_AWS(self.metadata())\n\t\t\treport.region = bucket.region\n\t\t\treport.resource_id = bucket.name\n\t\t\treport.resource_arn = arn\n\t\t\treport.resource_tags = bucket.tags\n\t\t\tif bucket.encryption:\n\t\t\t\treport.status = 'PASS'\n\t\t\t\treport.status_extended = f'S3 Bucket {bucket.name} has Server Side Encryption with {bucket.encryption}.'\n\t\t\telse:\n\t\t\t\treport.status = 'FAIL'\n\t\t\t\treport.status_extended = f'S3 Bucket {bucket.name} does not have Server Side Encryption enabled.'\n\t\t\tfindings.append(report)\n\t\treturn findings\n", + "bedrock_guardrail_prompt_attack_filter_enabled:", + "from prowler.lib.check.models import Check, Check_Report_AWS\nfrom prowler.providers.aws.services.bedrock.bedrock_client import bedrock_client\n\n\nclass bedrock_guardrail_prompt_attack_filter_enabled(Check):\n\tdef execute(self):\n\t\tfindings = []\n\t\tfor guardrail in bedrock_client.guardrails.values():\n\t\t\treport = Check_Report_AWS(self.metadata())\n\t\t\treport.region = guardrail.region\n\t\t\treport.resource_id = guardrail.id\n\t\t\treport.resource_arn = guardrail.arn\n\t\t\treport.resource_tags = guardrail.tags\n\t\t\treport.status = 'PASS'\n\t\t\treport.status_extended = f'Bedrock Guardrail {guardrail.name} is configured to detect and block prompt attacks with a HIGH strength.'\n\t\t\tif not guardrail.prompt_attack_filter_strength:\n\t\t\t\treport.status = 'FAIL'\n\t\t\t\treport.status_extended = f'Bedrock Guardrail {guardrail.name} is not configured to block prompt attacks.'\n\t\t\telif guardrail.prompt_attack_filter_strength != 'HIGH':\n\t\t\t\treport.status = 'FAIL'\n\t\t\t\treport.status_extended = f'Bedrock Guardrail {guardrail.name} is configured to block prompt attacks but with a filter strength of {guardrail.prompt_attack_filter_strength}, not HIGH.'\n\t\t\tfindings.append(report)\n\n\t\treturn findings", + "cloudwatch_alarm_actions_enabled:", + "from prowler.lib.check.models import Check, Check_Report_AWS\nfrom prowler.providers.aws.services.cloudwatch.cloudwatch_client import (\n\tcloudwatch_client,\n)\n\n\nclass cloudwatch_alarm_actions_enabled(Check):\n\tdef execute(self):\n\t\tfindings = []\n\t\tfor metric_alarm in cloudwatch_client.metric_alarms:\n\t\t\treport = Check_Report_AWS(self.metadata())\n\t\t\treport.region = metric_alarm.region\n\t\t\treport.resource_id = metric_alarm.name\n\t\t\treport.resource_arn = metric_alarm.arn\n\t\t\treport.resource_tags = metric_alarm.tags\n\t\t\treport.status = 'PASS'\n\t\t\treport.status_extended = (\n\t\t\t\tf'CloudWatch metric alarm {metric_alarm.name} has actions enabled.'\n\t\t\t)\n\t\t\tif not metric_alarm.actions_enabled:\n\t\t\t\treport.status = 'FAIL'\n\t\t\t\treport.status_extended = f'CloudWatch metric alarm {metric_alarm.name} does not have actions enabled.'\n\t\t\tfindings.append(report)\n\t\treturn findings", + "awslambda_function_not_publicly_accessible:", + "from prowler.lib.check.models import Check, Check_Report_AWS\nfrom prowler.providers.aws.services.awslambda.awslambda_client import awslambda_client\nfrom prowler.providers.aws.services.iam.lib.policy import is_policy_public\n\n\nclass awslambda_function_not_publicly_accessible(Check):\n\tdef execute(self):\n\t\tfindings = []\n\t\tfor function in awslambda_client.functions.values():\n\t\t\treport = Check_Report_AWS(self.metadata())\n\t\t\treport.region = function.region\n\t\t\treport.resource_id = function.name\n\t\t\treport.resource_arn = function.arn\n\t\t\treport.resource_tags = function.tags\n\n\t\t\treport.status = 'PASS'\n\t\t\treport.status_extended = f'Lambda function {function.name} has a policy resource-based policy not public.'\n\t\t\tif is_policy_public(\n\t\t\t\tfunction.policy,\n\t\t\t\tawslambda_client.audited_account,\n\t\t\t\tis_cross_account_allowed=True,\n\t\t\t):\n\t\t\t\treport.status = 'FAIL'\n\t\t\t\treport.status_extended = f'Lambda function {function.name} has a policy resource-based policy with public access.'\n\n\t\t\tfindings.append(report)\n\n\t\treturn findings", + f"{check_name}:", + ] + return prompt_parts + + def _prepare_test_prompt(self, check_name: str) -> list: + """Prepare the prompt for generating the test.""" + + prompt_parts = [ + "You are a AWS cybersecurity engineer working in Prowler, an open-source Cloud Security tool to audit Cloud environments in an automated way.", + f"Your task is to create a new unit test for the security check '{check_name}'.", + "The test must have one or more methods that start with the word 'test'.", + "The test methods must use the assert statement to check the results of the check.", + "I need the answer only with Python formatted text.", + "Use the following test as inspiration to create the new test: ", + "ec2_instance_port_ssh_exposed_to_internet:", + "from unittest import mock\n\nfrom boto3 import client, resource\nfrom moto import mock_aws\n\nfrom tests.providers.aws.utils import (\n\tAWS_REGION_EU_WEST_1,\n\tAWS_REGION_US_EAST_1,\n\tset_mocked_aws_provider,\n)\n\n\nclass Test_ec2_instance_port_ssh_exposed_to_internet:\n\t@mock_aws\n\tdef test_no_ec2_instances(self):\n\t\t# Create EC2 Mocked Resources\n\t\tec2_client = client('ec2', region_name=AWS_REGION_US_EAST_1)\n\t\tec2_client.create_vpc(CidrBlock='10.0.0.0/16')\n\n\t\tfrom prowler.providers.aws.services.ec2.ec2_service import EC2\n\t\tfrom prowler.providers.aws.services.vpc.vpc_service import VPC\n\n\t\taws_provider = set_mocked_aws_provider(\n\t\t\t[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]\n\t\t)\n\n\t\twith mock.patch(\n\t\t\t'prowler.providers.common.provider.Provider.get_global_provider',\n\t\t\treturn_value=aws_provider,\n\t\t), mock.patch(\n\t\t\t'prowler.providers.aws.services.ec2.ec2_instance_port_ssh_exposed_to_internet.ec2_instance_port_ssh_exposed_to_internet.ec2_client',\n\t\t\tnew=EC2(aws_provider),\n\t\t), mock.patch(\n\t\t\t'prowler.providers.aws.services.ec2.ec2_instance_port_ssh_exposed_to_internet.ec2_instance_port_ssh_exposed_to_internet.vpc_client',\n\t\t\tnew=VPC(aws_provider),\n\t\t):\n\t\t\t# Test Check\n\t\t\tfrom prowler.providers.aws.services.ec2.ec2_instance_port_ssh_exposed_to_internet.ec2_instance_port_ssh_exposed_to_internet import (\n\t\t\t\tec2_instance_port_ssh_exposed_to_internet,\n\t\t\t)\n\n\t\t\tcheck = ec2_instance_port_ssh_exposed_to_internet()\n\t\t\tresult = check.execute()\n\n\t\t\tassert len(result) == 0\n\n\t@mock_aws\n\tdef test_ec2_instance_no_port_exposed(self):\n\t\t# Create EC2 Mocked Resources\n\t\tec2_client = client('ec2', region_name=AWS_REGION_US_EAST_1)\n\t\tec2_resource = resource('ec2', region_name=AWS_REGION_US_EAST_1)\n\t\tvpc_id = ec2_client.create_vpc(CidrBlock='10.0.0.0/16')['Vpc']['VpcId']\n\t\tdefault_sg = ec2_client.describe_security_groups(GroupNames=['default'])[\n\t\t\t'SecurityGroups'\n\t\t][0]\n\t\tdefault_sg_id = default_sg['GroupId']\n\t\tec2_client.authorize_security_group_ingress(\n\t\t\tGroupId=default_sg_id,\n\t\t\tIpPermissions=[\n\t\t\t\t{\n\t\t\t\t\t'IpProtocol': 'tcp',\n\t\t\t\t\t'FromPort': 22,\n\t\t\t\t\t'ToPort': 22,\n\t\t\t\t\t'IpRanges': [{'CidrIp': '123.123.123.123/32'}],\n\t\t\t\t}\n\t\t\t],\n\t\t)\n\t\tsubnet_id = ec2_client.create_subnet(VpcId=vpc_id, CidrBlock='10.0.0.0/16')[\n\t\t\t'Subnet'\n\t\t]['SubnetId']\n\t\tinstance_id = ec2_resource.create_instances(\n\t\t\tImageId='ami-12345678',\n\t\t\tMinCount=1,\n\t\t\tMaxCount=1,\n\t\t\tInstanceType='t2.micro',\n\t\t\tSecurityGroupIds=[default_sg_id],\n\t\t\tSubnetId=subnet_id,\n\t\t\tTagSpecifications=[\n\t\t\t\t{'ResourceType': 'instance', 'Tags': [{'Key': 'Name', 'Value': 'test'}]}\n\t\t\t],\n\t\t)[0].id\n\n\t\tfrom prowler.providers.aws.services.ec2.ec2_service import EC2\n\t\tfrom prowler.providers.aws.services.vpc.vpc_service import VPC\n\n\t\taws_provider = set_mocked_aws_provider(\n\t\t\t[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]\n\t\t)\n\n\t\twith mock.patch(\n\t\t\t'prowler.providers.common.provider.Provider.get_global_provider',\n\t\t\treturn_value=aws_provider,\n\t\t), mock.patch(\n\t\t\t'prowler.providers.aws.services.ec2.ec2_instance_port_ssh_exposed_to_internet.ec2_instance_port_ssh_exposed_to_internet.ec2_client',\n\t\t\tnew=EC2(aws_provider),\n\t\t), mock.patch(\n\t\t\t'prowler.providers.aws.services.ec2.ec2_instance_port_ssh_exposed_to_internet.ec2_instance_port_ssh_exposed_to_internet.vpc_client',\n\t\t\tnew=VPC(aws_provider),\n\t\t):\n\t\t\t# Test Check\n\t\t\tfrom prowler.providers.aws.services.ec2.ec2_instance_port_ssh_exposed_to_internet.ec2_instance_port_ssh_exposed_to_internet import (\n\t\t\t\tec2_instance_port_ssh_exposed_to_internet,\n\t\t\t)\n\n\t\t\tcheck = ec2_instance_port_ssh_exposed_to_internet()\n\t\t\tresult = check.execute()\n\n\t\t\tassert len(result) == 1\n\t\t\tassert result[0].status == 'PASS'\n\t\t\tassert (\n\t\t\t\tresult[0].status_extended\n\t\t\t\t== f'Instance {instance_id} does not have SSH port 22 open to the Internet.'\n\t\t\t)\n\t\t\tassert result[0].resource_id == instance_id\n\t\t\tassert (\n\t\t\t\tresult[0].resource_arn\n\t\t\t\t== f'arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:instance/{instance_id}'\n\t\t\t)\n\t\t\tassert result[0].resource_tags == [{'Key': 'Name', 'Value': 'test'}]\n\t\t\tassert result[0].region == AWS_REGION_US_EAST_1\n\t\t\tassert result[0].check_metadata.Severity == 'critical'\n\n\t@mock_aws\n\tdef test_ec2_instance_exposed_port_in_private_subnet(self):\n\t\t# Create EC2 Mocked Resources\n\t\tec2_client = client('ec2', region_name=AWS_REGION_US_EAST_1)\n\t\tec2_resource = resource('ec2', region_name=AWS_REGION_US_EAST_1)\n\t\tvpc_id = ec2_client.create_vpc(CidrBlock='10.0.0.0/16')['Vpc']['VpcId']\n\t\tdefault_sg = ec2_client.describe_security_groups(GroupNames=['default'])[\n\t\t\t'SecurityGroups'\n\t\t][0]\n\t\tdefault_sg_id = default_sg['GroupId']\n\t\tec2_client.authorize_security_group_ingress(\n\t\t\tGroupId=default_sg_id,\n\t\t\tIpPermissions=[\n\t\t\t\t{\n\t\t\t\t\t'IpProtocol': 'tcp',\n\t\t\t\t\t'FromPort': 22,\n\t\t\t\t\t'ToPort': 22,\n\t\t\t\t\t'IpRanges': [{'CidrIp': '0.0.0.0/0'}],\n\t\t\t\t}\n\t\t\t],\n\t\t)\n\t\tsubnet_id = ec2_client.create_subnet(VpcId=vpc_id, CidrBlock='10.0.0.0/16')[\n\t\t\t'Subnet'\n\t\t]['SubnetId']\n\t\tinstance_id = ec2_resource.create_instances(\n\t\t\tImageId='ami-12345678',\n\t\t\tMinCount=1,\n\t\t\tMaxCount=1,\n\t\t\tInstanceType='t2.micro',\n\t\t\tSecurityGroupIds=[default_sg_id],\n\t\t\tSubnetId=subnet_id,\n\t\t\tTagSpecifications=[\n\t\t\t\t{'ResourceType': 'instance', 'Tags': [{'Key': 'Name', 'Value': 'test'}]}\n\t\t\t],\n\t\t)[0].id\n\n\t\tfrom prowler.providers.aws.services.ec2.ec2_service import EC2\n\t\tfrom prowler.providers.aws.services.vpc.vpc_service import VPC\n\n\t\taws_provider = set_mocked_aws_provider(\n\t\t\t[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]\n\t\t)\n\n\t\twith mock.patch(\n\t\t\t'prowler.providers.common.provider.Provider.get_global_provider',\n\t\t\treturn_value=aws_provider,\n\t\t), mock.patch(\n\t\t\t'prowler.providers.aws.services.ec2.ec2_instance_port_ssh_exposed_to_internet.ec2_instance_port_ssh_exposed_to_internet.ec2_client',\n\t\t\tnew=EC2(aws_provider),\n\t\t), mock.patch(\n\t\t\t'prowler.providers.aws.services.ec2.ec2_instance_port_ssh_exposed_to_internet.ec2_instance_port_ssh_exposed_to_internet.vpc_client',\n\t\t\tnew=VPC(aws_provider),\n\t\t):\n\t\t\t# Test Check\n\t\t\tfrom prowler.providers.aws.services.ec2.ec2_instance_port_ssh_exposed_to_internet.ec2_instance_port_ssh_exposed_to_internet import (\n\t\t\t\tec2_instance_port_ssh_exposed_to_internet,\n\t\t\t)\n\n\t\t\tcheck = ec2_instance_port_ssh_exposed_to_internet()\n\t\t\tresult = check.execute()\n\n\t\t\tassert len(result) == 1\n\t\t\tassert result[0].status == 'FAIL'\n\t\t\tassert (\n\t\t\t\tresult[0].status_extended\n\t\t\t\t== f'Instance {instance_id} has SSH exposed to 0.0.0.0/0 but with no public IP address.'\n\t\t\t)\n\t\t\tassert result[0].resource_id == instance_id\n\t\t\tassert (\n\t\t\t\tresult[0].resource_arn\n\t\t\t\t== f'arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:instance/{instance_id}'\n\t\t\t)\n\t\t\tassert result[0].resource_tags == [{'Key': 'Name', 'Value': 'test'}]\n\t\t\tassert result[0].region == AWS_REGION_US_EAST_1\n\t\t\tassert result[0].check_metadata.Severity == 'medium'", + "s3_bucket_default_encryption:", + "from unittest import mock\n\nfrom boto3 import client\nfrom moto import mock_aws\n\nfrom tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider\n\n\nclass Test_s3_bucket_default_encryption:\n\t@mock_aws\n\tdef test_bucket_no_encryption(self):\n\t\ts3_client_us_east_1 = client('s3', region_name=AWS_REGION_US_EAST_1)\n\t\tbucket_name_us = 'bucket_test_us'\n\t\ts3_client_us_east_1.create_bucket(Bucket=bucket_name_us)\n\n\t\tfrom prowler.providers.aws.services.s3.s3_service import S3\n\n\t\taws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])\n\n\t\twith mock.patch(\n\t\t\t'prowler.providers.common.provider.Provider.get_global_provider',\n\t\t\treturn_value=aws_provider,\n\t\t):\n\t\t\twith mock.patch(\n\t\t\t\t'prowler.providers.aws.services.s3.s3_bucket_default_encryption.s3_bucket_default_encryption.s3_client',\n\t\t\t\tnew=S3(aws_provider),\n\t\t\t):\n\t\t\t\t# Test Check\n\t\t\t\tfrom prowler.providers.aws.services.s3.s3_bucket_default_encryption.s3_bucket_default_encryption import (\n\t\t\t\t\ts3_bucket_default_encryption,\n\t\t\t\t)\n\n\t\t\t\tcheck = s3_bucket_default_encryption()\n\t\t\t\tresult = check.execute()\n\n\t\t\t\tassert len(result) == 1\n\t\t\t\tassert result[0].status == 'FAIL'\n\t\t\t\tassert (\n\t\t\t\t\tresult[0].status_extended\n\t\t\t\t\t== f'S3 Bucket {bucket_name_us} does not have Server Side Encryption enabled.'\n\t\t\t\t)\n\t\t\t\tassert result[0].resource_id == bucket_name_us\n\t\t\t\tassert (\n\t\t\t\t\tresult[0].resource_arn\n\t\t\t\t\t== f'arn:{aws_provider.identity.partition}:s3:::{bucket_name_us}'\n\t\t\t\t)\n\t\t\t\tassert result[0].region == AWS_REGION_US_EAST_1\n\n\t@mock_aws\n\tdef test_bucket_kms_encryption(self):\n\t\ts3_client_us_east_1 = client('s3', region_name=AWS_REGION_US_EAST_1)\n\t\tbucket_name_us = 'bucket_test_us'\n\t\ts3_client_us_east_1.create_bucket(\n\t\t\tBucket=bucket_name_us, ObjectOwnership='BucketOwnerEnforced'\n\t\t)\n\t\tsse_config = {\n\t\t\t'Rules': [\n\t\t\t\t{\n\t\t\t\t\t'ApplyServerSideEncryptionByDefault': {\n\t\t\t\t\t\t'SSEAlgorithm': 'aws:kms',\n\t\t\t\t\t\t'KMSMasterKeyID': '12345678',\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\n\t\ts3_client_us_east_1.put_bucket_encryption(\n\t\t\tBucket=bucket_name_us, ServerSideEncryptionConfiguration=sse_config\n\t\t)\n\n\t\tfrom prowler.providers.aws.services.s3.s3_service import S3\n\n\t\taws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])\n\n\t\twith mock.patch(\n\t\t\t'prowler.providers.common.provider.Provider.get_global_provider',\n\t\t\treturn_value=aws_provider,\n\t\t):\n\t\t\twith mock.patch(\n\t\t\t\t'prowler.providers.aws.services.s3.s3_bucket_default_encryption.s3_bucket_default_encryption.s3_client',\n\t\t\t\tnew=S3(aws_provider),\n\t\t\t):\n\t\t\t\t# Test Check\n\t\t\t\tfrom prowler.providers.aws.services.s3.s3_bucket_default_encryption.s3_bucket_default_encryption import (\n\t\t\t\t\ts3_bucket_default_encryption,\n\t\t\t\t)\n\n\t\t\t\tcheck = s3_bucket_default_encryption()\n\t\t\t\tresult = check.execute()\n\n\t\t\t\tassert len(result) == 1\n\t\t\t\tassert result[0].status == 'PASS'\n\t\t\t\tassert (\n\t\t\t\t\tresult[0].status_extended\n\t\t\t\t\t== f'S3 Bucket {bucket_name_us} has Server Side Encryption with aws:kms.'\n\t\t\t\t)\n\t\t\t\tassert result[0].resource_id == bucket_name_us\n\t\t\t\tassert (\n\t\t\t\t\tresult[0].resource_arn\n\t\t\t\t\t== f'arn:{aws_provider.identity.partition}:s3:::{bucket_name_us}'\n\t\t\t\t)\n\t\t\t\tassert result[0].region == AWS_REGION_US_EAST_1", + "cloudwatch_alarm_actions_enabled:", + "from unittest import mock\n\nfrom boto3 import client\nfrom moto import mock_aws\n\nfrom tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider\n\n\nclass Test_cloudwatch_alarm_actions_enabled:\n\t@mock_aws\n\tdef test_no_cloudwatch_alarms(self):\n\t\tcloudwatch_client = client('cloudwatch', region_name=AWS_REGION_US_EAST_1)\n\t\tcloudwatch_client.metric_alarms = []\n\n\t\tfrom prowler.providers.aws.services.cloudwatch.cloudwatch_service import (\n\t\t\tCloudWatch,\n\t\t)\n\n\t\taws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])\n\n\t\twith mock.patch(\n\t\t\t'prowler.providers.common.provider.Provider.get_global_provider',\n\t\t\treturn_value=aws_provider,\n\t\t), mock.patch(\n\t\t\t'prowler.providers.aws.services.cloudwatch.cloudwatch_alarm_actions_enabled.cloudwatch_alarm_actions_enabled.cloudwatch_client',\n\t\t\tnew=CloudWatch(aws_provider),\n\t\t):\n\n\t\t\tfrom prowler.providers.aws.services.cloudwatch.cloudwatch_alarm_actions_enabled.cloudwatch_alarm_actions_enabled import (\n\t\t\t\tcloudwatch_alarm_actions_enabled,\n\t\t\t)\n\n\t\t\tcheck = cloudwatch_alarm_actions_enabled()\n\t\t\tresult = check.execute()\n\n\t\t\tassert len(result) == 0\n\n\t@mock_aws\n\tdef test_cloudwatch_alarms_actions_enabled(self):\n\t\tcloudwatch_client = client('cloudwatch', region_name=AWS_REGION_US_EAST_1)\n\t\tcloudwatch_client.put_metric_alarm(\n\t\t\tAlarmName='test_alarm',\n\t\t\tAlarmDescription='Test alarm',\n\t\t\tActionsEnabled=True,\n\t\t\tAlarmActions=['arn:aws:sns:us-east-1:123456789012:my-sns-topic'],\n\t\t\tEvaluationPeriods=1,\n\t\t\tComparisonOperator='GreaterThanThreshold',\n\t\t)\n\n\t\tfrom prowler.providers.aws.services.cloudwatch.cloudwatch_service import (\n\t\t\tCloudWatch,\n\t\t)\n\n\t\taws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])\n\n\t\twith mock.patch(\n\t\t\t'prowler.providers.common.provider.Provider.get_global_provider',\n\t\t\treturn_value=aws_provider,\n\t\t), mock.patch(\n\t\t\t'prowler.providers.aws.services.cloudwatch.cloudwatch_alarm_actions_enabled.cloudwatch_alarm_actions_enabled.cloudwatch_client',\n\t\t\tnew=CloudWatch(aws_provider),\n\t\t):\n\n\t\t\tfrom prowler.providers.aws.services.cloudwatch.cloudwatch_alarm_actions_enabled.cloudwatch_alarm_actions_enabled import (\n\t\t\t\tcloudwatch_alarm_actions_enabled,\n\t\t\t)\n\n\t\t\tcheck = cloudwatch_alarm_actions_enabled()\n\t\t\tresult = check.execute()\n\n\t\t\tassert len(result) == 1\n\t\t\tassert result[0].status == 'PASS'\n\t\t\tassert (\n\t\t\t\tresult[0].status_extended\n\t\t\t\t== 'CloudWatch metric alarm test_alarm has actions enabled.'\n\t\t\t)\n\t\t\tassert result[0].resource_id == 'test_alarm'\n\t\t\tassert (\n\t\t\t\tresult[0].resource_arn\n\t\t\t\t== 'arn:aws:cloudwatch:us-east-1:123456789012:alarm:test_alarm'\n\t\t\t)\n\t\t\tassert result[0].region == AWS_REGION_US_EAST_1\n\t\t\tassert result[0].resource_tags == []\n\n\t@mock_aws\n\tdef test_cloudwatch_alarms_actions_disabled(self):\n\t\tcloudwatch_client = client('cloudwatch', region_name=AWS_REGION_US_EAST_1)\n\t\tcloudwatch_client.put_metric_alarm(\n\t\t\tAlarmName='test_alarm',\n\t\t\tAlarmDescription='Test alarm',\n\t\t\tActionsEnabled=False,\n\t\t\tAlarmActions=['arn:aws:sns:us-east-1:123456789012:my-sns-topic'],\n\t\t\tEvaluationPeriods=1,\n\t\t\tComparisonOperator='GreaterThanThreshold',\n\t\t)\n\n\t\tfrom prowler.providers.aws.services.cloudwatch.cloudwatch_service import (\n\t\t\tCloudWatch,\n\t\t)\n\n\t\taws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])\n\n\t\twith mock.patch(\n\t\t\t'prowler.providers.common.provider.Provider.get_global_provider',\n\t\t\treturn_value=aws_provider,\n\t\t), mock.patch(\n\t\t\t'prowler.providers.aws.services.cloudwatch.cloudwatch_alarm_actions_enabled.cloudwatch_alarm_actions_enabled.cloudwatch_client',\n\t\t\tnew=CloudWatch(aws_provider),\n\t\t):\n\n\t\t\tfrom prowler.providers.aws.services.cloudwatch.cloudwatch_alarm_actions_enabled.cloudwatch_alarm_actions_enabled import (\n\t\t\t\tcloudwatch_alarm_actions_enabled,\n\t\t\t)\n\n\t\t\tcheck = cloudwatch_alarm_actions_enabled()\n\t\t\tresult = check.execute()\n\n\t\t\tassert len(result) == 1\n\t\t\tassert result[0].status == 'FAIL'\n\t\t\tassert (\n\t\t\t\tresult[0].status_extended\n\t\t\t\t== 'CloudWatch metric alarm test_alarm does not have actions enabled.'\n\t\t\t)\n\t\t\tassert result[0].resource_id == 'test_alarm'\n\t\t\tassert (\n\t\t\t\tresult[0].resource_arn\n\t\t\t\t== 'arn:aws:cloudwatch:us-east-1:123456789012:alarm:test_alarm'\n\t\t\t)\n\t\t\tassert result[0].region == AWS_REGION_US_EAST_1\n\t\t\tassert result[0].resource_tags == []", + "awslambda_function_not_publicly_accessible:", + "from json import dumps\nfrom unittest import mock\n\nfrom boto3 import client\nfrom moto import mock_aws\n\nfrom prowler.providers.aws.services.awslambda.awslambda_service import Function\nfrom tests.providers.aws.utils import (\n\tAWS_ACCOUNT_NUMBER,\n\tAWS_REGION_EU_WEST_1,\n\tset_mocked_aws_provider,\n)\n\n\nclass Test_awslambda_function_not_publicly_accessible:\n\t@mock_aws\n\tdef test_no_functions(self):\n\t\taws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])\n\n\t\tfrom prowler.providers.aws.services.awslambda.awslambda_service import Lambda\n\n\t\twith mock.patch(\n\t\t\t'prowler.providers.common.provider.Provider.get_global_provider',\n\t\t\treturn_value=aws_provider,\n\t\t), mock.patch(\n\t\t\t'prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client',\n\t\t\tnew=Lambda(aws_provider),\n\t\t):\n\t\t\t# Test Check\n\t\t\tfrom prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import (\n\t\t\t\tawslambda_function_not_publicly_accessible,\n\t\t\t)\n\n\t\t\tcheck = awslambda_function_not_publicly_accessible()\n\t\t\tresult = check.execute()\n\n\t\t\tassert len(result) == 0\n\n\t@mock_aws\n\tdef test_function_public(self):\n\t\t# Create the mock IAM role\n\t\tiam_client = client('iam', region_name=AWS_REGION_EU_WEST_1)\n\t\trole_name = 'test-role'\n\t\tassume_role_policy_document = {\n\t\t\t'Version': '2012-10-17',\n\t\t\t'Statement': [\n\t\t\t\t{\n\t\t\t\t\t'Effect': 'Allow',\n\t\t\t\t\t'Principal': {'Service': 'lambda.amazonaws.com'},\n\t\t\t\t\t'Action': 'sts:AssumeRole',\n\t\t\t\t}\n\t\t\t],\n\t\t}\n\t\trole_arn = iam_client.create_role(\n\t\t\tRoleName=role_name,\n\t\t\tAssumeRolePolicyDocument=dumps(assume_role_policy_document),\n\t\t)['Role']['Arn']\n\n\t\tfunction_name = 'test-lambda'\n\n\t\t# Create the lambda function using boto3 client\n\t\tlambda_client = client('lambda', region_name=AWS_REGION_EU_WEST_1)\n\t\tfunction_arn = lambda_client.create_function(\n\t\t\tFunctionName=function_name,\n\t\t\tRuntime='nodejs4.3',\n\t\t\tRole=role_arn,\n\t\t\tHandler='index.handler',\n\t\t\tCode={'ZipFile': b'fileb://file-path/to/your-deployment-package.zip'},\n\t\t\tDescription='Test Lambda function',\n\t\t\tTimeout=3,\n\t\t\tMemorySize=128,\n\t\t\tPublish=True,\n\t\t\tTags={'tag1': 'value1', 'tag2': 'value2'},\n\t\t)['FunctionArn']\n\n\t\t# Attach the policy to the lambda function with a wildcard principal\n\t\tlambda_client.add_permission(\n\t\t\tFunctionName=function_name,\n\t\t\tStatementId='public-access',\n\t\t\tAction='lambda:InvokeFunction',\n\t\t\tPrincipal='*',\n\t\t)\n\n\t\taws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])\n\n\t\tfrom prowler.providers.aws.services.awslambda.awslambda_service import Lambda\n\n\t\twith mock.patch(\n\t\t\t'prowler.providers.common.provider.Provider.get_global_provider',\n\t\t\treturn_value=aws_provider,\n\t\t), mock.patch(\n\t\t\t'prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client',\n\t\t\tnew=Lambda(aws_provider),\n\t\t):\n\t\t\t# Test Check\n\t\t\tfrom prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import (\n\t\t\t\tawslambda_function_not_publicly_accessible,\n\t\t\t)\n\n\t\t\tcheck = awslambda_function_not_publicly_accessible()\n\t\t\tresult = check.execute()\n\n\t\t\tassert len(result) == 1\n\t\t\tassert result[0].region == AWS_REGION_EU_WEST_1\n\t\t\tassert result[0].resource_id == function_name\n\t\t\tassert result[0].resource_arn == function_arn\n\t\t\tassert result[0].status == 'FAIL'\n\t\t\tassert (\n\t\t\t\tresult[0].status_extended\n\t\t\t\t== f'Lambda function {function_name} has a policy resource-based policy with public access.'\n\t\t\t)\n\t\t\tassert result[0].resource_tags == [{'tag1': 'value1', 'tag2': 'value2'}]\n\n\t@mock_aws\n\tdef test_function_public_with_source_account(self):\n\t\t# Create the mock IAM role\n\t\tiam_client = client('iam', region_name=AWS_REGION_EU_WEST_1)\n\t\trole_name = 'test-role'\n\t\tassume_role_policy_document = {\n\t\t\t'Version': '2012-10-17',\n\t\t\t'Statement': [\n\t\t\t\t{\n\t\t\t\t\t'Effect': 'Allow',\n\t\t\t\t\t'Principal': {'Service': 'lambda.amazonaws.com'},\n\t\t\t\t\t'Action': 'sts:AssumeRole',\n\t\t\t\t}\n\t\t\t],\n\t\t}\n\t\trole_arn = iam_client.create_role(\n\t\t\tRoleName=role_name,\n\t\t\tAssumeRolePolicyDocument=dumps(assume_role_policy_document),\n\t\t)['Role']['Arn']\n\n\t\tfunction_name = 'test-lambda'\n\n\t\t# Create the lambda function using boto3 client\n\t\tlambda_client = client('lambda', region_name=AWS_REGION_EU_WEST_1)\n\t\tfunction_arn = lambda_client.create_function(\n\t\t\tFunctionName=function_name,\n\t\t\tRuntime='nodejs4.3',\n\t\t\tRole=role_arn,\n\t\t\tHandler='index.handler',\n\t\t\tCode={'ZipFile': b'fileb://file-path/to/your-deployment-package.zip'},\n\t\t\tDescription='Test Lambda function',\n\t\t\tTimeout=3,\n\t\t\tMemorySize=128,\n\t\t\tPublish=True,\n\t\t\tTags={'tag1': 'value1', 'tag2': 'value2'},\n\t\t)['FunctionArn']\n\n\t\t# Attach the policy to the lambda function with a wildcard principal\n\t\tlambda_client.add_permission(\n\t\t\tFunctionName=function_name,\n\t\t\tStatementId='non-public-access',\n\t\t\tAction='lambda:InvokeFunction',\n\t\t\tPrincipal='*',\n\t\t\tSourceArn=function_arn,\n\t\t\tSourceAccount=AWS_ACCOUNT_NUMBER,\n\t\t)\n\n\t\taws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])\n\n\t\tfrom prowler.providers.aws.services.awslambda.awslambda_service import Lambda\n\n\t\twith mock.patch(\n\t\t\t'prowler.providers.common.provider.Provider.get_global_provider',\n\t\t\treturn_value=aws_provider,\n\t\t), mock.patch(\n\t\t\t'prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client',\n\t\t\tnew=Lambda(aws_provider),\n\t\t):\n\t\t\t# Test Check\n\t\t\tfrom prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import (\n\t\t\t\tawslambda_function_not_publicly_accessible,\n\t\t\t)\n\n\t\t\tcheck = awslambda_function_not_publicly_accessible()\n\t\t\tresult = check.execute()\n\n\t\t\tassert len(result) == 1\n\t\t\tassert result[0].region == AWS_REGION_EU_WEST_1\n\t\t\tassert result[0].resource_id == function_name\n\t\t\tassert result[0].resource_arn == function_arn\n\t\t\tassert result[0].status == 'PASS'\n\t\t\tassert (\n\t\t\t\tresult[0].status_extended\n\t\t\t\t== f'Lambda function {function_name} has a policy resource-based policy not public.'\n\t\t\t)\n\t\t\tassert result[0].resource_tags == [{'tag1': 'value1', 'tag2': 'value2'}]\n\n\t@mock_aws\n\tdef test_function_not_public(self):\n\t\t# Create the mock IAM role\n\t\tiam_client = client('iam', region_name=AWS_REGION_EU_WEST_1)\n\t\trole_name = 'test-role'\n\t\tassume_role_policy_document = {\n\t\t\t'Version': '2012-10-17',\n\t\t\t'Statement': [\n\t\t\t\t{\n\t\t\t\t\t'Effect': 'Allow',\n\t\t\t\t\t'Principal': {'Service': 'lambda.amazonaws.com'},\n\t\t\t\t\t'Action': 'sts:AssumeRole',\n\t\t\t\t}\n\t\t\t],\n\t\t}\n\t\trole_arn = iam_client.create_role(\n\t\t\tRoleName=role_name,\n\t\t\tAssumeRolePolicyDocument=dumps(assume_role_policy_document),\n\t\t)['Role']['Arn']\n\n\t\tfunction_name = 'test-lambda'\n\n\t\t# Create the lambda function using boto3 client\n\t\tlambda_client = client('lambda', region_name=AWS_REGION_EU_WEST_1)\n\t\tfunction_arn = lambda_client.create_function(\n\t\t\tFunctionName=function_name,\n\t\t\tRuntime='nodejs4.3',\n\t\t\tRole=role_arn,\n\t\t\tHandler='index.handler',\n\t\t\tCode={'ZipFile': b'fileb://file-path/to/your-deployment-package.zip'},\n\t\t\tDescription='Test Lambda function',\n\t\t\tTimeout=3,\n\t\t\tMemorySize=128,\n\t\t\tPublish=True,\n\t\t\tTags={'tag1': 'value1', 'tag2': 'value2'},\n\t\t)['FunctionArn']\n\n\t\t# Attach the policy to the lambda function with a specific AWS account number as principal\n\t\tlambda_client.add_permission(\n\t\t\tFunctionName=function_name,\n\t\t\tStatementId='public-access',\n\t\t\tAction='lambda:InvokeFunction',\n\t\t\tPrincipal=AWS_ACCOUNT_NUMBER,\n\t\t\tSourceArn=function_arn,\n\t\t)\n\n\t\taws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])\n\n\t\tfrom prowler.providers.aws.services.awslambda.awslambda_service import Lambda\n\n\t\twith mock.patch(\n\t\t\t'prowler.providers.common.provider.Provider.get_global_provider',\n\t\t\treturn_value=aws_provider,\n\t\t), mock.patch(\n\t\t\t'prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client',\n\t\t\tnew=Lambda(aws_provider),\n\t\t):\n\t\t\t# Test Check\n\t\t\tfrom prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import (\n\t\t\t\tawslambda_function_not_publicly_accessible,\n\t\t\t)\n\n\t\t\tcheck = awslambda_function_not_publicly_accessible()\n\t\t\tresult = check.execute()\n\n\t\t\tassert len(result) == 1\n\t\t\tassert result[0].region == AWS_REGION_EU_WEST_1\n\t\t\tassert result[0].resource_id == function_name\n\t\t\tassert result[0].resource_arn == function_arn\n\t\t\tassert result[0].status == 'PASS'\n\t\t\tassert (\n\t\t\t\tresult[0].status_extended\n\t\t\t\t== f'Lambda function {function_name} has a policy resource-based policy not public.'\n\t\t\t)\n\t\t\tassert result[0].resource_tags == [{'tag1': 'value1', 'tag2': 'value2'}]", + f"{check_name}:", + ] + return prompt_parts + + def _prepare_metadata_prompt(self, metadata: dict, context: str) -> list: + """Prepare the prompt for generating the metadata.""" + + metadata.pop("SubServiceName", None) + metadata["Remediation"]["Code"].pop("NativeIaC", None) + metadata["Remediation"]["Code"].pop("Other", None) + metadata["Remediation"]["Code"].pop("Terraform", None) + metadata.pop("DependsOn", None) + metadata.pop("RelatedTo", None) + + valid_prowler_categories = [ + "encryption", + "forensics-ready", + "internet-exposed", + "logging", + "redundancy", + "secrets", + "thread-detection", + "trustboundaries", + "vulnerability-management", + ] + + metadata_placeholder_resource_type = get_metadata_placeholder_resource_type( + metadata.get("Provider") + ) + + prompt_parts = [ + "Your task is to fill the metadata for a new cybersecurity check in Prowler (a Cloud Security tool).", + "The metadata is a JSON object with the following fields: ", + json.dumps(metadata, indent=2), + "Use the following context sources as inspiration to fill the metadata: ", + context, + "The field CheckType should be filled following the format: 'namespace/category/classifier', where namespace, category, and classifier are the values from the following dict: ", + json.dumps( + get_metadata_valid_check_type(metadata.get("Provider")), indent=2 + ), + "One example of a valid CheckType value is: 'Software and Configuration Checks/Vulnerabilities/CVE'. If you don't have a valid value for CheckType, you can leave it empty.", + f"The field ResourceType must be one of the following values (if there is not a valid value, you can put '{metadata_placeholder_resource_type}'): ", + ", ".join(get_metadata_valid_resource_type(metadata.get("Provider"))), + "If you don't have a valid value for ResourceType, you can leave it empty.", + f"The field Category must be one or more of the following values: {', '.join(valid_prowler_categories)}.", + "I need the answer only with JSON formatted text.", + ] + return prompt_parts + + def generate_check(self, check_name: str, context: str) -> str: + """Fill the check with Gemini AI.""" + check = "" + + prompt_parts = self._prepare_check_prompt(check_name, context) + check = ( + self._generate_content(prompt_parts) + .replace("python", "") + .replace("```", "") + .strip() + ) + + return check + + def generate_test(self, check_name: str): + """Fill the test with Gemini AI.""" + test = "" + + prompt_parts = self._prepare_test_prompt( + check_name, + ) + test = ( + self._generate_content(prompt_parts) + .replace("python", "") + .replace("```", "") + .strip() + ) + + return test + + def generate_metadata(self, metadata: dict, context: str) -> dict: + """Fill the metadata with Gemini AI.""" + if not metadata: + return {} + + prompt_parts = self._prepare_metadata_prompt(metadata, context) + filled_metadata_json = self._generate_content(prompt_parts) + + # Parse the generated JSON and re-add the removed fields + filled_metadata = json.loads( + filled_metadata_json.replace("\n", "") + .replace("json", "") + .replace("JSON", "") + .replace("```", "") + .strip() + ) + + # Add the removed fields back in the same order + filled_metadata["Remediation"]["Code"]["NativeIaC"] = "" + filled_metadata["Remediation"]["Code"]["Other"] = "" + filled_metadata["Remediation"]["Code"]["Terraform"] = "" + + # Insert key SubServiceName after ServiceName key and RelatedTo and DependsOn just before Notes key + + ordered_filled_metadata = {} + + for key, value in filled_metadata.items(): + if key == "Notes": + ordered_filled_metadata["DependsOn"] = [] + ordered_filled_metadata["RelatedTo"] = [] + ordered_filled_metadata[key] = value + if key == "ServiceName": + ordered_filled_metadata["SubServiceName"] = "" + + # Check that resource type is valid + if filled_metadata["ResourceType"]: + valid_resource_types = get_metadata_valid_resource_type( + filled_metadata["Provider"] + ) + if filled_metadata["ResourceType"] not in valid_resource_types: + ordered_filled_metadata["ResourceType"] = "Other" + + return ordered_filled_metadata diff --git a/util/prowler_check_kreator/lib/metadata_types.py b/util/prowler_check_kreator/lib/metadata_types.py new file mode 100644 index 0000000000..9491472e9e --- /dev/null +++ b/util/prowler_check_kreator/lib/metadata_types.py @@ -0,0 +1,246 @@ +def get_metadata_valid_check_type(provider: str = "aws") -> list: + """Get the valid check types for the provider + + Args: + provider: The Prowler provider. + + Returns: + A list of valid check types for the given provider. + """ + check_types = [] + + if provider == "aws": + check_types = [ + { + "namespace": "Software and Configuration Checks", + "children": [ + { + "category": "Vulnerabilities", + "children": [{"classifier": "CVE"}], + }, + { + "category": "AWS Security Best Practices", + "children": [ + {"classifier": "Network Reachability"}, + {"classifier": "Runtime Behavior Analysis"}, + ], + }, + { + "category": "Industry and Regulatory Standards", + "children": [ + {"classifier": "AWS Foundational Security Best Practices"}, + {"classifier": "CIS Host Hardening Benchmarks"}, + {"classifier": "CIS AWS Foundations Benchmark"}, + {"classifier": "PCI-DSS"}, + {"classifier": "Cloud Security Alliance Controls"}, + {"classifier": "ISO 90001 Controls"}, + {"classifier": "ISO 27001 Controls"}, + {"classifier": "ISO 27017 Controls"}, + {"classifier": "ISO 27018 Controls"}, + {"classifier": "SOC 1"}, + {"classifier": "SOC 2"}, + {"classifier": "HIPAA Controls (USA)"}, + {"classifier": "NIST 800-53 Controls (USA)"}, + {"classifier": "NIST CSF Controls (USA)"}, + {"classifier": "IRAP Controls (Australia)"}, + {"classifier": "K-ISMS Controls (Korea)"}, + {"classifier": "MTCS Controls (Singapore)"}, + {"classifier": "FISC Controls (Japan)"}, + {"classifier": "My Number Act Controls (Japan)"}, + {"classifier": "ENS Controls (Spain)"}, + {"classifier": "Cyber Essentials Plus Controls (UK)"}, + {"classifier": "G-Cloud Controls (UK)"}, + {"classifier": "C5 Controls (Germany)"}, + {"classifier": "IT-Grundschutz Controls (Germany)"}, + {"classifier": "GDPR Controls (Europe)"}, + {"classifier": "TISAX Controls (Europe)"}, + ], + }, + {"category": "Patch Management"}, + ], + }, + { + "namespace": "TTPs", + "children": [ + {"category": "Initial Access"}, + {"category": "Execution"}, + {"category": "Persistence"}, + {"category": "Privilege Escalation"}, + {"category": "Defense Evasion"}, + {"category": "Credential Access"}, + {"category": "Discovery"}, + {"category": "Lateral Movement"}, + {"category": "Collection"}, + {"category": "Command and Control"}, + ], + }, + { + "namespace": "Effects", + "children": [ + {"category": "Data Exposure"}, + {"category": "Data Exfiltration"}, + {"category": "Data Destruction"}, + {"category": "Denial of Service"}, + {"category": "Resource Consumption"}, + ], + }, + { + "namespace": "Unusual Behaviors", + "children": [ + {"category": "Application"}, + {"category": "Network Flow"}, + {"category": "IP address"}, + {"category": "User"}, + {"category": "VM"}, + {"category": "Container"}, + {"category": "Serverless"}, + {"category": "Process"}, + {"category": "Database"}, + {"category": "Data"}, + ], + }, + { + "namespace": "Sensitive Data Identifications", + "children": [ + {"category": "PII"}, + {"category": "Passwords"}, + {"category": "Legal"}, + {"category": "Financial"}, + {"category": "Security"}, + {"category": "Business"}, + ], + }, + ] + + return check_types + + +def get_metadata_valid_resource_type(provider: str = "aws") -> set: + """Get the valid resource types for the provider + + Args: + provider: The Prowler provider. + + Returns: + A set of valid resource types for the given provider. + """ + valid_resource_types = set() + + if provider == "aws": + valid_resource_types = { + "AwsIamAccessKey", + "AwsElbLoadBalancer", + "AwsRedshiftCluster", + "AwsEventsEndpoint", + "AwsElbv2LoadBalancer", + "AwsAutoScalingLaunchConfiguration", + "AwsWafv2RuleGroup", + "AwsWafRegionalRule", + "AwsCloudFrontDistribution", + "AwsWafRegionalWebAcl", + "AwsWafRateBasedRule", + "AwsCertificateManagerCertificate", + "AwsKmsKey", + "AwsDmsEndpoint", + "AwsLambdaLayerVersion", + "AwsIamRole", + "AwsElasticBeanstalkEnvironment", + "AwsBackupBackupPlan", + "AwsEc2ClientVpnEndpoint", + "AwsEcrContainerImage", + "AwsSqsQueue", + "AwsIamGroup", + "AwsOpenSearchServiceDomain", + "AwsApiGatewayV2Api", + "AwsCloudTrailTrail", + "AwsWafWebAcl", + "AwsEc2Subnet", + "AwsEc2VpcPeeringConnection", + "AwsEc2VpcEndpointService", + "AwsCodeBuildProject", + "AwsLambdaFunction", + "AwsNetworkFirewallRuleGroup", + "AwsDmsReplicationInstance", + "AwsRdsEventSubscription", + "AwsCloudWatchAlarm", + "AwsS3AccountPublicAccessBlock", + "AwsWafRegionalRateBasedRule", + "AwsRdsDbInstance", + "AwsEksCluster", + "AwsXrayEncryptionConfig", + "AwsWafv2WebAcl", + "AwsWafRuleGroup", + "AwsBackupBackupVault", + "AwsKinesisStream", + "AwsNetworkFirewallFirewallPolicy", + "AwsEc2NetworkInterface", + "AwsEcsTaskDefinition", + "AwsMskCluster", + "AwsApiGatewayRestApi", + "AwsS3Object", + "AwsRdsDbSnapshot", + "AwsBackupRecoveryPoint", + "AwsWafRule", + "AwsS3AccessPoint", + "AwsApiGatewayV2Stage", + "AwsGuardDutyDetector", + "AwsEfsAccessPoint", + "AwsEcsContainer", + "AwsEcsTask", + "AwsS3Bucket", + "AwsSageMakerNotebookInstance", + "AwsNetworkFirewallFirewall", + "AwsStepFunctionStateMachine", + "AwsIamUser", + "AwsAppSyncGraphQLApi", + "AwsApiGatewayStage", + "AwsEcrRepository", + "AwsEcsService", + "AwsEc2Vpc", + "AwsAmazonMQBroker", + "AwsWafRegionalRuleGroup", + "AwsEventSchemasRegistry", + "AwsRoute53HostedZone", + "AwsEventsEventbus", + "AwsDmsReplicationTask", + "AwsEc2Instance", + "AwsEcsCluster", + "AwsRdsDbSecurityGroup", + "AwsCloudFormationStack", + "AwsSnsTopic", + "AwsDynamoDbTable", + "AwsRdsDbCluster", + "AwsEc2Eip", + "AwsEc2RouteTable", + "AwsEc2TransitGateway", + "AwsElasticSearchDomain", + "AwsEc2LaunchTemplate", + "AwsEc2Volume", + "AwsAthenaWorkGroup", + "AwsSecretsManagerSecret", + "AwsEc2SecurityGroup", + "AwsIamPolicy", + "AwsSsmPatchCompliance", + "AwsAutoScalingAutoScalingGroup", + "AwsEc2NetworkAcl", + "AwsRdsDbClusterSnapshot", + } + + return valid_resource_types + + +def get_metadata_placeholder_resource_type(provider: str = "aws") -> str: + """Get the placeholder for the resource type for the provider + + Args: + provider: The Prowler provider. + + Returns: + A placeholder for the resource type for the given provider. + """ + placeholder = "" + + if provider == "aws": + placeholder = "Other" + + return placeholder diff --git a/util/prowler_check_kreator/lib/templates.py b/util/prowler_check_kreator/lib/templates.py new file mode 100644 index 0000000000..d02a469818 --- /dev/null +++ b/util/prowler_check_kreator/lib/templates.py @@ -0,0 +1,132 @@ +def load_check_template(provider: str, service: str, check_name: str) -> str: + """Load the template for the check file. + + Args: + provider (str): The provider of the service. + service (str): The service to check. + check_name (str): The name of the check. + + Returns: + A check template used when the user does not want to generate the check with AI. + + Raises: + ValueError: If the provider is not implemented yet. + """ + if provider == "aws": + return f""" +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.{service}.{service}_client import {service}_client +from typing import List + + +class {check_name}(Check): + def execute(self) -> List[Check_Report_AWS]: + findings = [] + for , in {service}_client..items(): + report = Check_Report_AWS(self.metadata()) + report.region = .region + report.resource_id = .name + report.resource_arn = + report.resource_tags = .tags + report.status = "FAIL" + report.status_extended = f"..." + + if : + report.status = "PASS" + report.status_extended = f"..." + + findings.append(report) + + return findings +""" + else: + raise ValueError(f"Template for {provider} not implemented yet") + + +def load_test_template(provider: str, service: str, check_name: str) -> str: + """Load the template for the test file. + + Args: + provider: The provider of the service (e.g., "aws"). + service: The service to check (e.g., "s3"). + check_name: The name of the check (e.g., "check_bucket_encryption"). + + Returns: + A test template used when the user does not want to generate the check with AI. + + Raises: + ValueError: If the template for the given provider is not implemented. + """ + if provider == "aws": + return f""" +from unittest import mock + +from boto3 import client +from moto import mock_aws + +from tests.providers.aws.utils import ( + AWS_REGION_EU_WEST_1, + set_mocked_aws_provider, +) + + +class Test_{check_name}: + @mock_aws + def test_(self): + from prowler.providers.aws.services.{service}.{service}_service import + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.{service}.{check_name}.{check_name}.{service}_client", + new=(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.{service}.{check_name}.{check_name} import ( + {check_name}, + ) + + check = {check_name}() + result = check.execute() + + assert len(result) == 0 + + @mock_aws + def test_one_compliant_{service}(self): + {service}_client = client("{service}", region_name=AWS_REGION_EU_WEST_1) + # Create a compliant resource + + from prowler.providers.aws.services.{service}.{service}_service import + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.{service}.{check_name}.{check_name}.{service}_client", + new=(aws_provider), + ): + from prowler.providers.aws.services.{service}.{check_name}.{check_name} import ( + {check_name}, + ) + + check = {check_name}() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert result[0].status_extended == "..." + assert result[0].region == AWS_REGION_EU_WEST_1 + assert result[0].resource_id == .id + assert ( + result[0].resource_arn + == f"arn:(aws_partition):{service}:(region):(account_id):(resource)" + ) + assert result[0].resource_tags == .tags +""" + else: + raise ValueError(f"Template for {provider} not implemented yet") diff --git a/util/prowler_check_kreator/prowler_check_kreator.py b/util/prowler_check_kreator/prowler_check_kreator.py new file mode 100644 index 0000000000..24fa6140f6 --- /dev/null +++ b/util/prowler_check_kreator/prowler_check_kreator.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +import json +import os +import sys + +from util.prowler_check_kreator.lib.templates import ( + load_check_template, + load_test_template, +) + + +class ProwlerCheckKreator: + def __init__(self, provider: str, check_name: str): + # Validate provider + + SUPPORTED_PROVIDERS = {"aws"} + + if provider in SUPPORTED_PROVIDERS: + self._provider = provider + else: + raise ValueError( + f"Invalid provider. Supported providers: {', '.join(SUPPORTED_PROVIDERS)}" + ) + + # Find the Prowler folder + self._prowler_folder = os.path.abspath( + os.path.join(os.path.dirname(__file__), os.pardir, os.pardir) + ) + + # Validate if service exists for the selected provider + service_name = check_name.split("_")[0] + + service_path = os.path.join( + self._prowler_folder, + "prowler/providers/", + provider, + "services/", + service_name, + ) + + if os.path.exists(service_path): + self._service_name = service_name + else: + raise ValueError( + f"Service {service_name} does not exist for {provider}. Please introduce a valid service" + ) + + # Ask user if want to use Gemini for all the process + + user_input = ( + input( + "Do you want to use Gemini to create the check and metadata? Type 'yes'/'no' and press enter: " + ) + .strip() + .lower() + ) + + if user_input == "yes": + # Let the user to use the model that he wants + supported_models = [ + "gemini-1.5-flash", + "gemini-1.5-pro", + "gemini-1.0-pro", + ] + + print("Select the model that you want to use:") + for i, model in enumerate(supported_models): + print(f"{i + 1}. {model}") + + user_input = input( + "Type the number of the model and press enter (default is 1): " + ).strip() + + if not user_input: + model_index = 1 + else: + model_index = int(user_input) + + if model_index < 1 or model_index > len(supported_models): + raise ValueError("Invalid model selected.") + + model_name = supported_models[model_index - 1] + + if "gemini" in model_name: + from util.prowler_check_kreator.lib.llms.gemini import Gemini + + self._model = Gemini(model_name) + + # Provide some context about the check to create + self._context = ( + input( + "Please provide some context to generate the check and metadata:\n" + ) + ).strip() + + else: + raise ValueError("Invalid model selected.") + elif user_input == "no": + self._model = None + self._context = "" + else: + raise ValueError("Invalid input. Please type 'yes' or 'no'.") + + if not self._check_exists(check_name): + self._check_name = check_name + self._check_path = os.path.join( + self._prowler_folder, + "prowler/providers/", + provider, + "services/", + service_name, + check_name, + ) + else: + # Check already exists, give the user the possibility to continue or not + user_input = ( + input( + f"Some files of {check_name} already exists. Do you want to continue and overwrite it? Type 'yes' if you want to continue: " + ) + .strip() + .lower() + ) + + if user_input == "yes": + self._check_name = check_name + self._check_path = os.path.join( + self._prowler_folder, + "prowler/providers/", + provider, + "services/", + service_name, + check_name, + ) + else: + raise ValueError(f"Check {check_name} already exists.") + + def kreate_check(self) -> None: + """Create a new check in Prowler""" + + # Create the check + print(f"Creating check {self._check_name} for {self._provider}") + + # Inside the check folder, create the check files: __init__.py, check_name.py, and check_name.metadata.json + os.makedirs(self._check_path, exist_ok=True) + + with open(os.path.join(self._check_path, "__init__.py"), "w") as f: + f.write("") + + self._write_check_file() + self._write_metadata_file() + + # Create test directory if it does not exist + test_folder = os.path.join( + self._prowler_folder, + "tests/providers/", + self._provider, + "services/", + self._service_name, + self._check_name, + ) + + os.makedirs(test_folder, exist_ok=True) + + self._write_test_file() + + print(f"Check {self._check_name} created successfully") + + def _check_exists(self, check_name: str) -> bool: + """Ensure if any file related to the check already exists. + + Args: + check_name: The name of the check. + + Returns: + True if the check already exists, False otherwise. + """ + + # Get the check path + check_path = os.path.join( + self._prowler_folder, + "prowler/providers/", + self._provider, + "services/", + self._service_name, + check_name, + ) + + # Get the test path + _test_path = os.path.join( + self._prowler_folder, + "tests/providers/", + self._provider, + "services/", + self._service_name, + check_name, + ) + + # Check if exits check.py, check_metadata.json or check_test.py + return ( + os.path.exists(check_path) + or os.path.exists(os.path.join(check_path, "__init__.py")) + or os.path.exists(os.path.join(check_path, f"{check_name}.py")) + or os.path.exists(os.path.join(check_path, f"{check_name}.metadata.json")) + or os.path.exists(_test_path) + ) + + def _write_check_file(self) -> None: + """Write the check file""" + + if self._model is None: + check_content = load_check_template( + self._provider, self._service_name, self._check_name + ) + else: + check_content = self._model.generate_check( + check_name=self._check_name, context=self._context + ) + + with open(os.path.join(self._check_path, f"{self._check_name}.py"), "w") as f: + f.write(check_content) + + def _write_metadata_file(self) -> None: + """Write the metadata file""" + + metadata_template = { + "Provider": self._provider, + "CheckID": self._check_name, + "CheckTitle": "", + "CheckType": [], + "ServiceName": self._service_name, + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "", + "ResourceType": "", + "Description": "", + "Risk": "", + "RelatedUrl": "", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "", + }, + "Recommendation": {"Text": "", "Url": ""}, + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + } + + if self._model is None: + filled_metadata = metadata_template + else: + filled_metadata = self._model.generate_metadata( + metadata_template, self._context + ) + + with open( + os.path.join(self._check_path, f"{self._check_name}.metadata.json"), "w" + ) as f: + f.write(json.dumps(filled_metadata, indent=2)) + + def _write_test_file(self) -> None: + """Write the test file""" + + test_folder = os.path.join( + self._prowler_folder, + "tests/providers/", + self._provider, + "services/", + self._service_name, + self._check_name, + ) + + if self._model is None: + test_template = load_test_template( + self._provider, self._service_name, self._check_name + ) + else: + test_template = self._model.generate_test(self._check_name) + + with open(os.path.join(test_folder, f"{self._check_name}_test.py"), "w") as f: + f.write(test_template) + + +if __name__ == "__main__": + try: + if len(sys.argv) != 3: + raise ValueError( + "Invalid arguments. Usage: python prowler_check_kreator.py " + ) + + prowler_check_creator = ProwlerCheckKreator(sys.argv[1], sys.argv[2]) + + sys.exit(prowler_check_creator.kreate_check()) + + except ValueError as e: + print(f"Error: {e}") + sys.exit(1) + except Exception as e: + print(f"Unexpected error: {e}") + sys.exit(1) From dca49b19724692352910fa1b7d9497893f29117a Mon Sep 17 00:00:00 2001 From: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> Date: Tue, 12 Nov 2024 23:41:46 +0100 Subject: [PATCH 09/55] feat(aws): add new service `firehose` (#5620) Co-authored-by: MrCloudSec --- .../aws/services/firehose/__init__.py | 0 .../aws/services/firehose/firehose_client.py | 4 ++ .../aws/services/firehose/firehose_service.py | 58 ++++++++++++++++ .../providers/aws/services/mq/mq_service.py | 2 +- .../firehose/firehose_service_test.py | 69 +++++++++++++++++++ 5 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 prowler/providers/aws/services/firehose/__init__.py create mode 100644 prowler/providers/aws/services/firehose/firehose_client.py create mode 100644 prowler/providers/aws/services/firehose/firehose_service.py create mode 100644 tests/providers/aws/services/firehose/firehose_service_test.py diff --git a/prowler/providers/aws/services/firehose/__init__.py b/prowler/providers/aws/services/firehose/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/firehose/firehose_client.py b/prowler/providers/aws/services/firehose/firehose_client.py new file mode 100644 index 0000000000..46d3bdeab7 --- /dev/null +++ b/prowler/providers/aws/services/firehose/firehose_client.py @@ -0,0 +1,4 @@ +from prowler.providers.aws.services.firehose.firehose_service import Firehose +from prowler.providers.common.provider import Provider + +firehose_client = Firehose(Provider.get_global_provider()) diff --git a/prowler/providers/aws/services/firehose/firehose_service.py b/prowler/providers/aws/services/firehose/firehose_service.py new file mode 100644 index 0000000000..fafd87cd3c --- /dev/null +++ b/prowler/providers/aws/services/firehose/firehose_service.py @@ -0,0 +1,58 @@ +from typing import Dict, List, Optional + +from botocore.client import ClientError +from pydantic import BaseModel, Field + +from prowler.lib.logger import logger +from prowler.lib.scan_filters.scan_filters import is_resource_filtered +from prowler.providers.aws.lib.service.service import AWSService + + +class Firehose(AWSService): + def __init__(self, provider): + # Call AWSService's __init__ + super().__init__(__class__.__name__, provider) + self.delivery_streams = {} + self.__threading_call__(self._list_delivery_streams) + self.__threading_call__( + self._list_tags_for_delivery_stream, self.delivery_streams.values() + ) + + def _list_delivery_streams(self, regional_client): + logger.info("Firehose - Listing delivery streams...") + try: + for stream_name in regional_client.list_delivery_streams()[ + "DeliveryStreamNames" + ]: + stream_arn = f"arn:{self.audited_partition}:firehose:{regional_client.region}:{self.audited_account}:deliverystream/{stream_name}" + if not self.audit_resources or ( + is_resource_filtered(stream_arn, self.audit_resources) + ): + self.delivery_streams[stream_arn] = DeliveryStream( + arn=stream_arn, + name=stream_name, + region=regional_client.region, + ) + except ClientError as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + def _list_tags_for_delivery_stream(self, stream): + try: + stream.tags = ( + self.regional_clients[stream.region] + .list_tags_for_delivery_stream(DeliveryStreamName=stream.name) + .get("Tags", []) + ) + except ClientError as error: + logger.error( + f"{stream.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + +class DeliveryStream(BaseModel): + arn: str + name: str + region: str + tags: Optional[List[Dict[str, str]]] = Field(default_factory=list) diff --git a/prowler/providers/aws/services/mq/mq_service.py b/prowler/providers/aws/services/mq/mq_service.py index f6fe8b16f7..b97e51f35c 100644 --- a/prowler/providers/aws/services/mq/mq_service.py +++ b/prowler/providers/aws/services/mq/mq_service.py @@ -11,7 +11,7 @@ from prowler.providers.aws.lib.service.service import AWSService class MQ(AWSService): def __init__(self, provider): # Call AWSService's __init__ - super().__init__("mq", provider) + super().__init__(__class__.__name__, provider) self.brokers = {} self.__threading_call__(self._list_brokers) self.__threading_call__(self._describe_broker, self.brokers.values()) diff --git a/tests/providers/aws/services/firehose/firehose_service_test.py b/tests/providers/aws/services/firehose/firehose_service_test.py new file mode 100644 index 0000000000..46d6c8c522 --- /dev/null +++ b/tests/providers/aws/services/firehose/firehose_service_test.py @@ -0,0 +1,69 @@ +from boto3 import client +from moto import mock_aws + +from prowler.providers.aws.services.firehose.firehose_service import Firehose +from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider + + +class Test_Firehose_Service: + # Test Firehose Service + @mock_aws + def test_service(self): + # Firehose client for this test class + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + firehose = Firehose(aws_provider) + assert firehose.service == "firehose" + + # Test Firehose Client + @mock_aws + def test_client(self): + # Firehose client for this test class + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + firehose = Firehose(aws_provider) + for regional_client in firehose.regional_clients.values(): + assert regional_client.__class__.__name__ == "Firehose" + + # Test Firehose Session + @mock_aws + def test__get_session__(self): + # Firehose client for this test class + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + firehose = Firehose(aws_provider) + assert firehose.session.__class__.__name__ == "Session" + + # Test Firehose List Delivery Streams + @mock_aws + def test_list_delivery_streams(self): + # Generate S3 client + s3_client = client("s3", region_name=AWS_REGION_EU_WEST_1) + s3_client.create_bucket( + Bucket="test-bucket", + CreateBucketConfiguration={"LocationConstraint": AWS_REGION_EU_WEST_1}, + ) + + # Generate Firehose client + firehose_client = client("firehose", region_name=AWS_REGION_EU_WEST_1) + delivery_stream = firehose_client.create_delivery_stream( + DeliveryStreamName="test-delivery-stream", + DeliveryStreamType="DirectPut", + S3DestinationConfiguration={ + "RoleARN": "arn:aws:iam::012345678901:role/firehose-role", + "BucketARN": "arn:aws:s3:::test-bucket", + "Prefix": "", + "BufferingHints": {"IntervalInSeconds": 300, "SizeInMBs": 5}, + "CompressionFormat": "UNCOMPRESSED", + }, + Tags=[{"Key": "key", "Value": "value"}], + ) + arn = delivery_stream["DeliveryStreamARN"] + delivery_stream_name = arn.split("/")[-1] + + # Firehose Client for this test class + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + firehose = Firehose(aws_provider) + + assert len(firehose.delivery_streams) == 1 + assert firehose.delivery_streams[arn].arn == arn + assert firehose.delivery_streams[arn].name == delivery_stream_name + assert firehose.delivery_streams[arn].region == AWS_REGION_EU_WEST_1 + assert firehose.delivery_streams[arn].tags == [{"Key": "key", "Value": "value"}] From 5641160177cbb4a8fe101a2f2c6f37245ee0a068 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 18:17:31 -0500 Subject: [PATCH 10/55] chore(deps): bump boto3 from 1.35.57 to 1.35.58 (#5741) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 12 ++++++------ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3fc6af59ad..5604d41796 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "about-time" @@ -759,17 +759,17 @@ files = [ [[package]] name = "boto3" -version = "1.35.57" +version = "1.35.58" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.57-py3-none-any.whl", hash = "sha256:9edf49640c79a05b0a72f4c2d1e24dfc164344b680535a645f455ac624dc3680"}, - {file = "boto3-1.35.57.tar.gz", hash = "sha256:db58348849a5af061f0f5ec9c3b699da5221ca83354059fdccb798e3ddb6b62a"}, + {file = "boto3-1.35.58-py3-none-any.whl", hash = "sha256:856896fd5fc5871758eb04b27bad5bbbf0fdb6143a923f9e8d10125351efdf98"}, + {file = "boto3-1.35.58.tar.gz", hash = "sha256:1ee139e63f1545ee0192914cfe422b68360b8c344a94e4612ac657dd7ece93de"}, ] [package.dependencies] -botocore = ">=1.35.57,<1.36.0" +botocore = ">=1.35.58,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "6e29516fe339f665008550f771ec72aae633c306d280d0d1c3d63ecb2f7b329a" +content-hash = "93994490861441227b4db8ebb4acc46d54fad3cc2a6166286bd07eeebaaabb6c" diff --git a/pyproject.toml b/pyproject.toml index 6e76e7e214..d3473cb97f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ azure-mgmt-storage = "21.2.1" azure-mgmt-subscription = "3.1.1" azure-mgmt-web = "7.3.1" azure-storage-blob = "12.23.1" -boto3 = "1.35.57" +boto3 = "1.35.58" botocore = "1.35.58" colorama = "0.4.6" cryptography = "43.0.1" From e881a0f27476f5ccc6e43808202f0da8ebf706ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 08:13:18 -0500 Subject: [PATCH 11/55] chore(deps): bump google-api-python-client from 2.151.0 to 2.152.0 (#5742) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5604d41796..b3deb0bb9f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1703,13 +1703,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-api-python-client" -version = "2.151.0" +version = "2.152.0" description = "Google API Client Library for Python" optional = false python-versions = ">=3.7" files = [ - {file = "google_api_python_client-2.151.0-py2.py3-none-any.whl", hash = "sha256:4427b2f47cd88b0355d540c2c52215f68c337f3bc9d6aae1ceeae4525977504c"}, - {file = "google_api_python_client-2.151.0.tar.gz", hash = "sha256:a9d26d630810ed4631aea21d1de3e42072f98240aaf184a8a1a874a371115034"}, + {file = "google_api_python_client-2.152.0-py2.py3-none-any.whl", hash = "sha256:3e429b4584f6ccb620c1c4cdd43a6d78c5a8d307fc64860026aa26e203e8f932"}, + {file = "google_api_python_client-2.152.0.tar.gz", hash = "sha256:9593476dc548fcba794f2ea0e3cfbf927de5a7b79379781c1664637bf246f072"}, ] [package.dependencies] @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "93994490861441227b4db8ebb4acc46d54fad3cc2a6166286bd07eeebaaabb6c" +content-hash = "cd5b52e8ddbf13189b508bbf864a6b4bb3a3ca5e00832eda0c7100050a60d45b" diff --git a/pyproject.toml b/pyproject.toml index d3473cb97f..c09ea182f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ cryptography = "43.0.1" dash = "2.18.2" dash-bootstrap-components = "1.6.0" detect-secrets = "1.5.0" -google-api-python-client = "2.151.0" +google-api-python-client = "2.152.0" google-auth-httplib2 = ">=0.1,<0.3" jsonschema = "4.23.0" kubernetes = "31.0.0" From 8dc042e59475bff84561230bfc916e023224b3e6 Mon Sep 17 00:00:00 2001 From: Prowler Bot Date: Wed, 13 Nov 2024 14:13:35 +0100 Subject: [PATCH 12/55] chore(regions_update): Changes in regions for AWS services (#5744) Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com> --- prowler/providers/aws/aws_regions_by_service.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prowler/providers/aws/aws_regions_by_service.json b/prowler/providers/aws/aws_regions_by_service.json index fe8c5f2342..0e2c4bf5b1 100644 --- a/prowler/providers/aws/aws_regions_by_service.json +++ b/prowler/providers/aws/aws_regions_by_service.json @@ -4839,6 +4839,7 @@ "ap-southeast-2", "ap-southeast-3", "ap-southeast-4", + "ap-southeast-5", "ca-central-1", "ca-west-1", "eu-central-1", @@ -5549,6 +5550,7 @@ "iotfleetwise": { "regions": { "aws": [ + "ap-south-1", "eu-central-1", "us-east-1" ], @@ -11092,6 +11094,7 @@ "ap-south-1", "ap-southeast-1", "ap-southeast-2", + "ap-southeast-3", "ca-central-1", "eu-central-1", "eu-north-1", From 796983a5306de6e8bfb99ca02c10cd6c477d438e Mon Sep 17 00:00:00 2001 From: Sergio Garcia Date: Wed, 13 Nov 2024 09:18:42 -0500 Subject: [PATCH 13/55] fix(gcp): scan only ACTIVE projects (#5743) --- .../providers/gcp/exceptions/exceptions.py | 4 +- prowler/providers/gcp/gcp_provider.py | 32 ++++--- tests/providers/gcp/gcp_provider_test.py | 84 ++++++++++++++++--- 3 files changed, 95 insertions(+), 25 deletions(-) diff --git a/prowler/providers/gcp/exceptions/exceptions.py b/prowler/providers/gcp/exceptions/exceptions.py index c61bc525f8..2232d692f9 100644 --- a/prowler/providers/gcp/exceptions/exceptions.py +++ b/prowler/providers/gcp/exceptions/exceptions.py @@ -15,8 +15,8 @@ class GCPBaseException(ProwlerException): "remediation": "Check the HTTP error and ensure the request is properly formatted.", }, (3002, "GCPNoAccesibleProjectsError"): { - "message": "No Project IDs can be accessed via Google Credentials", - "remediation": "Ensure the project is accessible and properly set up.", + "message": "No Project IDs are active or can be accessed via Google Credentials", + "remediation": "Ensure the project is active and accessible.", }, (3003, "GCPSetUpSessionError"): { "message": "Error setting up session", diff --git a/prowler/providers/gcp/gcp_provider.py b/prowler/providers/gcp/gcp_provider.py index 72529a563c..6ad8e90f31 100644 --- a/prowler/providers/gcp/gcp_provider.py +++ b/prowler/providers/gcp/gcp_provider.py @@ -105,22 +105,28 @@ class GcpProvider(Provider): file=__file__, message="No Project IDs can be accessed via Google Credentials.", ) - if project_ids: + if self._default_project_id not in project_ids: + self._default_project_id = project_ids[0] for input_project in project_ids: - for accessible_project in accessible_projects: - if self.is_project_matching(input_project, accessible_project): - self._projects[accessible_project] = accessible_projects[ - accessible_project - ] - self._project_ids.append( - accessible_projects[accessible_project].id - ) + for ( + accessible_project_id, + accessible_project, + ) in accessible_projects.items(): + # Only scan active projects + if accessible_project.lifecycle_state == "ACTIVE": + if self.is_project_matching( + input_project, accessible_project_id + ): + self._projects[accessible_project_id] = accessible_project + self._project_ids.append(accessible_project_id) else: # If not projects were input, all accessible projects are scanned by default for project_id, project in accessible_projects.items(): - self._projects[project_id] = project - self._project_ids.append(project_id) + # Only scan active projects + if project.lifecycle_state == "ACTIVE": + self._projects[project_id] = project + self._project_ids.append(project_id) # Remove excluded projects if any input if excluded_project_ids: @@ -134,11 +140,11 @@ class GcpProvider(Provider): if not self._projects: logger.critical( - "No Input Project IDs can be accessed via Google Credentials." + "No Input Project IDs are active or can be accessed via Google Credentials." ) raise GCPNoAccesibleProjectsError( file=__file__, - message="No Input Project IDs can be accessed via Google Credentials.", + message="No Input Project IDs are active or can be accessed via Google Credentials.", ) if list_project_ids: diff --git a/tests/providers/gcp/gcp_provider_test.py b/tests/providers/gcp/gcp_provider_test.py index d32fa47b28..9c74821935 100644 --- a/tests/providers/gcp/gcp_provider_test.py +++ b/tests/providers/gcp/gcp_provider_test.py @@ -14,6 +14,7 @@ from prowler.config.config import ( from prowler.providers.common.models import Connection from prowler.providers.gcp.exceptions.exceptions import ( GCPInvalidProviderIdError, + GCPNoAccesibleProjectsError, GCPTestConnectionError, ) from prowler.providers.gcp.gcp_provider import GcpProvider @@ -40,7 +41,7 @@ class TestGCPProvider: id="project/55555555", name="test-project", labels={"test": "value"}, - lifecycle_state="", + lifecycle_state="ACTIVE", ) } @@ -109,7 +110,7 @@ class TestGCPProvider: id="project/55555555", name="test-project", labels={"test": "value"}, - lifecycle_state="", + lifecycle_state="ACTIVE", ) } @@ -183,7 +184,7 @@ class TestGCPProvider: id="project/55555555", name="test-project", labels={"test": "value"}, - lifecycle_state="", + lifecycle_state="ACTIVE", ) } @@ -247,7 +248,7 @@ class TestGCPProvider: id="project/55555555", name="test-project", labels={"test": "value"}, - lifecycle_state="", + lifecycle_state="ACTIVE", ) } @@ -319,7 +320,7 @@ class TestGCPProvider: id="project/55555555", name="test-project", labels={"test": "value"}, - lifecycle_state="", + lifecycle_state="ACTIVE", organization=GCPOrganization( id="test-organization-id", name="test-organization", @@ -369,6 +370,69 @@ class TestGCPProvider: == "test-organization-id" ) + def test_setup_session_with_inactive_project(self): + mocked_credentials = MagicMock() + + mocked_credentials.refresh.return_value = None + mocked_credentials._service_account_email = "test-service-account-email" + + arguments = Namespace() + arguments.project_id = ["project/55555555"] + arguments.excluded_project_id = [] + arguments.organization_id = None + arguments.list_project_id = False + arguments.credentials_file = "test_credentials_file" + arguments.impersonate_service_account = "" + arguments.config_file = default_config_file_path + arguments.fixer_config = default_fixer_config_file_path + + projects = { + "test-project": GCPProject( + number="55555555", + id="project/55555555", + name="test-project", + labels={"test": "value"}, + lifecycle_state="DELETE_REQUESTED", + ) + } + + mocked_service = MagicMock() + + mocked_service.projects.list.return_value = MagicMock( + execute=MagicMock(return_value={"projects": projects}) + ) + with patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", + return_value=projects, + ), patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", + return_value=None, + ), patch( + "os.path.abspath", + return_value="test_credentials_file", + ), patch( + "prowler.providers.gcp.gcp_provider.default", + return_value=(mocked_credentials, MagicMock()), + ), patch( + "prowler.providers.gcp.gcp_provider.discovery.build", + return_value=mocked_service, + ): + with pytest.raises(Exception) as e: + GcpProvider( + arguments.organization_id, + arguments.project_id, + arguments.excluded_project_id, + arguments.credentials_file, + arguments.impersonate_service_account, + arguments.list_project_id, + arguments.config_file, + arguments.fixer_config, + client_id=None, + client_secret=None, + refresh_token=None, + ) + assert e.type == GCPNoAccesibleProjectsError + def test_print_credentials_default_options(self, capsys): mocked_credentials = MagicMock() @@ -391,7 +455,7 @@ class TestGCPProvider: id="project/55555555", name="test-project", labels={"test": "value"}, - lifecycle_state="", + lifecycle_state="ACTIVE", ) } @@ -462,7 +526,7 @@ class TestGCPProvider: id="project/55555555", name="test-project", labels={"test": "value"}, - lifecycle_state="", + lifecycle_state="ACTIVE", ) } @@ -533,14 +597,14 @@ class TestGCPProvider: id="project/55555555", name="test-project", labels={"test": "value"}, - lifecycle_state="", + lifecycle_state="ACTIVE", ), "test-excluded-project": GCPProject( number="12345678", id="project/12345678", name="test-excluded-project", labels={"test": "value"}, - lifecycle_state="", + lifecycle_state="ACTIVE", ), } @@ -658,7 +722,7 @@ class TestGCPProvider: id="project/55555555", name="test-project", labels={"test": "value"}, - lifecycle_state="", + lifecycle_state="ACTIVE", ), } From c0659f712a5a36e614759796ef91d9a365c71980 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:20:49 -0500 Subject: [PATCH 14/55] chore(deps): bump botocore from 1.35.58 to 1.35.59 (#5740) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index b3deb0bb9f..0dfa605cc2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -778,13 +778,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.58" +version = "1.35.59" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.58-py3-none-any.whl", hash = "sha256:647b8706ae6484ee4c2208235f38976d9f0e52f80143e81d7941075215e96111"}, - {file = "botocore-1.35.58.tar.gz", hash = "sha256:8303309c7b59ddf04b11d79813530809d6b10b411ac9f93916d2032c283d6881"}, + {file = "botocore-1.35.59-py3-none-any.whl", hash = "sha256:bcd66d7f55c8d1b6020eb86f2d87893fe591fb4be6a7d2a689c18be586452334"}, + {file = "botocore-1.35.59.tar.gz", hash = "sha256:de0ce655fedfc02c87869dfaa3b622488a17ff37da316ef8106cbe1573b83c98"}, ] [package.dependencies] @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "cd5b52e8ddbf13189b508bbf864a6b4bb3a3ca5e00832eda0c7100050a60d45b" +content-hash = "d5bf0558ef4d03a1c35b6c5650acc6ea06a09e38ee8fdc8d76f818895a6bc800" diff --git a/pyproject.toml b/pyproject.toml index c09ea182f8..ce0057e4fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ azure-mgmt-subscription = "3.1.1" azure-mgmt-web = "7.3.1" azure-storage-blob = "12.23.1" boto3 = "1.35.58" -botocore = "1.35.58" +botocore = "1.35.59" colorama = "0.4.6" cryptography = "43.0.1" dash = "2.18.2" From 622bc486886c0579b65a9307b78efb7d43d2c59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Wed, 13 Nov 2024 15:22:52 +0100 Subject: [PATCH 15/55] fix(ec2): add default value to Name key for image information (#5747) --- prowler/providers/aws/services/ec2/ec2_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler/providers/aws/services/ec2/ec2_service.py b/prowler/providers/aws/services/ec2/ec2_service.py index 7ca7b7719f..dcf49f6066 100644 --- a/prowler/providers/aws/services/ec2/ec2_service.py +++ b/prowler/providers/aws/services/ec2/ec2_service.py @@ -357,7 +357,7 @@ class EC2(AWSService): Image( id=image["ImageId"], arn=arn, - name=image["Name"], + name=image.get("Name", None), public=image.get("Public", False), region=regional_client.region, tags=image.get("Tags"), From a419b4b898e68b629d9f25e148e984081523ad89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Wed, 13 Nov 2024 18:52:50 +0100 Subject: [PATCH 16/55] feat(aws): get regions by partition (#5748) --- prowler/providers/aws/aws_provider.py | 60 ++++++++++++------- .../providers/aws/exceptions/exceptions.py | 11 ++++ .../providers/aws/lib/arguments/arguments.py | 4 +- tests/config/config_test.py | 12 +++- tests/providers/aws/aws_provider_test.py | 58 +++++++++++++++--- 5 files changed, 112 insertions(+), 33 deletions(-) diff --git a/prowler/providers/aws/aws_provider.py b/prowler/providers/aws/aws_provider.py index 82d90ec61f..c841484eb7 100644 --- a/prowler/providers/aws/aws_provider.py +++ b/prowler/providers/aws/aws_provider.py @@ -39,6 +39,7 @@ from prowler.providers.aws.exceptions.exceptions import ( AWSIAMRoleARNPartitionEmptyError, AWSIAMRoleARNRegionNotEmtpyError, AWSIAMRoleARNServiceNotIAMnorSTSError, + AWSInvalidPartitionError, AWSInvalidProviderIdError, AWSNoCredentialsError, AWSProfileNotFoundError, @@ -1297,6 +1298,44 @@ class AwsProvider(Provider): ) raise error + @staticmethod + def get_regions_by_partition(partition: str = None) -> set: + """ + Get the available AWS regions from the AWS services JSON file with the ability of filtering by partition. + + Args: + - partition (str): The AWS partition name. Default is None. + + Returns: + set: A set of available AWS regions. All if no `partition` is especified. + """ + try: + data = read_aws_regions_file() + + regions = set() + if partition is None: + for service in data["services"].values(): + for partition in service["regions"]: + for item in service["regions"][partition]: + regions.add(item) + else: + for service in data["services"].values(): + try: + for item in service["regions"][partition]: + regions.add(item) + except KeyError as key_error: + logger.error( + f"{key_error.__class__.__name__}[{key_error.__traceback__.tb_lineno}]: {key_error}" + ) + raise AWSInvalidPartitionError( + message=f"Invalid partition name: {partition}", + file=os.path.basename(__file__), + ) + return regions + except Exception as error: + logger.error(f"{error.__class__.__name__}: {error}") + return set() + def read_aws_regions_file() -> dict: """ @@ -1313,27 +1352,6 @@ def read_aws_regions_file() -> dict: return data -def get_aws_available_regions() -> set: - """ - Get the available AWS regions from the AWS services JSON file. - - Returns: - set: A set of available AWS regions. - """ - try: - data = read_aws_regions_file() - - regions = set() - for service in data["services"].values(): - for partition in service["regions"]: - for item in service["regions"][partition]: - regions.add(item) - return regions - except Exception as error: - logger.error(f"{error.__class__.__name__}: {error}") - return set() - - # TODO: This can be moved to another class since it doesn't need self def get_aws_region_for_sts(session_region: str, regions: set[str]) -> str: # If there is no region passed with -f/--region/--filter-region diff --git a/prowler/providers/aws/exceptions/exceptions.py b/prowler/providers/aws/exceptions/exceptions.py index 236fc8dc55..4ea3d5e177 100644 --- a/prowler/providers/aws/exceptions/exceptions.py +++ b/prowler/providers/aws/exceptions/exceptions.py @@ -74,6 +74,10 @@ class AWSBaseException(ProwlerException): "message": "The provided AWS Session Token is expired", "remediation": "Get a new AWS Session Token and configure it for the provider.", }, + (1917, "AWSInvalidPartitionError"): { + "message": "The provided AWS partition is invalid", + "remediation": "Check the provided AWS partition and ensure it is valid.", + }, } def __init__(self, code, file=None, original_exception=None, message=None): @@ -220,3 +224,10 @@ class AWSSessionTokenExpiredError(AWSCredentialsError): super().__init__( 1016, file=file, original_exception=original_exception, message=message ) + + +class AWSInvalidPartitionError(AWSBaseException): + def __init__(self, file=None, original_exception=None, message=None): + super().__init__( + 1917, file=file, original_exception=original_exception, message=message + ) diff --git a/prowler/providers/aws/lib/arguments/arguments.py b/prowler/providers/aws/lib/arguments/arguments.py index 8cac4b6a1a..cc33184a0f 100644 --- a/prowler/providers/aws/lib/arguments/arguments.py +++ b/prowler/providers/aws/lib/arguments/arguments.py @@ -1,7 +1,7 @@ from argparse import ArgumentTypeError, Namespace from re import fullmatch, search -from prowler.providers.aws.aws_provider import get_aws_available_regions +from prowler.providers.aws.aws_provider import AwsProvider from prowler.providers.aws.config import ROLE_SESSION_NAME from prowler.providers.aws.lib.arn.arn import arn_type @@ -64,7 +64,7 @@ def init_parser(self): "-f", nargs="+", help="AWS region names to run Prowler against", - choices=get_aws_available_regions(), + choices=AwsProvider.get_regions_by_partition(), ) # AWS Organizations aws_orgs_subparser = aws_parser.add_argument_group("AWS Organizations") diff --git a/tests/config/config_test.py b/tests/config/config_test.py index 685d304a34..a3f05783ab 100644 --- a/tests/config/config_test.py +++ b/tests/config/config_test.py @@ -11,7 +11,7 @@ from prowler.config.config import ( load_and_validate_config_file, load_and_validate_fixer_config_file, ) -from prowler.providers.aws.aws_provider import get_aws_available_regions +from prowler.providers.aws.aws_provider import AwsProvider MOCK_PROWLER_VERSION = "3.3.0" MOCK_OLD_PROWLER_VERSION = "0.0.0" @@ -346,8 +346,14 @@ config_kubernetes = { class Test_Config: - def test_get_aws_available_regions(self): - assert len(get_aws_available_regions()) == 34 + def test_get_regions_by_partition(self): + assert len(AwsProvider.get_regions_by_partition()) == 34 + + def test_get_regions_by_partition_with_partition(self): + assert len(AwsProvider.get_regions_by_partition("aws-cn")) == 2 + + def test_get_regions_by_partition_with_unknown_partition(self): + assert len(AwsProvider.get_regions_by_partition("unknown")) == 0 @mock.patch( "prowler.config.config.requests.get", new=mock_prowler_get_latest_release diff --git a/tests/providers/aws/aws_provider_test.py b/tests/providers/aws/aws_provider_test.py index 7f3895af23..f53ef8b2b8 100644 --- a/tests/providers/aws/aws_provider_test.py +++ b/tests/providers/aws/aws_provider_test.py @@ -15,11 +15,7 @@ from moto import mock_aws from pytest import raises from tzlocal import get_localzone -from prowler.providers.aws.aws_provider import ( - AwsProvider, - get_aws_available_regions, - get_aws_region_for_sts, -) +from prowler.providers.aws.aws_provider import AwsProvider, get_aws_region_for_sts from prowler.providers.aws.config import ( AWS_STS_GLOBAL_ENDPOINT_REGION, BOTO3_USER_AGENT_EXTRA, @@ -1720,7 +1716,7 @@ aws: ) assert not recovered_regions - def test_get_aws_available_regions(self): + def test_get_regions_by_partition(self): with patch( "prowler.providers.aws.aws_provider.read_aws_regions_file", return_value={ @@ -1741,12 +1737,60 @@ aws: } }, ): - assert get_aws_available_regions() == { + assert AwsProvider.get_regions_by_partition() == { "af-south-1", "cn-north-1", "us-gov-west-1", } + def test_get_regions_by_partition_with_partition(self): + with patch( + "prowler.providers.aws.aws_provider.read_aws_regions_file", + return_value={ + "services": { + "acm": { + "regions": { + "aws": [ + "af-south-1", + ], + "aws-cn": [ + "cn-north-1", + ], + "aws-us-gov": [ + "us-gov-west-1", + ], + } + } + } + }, + ): + assert AwsProvider.get_regions_by_partition("aws-cn") == { + "cn-north-1", + } + + def test_get_regions_by_partition_with_unknown_partition(self): + with patch( + "prowler.providers.aws.aws_provider.read_aws_regions_file", + return_value={ + "services": { + "acm": { + "regions": { + "aws": [ + "af-south-1", + ], + "aws-cn": [ + "cn-north-1", + ], + "aws-us-gov": [ + "us-gov-west-1", + ], + } + } + } + }, + ): + assert AwsProvider.get_regions_by_partition("unknown") == set() + def test_get_aws_region_for_sts_input_regions_none_session_region_none(self): input_regions = None session_region = None From 06b41cf8e6f2d4f90463605ff0d265d604f45ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Wed, 13 Nov 2024 19:00:18 +0100 Subject: [PATCH 17/55] refactor(azure): get locations with self session (#5751) --- prowler/providers/azure/azure_provider.py | 44 ++++++++++--------- .../network_watcher_enabled.py | 2 +- .../network_watcher_enabled_test.py | 4 +- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/prowler/providers/azure/azure_provider.py b/prowler/providers/azure/azure_provider.py index de7d9bc42d..375a2b0c78 100644 --- a/prowler/providers/azure/azure_provider.py +++ b/prowler/providers/azure/azure_provider.py @@ -198,7 +198,7 @@ class AzureProvider(Provider): ) # TODO: should we keep this here or within the identity? - self._locations = self.get_locations(self.session) + self._locations = self.get_locations() # Audit Config if config_content: @@ -942,33 +942,35 @@ class AzureProvider(Provider): return identity - def get_locations(self, credentials) -> dict[str, list[str]]: + def get_locations(self) -> dict[str, list[str]]: """ Retrieves the locations available for each subscription using the provided credentials. - Args: - credentials: The credentials object used to authenticate the request. - Returns: A dictionary containing the locations available for each subscription. The dictionary has subscription display names as keys and lists of location names as values. + + Examples: + >>> provider = AzureProvider(...) + >>> provider.get_locations() + { + 'Subscription 1': ['eastus', 'eastus2', 'westus', 'westus2'], + 'Subscription 2': ['eastus', 'eastus2', 'westus', 'westus2'] + } """ - locations = None - if credentials: - locations = {} - token = credentials.get_token("https://management.azure.com/.default").token - for display_name, subscription_id in self._identity.subscriptions.items(): - locations.update({display_name: []}) - url = f"https://management.azure.com/subscriptions/{subscription_id}/locations?api-version=2022-12-01" - headers = { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json", - } - response = requests.get(url, headers=headers) - if response.status_code == 200: - data = response.json() - for location in data["value"]: - locations[display_name].append(location["name"]) + credentials = self.session + subscription_client = SubscriptionClient(credentials) + locations = {} + + for display_name, subscription_id in self._identity.subscriptions.items(): + locations[display_name] = [] + + # List locations for each subscription + for location in subscription_client.subscriptions.list_locations( + subscription_id + ): + locations[display_name].append(location.name) + return locations @staticmethod diff --git a/prowler/providers/azure/services/network/network_watcher_enabled/network_watcher_enabled.py b/prowler/providers/azure/services/network/network_watcher_enabled/network_watcher_enabled.py index 02694561f0..d16798c8f3 100644 --- a/prowler/providers/azure/services/network/network_watcher_enabled/network_watcher_enabled.py +++ b/prowler/providers/azure/services/network/network_watcher_enabled/network_watcher_enabled.py @@ -9,7 +9,7 @@ class network_watcher_enabled(Check): report = Check_Report_Azure(self.metadata()) report.subscription = subscription report.resource_name = "Network Watcher" - report.location = "Global" + report.location = "global" report.resource_id = f"/subscriptions/{network_client.subscriptions[subscription]}/resourceGroups/NetworkWatcherRG/providers/Microsoft.Network/networkWatchers/NetworkWatcher_*" missing_locations = set(network_client.locations[subscription]) - set( diff --git a/tests/providers/azure/services/network/network_watcher_enabled/network_watcher_enabled_test.py b/tests/providers/azure/services/network/network_watcher_enabled/network_watcher_enabled_test.py index bfb066ff7a..24a7f1ef4d 100644 --- a/tests/providers/azure/services/network/network_watcher_enabled/network_watcher_enabled_test.py +++ b/tests/providers/azure/services/network/network_watcher_enabled/network_watcher_enabled_test.py @@ -78,7 +78,7 @@ class Test_network_watcher_enabled: assert result[0].subscription == AZURE_SUBSCRIPTION_NAME assert result[0].resource_name == network_watcher_name assert result[0].resource_id == network_watcher_id - assert result[0].location == "Global" + assert result[0].location == "global" def test_network_valid_network_watchers(self): network_client = mock.MagicMock @@ -124,4 +124,4 @@ class Test_network_watcher_enabled: assert result[0].subscription == AZURE_SUBSCRIPTION_NAME assert result[0].resource_name == network_watcher_name assert result[0].resource_id == network_watcher_id - assert result[0].location == "Global" + assert result[0].location == "global" From 794268cec5791067ef6d8d6c7470732643129ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Wed, 13 Nov 2024 19:11:42 +0100 Subject: [PATCH 18/55] feat(gcp): add get regions method (#5756) --- prowler/providers/gcp/gcp_provider.py | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/prowler/providers/gcp/gcp_provider.py b/prowler/providers/gcp/gcp_provider.py index 6ad8e90f31..be47e2abd7 100644 --- a/prowler/providers/gcp/gcp_provider.py +++ b/prowler/providers/gcp/gcp_provider.py @@ -653,3 +653,31 @@ class GcpProvider(Provider): file=__file__, message="The provider ID does not match with the expected project_id.", ) + + def get_regions(self) -> set: + """ + Get the regions available in GCP for the given project IDs + + Returns: + set of regions + """ + try: + regions = set() + service = discovery.build("compute", "v1", credentials=self._session) + for project_id in self._project_ids: + try: + request = service.regions().list(project=project_id) + response = request.execute() + for region in response.get("items", []): + regions.add(region["name"]) + except Exception as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + continue + return regions + except Exception as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return set() From 73361a1cea3baedf894e0f140b53bec059f5248f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Wed, 13 Nov 2024 20:30:58 +0100 Subject: [PATCH 19/55] chore(ec2): add name from image information to status_extended (#5755) Co-authored-by: MrCloudSec --- .../aws/services/ec2/ec2_ami_public/ec2_ami_public.py | 7 ++++--- prowler/providers/aws/services/ec2/ec2_service.py | 2 +- .../aws/services/ec2/ec2_ami_public/ec2_ami_public_test.py | 6 ++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/prowler/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public.py b/prowler/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public.py index 50979265d5..1758a3ae02 100644 --- a/prowler/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public.py +++ b/prowler/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public.py @@ -12,11 +12,12 @@ class ec2_ami_public(Check): report.resource_arn = image.arn report.resource_tags = image.tags report.status = "PASS" - report.status_extended = f"EC2 AMI {image.id} is not public." + report.status_extended = ( + f"EC2 AMI {image.name if image.name else image.id} is not public." + ) if image.public: report.status = "FAIL" - report.status_extended = f"EC2 AMI {image.id} is currently public." - report.resource_id = image.id + report.status_extended = f"EC2 AMI {image.name if image.name else image.id} is currently public." findings.append(report) diff --git a/prowler/providers/aws/services/ec2/ec2_service.py b/prowler/providers/aws/services/ec2/ec2_service.py index dcf49f6066..4c695c3110 100644 --- a/prowler/providers/aws/services/ec2/ec2_service.py +++ b/prowler/providers/aws/services/ec2/ec2_service.py @@ -357,7 +357,7 @@ class EC2(AWSService): Image( id=image["ImageId"], arn=arn, - name=image.get("Name", None), + name=image.get("Name", ""), public=image.get("Public", False), region=regional_client.region, tags=image.get("Tags"), diff --git a/tests/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public_test.py b/tests/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public_test.py index c775c7cb0f..593128f506 100644 --- a/tests/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public_test.py +++ b/tests/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public_test.py @@ -72,7 +72,7 @@ class Test_ec2_ami_public: assert len(result) == 1 assert result[0].status == "PASS" - assert result[0].status_extended == f"EC2 AMI {image_id} is not public." + assert result[0].status_extended == "EC2 AMI test-ami is not public." assert result[0].resource_id == image_id assert ( result[0].resource_arn @@ -124,9 +124,7 @@ class Test_ec2_ami_public: assert len(result) == 1 assert result[0].status == "FAIL" - assert ( - result[0].status_extended == f"EC2 AMI {image_id} is currently public." - ) + assert result[0].status_extended == "EC2 AMI test-ami is currently public." assert result[0].resource_id == image_id assert ( result[0].resource_arn From c59b08c40b856718254d7bbea117cbac55e1f5d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 19:11:52 -0500 Subject: [PATCH 20/55] chore(deps): bump botocore from 1.35.59 to 1.35.60 (#5765) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0dfa605cc2..1493cb5d7e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -778,13 +778,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.59" +version = "1.35.60" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.59-py3-none-any.whl", hash = "sha256:bcd66d7f55c8d1b6020eb86f2d87893fe591fb4be6a7d2a689c18be586452334"}, - {file = "botocore-1.35.59.tar.gz", hash = "sha256:de0ce655fedfc02c87869dfaa3b622488a17ff37da316ef8106cbe1573b83c98"}, + {file = "botocore-1.35.60-py3-none-any.whl", hash = "sha256:ddccfc39a0a55ac0321191a36d29c2ea9be2c96ceefb3928dd3c91c79c494d50"}, + {file = "botocore-1.35.60.tar.gz", hash = "sha256:378f53037d817bed2c04a006b7319745e664030182211429c924647273b29bc9"}, ] [package.dependencies] @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "d5bf0558ef4d03a1c35b6c5650acc6ea06a09e38ee8fdc8d76f818895a6bc800" +content-hash = "56fbd05c04844964c41d0c1a5853192ffe19d9a146544de4b4ab64ad209e3947" diff --git a/pyproject.toml b/pyproject.toml index ce0057e4fd..2bf9713ac0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ azure-mgmt-subscription = "3.1.1" azure-mgmt-web = "7.3.1" azure-storage-blob = "12.23.1" boto3 = "1.35.58" -botocore = "1.35.59" +botocore = "1.35.60" colorama = "0.4.6" cryptography = "43.0.1" dash = "2.18.2" From 3608aa353685f194f6a860582dfceb2d3591affa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:01:57 +0100 Subject: [PATCH 21/55] chore(deps): bump boto3 from 1.35.58 to 1.35.60 (#5770) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 10 +++++----- pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1493cb5d7e..3c4856caa9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -759,17 +759,17 @@ files = [ [[package]] name = "boto3" -version = "1.35.58" +version = "1.35.60" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.58-py3-none-any.whl", hash = "sha256:856896fd5fc5871758eb04b27bad5bbbf0fdb6143a923f9e8d10125351efdf98"}, - {file = "boto3-1.35.58.tar.gz", hash = "sha256:1ee139e63f1545ee0192914cfe422b68360b8c344a94e4612ac657dd7ece93de"}, + {file = "boto3-1.35.60-py3-none-any.whl", hash = "sha256:a34d28de1a1f6ca6ec3edd05c26db16e422293d8f9dcd94f308059a434596753"}, + {file = "boto3-1.35.60.tar.gz", hash = "sha256:e573504c67c3e438fd4b0222119ed1a73b644c78eb3b6dee0b36a6c70ecf7677"}, ] [package.dependencies] -botocore = ">=1.35.58,<1.36.0" +botocore = ">=1.35.60,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "56fbd05c04844964c41d0c1a5853192ffe19d9a146544de4b4ab64ad209e3947" +content-hash = "5d03147c090eb4a95173d0a7222bca690eeaf56dad416098f71caa7f67acf069" diff --git a/pyproject.toml b/pyproject.toml index 2bf9713ac0..d3cff48f50 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ azure-mgmt-storage = "21.2.1" azure-mgmt-subscription = "3.1.1" azure-mgmt-web = "7.3.1" azure-storage-blob = "12.23.1" -boto3 = "1.35.58" +boto3 = "1.35.60" botocore = "1.35.60" colorama = "0.4.6" cryptography = "43.0.1" From cb74dae296acdb6d8b66b7576aec99d5d6501333 Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Thu, 14 Nov 2024 13:02:32 +0100 Subject: [PATCH 22/55] refactor(aws): Rename get_regions and validate partition (#5772) --- prowler/providers/aws/aws_provider.py | 60 ++++++++------ .../providers/aws/lib/arguments/arguments.py | 2 +- prowler/providers/aws/models.py | 24 ++++++ tests/config/config_test.py | 10 --- tests/providers/aws/aws_provider_test.py | 78 +++++++++++++++++-- 5 files changed, 135 insertions(+), 39 deletions(-) diff --git a/prowler/providers/aws/aws_provider.py b/prowler/providers/aws/aws_provider.py index c841484eb7..bcf8d1d8a7 100644 --- a/prowler/providers/aws/aws_provider.py +++ b/prowler/providers/aws/aws_provider.py @@ -63,6 +63,7 @@ from prowler.providers.aws.models import ( AWSMFAInfo, AWSOrganizationsInfo, AWSSession, + Partition, ) from prowler.providers.common.models import Audit_Metadata, Connection from prowler.providers.common.provider import Provider @@ -107,7 +108,7 @@ class AwsProvider(Provider): """ Initializes the AWS provider. - Arguments: + Args: - retries_max_attempts: The maximum number of retries for the AWS client. - role_arn: The ARN of the IAM role to assume. - session_duration: The duration of the session in seconds. @@ -368,7 +369,7 @@ class AwsProvider(Provider): """ get_organizations_info returns a AWSOrganizationsInfo object if the account to be audited is a delegated administrator for AWS Organizations or if the AWS Organizations Role ARN (--organizations-role) is passed. - Arguments: + Args: - organizations_session: needs to be a Session object with permissions to do organizations:DescribeAccount and organizations:ListTagsForResource. - aws_account_id: is the AWS Account ID from which we want to get the AWS Organizations account metadata @@ -660,7 +661,7 @@ class AwsProvider(Provider): """ get_available_aws_service_regions returns the available regions for the given service and partition. - Arguments: + Args: - service: The AWS service name. - partition: The AWS partition name. Default is "aws". - audited_regions: A set of regions to audit. Default is None. @@ -1274,7 +1275,7 @@ class AwsProvider(Provider): """ Create an STS session client. - Parameters: + Args: - session (session.Session): The AWS session object. - aws_region (str): The AWS region to use for the session. @@ -1299,42 +1300,57 @@ class AwsProvider(Provider): raise error @staticmethod - def get_regions_by_partition(partition: str = None) -> set: + def get_regions(partition: Partition = Partition.aws) -> set: """ Get the available AWS regions from the AWS services JSON file with the ability of filtering by partition. Args: - - partition (str): The AWS partition name. Default is None. + partition (str): The AWS partition to retrieve regions for. Defaults to "aws". Returns: - set: A set of available AWS regions. All if no `partition` is especified. + set: A set of region names. + + Raises: + AWSInvalidPartitionError: If the provided partition name is invalid. + + Example: + >>> AwsProvider.get_regions("aws") + {"af-south-1"} """ + try: + regions = set() data = read_aws_regions_file() - regions = set() if partition is None: for service in data["services"].values(): for partition in service["regions"]: - for item in service["regions"][partition]: - regions.add(item) + regions.update(service["regions"][partition]) else: + partition = Partition(partition) for service in data["services"].values(): - try: - for item in service["regions"][partition]: - regions.add(item) - except KeyError as key_error: - logger.error( - f"{key_error.__class__.__name__}[{key_error.__traceback__.tb_lineno}]: {key_error}" - ) - raise AWSInvalidPartitionError( - message=f"Invalid partition name: {partition}", - file=os.path.basename(__file__), - ) + regions.update(service["regions"][partition.value]) + return regions + except ValueError as value_error: + logger.error( + f"{value_error.__class__.__name__}[{value_error.__traceback__.tb_lineno}]: {value_error}" + ) + raise AWSInvalidPartitionError( + message=f"Invalid partition: {partition}", + file=os.path.basename(__file__), + ) + except KeyError as key_error: + logger.error( + f"{key_error.__class__.__name__}[{key_error.__traceback__.tb_lineno}]: {key_error}" + ) + raise AWSInvalidPartitionError( + message=f"Invalid partition: {partition}", + file=os.path.basename(__file__), + ) except Exception as error: logger.error(f"{error.__class__.__name__}: {error}") - return set() + raise error def read_aws_regions_file() -> dict: diff --git a/prowler/providers/aws/lib/arguments/arguments.py b/prowler/providers/aws/lib/arguments/arguments.py index cc33184a0f..7f0f7872f5 100644 --- a/prowler/providers/aws/lib/arguments/arguments.py +++ b/prowler/providers/aws/lib/arguments/arguments.py @@ -64,7 +64,7 @@ def init_parser(self): "-f", nargs="+", help="AWS region names to run Prowler against", - choices=AwsProvider.get_regions_by_partition(), + choices=AwsProvider.get_regions(partition=None), ) # AWS Organizations aws_orgs_subparser = aws_parser.add_argument_group("AWS Organizations") diff --git a/prowler/providers/aws/models.py b/prowler/providers/aws/models.py index d0cbcf0ff5..c97a6279a2 100644 --- a/prowler/providers/aws/models.py +++ b/prowler/providers/aws/models.py @@ -1,5 +1,6 @@ from dataclasses import dataclass from datetime import datetime +from enum import Enum from boto3.session import Session from botocore.config import Config @@ -82,6 +83,29 @@ class AWSMFAInfo: totp: str +class Partition(str, Enum): + """ + Enum class representing different AWS partitions. + + Attributes: + aws (str): Represents the standard AWS commercial regions. + aws_cn (str): Represents the AWS China regions. + aws_us_gov (str): Represents the AWS GovCloud (US) Regions. + aws_iso (str): Represents the AWS ISO (US) Regions. + aws_iso_b (str): Represents the AWS ISOB (US) Regions. + aws_iso_e (str): Represents the AWS ISOE (Europe) Regions. + aws_iso_f (str): Represents the AWS ISOF Regions. + """ + + aws = "aws" + aws_cn = "aws-cn" + aws_us_gov = "aws-us-gov" + aws_iso = "aws-iso" + aws_iso_b = "aws-iso-b" + aws_iso_e = "aws-iso-e" + aws_iso_f = "aws-iso-f" + + class AWSOutputOptions(ProviderOutputOptions): security_hub_enabled: bool diff --git a/tests/config/config_test.py b/tests/config/config_test.py index a3f05783ab..98e1e5f43e 100644 --- a/tests/config/config_test.py +++ b/tests/config/config_test.py @@ -11,7 +11,6 @@ from prowler.config.config import ( load_and_validate_config_file, load_and_validate_fixer_config_file, ) -from prowler.providers.aws.aws_provider import AwsProvider MOCK_PROWLER_VERSION = "3.3.0" MOCK_OLD_PROWLER_VERSION = "0.0.0" @@ -346,15 +345,6 @@ config_kubernetes = { class Test_Config: - def test_get_regions_by_partition(self): - assert len(AwsProvider.get_regions_by_partition()) == 34 - - def test_get_regions_by_partition_with_partition(self): - assert len(AwsProvider.get_regions_by_partition("aws-cn")) == 2 - - def test_get_regions_by_partition_with_unknown_partition(self): - assert len(AwsProvider.get_regions_by_partition("unknown")) == 0 - @mock.patch( "prowler.config.config.requests.get", new=mock_prowler_get_latest_release ) diff --git a/tests/providers/aws/aws_provider_test.py b/tests/providers/aws/aws_provider_test.py index f53ef8b2b8..0901c915b7 100644 --- a/tests/providers/aws/aws_provider_test.py +++ b/tests/providers/aws/aws_provider_test.py @@ -9,6 +9,7 @@ from unittest import mock import botocore import botocore.exceptions +import pytest from boto3 import client, resource, session from mock import patch from moto import mock_aws @@ -24,6 +25,7 @@ from prowler.providers.aws.config import ( from prowler.providers.aws.exceptions.exceptions import ( AWSArgumentTypeValidationError, AWSIAMRoleARNInvalidResourceTypeError, + AWSInvalidPartitionError, AWSInvalidProviderIdError, AWSNoCredentialsError, ) @@ -1716,7 +1718,16 @@ aws: ) assert not recovered_regions - def test_get_regions_by_partition(self): + def test_get_regions_all_count(self): + assert len(AwsProvider.get_regions(partition=None)) == 34 + + def test_get_regions_cn_count(self): + assert len(AwsProvider.get_regions("aws-cn")) == 2 + + def test_get_regions_aws_count(self): + assert len(AwsProvider.get_regions(partition="aws")) == 30 + + def test_get_all_regions(self): with patch( "prowler.providers.aws.aws_provider.read_aws_regions_file", return_value={ @@ -1737,13 +1748,13 @@ aws: } }, ): - assert AwsProvider.get_regions_by_partition() == { + assert AwsProvider.get_regions(partition=None) == { "af-south-1", "cn-north-1", "us-gov-west-1", } - def test_get_regions_by_partition_with_partition(self): + def test_get_regions_with_us_gov_partition(self): with patch( "prowler.providers.aws.aws_provider.read_aws_regions_file", return_value={ @@ -1764,11 +1775,61 @@ aws: } }, ): - assert AwsProvider.get_regions_by_partition("aws-cn") == { + assert AwsProvider.get_regions("aws-us-gov") == { + "us-gov-west-1", + } + + def test_get_regions_with_aws_partition(self): + with patch( + "prowler.providers.aws.aws_provider.read_aws_regions_file", + return_value={ + "services": { + "acm": { + "regions": { + "aws": [ + "af-south-1", + ], + "aws-cn": [ + "cn-north-1", + ], + "aws-us-gov": [ + "us-gov-west-1", + ], + } + } + } + }, + ): + assert AwsProvider.get_regions("aws") == { + "af-south-1", + } + + def test_get_regions_with_cn_partition(self): + with patch( + "prowler.providers.aws.aws_provider.read_aws_regions_file", + return_value={ + "services": { + "acm": { + "regions": { + "aws": [ + "af-south-1", + ], + "aws-cn": [ + "cn-north-1", + ], + "aws-us-gov": [ + "us-gov-west-1", + ], + } + } + } + }, + ): + assert AwsProvider.get_regions("aws-cn") == { "cn-north-1", } - def test_get_regions_by_partition_with_unknown_partition(self): + def test_get_regions_with_unknown_partition(self): with patch( "prowler.providers.aws.aws_provider.read_aws_regions_file", return_value={ @@ -1789,7 +1850,12 @@ aws: } }, ): - assert AwsProvider.get_regions_by_partition("unknown") == set() + partition = "unknown" + with pytest.raises(AWSInvalidPartitionError) as exception: + AwsProvider.get_regions(partition) + + assert exception.type == AWSInvalidPartitionError + assert f"Invalid partition: {partition}" in exception.value.args[0] def test_get_aws_region_for_sts_input_regions_none_session_region_none(self): input_regions = None From 1586cdae5ea6903cbd5f6b17853441b3b0142bfb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 08:57:09 -0500 Subject: [PATCH 23/55] chore(deps): bump google-api-python-client from 2.152.0 to 2.153.0 (#5763) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3c4856caa9..768d752a6a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1703,13 +1703,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-api-python-client" -version = "2.152.0" +version = "2.153.0" description = "Google API Client Library for Python" optional = false python-versions = ">=3.7" files = [ - {file = "google_api_python_client-2.152.0-py2.py3-none-any.whl", hash = "sha256:3e429b4584f6ccb620c1c4cdd43a6d78c5a8d307fc64860026aa26e203e8f932"}, - {file = "google_api_python_client-2.152.0.tar.gz", hash = "sha256:9593476dc548fcba794f2ea0e3cfbf927de5a7b79379781c1664637bf246f072"}, + {file = "google_api_python_client-2.153.0-py2.py3-none-any.whl", hash = "sha256:6ff13bbfa92a57972e33ec3808e18309e5981b8ca1300e5da23bf2b4d6947384"}, + {file = "google_api_python_client-2.153.0.tar.gz", hash = "sha256:35cce8647f9c163fc04fb4d811fc91aae51954a2bdd74918decbe0e65d791dd2"}, ] [package.dependencies] @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "5d03147c090eb4a95173d0a7222bca690eeaf56dad416098f71caa7f67acf069" +content-hash = "dfb9bd7693e859a7f781768145b5be17b2b31a2f17b6a7dd5f5eba3d6a32baf9" diff --git a/pyproject.toml b/pyproject.toml index d3cff48f50..d96f1fc525 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ cryptography = "43.0.1" dash = "2.18.2" dash-bootstrap-components = "1.6.0" detect-secrets = "1.5.0" -google-api-python-client = "2.152.0" +google-api-python-client = "2.153.0" google-auth-httplib2 = ">=0.1,<0.3" jsonschema = "4.23.0" kubernetes = "31.0.0" From 919acfd5488a8337051b7c933fa10eb5111cfb98 Mon Sep 17 00:00:00 2001 From: Prowler Bot Date: Thu, 14 Nov 2024 14:57:26 +0100 Subject: [PATCH 24/55] chore(regions_update): Changes in regions for AWS services (#5771) Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com> --- prowler/providers/aws/aws_regions_by_service.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prowler/providers/aws/aws_regions_by_service.json b/prowler/providers/aws/aws_regions_by_service.json index 0e2c4bf5b1..a7a95d0210 100644 --- a/prowler/providers/aws/aws_regions_by_service.json +++ b/prowler/providers/aws/aws_regions_by_service.json @@ -7163,6 +7163,7 @@ "ap-southeast-2", "ap-southeast-3", "ap-southeast-4", + "ap-southeast-5", "ca-central-1", "ca-west-1", "eu-central-1", @@ -9855,6 +9856,7 @@ "ap-southeast-2", "ap-southeast-3", "ap-southeast-4", + "ap-southeast-5", "ca-central-1", "ca-west-1", "eu-central-1", From 28c7e803ac3b0c5e72cb40d68279c3a6113a6a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20Poyatos?= Date: Thu, 14 Nov 2024 15:58:12 +0100 Subject: [PATCH 25/55] feat(azure): Add get_regions method for provider (#5774) --- prowler/providers/azure/azure_provider.py | 31 ++++++++++++++++++++ tests/providers/azure/azure_provider_test.py | 31 ++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/prowler/providers/azure/azure_provider.py b/prowler/providers/azure/azure_provider.py index 375a2b0c78..6a40349eb9 100644 --- a/prowler/providers/azure/azure_provider.py +++ b/prowler/providers/azure/azure_provider.py @@ -2,7 +2,9 @@ import asyncio import os import re from argparse import ArgumentTypeError +from itertools import chain from os import getenv +from typing import Union from uuid import UUID import requests @@ -973,6 +975,35 @@ class AzureProvider(Provider): return locations + def get_regions(self, subscription_ids: Union[list[str], None] = None) -> set: + """ + Retrieves a set of regions available across all subscriptions or specific subscriptions if provided. + + Args: + subscription_ids (List[str], optional): A list of subscription display names to filter the regions. + If None, regions from all subscriptions are returned. + + Returns: + Set[str]: A set containing the unique regions available across the specified subscriptions. + + Examples: + >>> provider = AzureProvider(...) + >>> provider.get_regions() + {'eastus', 'eastus2', 'westus', 'westus2'} + + >>> provider.get_regions(subscription_ids=['Subscription 1']) + {'eastus', 'eastus2', 'westus', 'westus2'} + """ + locations = self.get_locations() + if subscription_ids is not None: + locations = { + sid: regions + for sid, regions in locations.items() + if sid in subscription_ids + } + + return set(chain.from_iterable(locations.values())) + @staticmethod def validate_static_credentials( tenant_id: str = None, client_id: str = None, client_secret: str = None diff --git a/tests/providers/azure/azure_provider_test.py b/tests/providers/azure/azure_provider_test.py index ac6e90f4b4..ea798ad057 100644 --- a/tests/providers/azure/azure_provider_test.py +++ b/tests/providers/azure/azure_provider_test.py @@ -439,3 +439,34 @@ class TestAzureProvider: assert exception.type == Exception assert exception.value.args[0] == "Simulated Exception" + + @pytest.mark.parametrize( + "subscription_ids, expected_regions", + [ + (None, {"region1", "region2", "region3"}), + (["sub1", "sub2"], {"region1", "region2", "region3"}), + ("sub1", {"region1", "region2"}), + ("not_exists", set()), + ], + ) + @patch("prowler.providers.azure.azure_provider.AzureProvider.get_locations") + @patch( + "prowler.providers.azure.azure_provider.AzureProvider.__init__", + return_value=None, + ) + def test_get_regions( + self, + azure_provider_init_mock, # noqa: F841 + azure_get_locations_mock, + subscription_ids, + expected_regions, + ): + azure_get_locations_mock.return_value = { + "sub1": ["region1", "region2"], + "sub2": ["region2", "region3"], + } + + azure_provider = AzureProvider() + regions = azure_provider.get_regions(subscription_ids=subscription_ids) + + assert regions == expected_regions From c1b050b8b9acf80023c7ad1814b286dc78f14827 Mon Sep 17 00:00:00 2001 From: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:27:17 +0100 Subject: [PATCH 26/55] feat(firehose): add new check `firehose_stream_encrypted_at_rest` (#5635) Co-authored-by: Sergio Garcia --- .../aws/services/firehose/firehose_service.py | 38 ++++ .../__init__.py | 0 ...ose_stream_encrypted_at_rest.metadata.json | 32 +++ .../firehose_stream_encrypted_at_rest.py | 43 ++++ .../firehose/firehose_service_test.py | 89 +++++++- .../firehose_stream_encrypted_at_rest_test.py | 195 ++++++++++++++++++ 6 files changed, 395 insertions(+), 2 deletions(-) create mode 100644 prowler/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/__init__.py create mode 100644 prowler/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/firehose_stream_encrypted_at_rest.metadata.json create mode 100644 prowler/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/firehose_stream_encrypted_at_rest.py create mode 100644 tests/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/firehose_stream_encrypted_at_rest_test.py diff --git a/prowler/providers/aws/services/firehose/firehose_service.py b/prowler/providers/aws/services/firehose/firehose_service.py index fafd87cd3c..9ef76d2fbb 100644 --- a/prowler/providers/aws/services/firehose/firehose_service.py +++ b/prowler/providers/aws/services/firehose/firehose_service.py @@ -1,3 +1,4 @@ +from enum import Enum from typing import Dict, List, Optional from botocore.client import ClientError @@ -17,6 +18,9 @@ class Firehose(AWSService): self.__threading_call__( self._list_tags_for_delivery_stream, self.delivery_streams.values() ) + self.__threading_call__( + self._describe_delivery_stream, self.delivery_streams.values() + ) def _list_delivery_streams(self, regional_client): logger.info("Firehose - Listing delivery streams...") @@ -39,6 +43,7 @@ class Firehose(AWSService): ) def _list_tags_for_delivery_stream(self, stream): + logger.info(f"Firehose - Listing tags for stream {stream.name}...") try: stream.tags = ( self.regional_clients[stream.region] @@ -50,9 +55,42 @@ class Firehose(AWSService): f"{stream.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) + def _describe_delivery_stream(self, stream): + logger.info(f"Firehose - Describing stream {stream.name}...") + try: + describe_stream = self.regional_clients[ + stream.region + ].describe_delivery_stream(DeliveryStreamName=stream.name) + encryption_config = describe_stream.get( + "DeliveryStreamDescription", {} + ).get("DeliveryStreamEncryptionConfiguration", {}) + stream.kms_encryption = EncryptionStatus( + encryption_config.get("Status", "DISABLED") + ) + stream.kms_key_arn = encryption_config.get("KeyARN", "") + except ClientError as error: + logger.error( + f"{stream.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + +class EncryptionStatus(Enum): + """Possible values for the status of the encryption of a Firehose stream""" + + ENABLED = "ENABLED" + DISABLED = "DISABLED" + ENABLING = "ENABLING" + DISABLING = "DISABLING" + ENABLING_FAILED = "ENABLING_FAILED" + DISABLING_FAILED = "DISABLING_FAILED" + class DeliveryStream(BaseModel): + """Model for a Firehose Delivery Stream""" + arn: str name: str region: str + kms_key_arn: Optional[str] = Field(default_factory=str) + kms_encryption: Optional[str] = Field(default_factory=str) tags: Optional[List[Dict[str, str]]] = Field(default_factory=list) diff --git a/prowler/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/__init__.py b/prowler/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/firehose_stream_encrypted_at_rest.metadata.json b/prowler/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/firehose_stream_encrypted_at_rest.metadata.json new file mode 100644 index 0000000000..5061d1e037 --- /dev/null +++ b/prowler/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/firehose_stream_encrypted_at_rest.metadata.json @@ -0,0 +1,32 @@ +{ + "Provider": "aws", + "CheckID": "firehose_stream_encrypted_at_rest", + "CheckTitle": "DataFirehose delivery streams should be encrypted at rest.", + "CheckType": [ + "Software and Configuration Checks/Industry and Regulatory Standards/NIST 800-53 Controls" + ], + "ServiceName": "firehose", + "SubServiceName": "Ensure DataFirehose delivery streams are encrypted at rest.", + "ResourceIdTemplate": "arn:partition:firehose:region:account-id:deliverystream/delivery-stream-id", + "Severity": "medium", + "ResourceType": "AwsKinesisFirehoseDeliveryStream", + "Description": "", + "Risk": "Without encryption at rest, data in Amazon Kinesis Data Firehose delivery streams is vulnerable to unauthorized access if the storage layer is compromised. This increases the risk of sensitive information exposure, potentially leading to data breaches or non-compliance with security regulations.", + "RelatedUrl": "https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html", + "Remediation": { + "Code": { + "CLI": "aws firehose update-delivery-stream --delivery-stream-name --delivery-stream-encryption-configuration-input '{ \"KeyType\": \"CUSTOMER_MANAGED_CMK\", \"KeyARN\": \"\" }'", + "NativeIaC": "https://docs.prowler.com/checks/aws/general-policies/ensure-aws-kinesis-firehoses-delivery-stream-is-encrypted/", + "Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/datafirehose-controls.html#datafirehose-1", + "Terraform": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/Firehose/delivery-stream-encrypted-with-kms-customer-master-keys.html" + }, + "Recommendation": { + "Text": "Enable server-side encryption for Kinesis Firehose delivery streams using AWS Key Management Service (KMS). This encrypts data at rest, ensuring that sensitive information remains secure and compliant with regulatory standards.", + "Url": "https://docs.aws.amazon.com/firehose/latest/dev/encryption.html" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/firehose_stream_encrypted_at_rest.py b/prowler/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/firehose_stream_encrypted_at_rest.py new file mode 100644 index 0000000000..fe63558366 --- /dev/null +++ b/prowler/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/firehose_stream_encrypted_at_rest.py @@ -0,0 +1,43 @@ +from typing import List + +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.firehose.firehose_client import firehose_client +from prowler.providers.aws.services.firehose.firehose_service import EncryptionStatus + + +class firehose_stream_encrypted_at_rest(Check): + """Check if Firehose Streams are encrypted at rest. + + This class verifies that all Firehose Streams have at rest encryption enabled by checking if KMS encryption is active and a KMS Key is configured. + """ + + def execute(self) -> List[Check_Report_AWS]: + """Execute the Firehose Stream Encrypted at Rest check. + + Iterates over all Firehose Streams and checks if KMS encryption is enabled and a KMS Key is configured. + + Returns: + List[Check_Report_AWS]: A list of reports for each Firehose Stream. + """ + findings = [] + for stream in firehose_client.delivery_streams.values(): + report = Check_Report_AWS(self.metadata()) + report.region = stream.region + report.resource_id = stream.name + report.resource_arn = stream.arn + report.resource_tags = stream.tags + report.status = "PASS" + report.status_extended = ( + f"Firehose Stream {stream.name} does have at rest encryption enabled." + ) + + if ( + stream.kms_encryption != EncryptionStatus.ENABLED + or not stream.kms_key_arn + ): + report.status = "FAIL" + report.status_extended = f"Firehose Stream {stream.name} does not have at rest encryption enabled." + + findings.append(report) + + return findings diff --git a/tests/providers/aws/services/firehose/firehose_service_test.py b/tests/providers/aws/services/firehose/firehose_service_test.py index 46d6c8c522..9d0d72cfbd 100644 --- a/tests/providers/aws/services/firehose/firehose_service_test.py +++ b/tests/providers/aws/services/firehose/firehose_service_test.py @@ -1,8 +1,15 @@ from boto3 import client from moto import mock_aws -from prowler.providers.aws.services.firehose.firehose_service import Firehose -from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider +from prowler.providers.aws.services.firehose.firehose_service import ( + EncryptionStatus, + Firehose, +) +from tests.providers.aws.utils import ( + AWS_ACCOUNT_NUMBER, + AWS_REGION_EU_WEST_1, + set_mocked_aws_provider, +) class Test_Firehose_Service: @@ -67,3 +74,81 @@ class Test_Firehose_Service: assert firehose.delivery_streams[arn].name == delivery_stream_name assert firehose.delivery_streams[arn].region == AWS_REGION_EU_WEST_1 assert firehose.delivery_streams[arn].tags == [{"Key": "key", "Value": "value"}] + + @mock_aws + def test_list_tags_for_delivery_stream(self): + # Generate Firehose client + firehose_client = client("firehose", region_name=AWS_REGION_EU_WEST_1) + delivery_stream = firehose_client.create_delivery_stream( + DeliveryStreamName="test-delivery-stream", + DeliveryStreamType="DirectPut", + S3DestinationConfiguration={ + "RoleARN": "arn:aws:iam::012345678901:role/firehose-role", + "BucketARN": "arn:aws:s3:::test-bucket", + "Prefix": "", + "BufferingHints": {"IntervalInSeconds": 300, "SizeInMBs": 5}, + "CompressionFormat": "UNCOMPRESSED", + }, + Tags=[{"Key": "key", "Value": "value"}], + ) + arn = delivery_stream["DeliveryStreamARN"] + delivery_stream_name = arn.split("/")[-1] + + # Firehose Client for this test class + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + firehose = Firehose(aws_provider) + + assert len(firehose.delivery_streams) == 1 + assert firehose.delivery_streams[arn].arn == arn + assert firehose.delivery_streams[arn].name == delivery_stream_name + assert firehose.delivery_streams[arn].region == AWS_REGION_EU_WEST_1 + assert firehose.delivery_streams[arn].tags == [{"Key": "key", "Value": "value"}] + + @mock_aws + def test_describe_delivery_stream(self): + # Generate S3 client + s3_client = client("s3", region_name=AWS_REGION_EU_WEST_1) + s3_client.create_bucket( + Bucket="test-bucket", + CreateBucketConfiguration={"LocationConstraint": AWS_REGION_EU_WEST_1}, + ) + + # Generate Firehose client + firehose_client = client("firehose", region_name=AWS_REGION_EU_WEST_1) + delivery_stream = firehose_client.create_delivery_stream( + DeliveryStreamName="test-delivery-stream", + DeliveryStreamType="DirectPut", + S3DestinationConfiguration={ + "RoleARN": "arn:aws:iam::012345678901:role/firehose-role", + "BucketARN": "arn:aws:s3:::test-bucket", + "Prefix": "", + "BufferingHints": {"IntervalInSeconds": 300, "SizeInMBs": 5}, + "CompressionFormat": "UNCOMPRESSED", + }, + Tags=[{"Key": "key", "Value": "value"}], + ) + arn = delivery_stream["DeliveryStreamARN"] + delivery_stream_name = arn.split("/")[-1] + + firehose_client.start_delivery_stream_encryption( + DeliveryStreamName=delivery_stream_name, + DeliveryStreamEncryptionConfigurationInput={ + "KeyARN": f"arn:aws:kms:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:key/test-kms-key-id", + "KeyType": "CUSTOMER_MANAGED_CMK", + }, + ) + + # Firehose Client for this test class + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + firehose = Firehose(aws_provider) + + assert len(firehose.delivery_streams) == 1 + assert firehose.delivery_streams[arn].arn == arn + assert firehose.delivery_streams[arn].name == delivery_stream_name + assert firehose.delivery_streams[arn].region == AWS_REGION_EU_WEST_1 + assert firehose.delivery_streams[arn].tags == [{"Key": "key", "Value": "value"}] + assert firehose.delivery_streams[arn].kms_encryption == EncryptionStatus.ENABLED + assert ( + firehose.delivery_streams[arn].kms_key_arn + == f"arn:aws:kms:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:key/test-kms-key-id" + ) diff --git a/tests/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/firehose_stream_encrypted_at_rest_test.py b/tests/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/firehose_stream_encrypted_at_rest_test.py new file mode 100644 index 0000000000..d2a1fa40a9 --- /dev/null +++ b/tests/providers/aws/services/firehose/firehose_stream_encrypted_at_rest/firehose_stream_encrypted_at_rest_test.py @@ -0,0 +1,195 @@ +from unittest import mock + +from boto3 import client +from moto import mock_aws + +from tests.providers.aws.utils import ( + AWS_ACCOUNT_NUMBER, + AWS_REGION_EU_WEST_1, + set_mocked_aws_provider, +) + + +class Test_firehose_stream_encrypted_at_rest: + @mock_aws + def test_no_streams(self): + from prowler.providers.aws.services.firehose.firehose_service import Firehose + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.firehose.firehose_stream_encrypted_at_rest.firehose_stream_encrypted_at_rest.firehose_client", + new=Firehose(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.firehose.firehose_stream_encrypted_at_rest.firehose_stream_encrypted_at_rest import ( + firehose_stream_encrypted_at_rest, + ) + + check = firehose_stream_encrypted_at_rest() + result = check.execute() + + assert len(result) == 0 + + @mock_aws + def test_stream_kms_encryption_enabled(self): + # Generate S3 client + s3_client = client("s3", region_name=AWS_REGION_EU_WEST_1) + s3_client.create_bucket( + Bucket="test-bucket", + CreateBucketConfiguration={"LocationConstraint": AWS_REGION_EU_WEST_1}, + ) + + # Generate Firehose client + firehose_client = client("firehose", region_name=AWS_REGION_EU_WEST_1) + delivery_stream = firehose_client.create_delivery_stream( + DeliveryStreamName="test-delivery-stream", + DeliveryStreamType="DirectPut", + S3DestinationConfiguration={ + "RoleARN": "arn:aws:iam::012345678901:role/firehose-role", + "BucketARN": "arn:aws:s3:::test-bucket", + "Prefix": "", + "BufferingHints": {"IntervalInSeconds": 300, "SizeInMBs": 5}, + "CompressionFormat": "UNCOMPRESSED", + }, + Tags=[{"Key": "key", "Value": "value"}], + ) + arn = delivery_stream["DeliveryStreamARN"] + stream_name = arn.split("/")[-1] + + firehose_client.start_delivery_stream_encryption( + DeliveryStreamName=stream_name, + DeliveryStreamEncryptionConfigurationInput={ + "KeyARN": f"arn:aws:kms:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:key/test-kms-key-id", + "KeyType": "CUSTOMER_MANAGED_CMK", + }, + ) + + from prowler.providers.aws.services.firehose.firehose_service import Firehose + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.firehose.firehose_stream_encrypted_at_rest.firehose_stream_encrypted_at_rest.firehose_client", + new=Firehose(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.firehose.firehose_stream_encrypted_at_rest.firehose_stream_encrypted_at_rest import ( + firehose_stream_encrypted_at_rest, + ) + + check = firehose_stream_encrypted_at_rest() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Firehose Stream {stream_name} does have at rest encryption enabled." + ) + + @mock_aws + def test_stream_kms_encryption_not_enabled(self): + # Generate Firehose client + firehose_client = client("firehose", region_name=AWS_REGION_EU_WEST_1) + delivery_stream = firehose_client.create_delivery_stream( + DeliveryStreamName="test-delivery-stream", + DeliveryStreamType="DirectPut", + S3DestinationConfiguration={ + "RoleARN": "arn:aws:iam::012345678901:role/firehose-role", + "BucketARN": "arn:aws:s3:::test-bucket", + "Prefix": "", + "BufferingHints": {"IntervalInSeconds": 300, "SizeInMBs": 5}, + "CompressionFormat": "UNCOMPRESSED", + }, + Tags=[{"Key": "key", "Value": "value"}], + ) + arn = delivery_stream["DeliveryStreamARN"] + stream_name = arn.split("/")[-1] + + from prowler.providers.aws.services.firehose.firehose_service import Firehose + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.firehose.firehose_stream_encrypted_at_rest.firehose_stream_encrypted_at_rest.firehose_client", + new=Firehose(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.firehose.firehose_stream_encrypted_at_rest.firehose_stream_encrypted_at_rest import ( + firehose_stream_encrypted_at_rest, + ) + + check = firehose_stream_encrypted_at_rest() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Firehose Stream {stream_name} does not have at rest encryption enabled." + ) + + @mock_aws + def test_stream_kms_encryption_disabled(self): + # Generate Firehose client + firehose_client = client("firehose", region_name=AWS_REGION_EU_WEST_1) + delivery_stream = firehose_client.create_delivery_stream( + DeliveryStreamName="test-delivery-stream", + DeliveryStreamType="DirectPut", + S3DestinationConfiguration={ + "RoleARN": "arn:aws:iam::012345678901:role/firehose-role", + "BucketARN": "arn:aws:s3:::test-bucket", + "Prefix": "", + "BufferingHints": {"IntervalInSeconds": 300, "SizeInMBs": 5}, + "CompressionFormat": "UNCOMPRESSED", + }, + Tags=[{"Key": "key", "Value": "value"}], + ) + arn = delivery_stream["DeliveryStreamARN"] + stream_name = arn.split("/")[-1] + + firehose_client.start_delivery_stream_encryption( + DeliveryStreamName=stream_name, + DeliveryStreamEncryptionConfigurationInput={ + "KeyARN": f"arn:aws:kms:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:key/test-kms-key-id", + "KeyType": "CUSTOMER_MANAGED_CMK", + }, + ) + + firehose_client.stop_delivery_stream_encryption(DeliveryStreamName=stream_name) + + from prowler.providers.aws.services.firehose.firehose_service import Firehose + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.firehose.firehose_stream_encrypted_at_rest.firehose_stream_encrypted_at_rest.firehose_client", + new=Firehose(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.firehose.firehose_stream_encrypted_at_rest.firehose_stream_encrypted_at_rest import ( + firehose_stream_encrypted_at_rest, + ) + + check = firehose_stream_encrypted_at_rest() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Firehose Stream {stream_name} does not have at rest encryption enabled." + ) From b0bb34848017091a67ae24cc9a4f238c9f5b42b0 Mon Sep 17 00:00:00 2001 From: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:40:33 +0100 Subject: [PATCH 27/55] feat(rds): add new fixer `rds_snapshots_public_access_fixer` (#5773) --- .../rds_snapshots_public_access_fixer.py | 63 ++++++ .../rds_snapshots_public_access_fixer_test.py | 187 ++++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 prowler/providers/aws/services/rds/rds_snapshots_public_access/rds_snapshots_public_access_fixer.py create mode 100644 tests/providers/aws/services/rds/rds_snapshots_public_access/rds_snapshots_public_access_fixer_test.py diff --git a/prowler/providers/aws/services/rds/rds_snapshots_public_access/rds_snapshots_public_access_fixer.py b/prowler/providers/aws/services/rds/rds_snapshots_public_access/rds_snapshots_public_access_fixer.py new file mode 100644 index 0000000000..752c8586a5 --- /dev/null +++ b/prowler/providers/aws/services/rds/rds_snapshots_public_access/rds_snapshots_public_access_fixer.py @@ -0,0 +1,63 @@ +from prowler.lib.logger import logger +from prowler.providers.aws.services.rds.rds_client import rds_client + + +def fixer(resource_id: str, region: str) -> bool: + """ + Modify the attributes of an RDS DB snapshot or DB cluster snapshot to remove public access. + Specifically, this fixer removes the 'all' value from the 'restore' attribute to + prevent the snapshot from being publicly accessible for both DB snapshots and DB cluster snapshots. + + Requires the rds:ModifyDBSnapshotAttribute or rds:ModifyDBClusterSnapshotAttribute permissions. + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "rds:ModifyDBSnapshotAttribute", + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": "rds:ModifyDBClusterSnapshotAttribute", + "Resource": "*" + } + ] + } + + Args: + resource_id (str): The DB snapshot or DB cluster snapshot identifier. + region (str): AWS region where the snapshot exists. + + Returns: + bool: True if the operation is successful (public access is removed), False otherwise. + """ + try: + regional_client = rds_client.regional_clients[region] + + # Check if the resource is a DB Cluster Snapshot or a DB Instance Snapshot + try: + regional_client.describe_db_cluster_snapshots( + DBClusterSnapshotIdentifier=resource_id + ) + # If the describe call is successful, it's a DB cluster snapshot + regional_client.modify_db_cluster_snapshot_attribute( + DBClusterSnapshotIdentifier=resource_id, + AttributeName="restore", + ValuesToRemove=["all"], + ) + except regional_client.exceptions.DBClusterSnapshotNotFoundFault: + # If the DB cluster snapshot doesn't exist, it's an instance snapshot + regional_client.modify_db_snapshot_attribute( + DBSnapshotIdentifier=resource_id, + AttributeName="restore", + ValuesToRemove=["all"], + ) + + except Exception as error: + logger.error( + f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return False + else: + return True diff --git a/tests/providers/aws/services/rds/rds_snapshots_public_access/rds_snapshots_public_access_fixer_test.py b/tests/providers/aws/services/rds/rds_snapshots_public_access/rds_snapshots_public_access_fixer_test.py new file mode 100644 index 0000000000..6a915bfb1c --- /dev/null +++ b/tests/providers/aws/services/rds/rds_snapshots_public_access/rds_snapshots_public_access_fixer_test.py @@ -0,0 +1,187 @@ +from unittest import mock + +from boto3 import client +from moto import mock_aws + +from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider + + +class Test_rds_snapshots_public_access: + @mock_aws + def test_rds_private_snapshot(self): + conn = client("rds", region_name=AWS_REGION_US_EAST_1) + conn.create_db_instance( + DBInstanceIdentifier="db-primary-1", + AllocatedStorage=10, + Engine="postgres", + DBName="staging-postgres", + DBInstanceClass="db.m1.small", + ) + + conn.create_db_snapshot( + DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1" + ) + + from prowler.providers.aws.services.rds.rds_service import RDS + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access_fixer.rds_client", + new=RDS(aws_provider), + ): + # Test Fixer + from prowler.providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access_fixer import ( + fixer, + ) + + assert fixer("snapshot-1", AWS_REGION_US_EAST_1) + + @mock_aws + def test_rds_public_snapshot(self): + conn = client("rds", region_name=AWS_REGION_US_EAST_1) + conn.create_db_instance( + DBInstanceIdentifier="db-primary-1", + AllocatedStorage=10, + Engine="postgres", + DBName="staging-postgres", + DBInstanceClass="db.m1.small", + ) + + conn.create_db_snapshot( + DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1" + ) + + from prowler.providers.aws.services.rds.rds_service import RDS + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access_fixer.rds_client", + new=RDS(aws_provider), + ) as service_client: + + service_client.db_snapshots[0].public = True + + # Test Fixer + from prowler.providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access_fixer import ( + fixer, + ) + + assert fixer("snapshot-1", AWS_REGION_US_EAST_1) + + @mock_aws + def test_rds_cluster_private_snapshot(self): + conn = client("rds", region_name=AWS_REGION_US_EAST_1) + conn.create_db_cluster( + DBClusterIdentifier="db-primary-1", + AllocatedStorage=10, + Engine="postgres", + DBClusterInstanceClass="db.m1.small", + MasterUsername="root", + MasterUserPassword="hunter2000", + ) + + conn.create_db_cluster_snapshot( + DBClusterIdentifier="db-primary-1", DBClusterSnapshotIdentifier="snapshot-1" + ) + from prowler.providers.aws.services.rds.rds_service import RDS + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access_fixer.rds_client", + new=RDS(aws_provider), + ): + # Test Fixer + from prowler.providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access_fixer import ( + fixer, + ) + + assert fixer("snapshot-1", AWS_REGION_US_EAST_1) + + @mock_aws + def test_rds_cluster_public_snapshot(self): + conn = client("rds", region_name=AWS_REGION_US_EAST_1) + conn.create_db_cluster( + DBClusterIdentifier="db-primary-1", + AllocatedStorage=10, + Engine="postgres", + DBClusterInstanceClass="db.m1.small", + MasterUsername="root", + MasterUserPassword="hunter2000", + ) + + conn.create_db_cluster_snapshot( + DBClusterIdentifier="db-primary-1", DBClusterSnapshotIdentifier="snapshot-1" + ) + from prowler.providers.aws.services.rds.rds_service import RDS + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access_fixer.rds_client", + new=RDS(aws_provider), + ) as service_client: + + service_client.db_cluster_snapshots[0].public = True + + # Test Fixer + from prowler.providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access_fixer import ( + fixer, + ) + + assert fixer("snapshot-1", AWS_REGION_US_EAST_1) + + @mock_aws + def test_rds_cluster_public_snapshot_error(self): + conn = client("rds", region_name=AWS_REGION_US_EAST_1) + conn.create_db_cluster( + DBClusterIdentifier="db-primary-1", + AllocatedStorage=10, + Engine="postgres", + DBClusterInstanceClass="db.m1.small", + MasterUsername="root", + MasterUserPassword="hunter2000", + ) + + conn.create_db_cluster_snapshot( + DBClusterIdentifier="db-primary-1", DBClusterSnapshotIdentifier="snapshot-1" + ) + from prowler.providers.aws.services.rds.rds_service import RDS + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access_fixer.rds_client", + new=RDS(aws_provider), + ) as service_client: + + service_client.db_cluster_snapshots[0].public = True + + # Test Fixer + from prowler.providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access_fixer import ( + fixer, + ) + + assert not fixer("snapshot-2", AWS_REGION_US_EAST_1) From 7a711095cdef565deeba6d6bba80e245fbe01a12 Mon Sep 17 00:00:00 2001 From: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:01:01 +0100 Subject: [PATCH 28/55] feat(kinesis): add new check `kinesis_stream_data_retention_period` (#5547) Co-authored-by: Sergio Garcia --- prowler/config/config.yaml | 5 + .../aws/services/kinesis/kinesis_service.py | 8 +- .../__init__.py | 0 ...stream_data_retention_period.metadata.json | 32 ++++ .../kinesis_stream_data_retention_period.py | 37 +++++ .../services/kinesis/kinesis_service_test.py | 22 ++- ...nesis_stream_data_retention_period_test.py | 147 ++++++++++++++++++ 7 files changed, 247 insertions(+), 4 deletions(-) create mode 100644 prowler/providers/aws/services/kinesis/kinesis_stream_data_retention_period/__init__.py create mode 100644 prowler/providers/aws/services/kinesis/kinesis_stream_data_retention_period/kinesis_stream_data_retention_period.metadata.json create mode 100644 prowler/providers/aws/services/kinesis/kinesis_stream_data_retention_period/kinesis_stream_data_retention_period.py create mode 100644 tests/providers/aws/services/kinesis/kinesis_stream_data_retention_period/kinesis_stream_data_retention_period_test.py diff --git a/prowler/config/config.yaml b/prowler/config/config.yaml index 42e7afa6b9..7c42c521c0 100644 --- a/prowler/config/config.yaml +++ b/prowler/config/config.yaml @@ -368,6 +368,11 @@ aws: # Maximum number of days a secret should be rotated max_days_secret_unrotated: 90 + # AWS Kinesis Configuration + # Minimum retention period in hours for Kinesis streams + min_kinesis_stream_retention_hours: 168 # 7 days + + # Azure Configuration azure: # Azure Network Configuration diff --git a/prowler/providers/aws/services/kinesis/kinesis_service.py b/prowler/providers/aws/services/kinesis/kinesis_service.py index 530710b965..cc5a7c1495 100644 --- a/prowler/providers/aws/services/kinesis/kinesis_service.py +++ b/prowler/providers/aws/services/kinesis/kinesis_service.py @@ -1,7 +1,7 @@ from enum import Enum -from typing import Optional +from typing import Dict, List, Optional -from pydantic import BaseModel +from pydantic import BaseModel, Field from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered @@ -49,6 +49,7 @@ class Kinesis(AWSService): stream.encrypted_at_rest = EncryptionType( stream_description.get("EncryptionType", "NONE") ) + stream.retention_period = stream_description.get("RetentionPeriodHours", 24) except Exception as error: logger.error( f"{stream.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -91,5 +92,6 @@ class Stream(BaseModel): region: str name: str status: StreamStatus - tags: Optional[list] + tags: Optional[List[Dict[str, str]]] = Field(default_factory=list) encrypted_at_rest: EncryptionType = EncryptionType.NONE + retention_period: int = 24 # 1 day diff --git a/prowler/providers/aws/services/kinesis/kinesis_stream_data_retention_period/__init__.py b/prowler/providers/aws/services/kinesis/kinesis_stream_data_retention_period/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/kinesis/kinesis_stream_data_retention_period/kinesis_stream_data_retention_period.metadata.json b/prowler/providers/aws/services/kinesis/kinesis_stream_data_retention_period/kinesis_stream_data_retention_period.metadata.json new file mode 100644 index 0000000000..09a7cd4be7 --- /dev/null +++ b/prowler/providers/aws/services/kinesis/kinesis_stream_data_retention_period/kinesis_stream_data_retention_period.metadata.json @@ -0,0 +1,32 @@ +{ + "Provider": "aws", + "CheckID": "kinesis_stream_data_retention_period", + "CheckTitle": "Kinesis streams should have an adequate data retention period.", + "CheckType": [ + "Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices" + ], + "ServiceName": "kinesis", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:kinesis::account-id:stream/stream-name", + "Severity": "medium", + "ResourceType": "AwsKinesisStream", + "Description": "Ensure Kinesis streams have an adequate data retention period.", + "Risk": "An inadequate data retention period may result in data records being deleted before they can be processed or backed up, increasing the risk of data loss. This is especially critical for applications that rely on historical data availability for analysis, monitoring, and recovery in case of failures.", + "RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/kinesis-stream-backup-retention-check.html", + "Remediation": { + "Code": { + "CLI": "aws kinesis increase-stream-retention-period --stream-name --retention-period-hours ", + "NativeIaC": "", + "Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/kinesis-controls.html#kinesis-3", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure an adequate data retention period for Kinesis streams to ensure data is available for the required timeframe. Set the retention period based on your application’s data retention requirements, and consider at least 168 hours (or customize as necessary).", + "Url": "https://docs.aws.amazon.com/streams/latest/dev/kinesis-extended-retention.html" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/kinesis/kinesis_stream_data_retention_period/kinesis_stream_data_retention_period.py b/prowler/providers/aws/services/kinesis/kinesis_stream_data_retention_period/kinesis_stream_data_retention_period.py new file mode 100644 index 0000000000..b86e1dccab --- /dev/null +++ b/prowler/providers/aws/services/kinesis/kinesis_stream_data_retention_period/kinesis_stream_data_retention_period.py @@ -0,0 +1,37 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.kinesis.kinesis_client import kinesis_client + + +class kinesis_stream_data_retention_period(Check): + """Ensure Kinesis Stream has an adequate data retention period + + The retention period for Kinesis Streams should be set to a value that meets the organization's data retention policy. + """ + + def execute(self): + """Execute Check Kinesis Stream data retention period + + Iterate over all Kinesis Streams and check if the retention period is adequate. + + Returns: + findings (list): List of findings + """ + findings = [] + for stream in kinesis_client.streams.values(): + report = Check_Report_AWS(self.metadata()) + report.region = stream.region + report.resource_id = stream.name + report.resource_arn = stream.arn + report.resource_tags = stream.tags + report.status = "FAIL" + report.status_extended = f"Kinesis Stream {stream.name} does not have an adequate data retention period ({stream.retention_period}hrs)." + + if stream.retention_period >= kinesis_client.audit_config.get( + "min_kinesis_stream_retention_hours", 168 + ): + report.status = "PASS" + report.status_extended = f"Kinesis Stream {stream.name} does have an adequate data retention period ({stream.retention_period}hrs)." + + findings.append(report) + + return findings diff --git a/tests/providers/aws/services/kinesis/kinesis_service_test.py b/tests/providers/aws/services/kinesis/kinesis_service_test.py index 21dbd70804..4976e0087e 100644 --- a/tests/providers/aws/services/kinesis/kinesis_service_test.py +++ b/tests/providers/aws/services/kinesis/kinesis_service_test.py @@ -3,7 +3,11 @@ from unittest.mock import patch import botocore from moto import mock_aws -from prowler.providers.aws.services.kinesis.kinesis_service import Kinesis, StreamStatus +from prowler.providers.aws.services.kinesis.kinesis_service import ( + EncryptionType, + Kinesis, + StreamStatus, +) from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider make_api_call = botocore.client.BaseClient._make_api_call @@ -28,6 +32,8 @@ def mock_make_api_call(self, operation_name, kwarg): "StreamARN": "arn:aws:kinesis:us-east-1:123456789012:stream/test-stream", "StreamStatus": "ACTIVE", "Tags": [{"Key": "test_tag", "Value": "test_value"}], + "EncryptionType": "KMS", + "RetentionPeriodHours": 24, } } if operation_name == "ListTagsForStream": @@ -72,3 +78,17 @@ class Test_Kinesis_Service: assert kinesis.streams[arn].tags == [{"Key": "test_tag", "Value": "test_value"}] assert kinesis.streams[arn].region == AWS_REGION_US_EAST_1 assert kinesis.streams[arn].arn == arn + + @mock_aws + def test_describe_stream(self): + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + kinesis = Kinesis(aws_provider) + + arn = "arn:aws:kinesis:us-east-1:123456789012:stream/test-stream" + assert kinesis.streams[arn].name == "test-stream" + assert kinesis.streams[arn].status == StreamStatus.ACTIVE + assert kinesis.streams[arn].tags == [{"Key": "test_tag", "Value": "test_value"}] + assert kinesis.streams[arn].region == AWS_REGION_US_EAST_1 + assert kinesis.streams[arn].arn == arn + assert kinesis.streams[arn].encrypted_at_rest == EncryptionType.KMS + assert kinesis.streams[arn].retention_period == 24 diff --git a/tests/providers/aws/services/kinesis/kinesis_stream_data_retention_period/kinesis_stream_data_retention_period_test.py b/tests/providers/aws/services/kinesis/kinesis_stream_data_retention_period/kinesis_stream_data_retention_period_test.py new file mode 100644 index 0000000000..55f68c3604 --- /dev/null +++ b/tests/providers/aws/services/kinesis/kinesis_stream_data_retention_period/kinesis_stream_data_retention_period_test.py @@ -0,0 +1,147 @@ +from unittest import mock + +from boto3 import client +from moto import mock_aws + +from tests.providers.aws.utils import ( + AWS_ACCOUNT_NUMBER, + AWS_REGION_US_EAST_1, + set_mocked_aws_provider, +) + + +class Test_kinesis_encrypted_at_rest: + @mock_aws + def test_no_streams(self): + from prowler.providers.aws.services.kinesis.kinesis_service import Kinesis + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.kinesis.kinesis_stream_data_retention_period.kinesis_stream_data_retention_period.kinesis_client", + new=Kinesis(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.kinesis.kinesis_stream_data_retention_period.kinesis_stream_data_retention_period import ( + kinesis_stream_data_retention_period, + ) + + check = kinesis_stream_data_retention_period() + result = check.execute() + + assert len(result) == 0 + + @mock_aws + def test_adequate_retention_period(self): + kinesis_client = client("kinesis", region_name=AWS_REGION_US_EAST_1) + stream_name = "stream_test_us" + kinesis_client.create_stream( + StreamName=stream_name, + ShardCount=1, + StreamModeDetails={"StreamMode": "PROVISIONED"}, + ) + retention_period = 400 + kinesis_client.increase_stream_retention_period( + StreamName=stream_name, + RetentionPeriodHours=retention_period, + StreamARN=f"arn:aws:kinesis:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:stream/{stream_name}", + ) + + kinesis_client.audit_config = mock.MagicMock() + kinesis_client.audit_config = {"min_kinesis_stream_retention_hours": 350} + + from prowler.providers.aws.services.kinesis.kinesis_service import Kinesis + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.kinesis.kinesis_stream_data_retention_period.kinesis_stream_data_retention_period.kinesis_client", + new=Kinesis(aws_provider), + ): + with mock.patch( + "prowler.providers.aws.services.kinesis.kinesis_stream_data_retention_period.kinesis_stream_data_retention_period.kinesis_client.audit_config", + new=kinesis_client.audit_config, + ): + # Test Check + from prowler.providers.aws.services.kinesis.kinesis_stream_data_retention_period.kinesis_stream_data_retention_period import ( + kinesis_stream_data_retention_period, + ) + + check = kinesis_stream_data_retention_period() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Kinesis Stream {stream_name} does have an adequate data retention period ({retention_period}hrs)." + ) + assert result[0].resource_id == stream_name + assert ( + result[0].resource_arn + == f"arn:aws:kinesis:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:stream/{stream_name}" + ) + assert result[0].resource_tags == [] + assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_unadequate_retention_period(self): + kinesis_client = client("kinesis", region_name=AWS_REGION_US_EAST_1) + stream_name = "stream_test_us" + kinesis_client.create_stream( + StreamName=stream_name, + ShardCount=1, + StreamModeDetails={"StreamMode": "PROVISIONED"}, + ) + retention_period = 200 + kinesis_client.increase_stream_retention_period( + StreamName=stream_name, + RetentionPeriodHours=retention_period, + StreamARN=f"arn:aws:kinesis:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:stream/{stream_name}", + ) + + kinesis_client.audit_config = mock.MagicMock() + kinesis_client.audit_config = {"min_kinesis_stream_retention_hours": 250} + + from prowler.providers.aws.services.kinesis.kinesis_service import Kinesis + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.kinesis.kinesis_stream_data_retention_period.kinesis_stream_data_retention_period.kinesis_client", + new=Kinesis(aws_provider), + ): + with mock.patch( + "prowler.providers.aws.services.kinesis.kinesis_stream_data_retention_period.kinesis_stream_data_retention_period.kinesis_client.audit_config", + new=kinesis_client.audit_config, + ): + # Test Check + from prowler.providers.aws.services.kinesis.kinesis_stream_data_retention_period.kinesis_stream_data_retention_period import ( + kinesis_stream_data_retention_period, + ) + + check = kinesis_stream_data_retention_period() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Kinesis Stream {stream_name} does not have an adequate data retention period ({retention_period}hrs)." + ) + assert result[0].resource_id == stream_name + assert ( + result[0].resource_arn + == f"arn:aws:kinesis:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:stream/{stream_name}" + ) + assert result[0].resource_tags == [] + assert result[0].region == AWS_REGION_US_EAST_1 From b6c8adfc647928da3ee5c191439777b12e27e270 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:01:27 -0500 Subject: [PATCH 29/55] chore(deps): bump azure-storage-blob from 12.23.1 to 12.24.0 (#5767) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 768d752a6a..2d2a198bb5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -644,13 +644,13 @@ typing-extensions = ">=4.6.0" [[package]] name = "azure-storage-blob" -version = "12.23.1" +version = "12.24.0" description = "Microsoft Azure Blob Storage Client Library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "azure_storage_blob-12.23.1-py3-none-any.whl", hash = "sha256:1c2238aa841d1545f42714a5017c010366137a44a0605da2d45f770174bfc6b4"}, - {file = "azure_storage_blob-12.23.1.tar.gz", hash = "sha256:a587e54d4e39d2a27bd75109db164ffa2058fe194061e5446c5a89bca918272f"}, + {file = "azure_storage_blob-12.24.0-py3-none-any.whl", hash = "sha256:4f0bb4592ea79a2d986063696514c781c9e62be240f09f6397986e01755bc071"}, + {file = "azure_storage_blob-12.24.0.tar.gz", hash = "sha256:eaaaa1507c8c363d6e1d1342bd549938fdf1adec9b1ada8658c8f5bf3aea844e"}, ] [package.dependencies] @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "dfb9bd7693e859a7f781768145b5be17b2b31a2f17b6a7dd5f5eba3d6a32baf9" +content-hash = "244061534ffd26ff4078fafe275b4a835a35bcb23350937c9636513178cbf679" diff --git a/pyproject.toml b/pyproject.toml index d96f1fc525..9a241779d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ azure-mgmt-sql = "3.0.1" azure-mgmt-storage = "21.2.1" azure-mgmt-subscription = "3.1.1" azure-mgmt-web = "7.3.1" -azure-storage-blob = "12.23.1" +azure-storage-blob = "12.24.0" boto3 = "1.35.60" botocore = "1.35.60" colorama = "0.4.6" From 005d2511064bbc48ba34458f277a5ce10b752c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Fri, 15 Nov 2024 14:37:36 +0100 Subject: [PATCH 30/55] fix(iam): use `get` to get the key (#5785) --- prowler/providers/gcp/services/iam/iam_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler/providers/gcp/services/iam/iam_service.py b/prowler/providers/gcp/services/iam/iam_service.py index cac50fcb0b..77291563ce 100644 --- a/prowler/providers/gcp/services/iam/iam_service.py +++ b/prowler/providers/gcp/services/iam/iam_service.py @@ -34,7 +34,7 @@ class IAM(GCPService): ServiceAccount( name=account["name"], email=account["email"], - display_name=account["displayName"], + display_name=account.get("displayName", ""), project_id=project_id, ) ) From 8be83fc632445cd25eeb90ed20257716b673cead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Fri, 15 Nov 2024 14:40:33 +0100 Subject: [PATCH 31/55] fix(compliance): use SubscriptionID instead of name for Azure CIS (#5786) --- prowler/lib/outputs/compliance/cis/cis_azure.py | 4 ++-- prowler/lib/outputs/compliance/cis/models.py | 2 +- tests/lib/outputs/compliance/cis/cis_azure_test.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/prowler/lib/outputs/compliance/cis/cis_azure.py b/prowler/lib/outputs/compliance/cis/cis_azure.py index d101773a9d..9ebe7fddaf 100644 --- a/prowler/lib/outputs/compliance/cis/cis_azure.py +++ b/prowler/lib/outputs/compliance/cis/cis_azure.py @@ -42,7 +42,7 @@ class AzureCIS(ComplianceOutput): compliance_row = AzureCISModel( Provider=finding.provider, Description=compliance.Description, - Subscription=finding.account_name, + SubscriptionId=finding.account_uid, Location=finding.region, AssessmentDate=str(finding.timestamp), Requirements_Id=requirement.Id, @@ -73,7 +73,7 @@ class AzureCIS(ComplianceOutput): compliance_row = AzureCISModel( Provider=compliance.Provider.lower(), Description=compliance.Description, - Subscription="", + SubscriptionId="", Location="", AssessmentDate=str(finding.timestamp), Requirements_Id=requirement.Id, diff --git a/prowler/lib/outputs/compliance/cis/models.py b/prowler/lib/outputs/compliance/cis/models.py index 0086236023..d8e1889d52 100644 --- a/prowler/lib/outputs/compliance/cis/models.py +++ b/prowler/lib/outputs/compliance/cis/models.py @@ -38,7 +38,7 @@ class AzureCISModel(BaseModel): Provider: str Description: str - Subscription: str + SubscriptionId: str Location: str AssessmentDate: str Requirements_Id: str diff --git a/tests/lib/outputs/compliance/cis/cis_azure_test.py b/tests/lib/outputs/compliance/cis/cis_azure_test.py index a912383c7c..ce5ce6651b 100644 --- a/tests/lib/outputs/compliance/cis/cis_azure_test.py +++ b/tests/lib/outputs/compliance/cis/cis_azure_test.py @@ -30,7 +30,7 @@ class TestAzureCIS: output_data = output.data[0] assert isinstance(output_data, AzureCISModel) assert output_data.Provider == "azure" - assert output_data.Subscription == AZURE_SUBSCRIPTION_NAME + assert output_data.SubscriptionId == AZURE_SUBSCRIPTION_ID assert output_data.Location == "" assert output_data.Description == CIS_2_0_AZURE.Description assert output_data.Requirements_Id == CIS_2_0_AZURE.Requirements[0].Id @@ -91,7 +91,7 @@ class TestAzureCIS: # Test manual check output_data_manual = output.data[1] assert output_data_manual.Provider == "azure" - assert output_data_manual.Subscription == "" + assert output_data_manual.SubscriptionId == "" assert output_data_manual.Location == "" assert output_data_manual.Description == CIS_2_0_AZURE.Description assert output_data_manual.Requirements_Id == CIS_2_0_AZURE.Requirements[1].Id @@ -171,5 +171,5 @@ class TestAzureCIS: mock_file.seek(0) content = mock_file.read() - expected_csv = f"PROVIDER;DESCRIPTION;SUBSCRIPTION;LOCATION;ASSESSMENTDATE;REQUIREMENTS_ID;REQUIREMENTS_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_SECTION;REQUIREMENTS_ATTRIBUTES_PROFILE;REQUIREMENTS_ATTRIBUTES_ASSESSMENTSTATUS;REQUIREMENTS_ATTRIBUTES_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_RATIONALESTATEMENT;REQUIREMENTS_ATTRIBUTES_IMPACTSTATEMENT;REQUIREMENTS_ATTRIBUTES_REMEDIATIONPROCEDURE;REQUIREMENTS_ATTRIBUTES_AUDITPROCEDURE;REQUIREMENTS_ATTRIBUTES_ADDITIONALINFORMATION;REQUIREMENTS_ATTRIBUTES_DEFAULTVALUE;REQUIREMENTS_ATTRIBUTES_REFERENCES;STATUS;STATUSEXTENDED;RESOURCEID;RESOURCENAME;CHECKID;MUTED\r\nazure;The CIS Azure Foundations Benchmark provides prescriptive guidance for configuring security options for a subset of Azure with an emphasis on foundational, testable, and architecture agnostic settings.;Subscription Name;;{datetime.now()};2.1.3;Ensure That Microsoft Defender for Databases Is Set To 'On';2.1 Microsoft Defender for Cloud;Level 2;Manual;Turning on Microsoft Defender for Databases enables threat detection for the instances running your database software. This provides threat intelligence, anomaly detection, and behavior analytics in the Azure Microsoft Defender for Cloud. Instead of being enabled on services like Platform as a Service (PaaS), this implementation will run within your instances as Infrastructure as a Service (IaaS) on the Operating Systems hosting your databases.;Enabling Microsoft Defender for Azure SQL Databases allows your organization more granular control of the infrastructure running your database software. Instead of waiting on Microsoft release updates or other similar processes, you can manage them yourself. Threat detection is provided by the Microsoft Security Response Center (MSRC).;Running Defender on Infrastructure as a service (IaaS) may incur increased costs associated with running the service and the instance it is on. Similarly, you will need qualified personnel to maintain the operating system and software updates. If it is not maintained, security patches will not be applied and it may be open to vulnerabilities.;From Azure Portal 1. Go to Microsoft Defender for Cloud 2. Select Environment Settings 3. Click on the subscription name 4. Select Defender plans 5. Set Databases Status to On 6. Select Save Review the chosen pricing tier. For the Azure Databases resource review the different plan information and choose one that fits the needs of your organization. From Azure CLI Run the following commands: az security pricing create -n 'SqlServers' --tier 'Standard' az security pricing create -n 'SqlServerVirtualMachines' --tier 'Standard' az security pricing create -n 'OpenSourceRelationalDatabases' --tier 'Standard' az security pricing create -n 'CosmosDbs' --tier 'Standard' From Azure PowerShell Run the following commands: Set-AzSecurityPricing -Name 'SqlServers' -PricingTier 'Standard' Set-AzSecurityPricing -Name 'SqlServerVirtualMachines' -PricingTier 'Standard' Set-AzSecurityPricing -Name 'OpenSourceRelationalDatabases' -PricingTier 'Standard' Set-AzSecurityPricing -Name 'CosmosDbs' -PricingTier 'Standard';From Azure Portal 1. Go to Microsoft Defender for Cloud 2. Select Environment Settings 3. Click on the subscription name 4. Select Defender plans 5. Ensure Databases Status is set to On 6. Review the chosen pricing tier From Azure CLI Ensure the output of the below commands is Standard az security pricing show -n 'SqlServers' az security pricing show -n 'SqlServerVirtualMachines' az security pricing show -n 'OpenSourceRelationalDatabases' az security pricing show -n 'CosmosDbs' If the output of any of the above commands shows pricingTier with a value of Free, the setting is out of compliance. From PowerShell Connect-AzAccount Get-AzSecurityPricing |select-object Name,PricingTier |where-object {{$_.Name -match 'Sql' -or $_.Name -match 'Cosmos' -or $_.Name -match 'OpenSource'}} Ensure the output shows Standard for each database type under the PricingTier column. Any that show Free are considered out of compliance.;;By default, Microsoft Defender plan is off.;https://docs.microsoft.com/en-us/azure/azure-sql/database/azure-defender-for-sql?view=azuresql:https://docs.microsoft.com/en-us/azure/defender-for-cloud/quickstart-enable-database-protections:https://docs.microsoft.com/en-us/azure/defender-for-cloud/defender-for-databases-usage:https://docs.microsoft.com/en-us/azure/security-center/security-center-detection-capabilities:https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings/list:https://docs.microsoft.com/en-us/security/benchmark/azure/security-controls-v3-logging-threat-detection#lt-1-enable-threat-detection-capabilities;PASS;;;;test-check-id;False\r\nazure;The CIS Azure Foundations Benchmark provides prescriptive guidance for configuring security options for a subset of Azure with an emphasis on foundational, testable, and architecture agnostic settings.;;;{datetime.now()};2.1.4;Ensure That Microsoft Defender for Databases Is Set To 'On';2.1 Microsoft Defender for Cloud;Level 2;Manual;Turning on Microsoft Defender for Databases enables threat detection for the instances running your database software. This provides threat intelligence, anomaly detection, and behavior analytics in the Azure Microsoft Defender for Cloud. Instead of being enabled on services like Platform as a Service (PaaS), this implementation will run within your instances as Infrastructure as a Service (IaaS) on the Operating Systems hosting your databases.;Enabling Microsoft Defender for Azure SQL Databases allows your organization more granular control of the infrastructure running your database software. Instead of waiting on Microsoft release updates or other similar processes, you can manage them yourself. Threat detection is provided by the Microsoft Security Response Center (MSRC).;Running Defender on Infrastructure as a service (IaaS) may incur increased costs associated with running the service and the instance it is on. Similarly, you will need qualified personnel to maintain the operating system and software updates. If it is not maintained, security patches will not be applied and it may be open to vulnerabilities.;From Azure Portal 1. Go to Microsoft Defender for Cloud 2. Select Environment Settings 3. Click on the subscription name 4. Select Defender plans 5. Set Databases Status to On 6. Select Save Review the chosen pricing tier. For the Azure Databases resource review the different plan information and choose one that fits the needs of your organization. From Azure CLI Run the following commands: az security pricing create -n 'SqlServers' --tier 'Standard' az security pricing create -n 'SqlServerVirtualMachines' --tier 'Standard' az security pricing create -n 'OpenSourceRelationalDatabases' --tier 'Standard' az security pricing create -n 'CosmosDbs' --tier 'Standard' From Azure PowerShell Run the following commands: Set-AzSecurityPricing -Name 'SqlServers' -PricingTier 'Standard' Set-AzSecurityPricing -Name 'SqlServerVirtualMachines' -PricingTier 'Standard' Set-AzSecurityPricing -Name 'OpenSourceRelationalDatabases' -PricingTier 'Standard' Set-AzSecurityPricing -Name 'CosmosDbs' -PricingTier 'Standard';From Azure Portal 1. Go to Microsoft Defender for Cloud 2. Select Environment Settings 3. Click on the subscription name 4. Select Defender plans 5. Ensure Databases Status is set to On 6. Review the chosen pricing tier From Azure CLI Ensure the output of the below commands is Standard az security pricing show -n 'SqlServers' az security pricing show -n 'SqlServerVirtualMachines' az security pricing show -n 'OpenSourceRelationalDatabases' az security pricing show -n 'CosmosDbs' If the output of any of the above commands shows pricingTier with a value of Free, the setting is out of compliance. From PowerShell Connect-AzAccount Get-AzSecurityPricing |select-object Name,PricingTier |where-object {{$_.Name -match 'Sql' -or $_.Name -match 'Cosmos' -or $_.Name -match 'OpenSource'}} Ensure the output shows Standard for each database type under the PricingTier column. Any that show Free are considered out of compliance.;;By default, Microsoft Defender plan is off.;https://docs.microsoft.com/en-us/azure/azure-sql/database/azure-defender-for-sql?view=azuresql:https://docs.microsoft.com/en-us/azure/defender-for-cloud/quickstart-enable-database-protections:https://docs.microsoft.com/en-us/azure/defender-for-cloud/defender-for-databases-usage:https://docs.microsoft.com/en-us/azure/security-center/security-center-detection-capabilities:https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings/list:https://docs.microsoft.com/en-us/security/benchmark/azure/security-controls-v3-logging-threat-detection#lt-1-enable-threat-detection-capabilities;MANUAL;Manual check;manual_check;Manual check;manual;False\r\n" + expected_csv = f"PROVIDER;DESCRIPTION;SUBSCRIPTIONID;LOCATION;ASSESSMENTDATE;REQUIREMENTS_ID;REQUIREMENTS_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_SECTION;REQUIREMENTS_ATTRIBUTES_PROFILE;REQUIREMENTS_ATTRIBUTES_ASSESSMENTSTATUS;REQUIREMENTS_ATTRIBUTES_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_RATIONALESTATEMENT;REQUIREMENTS_ATTRIBUTES_IMPACTSTATEMENT;REQUIREMENTS_ATTRIBUTES_REMEDIATIONPROCEDURE;REQUIREMENTS_ATTRIBUTES_AUDITPROCEDURE;REQUIREMENTS_ATTRIBUTES_ADDITIONALINFORMATION;REQUIREMENTS_ATTRIBUTES_DEFAULTVALUE;REQUIREMENTS_ATTRIBUTES_REFERENCES;STATUS;STATUSEXTENDED;RESOURCEID;RESOURCENAME;CHECKID;MUTED\r\nazure;The CIS Azure Foundations Benchmark provides prescriptive guidance for configuring security options for a subset of Azure with an emphasis on foundational, testable, and architecture agnostic settings.;{AZURE_SUBSCRIPTION_ID};;{datetime.now()};2.1.3;Ensure That Microsoft Defender for Databases Is Set To 'On';2.1 Microsoft Defender for Cloud;Level 2;Manual;Turning on Microsoft Defender for Databases enables threat detection for the instances running your database software. This provides threat intelligence, anomaly detection, and behavior analytics in the Azure Microsoft Defender for Cloud. Instead of being enabled on services like Platform as a Service (PaaS), this implementation will run within your instances as Infrastructure as a Service (IaaS) on the Operating Systems hosting your databases.;Enabling Microsoft Defender for Azure SQL Databases allows your organization more granular control of the infrastructure running your database software. Instead of waiting on Microsoft release updates or other similar processes, you can manage them yourself. Threat detection is provided by the Microsoft Security Response Center (MSRC).;Running Defender on Infrastructure as a service (IaaS) may incur increased costs associated with running the service and the instance it is on. Similarly, you will need qualified personnel to maintain the operating system and software updates. If it is not maintained, security patches will not be applied and it may be open to vulnerabilities.;From Azure Portal 1. Go to Microsoft Defender for Cloud 2. Select Environment Settings 3. Click on the subscription name 4. Select Defender plans 5. Set Databases Status to On 6. Select Save Review the chosen pricing tier. For the Azure Databases resource review the different plan information and choose one that fits the needs of your organization. From Azure CLI Run the following commands: az security pricing create -n 'SqlServers' --tier 'Standard' az security pricing create -n 'SqlServerVirtualMachines' --tier 'Standard' az security pricing create -n 'OpenSourceRelationalDatabases' --tier 'Standard' az security pricing create -n 'CosmosDbs' --tier 'Standard' From Azure PowerShell Run the following commands: Set-AzSecurityPricing -Name 'SqlServers' -PricingTier 'Standard' Set-AzSecurityPricing -Name 'SqlServerVirtualMachines' -PricingTier 'Standard' Set-AzSecurityPricing -Name 'OpenSourceRelationalDatabases' -PricingTier 'Standard' Set-AzSecurityPricing -Name 'CosmosDbs' -PricingTier 'Standard';From Azure Portal 1. Go to Microsoft Defender for Cloud 2. Select Environment Settings 3. Click on the subscription name 4. Select Defender plans 5. Ensure Databases Status is set to On 6. Review the chosen pricing tier From Azure CLI Ensure the output of the below commands is Standard az security pricing show -n 'SqlServers' az security pricing show -n 'SqlServerVirtualMachines' az security pricing show -n 'OpenSourceRelationalDatabases' az security pricing show -n 'CosmosDbs' If the output of any of the above commands shows pricingTier with a value of Free, the setting is out of compliance. From PowerShell Connect-AzAccount Get-AzSecurityPricing |select-object Name,PricingTier |where-object {{$_.Name -match 'Sql' -or $_.Name -match 'Cosmos' -or $_.Name -match 'OpenSource'}} Ensure the output shows Standard for each database type under the PricingTier column. Any that show Free are considered out of compliance.;;By default, Microsoft Defender plan is off.;https://docs.microsoft.com/en-us/azure/azure-sql/database/azure-defender-for-sql?view=azuresql:https://docs.microsoft.com/en-us/azure/defender-for-cloud/quickstart-enable-database-protections:https://docs.microsoft.com/en-us/azure/defender-for-cloud/defender-for-databases-usage:https://docs.microsoft.com/en-us/azure/security-center/security-center-detection-capabilities:https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings/list:https://docs.microsoft.com/en-us/security/benchmark/azure/security-controls-v3-logging-threat-detection#lt-1-enable-threat-detection-capabilities;PASS;;;;test-check-id;False\r\nazure;The CIS Azure Foundations Benchmark provides prescriptive guidance for configuring security options for a subset of Azure with an emphasis on foundational, testable, and architecture agnostic settings.;;;{datetime.now()};2.1.4;Ensure That Microsoft Defender for Databases Is Set To 'On';2.1 Microsoft Defender for Cloud;Level 2;Manual;Turning on Microsoft Defender for Databases enables threat detection for the instances running your database software. This provides threat intelligence, anomaly detection, and behavior analytics in the Azure Microsoft Defender for Cloud. Instead of being enabled on services like Platform as a Service (PaaS), this implementation will run within your instances as Infrastructure as a Service (IaaS) on the Operating Systems hosting your databases.;Enabling Microsoft Defender for Azure SQL Databases allows your organization more granular control of the infrastructure running your database software. Instead of waiting on Microsoft release updates or other similar processes, you can manage them yourself. Threat detection is provided by the Microsoft Security Response Center (MSRC).;Running Defender on Infrastructure as a service (IaaS) may incur increased costs associated with running the service and the instance it is on. Similarly, you will need qualified personnel to maintain the operating system and software updates. If it is not maintained, security patches will not be applied and it may be open to vulnerabilities.;From Azure Portal 1. Go to Microsoft Defender for Cloud 2. Select Environment Settings 3. Click on the subscription name 4. Select Defender plans 5. Set Databases Status to On 6. Select Save Review the chosen pricing tier. For the Azure Databases resource review the different plan information and choose one that fits the needs of your organization. From Azure CLI Run the following commands: az security pricing create -n 'SqlServers' --tier 'Standard' az security pricing create -n 'SqlServerVirtualMachines' --tier 'Standard' az security pricing create -n 'OpenSourceRelationalDatabases' --tier 'Standard' az security pricing create -n 'CosmosDbs' --tier 'Standard' From Azure PowerShell Run the following commands: Set-AzSecurityPricing -Name 'SqlServers' -PricingTier 'Standard' Set-AzSecurityPricing -Name 'SqlServerVirtualMachines' -PricingTier 'Standard' Set-AzSecurityPricing -Name 'OpenSourceRelationalDatabases' -PricingTier 'Standard' Set-AzSecurityPricing -Name 'CosmosDbs' -PricingTier 'Standard';From Azure Portal 1. Go to Microsoft Defender for Cloud 2. Select Environment Settings 3. Click on the subscription name 4. Select Defender plans 5. Ensure Databases Status is set to On 6. Review the chosen pricing tier From Azure CLI Ensure the output of the below commands is Standard az security pricing show -n 'SqlServers' az security pricing show -n 'SqlServerVirtualMachines' az security pricing show -n 'OpenSourceRelationalDatabases' az security pricing show -n 'CosmosDbs' If the output of any of the above commands shows pricingTier with a value of Free, the setting is out of compliance. From PowerShell Connect-AzAccount Get-AzSecurityPricing |select-object Name,PricingTier |where-object {{$_.Name -match 'Sql' -or $_.Name -match 'Cosmos' -or $_.Name -match 'OpenSource'}} Ensure the output shows Standard for each database type under the PricingTier column. Any that show Free are considered out of compliance.;;By default, Microsoft Defender plan is off.;https://docs.microsoft.com/en-us/azure/azure-sql/database/azure-defender-for-sql?view=azuresql:https://docs.microsoft.com/en-us/azure/defender-for-cloud/quickstart-enable-database-protections:https://docs.microsoft.com/en-us/azure/defender-for-cloud/defender-for-databases-usage:https://docs.microsoft.com/en-us/azure/security-center/security-center-detection-capabilities:https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings/list:https://docs.microsoft.com/en-us/security/benchmark/azure/security-controls-v3-logging-threat-detection#lt-1-enable-threat-detection-capabilities;MANUAL;Manual check;manual_check;Manual check;manual;False\r\n" assert content == expected_csv From 4785feae0ecb70cbc9091020ed3f3efd01fb4484 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 09:33:05 -0500 Subject: [PATCH 32/55] chore(deps): bump botocore from 1.35.60 to 1.35.61 (#5780) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 2d2a198bb5..158ae718a2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -778,13 +778,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.60" +version = "1.35.61" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.60-py3-none-any.whl", hash = "sha256:ddccfc39a0a55ac0321191a36d29c2ea9be2c96ceefb3928dd3c91c79c494d50"}, - {file = "botocore-1.35.60.tar.gz", hash = "sha256:378f53037d817bed2c04a006b7319745e664030182211429c924647273b29bc9"}, + {file = "botocore-1.35.61-py3-none-any.whl", hash = "sha256:6bf15cb459d71e53239474bf8a47c1cda0e309b8e05cbe61561fd70cf0c4551d"}, + {file = "botocore-1.35.61.tar.gz", hash = "sha256:f6ddaa5f5eca2829ce5ed798d1cfee99479d5d4fb6d05fc20ca918d0dbe98d8f"}, ] [package.dependencies] @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "244061534ffd26ff4078fafe275b4a835a35bcb23350937c9636513178cbf679" +content-hash = "982a4958c5a0ebaee82750ccfec0f3d258546578ffe5eb5f659f8c6463a48a77" diff --git a/pyproject.toml b/pyproject.toml index 9a241779d8..6b5ccbaee9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ azure-mgmt-subscription = "3.1.1" azure-mgmt-web = "7.3.1" azure-storage-blob = "12.24.0" boto3 = "1.35.60" -botocore = "1.35.60" +botocore = "1.35.61" colorama = "0.4.6" cryptography = "43.0.1" dash = "2.18.2" From 259538d5e416de6d80d657d531471da740179f9e Mon Sep 17 00:00:00 2001 From: Prowler Bot Date: Fri, 15 Nov 2024 15:33:24 +0100 Subject: [PATCH 33/55] chore(regions_update): Changes in regions for AWS services (#5784) Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com> --- prowler/providers/aws/aws_regions_by_service.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prowler/providers/aws/aws_regions_by_service.json b/prowler/providers/aws/aws_regions_by_service.json index a7a95d0210..08db3d0789 100644 --- a/prowler/providers/aws/aws_regions_by_service.json +++ b/prowler/providers/aws/aws_regions_by_service.json @@ -3979,6 +3979,7 @@ "ap-northeast-2", "ap-northeast-3", "ap-south-1", + "ap-south-2", "ap-southeast-1", "ap-southeast-2", "ap-southeast-3", @@ -3991,6 +3992,7 @@ "eu-west-1", "eu-west-2", "eu-west-3", + "il-central-1", "me-central-1", "me-south-1", "sa-east-1", @@ -5974,6 +5976,7 @@ "ap-southeast-2", "ap-southeast-3", "ap-southeast-4", + "ap-southeast-5", "ca-central-1", "ca-west-1", "eu-central-1", @@ -10778,6 +10781,7 @@ "ap-southeast-2", "ap-southeast-3", "ap-southeast-4", + "ap-southeast-5", "ca-central-1", "ca-west-1", "eu-central-1", From c9898d6d010100c14b9ea053d007b672eade4353 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 09:35:54 -0500 Subject: [PATCH 34/55] chore(deps): bump codecov/codecov-action from 4 to 5 (#5783) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pull-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 6ec6e39c00..f0683c7eb0 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -91,6 +91,6 @@ jobs: poetry run pytest -n auto --cov=./prowler --cov-report=xml tests - name: Upload coverage reports to Codecov if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true' - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From 17af724995115c3b166aaad44b27f0f05d19224f Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Fri, 15 Nov 2024 16:16:03 +0100 Subject: [PATCH 35/55] chore(labeler): Add compliance (#5790) --- .github/labeler.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index 4fb6e1a0f5..cf0a47aaa1 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -79,3 +79,10 @@ output/csv: - changed-files: - any-glob-to-any-file: "prowler/lib/outputs/csv/**" - any-glob-to-any-file: "tests/lib/outputs/csv/**" + +compliance: + - changed-files: + - any-glob-to-any-file: "prowler/lib/outputs/compliance/**" + - any-glob-to-any-file: "tests/lib/outputs/compliance/**" + - any-glob-to-any-file: "prowler/compliance/**" + From 78b518e22b65e34dda098f51ba581c6da087e246 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 10:23:35 -0500 Subject: [PATCH 36/55] chore(deps-dev): bump coverage from 7.6.4 to 7.6.6 (#5793) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 128 ++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/poetry.lock b/poetry.lock index 158ae718a2..28a6a53dcf 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1083,73 +1083,73 @@ files = [ [[package]] name = "coverage" -version = "7.6.4" +version = "7.6.6" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07"}, - {file = "coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a"}, - {file = "coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa"}, - {file = "coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522"}, - {file = "coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf"}, - {file = "coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5"}, - {file = "coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17"}, - {file = "coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a"}, - {file = "coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e"}, - {file = "coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef"}, - {file = "coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e"}, - {file = "coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901"}, - {file = "coverage-7.6.4-cp39-cp39-win32.whl", hash = "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09"}, - {file = "coverage-7.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f"}, - {file = "coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e"}, - {file = "coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73"}, + {file = "coverage-7.6.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2c443d8a6585abb4dfce36cb3aba93b7ea402e51189f95d19a27f230c6516e7"}, + {file = "coverage-7.6.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a85ea9ad580db05745c042541d8e883b149504c8975d64d583946294bca3c0b9"}, + {file = "coverage-7.6.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a761c4c3d04ca4e2860b34df5ae816b4ea12bddf1b3c086923ef04617acf44c"}, + {file = "coverage-7.6.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c6f8deabfa6d0484f0fdba38353bb33950a6138caf1d7d00ba70253dd647a6"}, + {file = "coverage-7.6.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92897a8c07dedbbd9b091275f11f774b06f1e51462441f046dfd46f0b59501f1"}, + {file = "coverage-7.6.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:503f4db0bf83946d8cebb60abd27f3105372ad7e63fb9483db42cabc41536dfc"}, + {file = "coverage-7.6.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:317592d345a3c6b49b723183c9f7c403ae0b9321b79d9c94e9f8997c9d8615ec"}, + {file = "coverage-7.6.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4a484ecf61591e79d7dee29f3f578f5bd42c166a1fa30148f0d68121f313a73"}, + {file = "coverage-7.6.6-cp310-cp310-win32.whl", hash = "sha256:9ddc7bd98dcd4d0b02ccc834d0ddceeede658d3a63bf56bf830aae44e4658af2"}, + {file = "coverage-7.6.6-cp310-cp310-win_amd64.whl", hash = "sha256:5500ff3552bb8fb3d2e9faa23ec59cdfea5943f924500d17cd49b8b99d1e8573"}, + {file = "coverage-7.6.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b680d5894df58459d5bff17a3c98adc37a6a8041f721282855b42a34f10427e1"}, + {file = "coverage-7.6.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2c17668ab6fc21855d4a26ec5ea48b39f2bd7d2a458c26f9e284b72e2cd5a3df"}, + {file = "coverage-7.6.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b285cc58a1c05a61d0f8feb0774099405e7881b419222cd19be3e970da2cecdc"}, + {file = "coverage-7.6.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e81256e1a3b2d901f9232e33746f0008d7bb7ca5b163aba0dfe6c92f445e7e5b"}, + {file = "coverage-7.6.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6c392c24242715f0999760afb05b49451261596f4b1ae7a9d0cea5fda1d12b2"}, + {file = "coverage-7.6.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9ddeb496322dd9069f59e81e4b76b063e748baa05870217caa94a00f5ea8424a"}, + {file = "coverage-7.6.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:10200c9c6642fb1751d1bf85fd819362edd286474c906017da5223ad026f75b5"}, + {file = "coverage-7.6.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54b9b355673f7694ef86bbbf8f2d3b9da052e3ec99153710c5271a62815fa76c"}, + {file = "coverage-7.6.6-cp311-cp311-win32.whl", hash = "sha256:de9651c2c04882c1208263b762b2373b14a0015d86508ddc58e26a85b9a047ca"}, + {file = "coverage-7.6.6-cp311-cp311-win_amd64.whl", hash = "sha256:8780f45433615ad56af0ee82065b01c425f99b7a79d2bc7758588a3a350da65f"}, + {file = "coverage-7.6.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dbad153995872563f42b5c253e4bdff6174a3f311fd1290ab82a3a57fea4c45e"}, + {file = "coverage-7.6.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18d5f58496c6115a66cd435031cb7937f66c9e2fc69acff629af6c89ae4407b5"}, + {file = "coverage-7.6.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd4fbe51c4ae6d277dd4f179507b144ef658cbc4b35701c67eaf61e89c03d2d6"}, + {file = "coverage-7.6.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab34ee1a0c6e939197d794248b67ad01708f5da8e71c6bcd605bcdb957c097cd"}, + {file = "coverage-7.6.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38f631707519da8593a39aa4f5834593973c0311281a2e214a9ab1303a83e63a"}, + {file = "coverage-7.6.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ffcb64ecc31d16c4a38ea68c5d47ca918701aa72876e111d739f052b18d2bd38"}, + {file = "coverage-7.6.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8a5179400043040e920c0e5dfcc62296fdbb84e3db4df23b68cf88c6c1d8e3d0"}, + {file = "coverage-7.6.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9b62440718e77495936203c930990ba76a2ba54a8a527b9221d0eca66542e11e"}, + {file = "coverage-7.6.6-cp312-cp312-win32.whl", hash = "sha256:d3fa8a14927830db393e688dfc6128692d1fafae4adfcab57bee60d4d269691f"}, + {file = "coverage-7.6.6-cp312-cp312-win_amd64.whl", hash = "sha256:03d39c459a3780a1ccc2769ad2df04fb181ee804b90aca2390946756996658df"}, + {file = "coverage-7.6.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7505845756057a89e95c4e922ef205324c9d8bb1bc50dd244f0b296e25be75d"}, + {file = "coverage-7.6.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:98492faa82604ee7c50c2634381576d27ab8b9087d9fe6ed4cc4977dc9925fd2"}, + {file = "coverage-7.6.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c27ee2ff00a97e7c61f14920dcc34fac46ba8613083c94025240ce2e4fe34c8"}, + {file = "coverage-7.6.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab9cb27cca0ec67c1954963da0a260607323418326a8c50945755a8c480debec"}, + {file = "coverage-7.6.6-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c74bae4e7651c75f15edf32e139fcb77ed104e3450ed1f62d2a89195099630d"}, + {file = "coverage-7.6.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d9f12294d08fd778393a3645de5abd366f895e26112fa712b0236bc08401719"}, + {file = "coverage-7.6.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b53851e12b9445a0a3ea3366d3413345302ec3e4bb329404ab5e1f075030c812"}, + {file = "coverage-7.6.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:535920a414b77f56473b5206186feb70c11ae395c52171e2db8d0141594cbcab"}, + {file = "coverage-7.6.6-cp313-cp313-win32.whl", hash = "sha256:40349fe32e9d24c4316d179873252c33aa06ea16ec24d0073d13a009d799b6fb"}, + {file = "coverage-7.6.6-cp313-cp313-win_amd64.whl", hash = "sha256:fbb4a26a61c8da39d1fbcc589dacb619bcdf4c71cd2f051e8259149c9655d186"}, + {file = "coverage-7.6.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c1f59d879a6b3e5d12d28a785f4af31d98633d0dddd3a1a89ce98483cd8d52fa"}, + {file = "coverage-7.6.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:2987be76644a7986033ea4c639b144d8dd29881696eaec1679fef5aae250ecf4"}, + {file = "coverage-7.6.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8013a9a73b7d17f923bba31997adb0c9e0901912cd56be8a8ee274a65c159a3"}, + {file = "coverage-7.6.6-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1743da523f6ca2d12a78eb68bac750acfdaa46d2049e1abf44f6c21315dca6e"}, + {file = "coverage-7.6.6-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5afd8a1f52fdfc061376dbb60471ec01d68618aa9565b6f4a5a83ad8ade6c5"}, + {file = "coverage-7.6.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2b1eac319dfc328837a76cad18c5d3213ea1e92e9e6713286118171850762b46"}, + {file = "coverage-7.6.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3025c2e9ad38efd0fc51034534a2d6a8cb1bb71024aa59673bd70d98e362dcba"}, + {file = "coverage-7.6.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:736d0ee3ffacea6b32a867d2498ea5778f24a688fdf0488d54093c2be7d25ce8"}, + {file = "coverage-7.6.6-cp313-cp313t-win32.whl", hash = "sha256:fd8ef19dbeae9ec18cb5252d2b8d4b5ccc777ccf575df69bac97ab950b99dfab"}, + {file = "coverage-7.6.6-cp313-cp313t-win_amd64.whl", hash = "sha256:8a82a07f826e8819572b0d412ba9563c1e8746e842d5f7a5829068427100e67f"}, + {file = "coverage-7.6.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37df1e03ea19e0c963df59c66df82cdc42ed8955e207a8f804bd6c41e143089b"}, + {file = "coverage-7.6.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:410d986968be5f50fd3c6406c17d766e34e17824d4ea8633809175fd9d4ed30c"}, + {file = "coverage-7.6.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb1cfce3379e7707f525a66138dcf74867eeec8ab66699426085148a2ea0a0e8"}, + {file = "coverage-7.6.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3edd743c8e20e6c8c1df2054bd4d7fc1713530cdb352e3476cb0b7c8642decf7"}, + {file = "coverage-7.6.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54c619a58ac1e01501f510953e86a6084ed9e01125cd079e9440a10c7115808f"}, + {file = "coverage-7.6.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:06ff7e12bcb10af3add8bc95e69de0a9c2a0d91cea55ccedfaa88ef4dbeabd0e"}, + {file = "coverage-7.6.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:b535c74338d5c47adf358488e7d1c73fbaf2d00d6993d0f3f206b5d606d34b7d"}, + {file = "coverage-7.6.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57a868e2a0de53b9f3ea1141a756d2b44285de705637fbc93bc7f7f726d34785"}, + {file = "coverage-7.6.6-cp39-cp39-win32.whl", hash = "sha256:10e50e4a786022a56a1007d4c3e346420cd9d84e88c912bab961662303718b90"}, + {file = "coverage-7.6.6-cp39-cp39-win_amd64.whl", hash = "sha256:d6d7933319399e158baf7ab9c46c84ab97516ba75e84a029a685660c77fb754a"}, + {file = "coverage-7.6.6-pp39.pp310-none-any.whl", hash = "sha256:32b721113ff85ef7ab6f4090689498b7f520e2b4d31996ad802917201a66627e"}, + {file = "coverage-7.6.6.tar.gz", hash = "sha256:84f91414af6cfa55821eb96b8e43bc1955774f57fe04af8bcaf8769123eddf4d"}, ] [package.dependencies] @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "982a4958c5a0ebaee82750ccfec0f3d258546578ffe5eb5f659f8c6463a48a77" +content-hash = "d5381da6beaeae6f98a462598ed78b5a315cfade85fb3688b25eb76181bf24c6" diff --git a/pyproject.toml b/pyproject.toml index 6b5ccbaee9..f8ac3e19b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,7 +76,7 @@ tzlocal = "5.2" [tool.poetry.group.dev.dependencies] bandit = "1.7.10" black = "24.10.0" -coverage = "7.6.4" +coverage = "7.6.6" docker = "7.1.0" flake8 = "7.1.1" freezegun = "1.5.1" From 0d01790b227a1634b40196a6f11d71dcabafec68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Mon, 18 Nov 2024 10:46:34 +0100 Subject: [PATCH 37/55] docs(azure): improve docstrings (#5715) --- prowler/providers/azure/azure_provider.py | 62 +++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/prowler/providers/azure/azure_provider.py b/prowler/providers/azure/azure_provider.py index 6a40349eb9..cbde56a40c 100644 --- a/prowler/providers/azure/azure_provider.py +++ b/prowler/providers/azure/azure_provider.py @@ -152,6 +152,68 @@ class AzureProvider(Provider): AzureConfigCredentialsError: If there is an error in configuring the Azure credentials from a dictionary. AzureGetTokenIdentityError: If there is an error in getting the token from the Azure identity. AzureHTTPResponseError: If there is an HTTP response error. + + Usage: + - Authentication: By default Prowler uses Azure Python SDK identity package authentication methods using the classes DefaultAzureCredential and InteractiveBrowserCredential. + - Using static credentials: + >>> AzureProvider( + ... az_cli_auth=False, + ... sp_env_auth=False, + ... browser_auth=False, + ... managed_identity_auth=False, + ... tenant_id="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", + ... client_id="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", + ... client_secret="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + ... ) + - Using Azure CLI authentication: + >>> AzureProvider( + ... az_cli_auth=True, + ... sp_env_auth=False, + ... browser_auth=False, + ... managed_identity_auth=False, + ... ) + - Using Service Principal environment authentication: + >>> AzureProvider( + ... az_cli_auth=False, + ... sp_env_auth=True, + ... browser_auth=False, + ... managed_identity_auth=False, + ... ) + - Using interactive browser authentication: + >>> AzureProvider( + ... az_cli_auth=False, + ... sp_env_auth=False, + ... browser_auth=True, + ... managed_identity_auth=False, + ... tenant_id="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", + ... ) + * Note: Azure Tenant ID is required for browser authentication mode. + - Using managed identity authentication: + >>> AzureProvider( + ... az_cli_auth=False, + ... sp_env_auth=False, + ... browser_auth=False, + ... managed_identity_auth=True, + ... ) + - Non default azure region: Microsoft provides clouds for compliance with regional laws, which are available for your use. By default, Prowler uses AzureCloud cloud which is the comercial one. + If you want to use a different one, you can specify it using the region parameter. + >>> AzureProvider( + ... az_cli_auth=False, + ... sp_env_auth=True, + ... browser_auth=False, + ... managed_identity_auth=False, + ... region="AzureUSGovernment", + ... ) + - Subscriptions: rowler is multisubscription, which means that is going to scan all the subscriptions is able to list. If you only assign permissions to one subscription, it is going to scan a single one. + Prowler also allows you to specify the subscriptions you want to scan by passing a list of subscription IDs. + >>> AzureProvider( + ... az_cli_auth=False, + ... sp_env_auth=True, + ... browser_auth=False, + ... managed_identity_auth=False, + ... subscription_ids=["XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"], + ... ) + """ logger.info("Setting Azure provider ...") From 5d7c8d9cd27c37600136144dd14d00615120f957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Mon, 18 Nov 2024 12:17:05 +0100 Subject: [PATCH 38/55] docs(aws): improve docstrings (#5714) Co-authored-by: Pepe Fagoaga --- prowler/providers/aws/aws_provider.py | 189 +++++++++++++++++++++++++- 1 file changed, 183 insertions(+), 6 deletions(-) diff --git a/prowler/providers/aws/aws_provider.py b/prowler/providers/aws/aws_provider.py index bcf8d1d8a7..de82b0342a 100644 --- a/prowler/providers/aws/aws_provider.py +++ b/prowler/providers/aws/aws_provider.py @@ -70,6 +70,25 @@ from prowler.providers.common.provider import Provider class AwsProvider(Provider): + """ + AwsProvider class is the main class for the AWS provider. + + This class is responsible for initializing the AWS provider, setting up the AWS session, validating the AWS + credentials, assuming an IAM role, getting the AWS Organizations metadata, and setting the AWS identity. + + Attributes: + _type (str): The provider type. + _identity (AWSIdentityInfo): The AWS provider identity information. + _session (AWSSession): The AWS provider session. + _organizations_metadata (AWSOrganizationsInfo): The AWS Organizations metadata. + _audit_resources (list): The list of resources to audit. + _audit_config (dict): The audit configuration. + _scan_unused_services (bool): A boolean indicating whether to scan unused services. + _enabled_regions (set): The set of enabled regions. + _mutelist (AWSMutelist): The AWS provider mutelist. + audit_metadata (Audit_Metadata): The audit metadata. + """ + _type: str = "aws" _identity: AWSIdentityInfo _session: AWSSession @@ -111,7 +130,7 @@ class AwsProvider(Provider): Args: - retries_max_attempts: The maximum number of retries for the AWS client. - role_arn: The ARN of the IAM role to assume. - - session_duration: The duration of the session in seconds. + - session_duration: The duration of the session in seconds, between 900 and 43200. - external_id: The external ID to use when assuming the IAM role. - role_session_name: The name of the session when assuming the IAM role. - mfa: A boolean indicating whether MFA is enabled. @@ -136,6 +155,37 @@ class AwsProvider(Provider): - ArgumentTypeError: If the input external ID is invalid. - ArgumentTypeError: If the input role session name is invalid. + Usage: + - Boto3 is used so we follow their credential setup process: + - Authentication: Make sure you have properly configured your AWS CLI with a valid Access Key and Region or declare the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables. + - aws configure + or + - export AWS_ACCESS_KEY_ID="ASXXXXXXX" + export AWS_SECRET_ACCESS_KEY="XXXXXXXXX" + export AWS_SESSION_TOKEN="XXXXXXXXX" + - To create a new aws object you can use: + - aws = AwsProvider() + - aws = AwsProvider(aws_access_key_id="ASXXXXXXX", aws_secret_access_key="XXXXXXXXX", aws_session_token="XXXXXXXXX") + - Profile: If you have multiple profiles in your AWS CLI configuration, you can specify the profile to use: + - aws = AwsProvider(profile="profile_name") + - MFA: If you have MFA enabled you can specify it: + - aws = AwsProvider(mfa=True) + * Note: If you have MFA enabled you will be prompted to enter the MFA ARN and the MFA TOTP code. + * Note: Take into account that you can use static credentials or a profile, with the combination of MFA. + + - Assume Role: *Requires authentication.* Prowler can be used against multiple accounts using IAM Assume Role features depending on each use case: + - Set up a custom profile inside your AWS CLI configuration file: + - [profile profile_name] + role_arn = arn:aws:iam::123456789012:role/role_name + - aws = AwsProvider(profile="profile_name") + - Use role_arn directly: + - aws = AwsProvider(role_arn="arn:aws:iam::123456789012:role/role_name") + - Use role_arn with session duration(in seconds, by default 3600) and external ID: + - aws = AwsProvider(role_arn="arn:aws:iam::123456789012:role/role_name", session_duration=3600, external_id="external_id") + - Use custom role session name: + - aws = AwsProvider(role_arn="arn:aws:iam::123456789012:role/role_name", role_session_name="custom_session_name") + * Note: You can use the combination of MFA with Assume Role. + - aws = AwsProvider(role_arn="arn:aws:iam::123456789012:role/role_name", mfa=True) """ logger.info("Initializing AWS provider ...") @@ -426,6 +476,21 @@ class AwsProvider(Provider): regions: set, profile_region: str, ) -> AWSIdentityInfo: + """ + set_identity sets the AWS provider identity information. + + Args: + - caller_identity: The AWS caller identity information. + - profile: The AWS CLI profile name. + - regions: A set of regions to audit. + - profile_region: The AWS CLI profile region. + + Returns: + - AWSIdentityInfo: The AWS provider identity information. + + Raises: + - AWSInvalidProviderIdError: If the AWS provider ID is invalid. + """ logger.info(f"Original AWS Caller Identity UserId: {caller_identity.user_id}") logger.info(f"Original AWS Caller Identity ARN: {caller_identity.arn}") @@ -449,6 +514,22 @@ class AwsProvider(Provider): aws_secret_access_key: str = None, aws_session_token: Optional[str] = None, ) -> Session: + """ + setup_session sets up an AWS session using the provided credentials. + + Args: + - mfa: A boolean indicating whether MFA is enabled. + - profile: The name of the AWS CLI profile to use. + - aws_access_key_id: The AWS access key ID. + - aws_secret_access_key: The AWS secret access key. + - aws_session_token: The AWS session token, optional. + + Returns: + - Session: The AWS session. + + Raises: + - AWSSetUpSessionError: If an error occurs during the setup process. + """ try: logger.debug("Creating original session ...") @@ -590,6 +671,21 @@ class AwsProvider(Provider): return refreshed_credentials def print_credentials(self): + """ + Print the AWS credentials. + + This method prints the AWS credentials used by the provider. + + Example output: + ``` + Using the AWS credentials below: + AWS-CLI Profile: default + AWS Regions: all + AWS Account: 123456789012 + User Id: AIDAJDPLRKLG7EXAMPLE + Caller Identity ARN: arn:aws:iam::123456789012:user/prowler + ``` + """ # Beautify audited regions, set "all" if there is no filter region regions = ( ", ".join(self._identity.audited_regions) @@ -681,6 +777,12 @@ class AwsProvider(Provider): def get_checks_from_input_arn(self) -> set: """ get_checks_from_input_arn gets the list of checks from the input arns + + Returns: + - set: set of strings representing the checks from the input arns + + Example: + checks = get_checks_from_input_arn() """ checks_from_arn = set() is_subservice_in_checks = False @@ -747,7 +849,18 @@ class AwsProvider(Provider): # TODO: This can be moved to another class since it doesn't need self def get_regions_from_audit_resources(self, audit_resources: list) -> set: - """get_regions_from_audit_resources gets the regions from the audit resources arns""" + """get_regions_from_audit_resources gets the regions from the audit resources arns + + Args: + - audit_resources: list of ARNs of the resources to audit + + Returns: + - set: set of strings representing the regions from the audit resources arns + + Example: + audit_resources = ["arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0"] + regions = get_regions_from_audit_resources(audit_resources) + """ audited_regions = set() for resource in audit_resources: region = resource.split(":")[3] @@ -807,7 +920,18 @@ class AwsProvider(Provider): raise error def get_default_region(self, service: str) -> str: - """get_default_region returns the default region based on the profile and audited service regions""" + """get_default_region returns the default region based on the profile and audited service regions + + Args: + - service: The AWS service name + + Returns: + - str: The default region for the given service + + Example: + service = "ec2" + default_region = get_default_region(service) + """ try: service_regions = AwsProvider.get_available_aws_service_regions( service, self._identity.partition, self._identity.audited_regions @@ -828,7 +952,14 @@ class AwsProvider(Provider): raise error def get_global_region(self) -> str: - """get_global_region returns the global region based on the audited partition""" + """get_global_region returns the global region based on the audited partition + + Returns: + - str: The global region for the audited partition + + Example: + global_region = get_global_region()a + """ global_region = "us-east-1" if self._identity.partition == "aws-cn": global_region = "cn-north-1" @@ -840,7 +971,14 @@ class AwsProvider(Provider): @staticmethod def input_role_mfa_token_and_code() -> AWSMFAInfo: - """input_role_mfa_token_and_code ask for the AWS MFA ARN and TOTP and returns it.""" + """input_role_mfa_token_and_code ask for the AWS MFA ARN and TOTP and returns it. + + Returns: + - AWSMFAInfo: An object containing the MFA ARN and TOTP code + + Example: + mfa_info = input_role_mfa_token_and_code() + """ mfa_ARN = input("Enter ARN of MFA: ") mfa_TOTP = input("Enter MFA code: ") return AWSMFAInfo(arn=mfa_ARN, totp=mfa_TOTP) @@ -848,6 +986,12 @@ class AwsProvider(Provider): def set_session_config(self, retries_max_attempts: int) -> Config: """ set_session_config returns a botocore Config object with the Prowler user agent and the default retrier configuration if nothing is passed as argument + + Args: + - retries_max_attempts: The maximum number of retries for the standard retrier config + + Returns: + - Config: The botocore Config object """ # Set the maximum retries for the standard retrier config default_session_config = Config( @@ -874,6 +1018,13 @@ class AwsProvider(Provider): ) -> AWSCredentials: """ assume_role assumes the IAM roles passed with the given session and returns AWSCredentials + + Args: + - session: The AWS session object + - assumed_role_info: The AWSAssumeRoleInfo object + + Returns: + - AWSCredentials: The AWS credentials for the assumed role """ try: role_session_name = ( @@ -925,7 +1076,14 @@ class AwsProvider(Provider): ) def get_aws_enabled_regions(self, current_session: Session) -> set: - """get_aws_enabled_regions returns a set of enabled AWS regions""" + """get_aws_enabled_regions returns a set of enabled AWS regions + + Args: + - current_session: The AWS session object + + Returns: + - set: set of strings representing the enabled AWS regions + """ try: # EC2 Client to check enabled regions service = "ec2" @@ -949,6 +1107,12 @@ class AwsProvider(Provider): # TODO: review this function # Maybe this should be done within the AwsProvider and not in __main__.py def get_checks_to_execute_by_audit_resources(self) -> set[str]: + """ + get_checks_to_execute_by_audit_resources gets the checks to execute based on the audit resources + + Returns: + - set: set of strings representing the checks to execute + """ # Once the provider is set and we have the eventual checks from arn, it is time to exclude the others try: checks = set() @@ -1370,6 +1534,19 @@ def read_aws_regions_file() -> dict: # TODO: This can be moved to another class since it doesn't need self def get_aws_region_for_sts(session_region: str, regions: set[str]) -> str: + """ + Get the AWS region for the STS Assume Role operation. + + Args: + - session_region (str): The region configured in the AWS session. + - regions (set[str]): The regions passed with the -f/--region/--filter-region option. + + Returns: + str: The AWS region for the STS Assume Role operation + + Example: + aws_region = get_aws_region_for_sts(session_region, regions) + """ # If there is no region passed with -f/--region/--filter-region if regions is None or len(regions) == 0: # If you have a region configured in your AWS config or credentials file From ca35510d749a9bf9e9fc0356029a015b25e5a814 Mon Sep 17 00:00:00 2001 From: Prowler Bot Date: Mon, 18 Nov 2024 14:37:25 +0100 Subject: [PATCH 39/55] chore(regions_update): Changes in regions for AWS services (#5802) Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com> --- prowler/providers/aws/aws_regions_by_service.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/prowler/providers/aws/aws_regions_by_service.json b/prowler/providers/aws/aws_regions_by_service.json index 08db3d0789..df3f67950d 100644 --- a/prowler/providers/aws/aws_regions_by_service.json +++ b/prowler/providers/aws/aws_regions_by_service.json @@ -7630,14 +7630,9 @@ "opsworkscm": { "regions": { "aws": [ - "ap-southeast-1", "ap-southeast-2", - "eu-central-1", "eu-west-1", - "us-east-1", - "us-east-2", - "us-west-1", - "us-west-2" + "us-east-1" ], "aws-cn": [], "aws-us-gov": [] From f53a8872919a7ff88a71e81f104c356c3f31ca86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 09:37:53 -0400 Subject: [PATCH 40/55] chore(deps): bump botocore from 1.35.61 to 1.35.63 (#5797) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 28a6a53dcf..59bbf22461 100644 --- a/poetry.lock +++ b/poetry.lock @@ -778,13 +778,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.61" +version = "1.35.63" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.61-py3-none-any.whl", hash = "sha256:6bf15cb459d71e53239474bf8a47c1cda0e309b8e05cbe61561fd70cf0c4551d"}, - {file = "botocore-1.35.61.tar.gz", hash = "sha256:f6ddaa5f5eca2829ce5ed798d1cfee99479d5d4fb6d05fc20ca918d0dbe98d8f"}, + {file = "botocore-1.35.63-py3-none-any.whl", hash = "sha256:0ca1200694a4c0a3fa846795d8e8a08404c214e21195eb9e010c4b8a4ca78a4a"}, + {file = "botocore-1.35.63.tar.gz", hash = "sha256:2b8196bab0a997d206c3d490b52e779ef47dffb68c57c685443f77293aca1589"}, ] [package.dependencies] @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "d5381da6beaeae6f98a462598ed78b5a315cfade85fb3688b25eb76181bf24c6" +content-hash = "413424880ae8cc17f5a0254cf54d2c0a91a76c2efaf4bd6e7801366da1268f09" diff --git a/pyproject.toml b/pyproject.toml index f8ac3e19b7..a9e450dc24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ azure-mgmt-subscription = "3.1.1" azure-mgmt-web = "7.3.1" azure-storage-blob = "12.24.0" boto3 = "1.35.60" -botocore = "1.35.61" +botocore = "1.35.63" colorama = "0.4.6" cryptography = "43.0.1" dash = "2.18.2" From 0a63e707c2caa9c2357cb55581d5adf6480a13a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Mon, 18 Nov 2024 15:18:57 +0100 Subject: [PATCH 41/55] docs(kubernetes): improve docstrings for methods (#5717) --- .../kubernetes/kubernetes_provider.py | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/prowler/providers/kubernetes/kubernetes_provider.py b/prowler/providers/kubernetes/kubernetes_provider.py index dce691beb3..9182d3a4ca 100644 --- a/prowler/providers/kubernetes/kubernetes_provider.py +++ b/prowler/providers/kubernetes/kubernetes_provider.py @@ -33,6 +33,28 @@ from prowler.providers.kubernetes.models import ( class KubernetesProvider(Provider): + """ + Represents the Kubernetes provider. + + Attributes: + _type (str): The provider type, wich is 'kubernetes'. + _session (KubernetesSession): The Kubernetes session. + _namespaces (list): The list of namespaces to audit. + _audit_config (dict): The audit configuration. + _identity (KubernetesIdentityInfo): The Kubernetes identity information. + _mutelist (dict): The mutelist. + audit_metadata (Audit_Metadata): The audit metadata. + + Methods: + setup_session: Sets up the Kubernetes session. + test_connection: Tests the connection to the Kubernetes cluster. + search_and_save_roles: Searches for and saves roles. + get_context_user_roles: Retrieves the context user roles. + get_all_namespaces: Retrieves all namespaces. + get_pod_current_namespace: Retrieves the current namespace from the pod's mounted service account info. + print_credentials: Prints the Kubernetes credentials. + """ + _type: str = "kubernetes" _session: KubernetesSession _namespaces: list @@ -56,6 +78,7 @@ class KubernetesProvider(Provider): ): """ Initializes the KubernetesProvider instance. + Args: kubeconfig_file (str): Path to the kubeconfig file. kubeconfig_content (dict): Content of the kubeconfig file. @@ -66,6 +89,60 @@ class KubernetesProvider(Provider): fixer_config (dict): Fixer configuration. mutelist_path (str): Path to the mutelist file. mutelist_content (dict): Mutelist content. + + Raises: + KubernetesCloudResourceManagerAPINotUsedError: If the Kubernetes Cloud Resource Manager API is not used. + KubernetesError: If an error occurs. + + Returns: + KubernetesProvider: The KubernetesProvider instance. + + Usage: + - Authentication: The provider can be instantiated in two ways: + 1. Using the kubeconfig file. + >>> provider = KubernetesProvider( + ... kubeconfig_file="~/.kube/config", + ... context="my-context", + ... namespace=["default"], + ... ) + 2. Using the kubeconfig content. + >>> provider = KubernetesProvider( + ... kubeconfig_content={"kubecofig": "content"}, + ... context="my-context", + ... namespace=["default"], + ... ) + - Namespace and context: The provider can be instantiated with or without specifying the namespace and context. + - Without specifying the namespace: + >>> provider = KubernetesProvider( + ... kubeconfig_file="~/.kube/config", + ... context="my-context", + ... ) + - With specifying the namespace: + >>> provider = KubernetesProvider( + ... kubeconfig_file="~/.kube/config", + ... context="my-context", + ... namespace=["default"], + ... ) + - With specifying the context: + >>> provider = KubernetesProvider( + ... kubeconfig_file="~/.kube/config", + ... context="my-context", + ... namespace=["default"], + ... ) + - Configuration: The provider can be instantiated with or without specifying the configuration. + - Without specifying the configuration: + >>> provider = KubernetesProvider( + ... kubeconfig_file="~/.kube/config", + ... context="my-context", + ... namespace=["default"], + ... ) + - With specifying the configuration: + >>> provider = KubernetesProvider( + ... kubeconfig_file="~/.kube/config", + ... context="my-context", + ... namespace=["default"], + ... config_path="path/to/config.yaml", + ... ) """ logger.info("Instantiating Kubernetes Provider ...") @@ -160,6 +237,11 @@ class KubernetesProvider(Provider): Returns: Tuple: A tuple containing the API client and the context. + + Raises: + KubernetesInvalidKubeConfigFileError: If the kubeconfig file is invalid. + KubernetesInvalidProviderIdError: If the provider ID is invalid. + KubernetesSetUpSessionError: If an error occurs while setting up the session. """ logger.info(f"Using kubeconfig file: {kubeconfig_file}") try: @@ -250,8 +332,45 @@ class KubernetesProvider(Provider): namespace (str): Namespace name. provider_id (str): Provider ID to use, in this case, the Kubernetes context. raise_on_exception (bool): Whether to raise an exception on error. + Returns: Connection: A Connection object. + + Raises: + KubernetesInvalidKubeConfigFileError: If the kubeconfig file is invalid. + KubernetesInvalidProviderIdError: If the provider ID is invalid. + KubernetesSetUpSessionError: If an error occurs while setting up the session. + KubernetesAPIError: If an error occurs while testing the connection. + + Usage: + - Using the kubeconfig file: + >>> connection = KubernetesProvider.test_connection( + ... kubeconfig_file="~/.kube/config", + ... namespace="default", + ... provider_id="my-context", + ... raise_on_exception=True, + ... ) + - Using the kubeconfig content: + >>> connection = KubernetesProvider.test_connection( + ... kubeconfig_content={"kubecofig": "content"}, + ... namespace="default", + ... provider_id="my-context", + ... raise_on_exception=True, + ... ) + - Using the namespace: + >>> connection = KubernetesProvider.test_connection( + ... kubeconfig_file="~/.kube/config", + ... namespace="default", + ... provider_id="my-context", + ... raise_on_exception=True, + ... ) + - Without raising an exception: + >>> connection = KubernetesProvider.test_connection( + ... kubeconfig_file="~/.kube/config", + ... namespace="default", + ... provider_id="my-context", + ... raise_on_exception=False, + ... ) """ try: KubernetesProvider.setup_session( @@ -316,6 +435,17 @@ class KubernetesProvider(Provider): Returns: list: A list containing the roles. + + Raises: + KubernetesError: If an error occurs. + + Usage: + >>> roles = self.search_and_save_roles( + ... roles=[], + ... role_bindings=rbac_api.list_cluster_role_binding().items, + ... context_user="my-user", + ... role_binding_type="ClusterRole", + ... ) """ try: for rb in role_bindings: @@ -343,6 +473,12 @@ class KubernetesProvider(Provider): Returns: list: A list containing the context user roles. + + Raises: + KubernetesError: If an error occurs. + + Usage: + >>> roles = self.get_context_user_roles() """ try: rbac_api = client.RbacAuthorizationV1Api() @@ -379,8 +515,17 @@ class KubernetesProvider(Provider): def get_all_namespaces(self) -> list[str]: """ Retrieves all namespaces. + Returns: list: A list containing all namespace names. + + Raises: + KubernetesAPIError: If an error occurs while retrieving the namespaces. + KubernetesTimeoutError: If a timeout occurs while retrieving the namespaces. + KubernetesError: If an error occurs. + + Usage: + >>> namespaces = self.get_all_namespaces() """ try: v1 = client.CoreV1Api() @@ -413,8 +558,12 @@ class KubernetesProvider(Provider): def get_pod_current_namespace(self): """ Retrieves the current namespace from the pod's mounted service account info. + Returns: str: The current namespace. + + Usage: + >>> namespace = self.get_pod_current_namespace() """ try: with open( @@ -430,6 +579,9 @@ class KubernetesProvider(Provider): def print_credentials(self): """ Prints the Kubernetes credentials. + + Usage: + >>> self.print_credentials() """ if self._identity.context == "In-Cluster": report_lines = [ From a25a6148f2067b26870e478c29f90422556fa926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Mon, 18 Nov 2024 15:19:15 +0100 Subject: [PATCH 42/55] docs(gcp): improve docstrings (#5716) --- prowler/providers/gcp/gcp_provider.py | 148 ++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/prowler/providers/gcp/gcp_provider.py b/prowler/providers/gcp/gcp_provider.py index be47e2abd7..0c3da34b32 100644 --- a/prowler/providers/gcp/gcp_provider.py +++ b/prowler/providers/gcp/gcp_provider.py @@ -36,6 +36,31 @@ from prowler.providers.gcp.models import GCPIdentityInfo, GCPOrganization, GCPPr class GcpProvider(Provider): + """ + GCP Provider class to handle the GCP provider + + Attributes: + - _type: str -> The provider type + - _session: Credentials -> The GCP credentials session + - _project_ids: list -> The list of project IDs + - _excluded_project_ids: list -> The list of excluded project IDs + - _identity: GCPIdentityInfo -> The GCP identity info + - _audit_config: dict -> The audit config + - _mutelist: GCPMutelist -> The GCP mutelist + - audit_metadata: Audit_Metadata -> The audit metadata + + Methods: + - __init__ -> GCP Provider + - setup_session -> Setup the GCP session + - test_connection -> Test the connection to GCP + - print_credentials -> Print the GCP credentials + - get_projects -> Get the projects accessible by the provided credentials + - update_projects_with_organizations -> Update the projects with organizations + - is_project_matching -> Check if the input project matches the project to match + - validate_static_arguments -> Validate the static arguments + - validate_project_id -> Validate the provider ID + """ + _type: str = "gcp" _session: Credentials _project_ids: list @@ -81,6 +106,56 @@ class GcpProvider(Provider): client_id: str client_secret: str refresh_token: str + + Raises: + GCPNoAccesibleProjectsError if no project IDs can be accessed via Google Credentials + GCPSetUpSessionError if an error occurs during the setup session + GCPLoadCredentialsFromDictError if an error occurs during the loading credentials from dict + GCPGetProjectError if an error occurs during the get project + + Returns: + None + + Usage: + - Authentication: Prowler will use by default your User Account credentials, you can configure it using: + - Using the gcloud command: + - gcloud init to use a new account + - gcloud config set account to use a specific account + - gcloud auth application-default login to use the Application Default Credentials + - Prowler will use the Application Default Credentials if no credentials are provided + - Using static credentials: + - Using the client_id, client_secret and refresh_token: + >>> GcpProvider( + ... client_id="client_id", + ... client_secret="client_secret", + ... refresh_token="refresh_token" + ... ) + - Using a credentials file: + >>> GcpProvider( + ... credentials_file="credentials_file" + ... ) + - Impersonating a service account: If you want to impersonate a GCP service account, you can use the impersonate_service_account parameter. For this method user must be authenticated: + >>> GcpProvider( + ... impersonate_service_account="service_account" + ... ) + - Projects: The GCP provider supports multi-project, which means that is capable of scan all the Google Cloud projects the user has access to. + - If you want to scan a specific project(s), you can use the project-ids argument. + >>> GcpProvider( + ... project_ids=["project_id1", "project_id2"] + ... ) + - If you want to exclude a specific project(s), you can use the excluded-project-ids argument. + >>> GcpProvider( + ... excluded_project_ids=["project_id1", "project_id2"] + ... ) + * Note: You can use asterisk * to exclude projects that match a pattern. For example, using "sys*" will exclude all the projects that start with sys. + - If you want to list all the available project IDs, you can use the list-project-ids argument. + >>> GcpProvider( + ... list_project_ids=True + ... ) + - Organizations: If you want to scan a specific organization, you can use the organization-id argument. With this argument, Prowler will scan all the projects under that organization. + >>> GcpProvider( + ... organization_id="organization_id" + ... ) """ logger.info("Instantiating GCP Provider ...") self._impersonated_service_account = impersonate_service_account @@ -239,11 +314,21 @@ class GcpProvider(Provider): ) -> tuple: """ Setup the GCP session with the provided credentials file or service account to impersonate + Args: credentials_file: str service_account: str + Returns: Credentials object and default project ID + + Raises: + GCPLoadCredentialsFromDictError if an error occurs during the loading credentials from dict + GCPSetUpSessionError if an error occurs during the setup session + + Usage: + >>> GcpProvider.setup_session(credentials_file, service_account) + >>> GcpProvider.setup_session(service_account, gcp_credentials) """ try: scopes = ["https://www.googleapis.com/auth/cloud-platform"] @@ -311,6 +396,7 @@ class GcpProvider(Provider): If the connection is successful, return a Connection object with is_connected set to True. If the connection fails, return a Connection object with error set to the exception. Raise an exception if raise_on_exception is set to True. If the Cloud Resource Manager API has not been used before or it is disabled, log a critical message and return a Connection object with error set to the exception. + Args: credentials_file: str service_account: str @@ -319,8 +405,35 @@ class GcpProvider(Provider): client_secret: str refresh_token: str provider_id: Optional[str] -> The provider ID, for GCP it is the project ID + Returns: Connection object with is_connected set to True if the connection is successful, or error set to the exception if the connection fails + + Raises: + GCPLoadCredentialsFromDictError if an error occurs during the loading credentials from dict + GCPSetUpSessionError if an error occurs during the setup session + GCPCloudResourceManagerAPINotUsedError if the Cloud Resource Manager API has not been used before or it is disabled + GCPInvalidProviderIdError if the provider ID does not match with the expected project_id + GCPTestConnectionError if an error occurs during the test connection + + Usage: + - Using static credentials: + >>> GcpProvider.test_connection( + ... client_id="client_id", + ... client_secret="client_secret", + ... refresh_token="refresh_token" + ... ) + - Using a credentials file: + >>> GcpProvider.test_connection( + ... credentials_file="credentials_file" + ... ) + - Using a service account to impersonate, authentication is required to impersonate a service account: + >>> GcpProvider.test_connection( + ... client_id="client_id", + ... client_secret="client_secret", + ... refresh_token="refresh_token", + ... service_account="service_account" + ... ) """ try: # Set the GCP credentials using the provided client_id, client_secret and refresh_token @@ -421,11 +534,22 @@ class GcpProvider(Provider): ) -> dict[str, GCPProject]: """ Get the projects accessible by the provided credentials. If an organization ID is provided, only the projects under that organization are returned. + Args: credentials: Credentials organization_id: str + Returns: dict[str, GCPProject] + + Raises: + GCPCloudResourceManagerAPINotUsedError if the Cloud Resource Manager API has not been used before or it is disabled + GCPCloudAssetAPINotUsedError if the Cloud Asset API has not been used before or it is disabled + GCPHTTPError if an error occurs during the HTTP request + GCPGetProjectError if an error occurs during the get project + + Usage: + >>> GcpProvider.get_projects(credentials=credentials, organization_id=organization_id) """ try: projects = {} @@ -541,6 +665,19 @@ class GcpProvider(Provider): return projects def update_projects_with_organizations(self): + """ + Update the projects with organizations + + Returns: + None + + Raises: + GCPHTTPError if an error occurs during the HTTP request + GCPGetProjectError if an error occurs during the get project + + Usage: + >>> GcpProvider.update_projects_with_organizations() + """ try: service = discovery.build( "cloudresourcemanager", "v1", credentials=self._session @@ -577,11 +714,16 @@ class GcpProvider(Provider): def is_project_matching(self, input_project: str, project_to_match: str) -> bool: """ Check if the input project matches the project to match + Args: input_project: str project_to_match: str + Returns: bool + + Usage: + >>> GcpProvider.is_project_matching(input_project, project_to_match) """ return ( "*" in input_project @@ -608,6 +750,9 @@ class GcpProvider(Provider): Raises: GCPStaticCredentialsError if any of the static arguments is missing + + Usage: + >>> GcpProvider.validate_static_arguments(client_id, client_secret, refresh_token) """ if not client_id or not client_secret or not refresh_token: @@ -637,6 +782,9 @@ class GcpProvider(Provider): Raises: GCPInvalidProviderIdError if the provider ID does not match with the expected project_id + + Usage: + >>> GcpProvider.validate_project_id(provider_id, credentials) """ available_projects = list( From 193b79c221275c59b9979bc2b47d53a0f04e32fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:36:03 -0400 Subject: [PATCH 43/55] chore(deps-dev): bump coverage from 7.6.6 to 7.6.7 (#5795) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 128 ++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/poetry.lock b/poetry.lock index 59bbf22461..52bdcf54ff 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1083,73 +1083,73 @@ files = [ [[package]] name = "coverage" -version = "7.6.6" +version = "7.6.7" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2c443d8a6585abb4dfce36cb3aba93b7ea402e51189f95d19a27f230c6516e7"}, - {file = "coverage-7.6.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a85ea9ad580db05745c042541d8e883b149504c8975d64d583946294bca3c0b9"}, - {file = "coverage-7.6.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a761c4c3d04ca4e2860b34df5ae816b4ea12bddf1b3c086923ef04617acf44c"}, - {file = "coverage-7.6.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c6f8deabfa6d0484f0fdba38353bb33950a6138caf1d7d00ba70253dd647a6"}, - {file = "coverage-7.6.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92897a8c07dedbbd9b091275f11f774b06f1e51462441f046dfd46f0b59501f1"}, - {file = "coverage-7.6.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:503f4db0bf83946d8cebb60abd27f3105372ad7e63fb9483db42cabc41536dfc"}, - {file = "coverage-7.6.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:317592d345a3c6b49b723183c9f7c403ae0b9321b79d9c94e9f8997c9d8615ec"}, - {file = "coverage-7.6.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4a484ecf61591e79d7dee29f3f578f5bd42c166a1fa30148f0d68121f313a73"}, - {file = "coverage-7.6.6-cp310-cp310-win32.whl", hash = "sha256:9ddc7bd98dcd4d0b02ccc834d0ddceeede658d3a63bf56bf830aae44e4658af2"}, - {file = "coverage-7.6.6-cp310-cp310-win_amd64.whl", hash = "sha256:5500ff3552bb8fb3d2e9faa23ec59cdfea5943f924500d17cd49b8b99d1e8573"}, - {file = "coverage-7.6.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b680d5894df58459d5bff17a3c98adc37a6a8041f721282855b42a34f10427e1"}, - {file = "coverage-7.6.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2c17668ab6fc21855d4a26ec5ea48b39f2bd7d2a458c26f9e284b72e2cd5a3df"}, - {file = "coverage-7.6.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b285cc58a1c05a61d0f8feb0774099405e7881b419222cd19be3e970da2cecdc"}, - {file = "coverage-7.6.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e81256e1a3b2d901f9232e33746f0008d7bb7ca5b163aba0dfe6c92f445e7e5b"}, - {file = "coverage-7.6.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6c392c24242715f0999760afb05b49451261596f4b1ae7a9d0cea5fda1d12b2"}, - {file = "coverage-7.6.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9ddeb496322dd9069f59e81e4b76b063e748baa05870217caa94a00f5ea8424a"}, - {file = "coverage-7.6.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:10200c9c6642fb1751d1bf85fd819362edd286474c906017da5223ad026f75b5"}, - {file = "coverage-7.6.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54b9b355673f7694ef86bbbf8f2d3b9da052e3ec99153710c5271a62815fa76c"}, - {file = "coverage-7.6.6-cp311-cp311-win32.whl", hash = "sha256:de9651c2c04882c1208263b762b2373b14a0015d86508ddc58e26a85b9a047ca"}, - {file = "coverage-7.6.6-cp311-cp311-win_amd64.whl", hash = "sha256:8780f45433615ad56af0ee82065b01c425f99b7a79d2bc7758588a3a350da65f"}, - {file = "coverage-7.6.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dbad153995872563f42b5c253e4bdff6174a3f311fd1290ab82a3a57fea4c45e"}, - {file = "coverage-7.6.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18d5f58496c6115a66cd435031cb7937f66c9e2fc69acff629af6c89ae4407b5"}, - {file = "coverage-7.6.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd4fbe51c4ae6d277dd4f179507b144ef658cbc4b35701c67eaf61e89c03d2d6"}, - {file = "coverage-7.6.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab34ee1a0c6e939197d794248b67ad01708f5da8e71c6bcd605bcdb957c097cd"}, - {file = "coverage-7.6.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38f631707519da8593a39aa4f5834593973c0311281a2e214a9ab1303a83e63a"}, - {file = "coverage-7.6.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ffcb64ecc31d16c4a38ea68c5d47ca918701aa72876e111d739f052b18d2bd38"}, - {file = "coverage-7.6.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8a5179400043040e920c0e5dfcc62296fdbb84e3db4df23b68cf88c6c1d8e3d0"}, - {file = "coverage-7.6.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9b62440718e77495936203c930990ba76a2ba54a8a527b9221d0eca66542e11e"}, - {file = "coverage-7.6.6-cp312-cp312-win32.whl", hash = "sha256:d3fa8a14927830db393e688dfc6128692d1fafae4adfcab57bee60d4d269691f"}, - {file = "coverage-7.6.6-cp312-cp312-win_amd64.whl", hash = "sha256:03d39c459a3780a1ccc2769ad2df04fb181ee804b90aca2390946756996658df"}, - {file = "coverage-7.6.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7505845756057a89e95c4e922ef205324c9d8bb1bc50dd244f0b296e25be75d"}, - {file = "coverage-7.6.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:98492faa82604ee7c50c2634381576d27ab8b9087d9fe6ed4cc4977dc9925fd2"}, - {file = "coverage-7.6.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c27ee2ff00a97e7c61f14920dcc34fac46ba8613083c94025240ce2e4fe34c8"}, - {file = "coverage-7.6.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab9cb27cca0ec67c1954963da0a260607323418326a8c50945755a8c480debec"}, - {file = "coverage-7.6.6-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c74bae4e7651c75f15edf32e139fcb77ed104e3450ed1f62d2a89195099630d"}, - {file = "coverage-7.6.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d9f12294d08fd778393a3645de5abd366f895e26112fa712b0236bc08401719"}, - {file = "coverage-7.6.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b53851e12b9445a0a3ea3366d3413345302ec3e4bb329404ab5e1f075030c812"}, - {file = "coverage-7.6.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:535920a414b77f56473b5206186feb70c11ae395c52171e2db8d0141594cbcab"}, - {file = "coverage-7.6.6-cp313-cp313-win32.whl", hash = "sha256:40349fe32e9d24c4316d179873252c33aa06ea16ec24d0073d13a009d799b6fb"}, - {file = "coverage-7.6.6-cp313-cp313-win_amd64.whl", hash = "sha256:fbb4a26a61c8da39d1fbcc589dacb619bcdf4c71cd2f051e8259149c9655d186"}, - {file = "coverage-7.6.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c1f59d879a6b3e5d12d28a785f4af31d98633d0dddd3a1a89ce98483cd8d52fa"}, - {file = "coverage-7.6.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:2987be76644a7986033ea4c639b144d8dd29881696eaec1679fef5aae250ecf4"}, - {file = "coverage-7.6.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8013a9a73b7d17f923bba31997adb0c9e0901912cd56be8a8ee274a65c159a3"}, - {file = "coverage-7.6.6-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1743da523f6ca2d12a78eb68bac750acfdaa46d2049e1abf44f6c21315dca6e"}, - {file = "coverage-7.6.6-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5afd8a1f52fdfc061376dbb60471ec01d68618aa9565b6f4a5a83ad8ade6c5"}, - {file = "coverage-7.6.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2b1eac319dfc328837a76cad18c5d3213ea1e92e9e6713286118171850762b46"}, - {file = "coverage-7.6.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3025c2e9ad38efd0fc51034534a2d6a8cb1bb71024aa59673bd70d98e362dcba"}, - {file = "coverage-7.6.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:736d0ee3ffacea6b32a867d2498ea5778f24a688fdf0488d54093c2be7d25ce8"}, - {file = "coverage-7.6.6-cp313-cp313t-win32.whl", hash = "sha256:fd8ef19dbeae9ec18cb5252d2b8d4b5ccc777ccf575df69bac97ab950b99dfab"}, - {file = "coverage-7.6.6-cp313-cp313t-win_amd64.whl", hash = "sha256:8a82a07f826e8819572b0d412ba9563c1e8746e842d5f7a5829068427100e67f"}, - {file = "coverage-7.6.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37df1e03ea19e0c963df59c66df82cdc42ed8955e207a8f804bd6c41e143089b"}, - {file = "coverage-7.6.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:410d986968be5f50fd3c6406c17d766e34e17824d4ea8633809175fd9d4ed30c"}, - {file = "coverage-7.6.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb1cfce3379e7707f525a66138dcf74867eeec8ab66699426085148a2ea0a0e8"}, - {file = "coverage-7.6.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3edd743c8e20e6c8c1df2054bd4d7fc1713530cdb352e3476cb0b7c8642decf7"}, - {file = "coverage-7.6.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54c619a58ac1e01501f510953e86a6084ed9e01125cd079e9440a10c7115808f"}, - {file = "coverage-7.6.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:06ff7e12bcb10af3add8bc95e69de0a9c2a0d91cea55ccedfaa88ef4dbeabd0e"}, - {file = "coverage-7.6.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:b535c74338d5c47adf358488e7d1c73fbaf2d00d6993d0f3f206b5d606d34b7d"}, - {file = "coverage-7.6.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57a868e2a0de53b9f3ea1141a756d2b44285de705637fbc93bc7f7f726d34785"}, - {file = "coverage-7.6.6-cp39-cp39-win32.whl", hash = "sha256:10e50e4a786022a56a1007d4c3e346420cd9d84e88c912bab961662303718b90"}, - {file = "coverage-7.6.6-cp39-cp39-win_amd64.whl", hash = "sha256:d6d7933319399e158baf7ab9c46c84ab97516ba75e84a029a685660c77fb754a"}, - {file = "coverage-7.6.6-pp39.pp310-none-any.whl", hash = "sha256:32b721113ff85ef7ab6f4090689498b7f520e2b4d31996ad802917201a66627e"}, - {file = "coverage-7.6.6.tar.gz", hash = "sha256:84f91414af6cfa55821eb96b8e43bc1955774f57fe04af8bcaf8769123eddf4d"}, + {file = "coverage-7.6.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:108bb458827765d538abcbf8288599fee07d2743357bdd9b9dad456c287e121e"}, + {file = "coverage-7.6.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c973b2fe4dc445cb865ab369df7521df9c27bf40715c837a113edaa2aa9faf45"}, + {file = "coverage-7.6.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c6b24007c4bcd0b19fac25763a7cac5035c735ae017e9a349b927cfc88f31c1"}, + {file = "coverage-7.6.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acbb8af78f8f91b3b51f58f288c0994ba63c646bc1a8a22ad072e4e7e0a49f1c"}, + {file = "coverage-7.6.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad32a981bcdedb8d2ace03b05e4fd8dace8901eec64a532b00b15217d3677dd2"}, + {file = "coverage-7.6.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:34d23e28ccb26236718a3a78ba72744212aa383141961dd6825f6595005c8b06"}, + {file = "coverage-7.6.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e25bacb53a8c7325e34d45dddd2f2fbae0dbc230d0e2642e264a64e17322a777"}, + {file = "coverage-7.6.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af05bbba896c4472a29408455fe31b3797b4d8648ed0a2ccac03e074a77e2314"}, + {file = "coverage-7.6.7-cp310-cp310-win32.whl", hash = "sha256:796c9b107d11d2d69e1849b2dfe41730134b526a49d3acb98ca02f4985eeff7a"}, + {file = "coverage-7.6.7-cp310-cp310-win_amd64.whl", hash = "sha256:987a8e3da7da4eed10a20491cf790589a8e5e07656b6dc22d3814c4d88faf163"}, + {file = "coverage-7.6.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e61b0e77ff4dddebb35a0e8bb5a68bf0f8b872407d8d9f0c726b65dfabe2469"}, + {file = "coverage-7.6.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a5407a75ca4abc20d6252efeb238377a71ce7bda849c26c7a9bece8680a5d99"}, + {file = "coverage-7.6.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df002e59f2d29e889c37abd0b9ee0d0e6e38c24f5f55d71ff0e09e3412a340ec"}, + {file = "coverage-7.6.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:673184b3156cba06154825f25af33baa2671ddae6343f23175764e65a8c4c30b"}, + {file = "coverage-7.6.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e69ad502f1a2243f739f5bd60565d14a278be58be4c137d90799f2c263e7049a"}, + {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60dcf7605c50ea72a14490d0756daffef77a5be15ed1b9fea468b1c7bda1bc3b"}, + {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9c2eb378bebb2c8f65befcb5147877fc1c9fbc640fc0aad3add759b5df79d55d"}, + {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c0317288f032221d35fa4cbc35d9f4923ff0dfd176c79c9b356e8ef8ef2dff4"}, + {file = "coverage-7.6.7-cp311-cp311-win32.whl", hash = "sha256:951aade8297358f3618a6e0660dc74f6b52233c42089d28525749fc8267dccd2"}, + {file = "coverage-7.6.7-cp311-cp311-win_amd64.whl", hash = "sha256:5e444b8e88339a2a67ce07d41faabb1d60d1004820cee5a2c2b54e2d8e429a0f"}, + {file = "coverage-7.6.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f07ff574986bc3edb80e2c36391678a271d555f91fd1d332a1e0f4b5ea4b6ea9"}, + {file = "coverage-7.6.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:49ed5ee4109258973630c1f9d099c7e72c5c36605029f3a91fe9982c6076c82b"}, + {file = "coverage-7.6.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3e8796434a8106b3ac025fd15417315d7a58ee3e600ad4dbcfddc3f4b14342c"}, + {file = "coverage-7.6.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3b925300484a3294d1c70f6b2b810d6526f2929de954e5b6be2bf8caa1f12c1"}, + {file = "coverage-7.6.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c42ec2c522e3ddd683dec5cdce8e62817afb648caedad9da725001fa530d354"}, + {file = "coverage-7.6.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0266b62cbea568bd5e93a4da364d05de422110cbed5056d69339bd5af5685433"}, + {file = "coverage-7.6.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e5f2a0f161d126ccc7038f1f3029184dbdf8f018230af17ef6fd6a707a5b881f"}, + {file = "coverage-7.6.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c132b5a22821f9b143f87446805e13580b67c670a548b96da945a8f6b4f2efbb"}, + {file = "coverage-7.6.7-cp312-cp312-win32.whl", hash = "sha256:7c07de0d2a110f02af30883cd7dddbe704887617d5c27cf373362667445a4c76"}, + {file = "coverage-7.6.7-cp312-cp312-win_amd64.whl", hash = "sha256:fd49c01e5057a451c30c9b892948976f5d38f2cbd04dc556a82743ba8e27ed8c"}, + {file = "coverage-7.6.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:46f21663e358beae6b368429ffadf14ed0a329996248a847a4322fb2e35d64d3"}, + {file = "coverage-7.6.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:40cca284c7c310d622a1677f105e8507441d1bb7c226f41978ba7c86979609ab"}, + {file = "coverage-7.6.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77256ad2345c29fe59ae861aa11cfc74579c88d4e8dbf121cbe46b8e32aec808"}, + {file = "coverage-7.6.7-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87ea64b9fa52bf395272e54020537990a28078478167ade6c61da7ac04dc14bc"}, + {file = "coverage-7.6.7-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d608a7808793e3615e54e9267519351c3ae204a6d85764d8337bd95993581a8"}, + {file = "coverage-7.6.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdd94501d65adc5c24f8a1a0eda110452ba62b3f4aeaba01e021c1ed9cb8f34a"}, + {file = "coverage-7.6.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82c809a62e953867cf57e0548c2b8464207f5f3a6ff0e1e961683e79b89f2c55"}, + {file = "coverage-7.6.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bb684694e99d0b791a43e9fc0fa58efc15ec357ac48d25b619f207c41f2fd384"}, + {file = "coverage-7.6.7-cp313-cp313-win32.whl", hash = "sha256:963e4a08cbb0af6623e61492c0ec4c0ec5c5cf74db5f6564f98248d27ee57d30"}, + {file = "coverage-7.6.7-cp313-cp313-win_amd64.whl", hash = "sha256:14045b8bfd5909196a90da145a37f9d335a5d988a83db34e80f41e965fb7cb42"}, + {file = "coverage-7.6.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f2c7a045eef561e9544359a0bf5784b44e55cefc7261a20e730baa9220c83413"}, + {file = "coverage-7.6.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dd4e4a49d9c72a38d18d641135d2fb0bdf7b726ca60a103836b3d00a1182acd"}, + {file = "coverage-7.6.7-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c95e0fa3d1547cb6f021ab72f5c23402da2358beec0a8e6d19a368bd7b0fb37"}, + {file = "coverage-7.6.7-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f63e21ed474edd23f7501f89b53280014436e383a14b9bd77a648366c81dce7b"}, + {file = "coverage-7.6.7-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead9b9605c54d15be228687552916c89c9683c215370c4a44f1f217d2adcc34d"}, + {file = "coverage-7.6.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0573f5cbf39114270842d01872952d301027d2d6e2d84013f30966313cadb529"}, + {file = "coverage-7.6.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e2c8e3384c12dfa19fa9a52f23eb091a8fad93b5b81a41b14c17c78e23dd1d8b"}, + {file = "coverage-7.6.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:70a56a2ec1869e6e9fa69ef6b76b1a8a7ef709972b9cc473f9ce9d26b5997ce3"}, + {file = "coverage-7.6.7-cp313-cp313t-win32.whl", hash = "sha256:dbba8210f5067398b2c4d96b4e64d8fb943644d5eb70be0d989067c8ca40c0f8"}, + {file = "coverage-7.6.7-cp313-cp313t-win_amd64.whl", hash = "sha256:dfd14bcae0c94004baba5184d1c935ae0d1231b8409eb6c103a5fd75e8ecdc56"}, + {file = "coverage-7.6.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37a15573f988b67f7348916077c6d8ad43adb75e478d0910957394df397d2874"}, + {file = "coverage-7.6.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b6cce5c76985f81da3769c52203ee94722cd5d5889731cd70d31fee939b74bf0"}, + {file = "coverage-7.6.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ab9763d291a17b527ac6fd11d1a9a9c358280adb320e9c2672a97af346ac2c"}, + {file = "coverage-7.6.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cf96ceaa275f071f1bea3067f8fd43bec184a25a962c754024c973af871e1b7"}, + {file = "coverage-7.6.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aee9cf6b0134d6f932d219ce253ef0e624f4fa588ee64830fcba193269e4daa3"}, + {file = "coverage-7.6.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2bc3e45c16564cc72de09e37413262b9f99167803e5e48c6156bccdfb22c8327"}, + {file = "coverage-7.6.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:623e6965dcf4e28a3debaa6fcf4b99ee06d27218f46d43befe4db1c70841551c"}, + {file = "coverage-7.6.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:850cfd2d6fc26f8346f422920ac204e1d28814e32e3a58c19c91980fa74d8289"}, + {file = "coverage-7.6.7-cp39-cp39-win32.whl", hash = "sha256:c296263093f099da4f51b3dff1eff5d4959b527d4f2f419e16508c5da9e15e8c"}, + {file = "coverage-7.6.7-cp39-cp39-win_amd64.whl", hash = "sha256:90746521206c88bdb305a4bf3342b1b7316ab80f804d40c536fc7d329301ee13"}, + {file = "coverage-7.6.7-pp39.pp310-none-any.whl", hash = "sha256:0ddcb70b3a3a57581b450571b31cb774f23eb9519c2aaa6176d3a84c9fc57671"}, + {file = "coverage-7.6.7.tar.gz", hash = "sha256:d79d4826e41441c9a118ff045e4bccb9fdbdcb1d02413e7ea6eb5c87b5439d24"}, ] [package.dependencies] @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "413424880ae8cc17f5a0254cf54d2c0a91a76c2efaf4bd6e7801366da1268f09" +content-hash = "ca795f1b31506a0e1cca079725ab940c7316482dbc973f8f5c050cfb2c5801e3" diff --git a/pyproject.toml b/pyproject.toml index a9e450dc24..83266bb4a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,7 +76,7 @@ tzlocal = "5.2" [tool.poetry.group.dev.dependencies] bandit = "1.7.10" black = "24.10.0" -coverage = "7.6.6" +coverage = "7.6.7" docker = "7.1.0" flake8 = "7.1.1" freezegun = "1.5.1" From 8ddb9fbb8420d4fe761ebd1947c1fb2e8162b615 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 11:27:20 -0400 Subject: [PATCH 44/55] chore(deps): bump boto3 from 1.35.60 to 1.35.63 (#5809) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 10 +++++----- pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 52bdcf54ff..92d1ae0130 100644 --- a/poetry.lock +++ b/poetry.lock @@ -759,17 +759,17 @@ files = [ [[package]] name = "boto3" -version = "1.35.60" +version = "1.35.63" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.60-py3-none-any.whl", hash = "sha256:a34d28de1a1f6ca6ec3edd05c26db16e422293d8f9dcd94f308059a434596753"}, - {file = "boto3-1.35.60.tar.gz", hash = "sha256:e573504c67c3e438fd4b0222119ed1a73b644c78eb3b6dee0b36a6c70ecf7677"}, + {file = "boto3-1.35.63-py3-none-any.whl", hash = "sha256:d0f938d4f6f392b6ffc5e75fff14a42e5bbb5228675a0367c8af55398abadbec"}, + {file = "boto3-1.35.63.tar.gz", hash = "sha256:deb593d9a0fb240deb4c43e4da8e6626d7c36be7b2fd2fe28f49d44d395b7de0"}, ] [package.dependencies] -botocore = ">=1.35.60,<1.36.0" +botocore = ">=1.35.63,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "ca795f1b31506a0e1cca079725ab940c7316482dbc973f8f5c050cfb2c5801e3" +content-hash = "221f02866e4ad60405ac3bfd61f98d2eab084e82478be7ac8f42439bf37afb3e" diff --git a/pyproject.toml b/pyproject.toml index 83266bb4a7..8a4c17a382 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ azure-mgmt-storage = "21.2.1" azure-mgmt-subscription = "3.1.1" azure-mgmt-web = "7.3.1" azure-storage-blob = "12.24.0" -boto3 = "1.35.60" +boto3 = "1.35.63" botocore = "1.35.63" colorama = "0.4.6" cryptography = "43.0.1" From c69571abcd0f9e328f6f6ac9b0a3739850f4ae3d Mon Sep 17 00:00:00 2001 From: Sergio Garcia Date: Mon, 18 Nov 2024 10:59:35 -0500 Subject: [PATCH 45/55] feat(aws): add new check `iam_root_credentials_management_enabled` (#5801) --- .../iam_no_root_access_key.py | 61 +++---- .../__init__.py | 0 ...edentials_management_enabled.metadata.json | 33 ++++ ...iam_root_credentials_management_enabled.py | 27 +++ .../iam_root_hardware_mfa_enabled.py | 51 +++--- .../iam_root_mfa_enabled.py | 32 ++-- .../providers/aws/services/iam/iam_service.py | 27 +++ ...nizations_account_part_of_organizations.py | 12 +- .../organizations_delegated_administrators.py | 34 ++-- ...rganizations_opt_out_ai_services_policy.py | 20 ++- .../organizations_scp_check_deny_regions.py | 30 ++-- .../organizations/organizations_service.py | 43 ++--- ...ions_tags_policies_enabled_and_attached.py | 24 +-- ...rtfolio_shared_within_organization_only.py | 36 ++-- .../iam_no_root_access_key_test.py | 29 ++-- ...oot_credentials_management_enabled_test.py | 154 ++++++++++++++++++ .../iam_root_hardware_mfa_enabled_test.py | 3 + ...zations_opt_out_ai_services_policy_test.py | 122 +++++++------- ...ganizations_scp_check_deny_regions_test.py | 6 +- .../organizations_service_test.py | 11 +- ...tags_policies_enabled_and_attached_test.py | 94 +++++------ 21 files changed, 541 insertions(+), 308 deletions(-) create mode 100644 prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/__init__.py create mode 100644 prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.metadata.json create mode 100644 prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.py create mode 100644 tests/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled_test.py diff --git a/prowler/providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key.py b/prowler/providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key.py index 3ff60c4276..365a9f2fda 100644 --- a/prowler/providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key.py +++ b/prowler/providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key.py @@ -5,35 +5,36 @@ from prowler.providers.aws.services.iam.iam_client import iam_client class iam_no_root_access_key(Check): def execute(self) -> Check_Report_AWS: findings = [] - response = iam_client.credential_report - - for user in response: - if user["user"] == "": - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_id = user["user"] - report.resource_arn = user["arn"] - if ( - user["access_key_1_active"] == "false" - and user["access_key_2_active"] == "false" - ): - report.status = "PASS" - report.status_extended = ( - f"User {user['user']} does not have access keys." - ) - elif ( - user["access_key_1_active"] == "true" - and user["access_key_2_active"] == "true" - ): - report.status = "FAIL" - report.status_extended = ( - f"User {user['user']} has two active access keys." - ) - else: - report.status = "FAIL" - report.status_extended = ( - f"User {user['user']} has one active access key." - ) - findings.append(report) + # Check if the root credentials are managed by AWS Organizations + if "RootCredentialsManagement" not in iam_client.organization_features: + for user in iam_client.credential_report: + if user["user"] == "": + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_id = user["user"] + report.resource_arn = user["arn"] + if ( + user["access_key_1_active"] == "false" + and user["access_key_2_active"] == "false" + ): + report.status = "PASS" + report.status_extended = ( + "Root account does not have access keys." + ) + elif ( + user["access_key_1_active"] == "true" + and user["access_key_2_active"] == "true" + ): + report.status = "FAIL" + report.status_extended = ( + "Root account has two active access keys." + ) + else: + report.status = "FAIL" + report.status_extended = ( + "Root account has one active access key." + ) + findings.append(report) + break return findings diff --git a/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/__init__.py b/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.metadata.json b/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.metadata.json new file mode 100644 index 0000000000..69a71f9510 --- /dev/null +++ b/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.metadata.json @@ -0,0 +1,33 @@ +{ + "Provider": "aws", + "CheckID": "iam_root_credentials_management_enabled", + "CheckTitle": "Ensure centralized root credentials management is enabled", + "CheckType": [], + "ServiceName": "iam", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id", + "Severity": "high", + "ResourceType": "Other", + "Description": "Checks if centralized management of root credentials for member accounts in AWS Organizations is enabled. This ensures that root credentials are managed centrally, reducing the risk of unauthorized access or mismanagement.", + "Risk": "Without centralized root credentials management, member accounts retain full control over their root user credentials, increasing the risk of credential misuse, mismanagement, or compromise.", + "RelatedUrl": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html#id_root-user-access-management", + "Remediation": { + "Code": { + "CLI": "aws iam enable-organizations-root-credentials-management", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable centralized management of root access for member accounts using the CLI or IAM console.", + "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-enable-root-access.html" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [ + "iam_root_hardware_mfa_enabled", + "iam_root_mfa_enabled" + ], + "Notes": "" +} diff --git a/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.py b/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.py new file mode 100644 index 0000000000..5895c0e08a --- /dev/null +++ b/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.py @@ -0,0 +1,27 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.iam.iam_client import iam_client +from prowler.providers.aws.services.organizations.organizations_client import ( + organizations_client, +) + + +class iam_root_credentials_management_enabled(Check): + def execute(self) -> Check_Report_AWS: + findings = [] + if ( + organizations_client.organization + and organizations_client.organization.status == "ACTIVE" + ): + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_arn = iam_client.audited_account_arn + report.resource_id = iam_client.audited_account + if "RootCredentialsManagement" in iam_client.organization_features: + report.status = "PASS" + report.status_extended = "Root credentials management is enabled." + else: + report.status = "FAIL" + report.status_extended = "Root credentials management is not enabled." + findings.append(report) + + return findings diff --git a/prowler/providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled.py b/prowler/providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled.py index 8497da78b9..284ad86b98 100644 --- a/prowler/providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled.py +++ b/prowler/providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled.py @@ -5,31 +5,36 @@ from prowler.providers.aws.services.iam.iam_client import iam_client class iam_root_hardware_mfa_enabled(Check): def execute(self) -> Check_Report_AWS: findings = [] - # This check is only avaible in Commercial Partition + # This check is only available in Commercial Partition if iam_client.audited_partition == "aws": - if iam_client.account_summary: - virtual_mfa = False - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_id = "" - report.resource_arn = iam_client.mfa_arn_template + # Check if the root credentials are managed by AWS Organizations + if "RootCredentialsManagement" not in iam_client.organization_features: + if iam_client.account_summary: + virtual_mfa = False + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_id = "" + report.resource_arn = iam_client.mfa_arn_template - if iam_client.account_summary["SummaryMap"]["AccountMFAEnabled"] > 0: - for mfa in iam_client.virtual_mfa_devices: - # If the ARN of the associated IAM user of the Virtual MFA device is "arn:aws:iam::[aws-account-id]:root", your AWS root account is not using a hardware-based MFA device for MFA protection. - if "root" in mfa.get("User", {}).get("Arn", ""): - virtual_mfa = True - report.status = "FAIL" - report.status_extended = "Root account has a virtual MFA instead of a hardware MFA device enabled." - if not virtual_mfa: - report.status = "PASS" - report.status_extended = ( - "Root account has a hardware MFA device enabled." - ) - else: - report.status = "FAIL" - report.status_extended = "MFA is not enabled for root account." + if ( + iam_client.account_summary["SummaryMap"]["AccountMFAEnabled"] + > 0 + ): + for mfa in iam_client.virtual_mfa_devices: + # If the ARN of the associated IAM user of the Virtual MFA device is "arn:aws:iam::[aws-account-id]:root", your AWS root account is not using a hardware-based MFA device for MFA protection. + if "root" in mfa.get("User", {}).get("Arn", ""): + virtual_mfa = True + report.status = "FAIL" + report.status_extended = "Root account has a virtual MFA instead of a hardware MFA device enabled." + if not virtual_mfa: + report.status = "PASS" + report.status_extended = ( + "Root account has a hardware MFA device enabled." + ) + else: + report.status = "FAIL" + report.status_extended = "MFA is not enabled for root account." - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/iam/iam_root_mfa_enabled/iam_root_mfa_enabled.py b/prowler/providers/aws/services/iam/iam_root_mfa_enabled/iam_root_mfa_enabled.py index d515e41190..2db6c4bfff 100644 --- a/prowler/providers/aws/services/iam/iam_root_mfa_enabled/iam_root_mfa_enabled.py +++ b/prowler/providers/aws/services/iam/iam_root_mfa_enabled/iam_root_mfa_enabled.py @@ -5,19 +5,23 @@ from prowler.providers.aws.services.iam.iam_client import iam_client class iam_root_mfa_enabled(Check): def execute(self) -> Check_Report_AWS: findings = [] - if iam_client.credential_report: - for user in iam_client.credential_report: - if user["user"] == "": - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_id = user["user"] - report.resource_arn = user["arn"] - if user["mfa_active"] == "false": - report.status = "FAIL" - report.status_extended = "MFA is not enabled for root account." - else: - report.status = "PASS" - report.status_extended = "MFA is enabled for root account." - findings.append(report) + # Check if the root credentials are managed by AWS Organizations + if "RootCredentialsManagement" not in iam_client.organization_features: + if iam_client.credential_report: + for user in iam_client.credential_report: + if user["user"] == "": + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_id = user["user"] + report.resource_arn = user["arn"] + if user["mfa_active"] == "false": + report.status = "FAIL" + report.status_extended = ( + "MFA is not enabled for root account." + ) + else: + report.status = "PASS" + report.status_extended = "MFA is enabled for root account." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/iam/iam_service.py b/prowler/providers/aws/services/iam/iam_service.py index b7f1ef0c3d..2fc4ef4795 100644 --- a/prowler/providers/aws/services/iam/iam_service.py +++ b/prowler/providers/aws/services/iam/iam_service.py @@ -102,6 +102,8 @@ class IAM(AWSService): self._get_last_accessed_services() self.user_temporary_credentials_usage = {} self._get_user_temporary_credentials_usage() + self.organization_features = [] + self._list_organizations_features() # List missing tags self.__threading_call__(self._list_tags, self.users) self.__threading_call__(self._list_tags, self.roles) @@ -964,6 +966,31 @@ class IAM(AWSService): f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) + def _list_organizations_features(self): + logger.info("IAM - List Organization Features...") + try: + organization_features = self.client.list_organizations_features() + self.organization_features = organization_features.get( + "EnabledFeatures", [] + ) + except ClientError as error: + if error.response["Error"]["Code"] == "OrganizationNotFoundException": + logger.warning( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + elif error.response["Error"]["Code"] == "ServiceAccessNotEnabledException": + logger.warning( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + else: + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + except Exception as error: + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + class MFADevice(BaseModel): serial_number: str diff --git a/prowler/providers/aws/services/organizations/organizations_account_part_of_organizations/organizations_account_part_of_organizations.py b/prowler/providers/aws/services/organizations/organizations_account_part_of_organizations/organizations_account_part_of_organizations.py index 6520405745..f3ff390429 100644 --- a/prowler/providers/aws/services/organizations/organizations_account_part_of_organizations/organizations_account_part_of_organizations.py +++ b/prowler/providers/aws/services/organizations/organizations_account_part_of_organizations/organizations_account_part_of_organizations.py @@ -7,21 +7,19 @@ from prowler.providers.aws.services.organizations.organizations_client import ( class organizations_account_part_of_organizations(Check): def execute(self): findings = [] - for org in organizations_client.organizations: + if organizations_client.organization: report = Check_Report_AWS(self.metadata()) - if org.status == "ACTIVE": + if organizations_client.organization.status == "ACTIVE": report.status = "PASS" - report.status_extended = ( - f"AWS Organization {org.id} contains this AWS account." - ) + report.status_extended = f"AWS Organization {organizations_client.organization.id} contains this AWS account." else: report.status = "FAIL" report.status_extended = ( "AWS Organizations is not in-use for this AWS Account." ) report.region = organizations_client.region - report.resource_id = org.id - report.resource_arn = org.arn + report.resource_id = organizations_client.organization.id + report.resource_arn = organizations_client.organization.arn findings.append(report) return findings diff --git a/prowler/providers/aws/services/organizations/organizations_delegated_administrators/organizations_delegated_administrators.py b/prowler/providers/aws/services/organizations/organizations_delegated_administrators/organizations_delegated_administrators.py index 326111c03b..043a087e3c 100644 --- a/prowler/providers/aws/services/organizations/organizations_delegated_administrators/organizations_delegated_administrators.py +++ b/prowler/providers/aws/services/organizations/organizations_delegated_administrators/organizations_delegated_administrators.py @@ -14,31 +14,33 @@ class organizations_delegated_administrators(Check): ) ) - for org in organizations_client.organizations: - if org.status == "ACTIVE": - report = Check_Report_AWS(self.metadata()) - report.resource_id = org.id - report.resource_arn = org.arn - report.region = organizations_client.region - if org.delegated_administrators is None: - # Access Denied to list_policies - continue - if org.delegated_administrators: - for delegated_administrator in org.delegated_administrators: + if ( + organizations_client.organization + and organizations_client.organization.status == "ACTIVE" + ): + report = Check_Report_AWS(self.metadata()) + report.resource_id = organizations_client.organization.id + report.resource_arn = organizations_client.organization.arn + report.region = organizations_client.region + if ( + organizations_client.organization.delegated_administrators is not None + ): # Check if Access Denied to list_delegated_administrators + if organizations_client.organization.delegated_administrators: + for ( + delegated_administrator + ) in organizations_client.organization.delegated_administrators: if ( delegated_administrator.id not in organizations_trusted_delegated_administrators ): report.status = "FAIL" - report.status_extended = f"AWS Organization {org.id} has an untrusted Delegated Administrator: {delegated_administrator.id}." + report.status_extended = f"AWS Organization {organizations_client.organization.id} has an untrusted Delegated Administrator: {delegated_administrator.id}." else: report.status = "PASS" - report.status_extended = f"AWS Organization {org.id} has a trusted Delegated Administrator: {delegated_administrator.id}." + report.status_extended = f"AWS Organization {organizations_client.organization.id} has a trusted Delegated Administrator: {delegated_administrator.id}." else: report.status = "PASS" - report.status_extended = ( - f"AWS Organization {org.id} has no Delegated Administrators." - ) + report.status_extended = f"AWS Organization {organizations_client.organization.id} has no Delegated Administrators." findings.append(report) diff --git a/prowler/providers/aws/services/organizations/organizations_opt_out_ai_services_policy/organizations_opt_out_ai_services_policy.py b/prowler/providers/aws/services/organizations/organizations_opt_out_ai_services_policy/organizations_opt_out_ai_services_policy.py index ca178aa826..3894f723eb 100644 --- a/prowler/providers/aws/services/organizations/organizations_opt_out_ai_services_policy/organizations_opt_out_ai_services_policy.py +++ b/prowler/providers/aws/services/organizations/organizations_opt_out_ai_services_policy/organizations_opt_out_ai_services_policy.py @@ -8,19 +8,23 @@ class organizations_opt_out_ai_services_policy(Check): def execute(self): findings = [] - for org in organizations_client.organizations: - if org.policies is not None: # Access Denied to list_policies + if organizations_client.organization: + if ( + organizations_client.organization.policies is not None + ): # Access Denied to list_policies report = Check_Report_AWS(self.metadata()) - report.resource_id = org.id - report.resource_arn = org.arn + report.resource_id = organizations_client.organization.id + report.resource_arn = organizations_client.organization.arn report.region = organizations_client.region report.status = "FAIL" report.status_extended = ( "AWS Organizations is not in-use for this AWS Account." ) - if org.status == "ACTIVE": - report.status_extended = f"AWS Organization {org.id} has not opted out of all AI services, granting consent for AWS to access its data." - for policy in org.policies.get("AISERVICES_OPT_OUT_POLICY", []): + if organizations_client.organization.status == "ACTIVE": + report.status_extended = f"AWS Organization {organizations_client.organization.id} has not opted out of all AI services, granting consent for AWS to access its data." + for policy in organizations_client.organization.policies.get( + "AISERVICES_OPT_OUT_POLICY", [] + ): if ( policy.content.get("services", {}) .get("default", {}) @@ -29,7 +33,7 @@ class organizations_opt_out_ai_services_policy(Check): == "optOut" ): report.status = "PASS" - report.status_extended = f"AWS Organization {org.id} has opted out of all AI services, not granting consent for AWS to access its data." + report.status_extended = f"AWS Organization {organizations_client.organization.id} has opted out of all AI services, not granting consent for AWS to access its data." break findings.append(report) diff --git a/prowler/providers/aws/services/organizations/organizations_scp_check_deny_regions/organizations_scp_check_deny_regions.py b/prowler/providers/aws/services/organizations/organizations_scp_check_deny_regions/organizations_scp_check_deny_regions.py index 39b341bc0b..c251019833 100644 --- a/prowler/providers/aws/services/organizations/organizations_scp_check_deny_regions/organizations_scp_check_deny_regions.py +++ b/prowler/providers/aws/services/organizations/organizations_scp_check_deny_regions/organizations_scp_check_deny_regions.py @@ -11,25 +11,27 @@ class organizations_scp_check_deny_regions(Check): "organizations_enabled_regions", [] ) - for org in organizations_client.organizations: - if org.policies is not None: # Access denied to list policies + if organizations_client.organization: + if ( + organizations_client.organization.policies is not None + ): # Access denied to list policies report = Check_Report_AWS(self.metadata()) - report.resource_id = org.id - report.resource_arn = org.arn + report.resource_id = organizations_client.organization.id + report.resource_arn = organizations_client.organization.arn report.region = organizations_client.region report.status = "FAIL" report.status_extended = ( "AWS Organizations is not in-use for this AWS Account." ) - if org.status == "ACTIVE": - report.status_extended = ( - f"AWS Organizations {org.id} does not have SCP policies." - ) + if organizations_client.organization.status == "ACTIVE": + report.status_extended = f"AWS Organizations {organizations_client.organization.id} does not have SCP policies." # We use this flag if we find a statement that is restricting regions but not all the configured ones: is_region_restricted_statement = False - for policy in org.policies.get("SERVICE_CONTROL_POLICY", []): + for policy in organizations_client.organization.policies.get( + "SERVICE_CONTROL_POLICY", [] + ): # Statements are not always list statements = policy.content.get("Statement") @@ -54,14 +56,14 @@ class organizations_scp_check_deny_regions(Check): ): # All defined regions are restricted, we exit here, no need to continue. report.status = "PASS" - report.status_extended = f"AWS Organization {org.id} has SCP policy {policy.id} restricting all configured regions found." + report.status_extended = f"AWS Organization {organizations_client.organization.id} has SCP policy {policy.id} restricting all configured regions found." findings.append(report) return findings else: # Regions are restricted, but not the ones defined, we keep this finding, but we continue analyzing: is_region_restricted_statement = True report.status = "FAIL" - report.status_extended = f"AWS Organization {org.id} has SCP policies {policy.id} restricting some AWS Regions, but not all the configured ones, please check config." + report.status_extended = f"AWS Organization {organizations_client.organization.id} has SCP policies {policy.id} restricting some AWS Regions, but not all the configured ones, please check config." # Allow if Condition = {"StringEquals": {"aws:RequestedRegion": [region1, region2]}} if ( @@ -80,18 +82,18 @@ class organizations_scp_check_deny_regions(Check): ): # All defined regions are restricted, we exit here, no need to continue. report.status = "PASS" - report.status_extended = f"AWS Organization {org.id} has SCP policy {policy.id} restricting all configured regions found." + report.status_extended = f"AWS Organization {organizations_client.organization.id} has SCP policy {policy.id} restricting all configured regions found." findings.append(report) return findings else: # Regions are restricted, but not the ones defined, we keep this finding, but we continue analyzing: is_region_restricted_statement = True report.status = "FAIL" - report.status_extended = f"AWS Organization {org.id} has SCP policies {policy.id} restricting some AWS Regions, but not all the configured ones, please check config." + report.status_extended = f"AWS Organization {organizations_client.organization.id} has SCP policies {policy.id} restricting some AWS Regions, but not all the configured ones, please check config." if not is_region_restricted_statement: report.status = "FAIL" - report.status_extended = f"AWS Organization {org.id} has SCP policies but don't restrict AWS Regions." + report.status_extended = f"AWS Organization {organizations_client.organization.id} has SCP policies but don't restrict AWS Regions." findings.append(report) diff --git a/prowler/providers/aws/services/organizations/organizations_service.py b/prowler/providers/aws/services/organizations/organizations_service.py index 98153ddaaa..32852df4d5 100644 --- a/prowler/providers/aws/services/organizations/organizations_service.py +++ b/prowler/providers/aws/services/organizations/organizations_service.py @@ -20,7 +20,7 @@ class Organizations(AWSService): def __init__(self, provider): # Call AWSService's __init__ super().__init__(__class__.__name__, provider) - self.organizations = [] + self.organization = None self.policies = {} self.delegated_administrators = [] self._describe_organization() @@ -43,13 +43,11 @@ class Organizations(AWSService): error.response["Error"]["Code"] == "AWSOrganizationsNotInUseException" ): - self.organizations.append( - Organization( - arn=self.audited_account_arn, - id="AWS Organization", - status="NOT_AVAILABLE", - master_id="", - ) + self.organization = Organization( + arn=self.audited_account_arn, + id="AWS Organization", + status="NOT_AVAILABLE", + master_id="", ) else: logger.error( @@ -59,25 +57,20 @@ class Organizations(AWSService): if not self.audit_resources or ( is_resource_filtered(organization_arn, self.audit_resources) ): - self.organizations.append( - Organization( - arn=organization_arn, - id=organization_id, - status="ACTIVE", - master_id=organization_master_id, - policies=organization_policies, - delegated_administrators=organization_delegated_administrator, - ) + self.organization = Organization( + arn=organization_arn, + id=organization_id, + status="ACTIVE", + master_id=organization_master_id, + policies=organization_policies, + delegated_administrators=organization_delegated_administrator, ) else: - # is filtered - self.organizations.append( - Organization( - arn=self.audited_account_arn, - id="AWS Organization", - status="NOT_AVAILABLE", - master_id="", - ) + self.organization = Organization( + arn=self.audited_account_arn, + id="AWS Organization", + status="NOT_AVAILABLE", + master_id="", ) except Exception as error: diff --git a/prowler/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached.py b/prowler/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached.py index 80d6b3e052..ce06740214 100644 --- a/prowler/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached.py +++ b/prowler/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached.py @@ -8,26 +8,28 @@ class organizations_tags_policies_enabled_and_attached(Check): def execute(self): findings = [] - for org in organizations_client.organizations: - if org.policies is not None: # Access Denied to list_policies + if organizations_client.organization: + if ( + organizations_client.organization.policies is not None + ): # Access Denied to list_policies report = Check_Report_AWS(self.metadata()) - report.resource_id = org.id - report.resource_arn = org.arn + report.resource_id = organizations_client.organization.id + report.resource_arn = organizations_client.organization.arn report.region = organizations_client.region report.status = "FAIL" report.status_extended = ( "AWS Organizations is not in-use for this AWS Account." ) - if org.status == "ACTIVE": - report.status_extended = ( - f"AWS Organizations {org.id} does not have tag policies." - ) - for policy in org.policies.get("TAG_POLICY", []): - report.status_extended = f"AWS Organization {org.id} has tag policies enabled but not attached." + if organizations_client.organization.status == "ACTIVE": + report.status_extended = f"AWS Organizations {organizations_client.organization.id} does not have tag policies." + for policy in organizations_client.organization.policies.get( + "TAG_POLICY", [] + ): + report.status_extended = f"AWS Organization {organizations_client.organization.id} has tag policies enabled but not attached." if policy.targets: report.status = "PASS" - report.status_extended = f"AWS Organization {org.id} has tag policies enabled and attached to an AWS account." + report.status_extended = f"AWS Organization {organizations_client.organization.id} has tag policies enabled and attached to an AWS account." findings.append(report) diff --git a/prowler/providers/aws/services/servicecatalog/servicecatalog_portfolio_shared_within_organization_only/servicecatalog_portfolio_shared_within_organization_only.py b/prowler/providers/aws/services/servicecatalog/servicecatalog_portfolio_shared_within_organization_only/servicecatalog_portfolio_shared_within_organization_only.py index b6390bacb2..38ffb7e31b 100644 --- a/prowler/providers/aws/services/servicecatalog/servicecatalog_portfolio_shared_within_organization_only/servicecatalog_portfolio_shared_within_organization_only.py +++ b/prowler/providers/aws/services/servicecatalog/servicecatalog_portfolio_shared_within_organization_only/servicecatalog_portfolio_shared_within_organization_only.py @@ -10,23 +10,25 @@ from prowler.providers.aws.services.servicecatalog.servicecatalog_client import class servicecatalog_portfolio_shared_within_organization_only(Check): def execute(self): findings = [] - for org in organizations_client.organizations: - if org.status == "ACTIVE": - for portfolio in servicecatalog_client.portfolios.values(): - if portfolio.shares is not None: - report = Check_Report_AWS(self.metadata()) - report.region = portfolio.region - report.resource_id = portfolio.id - report.resource_arn = portfolio.arn - report.resource_tags = portfolio.tags - report.status = "PASS" - report.status_extended = f"ServiceCatalog Portfolio {portfolio.name} is shared within your AWS Organization." - for portfolio_share in portfolio.shares: - if portfolio_share.type == "ACCOUNT": - report.status = "FAIL" - report.status_extended = f"ServiceCatalog Portfolio {portfolio.name} is shared with an account." - break + if ( + organizations_client.organization + and organizations_client.organization.status == "ACTIVE" + ): + for portfolio in servicecatalog_client.portfolios.values(): + if portfolio.shares is not None: + report = Check_Report_AWS(self.metadata()) + report.region = portfolio.region + report.resource_id = portfolio.id + report.resource_arn = portfolio.arn + report.resource_tags = portfolio.tags + report.status = "PASS" + report.status_extended = f"ServiceCatalog Portfolio {portfolio.name} is shared within your AWS Organization." + for portfolio_share in portfolio.shares: + if portfolio_share.type == "ACCOUNT": + report.status = "FAIL" + report.status_extended = f"ServiceCatalog Portfolio {portfolio.name} is shared with an account." + break - findings.append(report) + findings.append(report) return findings diff --git a/tests/providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key_test.py b/tests/providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key_test.py index 9f891f2b65..b2a9501e4b 100644 --- a/tests/providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key_test.py +++ b/tests/providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key_test.py @@ -1,4 +1,3 @@ -from re import search from unittest import mock from boto3 import client @@ -39,11 +38,10 @@ class Test_iam_no_root_access_key_test: check = iam_no_root_access_key() result = check.execute() - # raise Exception assert result[0].status == "PASS" - assert search( - "User does not have access keys.", - result[0].status_extended, + assert ( + result[0].status_extended + == "Root account does not have access keys." ) assert result[0].resource_id == "" assert ( @@ -82,11 +80,10 @@ class Test_iam_no_root_access_key_test: check = iam_no_root_access_key() result = check.execute() - # raise Exception assert result[0].status == "FAIL" - assert search( - "User has one active access key.", - result[0].status_extended, + assert ( + result[0].status_extended + == "Root account has one active access key." ) assert result[0].resource_id == "" assert ( @@ -125,11 +122,10 @@ class Test_iam_no_root_access_key_test: check = iam_no_root_access_key() result = check.execute() - # raise Exception assert result[0].status == "FAIL" - assert search( - "User has one active access key.", - result[0].status_extended, + assert ( + result[0].status_extended + == "Root account has one active access key." ) assert result[0].resource_id == "" assert ( @@ -168,11 +164,10 @@ class Test_iam_no_root_access_key_test: check = iam_no_root_access_key() result = check.execute() - # raise Exception assert result[0].status == "FAIL" - assert search( - "User has two active access key.", - result[0].status_extended, + assert ( + result[0].status_extended + == "Root account has two active access keys." ) assert result[0].resource_id == "" assert ( diff --git a/tests/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled_test.py b/tests/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled_test.py new file mode 100644 index 0000000000..e930e9a2db --- /dev/null +++ b/tests/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled_test.py @@ -0,0 +1,154 @@ +from unittest import mock + +import botocore +from boto3 import client +from moto import mock_aws + +from tests.providers.aws.utils import ( + AWS_ACCOUNT_ARN, + AWS_ACCOUNT_NUMBER, + AWS_REGION_US_EAST_1, + set_mocked_aws_provider, +) + +# Original botocore _make_api_call function +orig = botocore.client.BaseClient._make_api_call + + +# Mocked botocore _make_api_call function +def mock_make_api_call_enabled(self, operation_name, kwarg): + if operation_name == "ListOrganizationsFeatures": + return { + "OrganizationId": "o-test", + "EnabledFeatures": ["RootSessions", "RootCredentialsManagement"], + } + # If we don't want to patch the API call + return orig(self, operation_name, kwarg) + + +def mock_make_api_call_disabled(self, operation_name, kwarg): + if operation_name == "ListOrganizationsFeatures": + return {"OrganizationId": "o-test", "EnabledFeatures": []} + # If we don't want to patch the API call + return orig(self, operation_name, kwarg) + + +class Test_iam_root_credentials_management_enabled_test: + @mock_aws + def test_no_organization(self): + from prowler.providers.aws.services.iam.iam_service import IAM + from prowler.providers.aws.services.organizations.organizations_service import ( + Organizations, + ) + + aws_provider = set_mocked_aws_provider( + [AWS_REGION_US_EAST_1], create_default_organization=False + ) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.iam.iam_root_credentials_management_enabled.iam_root_credentials_management_enabled.iam_client", + new=IAM(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.iam.iam_root_credentials_management_enabled.iam_root_credentials_management_enabled.organizations_client", + new=Organizations(aws_provider), + ): + from prowler.providers.aws.services.iam.iam_root_credentials_management_enabled.iam_root_credentials_management_enabled import ( + iam_root_credentials_management_enabled, + ) + + check = iam_root_credentials_management_enabled() + result = check.execute() + + assert len(result) == 0 + + @mock.patch( + "botocore.client.BaseClient._make_api_call", new=mock_make_api_call_enabled + ) + @mock_aws + def test__root_credentials_management_enabled(self): + # Create Organization + conn = client("organizations") + conn.create_organization() + from prowler.providers.aws.services.iam.iam_service import IAM + from prowler.providers.aws.services.organizations.organizations_service import ( + Organizations, + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.iam.iam_root_credentials_management_enabled.iam_root_credentials_management_enabled.iam_client", + new=IAM(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.iam.iam_root_credentials_management_enabled.iam_root_credentials_management_enabled.organizations_client", + new=Organizations(aws_provider), + ): + from prowler.providers.aws.services.iam.iam_root_credentials_management_enabled.iam_root_credentials_management_enabled import ( + iam_root_credentials_management_enabled, + ) + + check = iam_root_credentials_management_enabled() + result = check.execute() + + assert len(result) == 1 + + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Root credentials management is enabled." + ) + assert result[0].resource_id == AWS_ACCOUNT_NUMBER + assert result[0].resource_arn == AWS_ACCOUNT_ARN + assert result[0].region == AWS_REGION_US_EAST_1 + + @mock.patch( + "botocore.client.BaseClient._make_api_call", new=mock_make_api_call_disabled + ) + @mock_aws + def test__root_credentials_management_disabled(self): + # Create Organization + conn = client("organizations") + conn.create_organization() + from prowler.providers.aws.services.iam.iam_service import IAM + from prowler.providers.aws.services.organizations.organizations_service import ( + Organizations, + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.iam.iam_root_credentials_management_enabled.iam_root_credentials_management_enabled.iam_client", + new=IAM(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.iam.iam_root_credentials_management_enabled.iam_root_credentials_management_enabled.organizations_client", + new=Organizations(aws_provider), + ): + from prowler.providers.aws.services.iam.iam_root_credentials_management_enabled.iam_root_credentials_management_enabled import ( + iam_root_credentials_management_enabled, + ) + + check = iam_root_credentials_management_enabled() + result = check.execute() + + assert len(result) == 1 + + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Root credentials management is not enabled." + ) + assert result[0].resource_id == AWS_ACCOUNT_NUMBER + assert result[0].resource_arn == AWS_ACCOUNT_ARN + assert result[0].region == AWS_REGION_US_EAST_1 diff --git a/tests/providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled_test.py b/tests/providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled_test.py index f2b39875c2..9c2bca4ff3 100644 --- a/tests/providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled_test.py +++ b/tests/providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled_test.py @@ -29,6 +29,7 @@ class Test_iam_root_hardware_mfa_enabled_test: iam_client.audited_partition = "aws" iam_client.region = AWS_REGION_US_EAST_1 iam_client.mfa_arn_template = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa" + iam_client.organization_features = [] aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) @@ -63,6 +64,7 @@ class Test_iam_root_hardware_mfa_enabled_test: iam_client.audited_partition = "aws" iam_client.region = AWS_REGION_US_EAST_1 iam_client.mfa_arn_template = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa" + iam_client.organization_features = [] aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) @@ -95,6 +97,7 @@ class Test_iam_root_hardware_mfa_enabled_test: iam_client.audited_partition = "aws" iam_client.region = AWS_REGION_US_EAST_1 iam_client.mfa_arn_template = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa" + iam_client.organization_features = [] aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) diff --git a/tests/providers/aws/services/organizations/organizations_opt_out_ai_services_policy/organizations_opt_out_ai_services_policy_test.py b/tests/providers/aws/services/organizations/organizations_opt_out_ai_services_policy/organizations_opt_out_ai_services_policy_test.py index 5d94c36300..0219041c5f 100644 --- a/tests/providers/aws/services/organizations/organizations_opt_out_ai_services_policy/organizations_opt_out_ai_services_policy_test.py +++ b/tests/providers/aws/services/organizations/organizations_opt_out_ai_services_policy/organizations_opt_out_ai_services_policy_test.py @@ -15,14 +15,12 @@ class Test_organizations_tags_policies_enabled_and_attached: def test_organization_no_organization(self): organizations_client = mock.MagicMock organizations_client.region = AWS_REGION_EU_WEST_1 - organizations_client.organizations = [ - Organization( - arn=AWS_ACCOUNT_ARN, - id="AWS Organization", - status="NOT_AVAILABLE", - master_id="", - ) - ] + organizations_client.organization = Organization( + arn=AWS_ACCOUNT_ARN, + id="AWS Organization", + status="NOT_AVAILABLE", + master_id="", + ) aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) @@ -55,16 +53,14 @@ class Test_organizations_tags_policies_enabled_and_attached: def test_organization_with_AI_optout_no_policies(self): organizations_client = mock.MagicMock organizations_client.region = AWS_REGION_EU_WEST_1 - organizations_client.organizations = [ - Organization( - id="o-1234567890", - arn="arn:aws:organizations::1234567890:organization/o-1234567890", - status="ACTIVE", - master_id="1234567890", - policies={"AISERVICES_OPT_OUT_POLICY": []}, - delegated_administrators=None, - ) - ] + organizations_client.organization = Organization( + id="o-1234567890", + arn="arn:aws:organizations::1234567890:organization/o-1234567890", + status="ACTIVE", + master_id="1234567890", + policies={"AISERVICES_OPT_OUT_POLICY": []}, + delegated_administrators=None, + ) aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) @@ -100,33 +96,29 @@ class Test_organizations_tags_policies_enabled_and_attached: def test_organization_with_AI_optout_policy(self): organizations_client = mock.MagicMock organizations_client.region = AWS_REGION_EU_WEST_1 - organizations_client.organizations = [ - Organization( - id="o-1234567890", - arn="arn:aws:organizations::1234567890:organization/o-1234567890", - status="ACTIVE", - master_id="1234567890", - policies={ - "AISERVICES_OPT_OUT_POLICY": [ - Policy( - id="p-1234567890", - arn="arn:aws:organizations::1234567890:policy/o-1234567890/p-1234567890", - type="AISERVICES_OPT_OUT_POLICY", - aws_managed=False, - content={ - "services": { - "default": { - "opt_out_policy": {"@@assign": "optOut"} - } - } - }, - targets=[], - ) - ] - }, - delegated_administrators=None, - ) - ] + organizations_client.organization = Organization( + id="o-1234567890", + arn="arn:aws:organizations::1234567890:organization/o-1234567890", + status="ACTIVE", + master_id="1234567890", + policies={ + "AISERVICES_OPT_OUT_POLICY": [ + Policy( + id="p-1234567890", + arn="arn:aws:organizations::1234567890:policy/o-1234567890/p-1234567890", + type="AISERVICES_OPT_OUT_POLICY", + aws_managed=False, + content={ + "services": { + "default": {"opt_out_policy": {"@@assign": "optOut"}} + } + }, + targets=[], + ) + ] + }, + delegated_administrators=None, + ) aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) @@ -162,27 +154,25 @@ class Test_organizations_tags_policies_enabled_and_attached: def test_organization_with_AI_optout_policy_no_content(self): organizations_client = mock.MagicMock organizations_client.region = AWS_REGION_EU_WEST_1 - organizations_client.organizations = [ - Organization( - id="o-1234567890", - arn="arn:aws:organizations::1234567890:organization/o-1234567890", - status="ACTIVE", - master_id="1234567890", - policies={ - "AISERVICES_OPT_OUT_POLICY": [ - Policy( - id="p-1234567890", - arn="arn:aws:organizations::1234567890:policy/o-1234567890/p-1234567890", - type="AISERVICES_OPT_OUT_POLICY", - aws_managed=False, - content={}, - targets=[], - ) - ] - }, - delegated_administrators=None, - ) - ] + organizations_client.organization = Organization( + id="o-1234567890", + arn="arn:aws:organizations::1234567890:organization/o-1234567890", + status="ACTIVE", + master_id="1234567890", + policies={ + "AISERVICES_OPT_OUT_POLICY": [ + Policy( + id="p-1234567890", + arn="arn:aws:organizations::1234567890:policy/o-1234567890/p-1234567890", + type="AISERVICES_OPT_OUT_POLICY", + aws_managed=False, + content={}, + targets=[], + ) + ] + }, + delegated_administrators=None, + ) aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) diff --git a/tests/providers/aws/services/organizations/organizations_scp_check_deny_regions/organizations_scp_check_deny_regions_test.py b/tests/providers/aws/services/organizations/organizations_scp_check_deny_regions/organizations_scp_check_deny_regions_test.py index 645c0c9170..85c910b4d0 100644 --- a/tests/providers/aws/services/organizations/organizations_scp_check_deny_regions/organizations_scp_check_deny_regions_test.py +++ b/tests/providers/aws/services/organizations/organizations_scp_check_deny_regions/organizations_scp_check_deny_regions_test.py @@ -247,7 +247,7 @@ class Test_organizations_scp_check_deny_regions: # Create Organization conn = client("organizations", region_name=AWS_REGION_EU_WEST_1) response = conn.create_organization() - org_arn = response["Organization"]["Arn"] + response["Organization"]["Arn"] with mock.patch( "prowler.providers.common.provider.Provider.get_global_provider", @@ -262,9 +262,7 @@ class Test_organizations_scp_check_deny_regions: organizations_scp_check_deny_regions, ) - for org in organizations_client.organizations: - if org.arn == org_arn: - org.policies = None + organizations_client.organization.policies = None check = organizations_scp_check_deny_regions() result = check.execute() diff --git a/tests/providers/aws/services/organizations/organizations_service_test.py b/tests/providers/aws/services/organizations/organizations_service_test.py index dfae12d402..8f6b56fb99 100644 --- a/tests/providers/aws/services/organizations/organizations_service_test.py +++ b/tests/providers/aws/services/organizations/organizations_service_test.py @@ -28,15 +28,14 @@ class Test_Organizations_Service: [AWS_REGION_EU_WEST_1], create_default_organization=False ) organizations = Organizations(aws_provider) - assert len(organizations.organizations) == 1 - assert organizations.organizations[0].arn == response["Organization"]["Arn"] - assert organizations.organizations[0].id == response["Organization"]["Id"] + assert organizations.organization.arn == response["Organization"]["Arn"] + assert organizations.organization.id == response["Organization"]["Id"] assert ( - organizations.organizations[0].master_id + organizations.organization.master_id == response["Organization"]["MasterAccountId"] ) - assert organizations.organizations[0].status == "ACTIVE" - assert organizations.organizations[0].delegated_administrators == [] + assert organizations.organization.status == "ACTIVE" + assert organizations.organization.delegated_administrators == [] @mock_aws def test_list_policies(self): diff --git a/tests/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached_test.py b/tests/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached_test.py index 9a90c9f61c..4c2cf4def7 100644 --- a/tests/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached_test.py +++ b/tests/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached_test.py @@ -15,14 +15,12 @@ class Test_organizations_tags_policies_enabled_and_attached: def test_organization_no_organization(self): organizations_client = mock.MagicMock organizations_client.region = AWS_REGION_EU_WEST_1 - organizations_client.organizations = [ - Organization( - arn=AWS_ACCOUNT_ARN, - id="AWS Organization", - status="NOT_AVAILABLE", - master_id="", - ) - ] + organizations_client.organization = Organization( + arn=AWS_ACCOUNT_ARN, + id="AWS Organization", + status="NOT_AVAILABLE", + master_id="", + ) aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) @@ -55,27 +53,25 @@ class Test_organizations_tags_policies_enabled_and_attached: def test_organization_with_tag_policies_not_attached(self): organizations_client = mock.MagicMock organizations_client.region = AWS_REGION_EU_WEST_1 - organizations_client.organizations = [ - Organization( - id="o-1234567890", - arn="arn:aws:organizations::1234567890:organization/o-1234567890", - status="ACTIVE", - master_id="1234567890", - policies={ - "TAG_POLICY": [ - Policy( - id="p-1234567890", - arn="arn:aws:organizations::1234567890:policy/o-1234567890/p-1234567890", - type="TAG_POLICY", - aws_managed=False, - content={"tags": {"Owner": {}}}, - targets=[], - ) - ] - }, - delegated_administrators=None, - ) - ] + organizations_client.organization = Organization( + id="o-1234567890", + arn="arn:aws:organizations::1234567890:organization/o-1234567890", + status="ACTIVE", + master_id="1234567890", + policies={ + "TAG_POLICY": [ + Policy( + id="p-1234567890", + arn="arn:aws:organizations::1234567890:policy/o-1234567890/p-1234567890", + type="TAG_POLICY", + aws_managed=False, + content={"tags": {"Owner": {}}}, + targets=[], + ) + ] + }, + delegated_administrators=None, + ) aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) @@ -111,27 +107,25 @@ class Test_organizations_tags_policies_enabled_and_attached: def test_organization_with_tag_policies_attached(self): organizations_client = mock.MagicMock organizations_client.region = AWS_REGION_EU_WEST_1 - organizations_client.organizations = [ - Organization( - id="o-1234567890", - arn="arn:aws:organizations::1234567890:organization/o-1234567890", - status="ACTIVE", - master_id="1234567890", - policies={ - "TAG_POLICY": [ - Policy( - id="p-1234567890", - arn="arn:aws:organizations::1234567890:policy/o-1234567890/p-1234567890", - type="TAG_POLICY", - aws_managed=False, - content={"tags": {"Owner": {}}}, - targets=["1234567890"], - ) - ] - }, - delegated_administrators=None, - ) - ] + organizations_client.organization = Organization( + id="o-1234567890", + arn="arn:aws:organizations::1234567890:organization/o-1234567890", + status="ACTIVE", + master_id="1234567890", + policies={ + "TAG_POLICY": [ + Policy( + id="p-1234567890", + arn="arn:aws:organizations::1234567890:policy/o-1234567890/p-1234567890", + type="TAG_POLICY", + aws_managed=False, + content={"tags": {"Owner": {}}}, + targets=["1234567890"], + ) + ] + }, + delegated_administrators=None, + ) aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) From 572d5a1f2e2bd336c40721324a159f18fd413a58 Mon Sep 17 00:00:00 2001 From: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> Date: Mon, 18 Nov 2024 17:09:23 +0100 Subject: [PATCH 46/55] fix(wafv2): only list resources for regional Web ACLs (#5811) --- prowler/providers/aws/services/wafv2/wafv2_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler/providers/aws/services/wafv2/wafv2_service.py b/prowler/providers/aws/services/wafv2/wafv2_service.py index 4971f1c422..85feed76f6 100644 --- a/prowler/providers/aws/services/wafv2/wafv2_service.py +++ b/prowler/providers/aws/services/wafv2/wafv2_service.py @@ -99,7 +99,7 @@ class WAFv2(AWSService): def _list_resources_for_web_acl(self, acl): logger.info("WAFv2 - Describing resources...") try: - if acl.scope == Scope.REGIONAL or acl.region in self.regional_clients: + if acl.scope == Scope.REGIONAL: for resource in self.regional_clients[ acl.region ].list_resources_for_web_acl( From f01ce849dcd00a23e5ddd675c20e0375fbf9745b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 18:35:48 -0400 Subject: [PATCH 47/55] chore(deps): bump aiohttp from 3.10.10 to 3.10.11 (#5815) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 186 ++++++++++++++++++++++++++-------------------------- 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/poetry.lock b/poetry.lock index 92d1ae0130..cfef7fd382 100644 --- a/poetry.lock +++ b/poetry.lock @@ -24,108 +24,108 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.10" +version = "3.10.11" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be7443669ae9c016b71f402e43208e13ddf00912f47f623ee5994e12fc7d4b3f"}, - {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b06b7843929e41a94ea09eb1ce3927865387e3e23ebe108e0d0d09b08d25be9"}, - {file = "aiohttp-3.10.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:333cf6cf8e65f6a1e06e9eb3e643a0c515bb850d470902274239fea02033e9a8"}, - {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:274cfa632350225ce3fdeb318c23b4a10ec25c0e2c880eff951a3842cf358ac1"}, - {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9e5e4a85bdb56d224f412d9c98ae4cbd032cc4f3161818f692cd81766eee65a"}, - {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b606353da03edcc71130b52388d25f9a30a126e04caef1fd637e31683033abd"}, - {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026"}, - {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:578a4b875af3e0daaf1ac6fa983d93e0bbfec3ead753b6d6f33d467100cdc67b"}, - {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8105fd8a890df77b76dd3054cddf01a879fc13e8af576805d667e0fa0224c35d"}, - {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3bcd391d083f636c06a68715e69467963d1f9600f85ef556ea82e9ef25f043f7"}, - {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fbc6264158392bad9df19537e872d476f7c57adf718944cc1e4495cbabf38e2a"}, - {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e48d5021a84d341bcaf95c8460b152cfbad770d28e5fe14a768988c461b821bc"}, - {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2609e9ab08474702cc67b7702dbb8a80e392c54613ebe80db7e8dbdb79837c68"}, - {file = "aiohttp-3.10.10-cp310-cp310-win32.whl", hash = "sha256:84afcdea18eda514c25bc68b9af2a2b1adea7c08899175a51fe7c4fb6d551257"}, - {file = "aiohttp-3.10.10-cp310-cp310-win_amd64.whl", hash = "sha256:9c72109213eb9d3874f7ac8c0c5fa90e072d678e117d9061c06e30c85b4cf0e6"}, - {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c30a0eafc89d28e7f959281b58198a9fa5e99405f716c0289b7892ca345fe45f"}, - {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:258c5dd01afc10015866114e210fb7365f0d02d9d059c3c3415382ab633fcbcb"}, - {file = "aiohttp-3.10.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:15ecd889a709b0080f02721255b3f80bb261c2293d3c748151274dfea93ac871"}, - {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3935f82f6f4a3820270842e90456ebad3af15810cf65932bd24da4463bc0a4c"}, - {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:413251f6fcf552a33c981c4709a6bba37b12710982fec8e558ae944bfb2abd38"}, - {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1720b4f14c78a3089562b8875b53e36b51c97c51adc53325a69b79b4b48ebcb"}, - {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:679abe5d3858b33c2cf74faec299fda60ea9de62916e8b67e625d65bf069a3b7"}, - {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79019094f87c9fb44f8d769e41dbb664d6e8fcfd62f665ccce36762deaa0e911"}, - {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092"}, - {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a3f00003de6eba42d6e94fabb4125600d6e484846dbf90ea8e48a800430cc142"}, - {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbb122c557a16fafc10354b9d99ebf2f2808a660d78202f10ba9d50786384b9"}, - {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:30ca7c3b94708a9d7ae76ff281b2f47d8eaf2579cd05971b5dc681db8caac6e1"}, - {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df9270660711670e68803107d55c2b5949c2e0f2e4896da176e1ecfc068b974a"}, - {file = "aiohttp-3.10.10-cp311-cp311-win32.whl", hash = "sha256:aafc8ee9b742ce75044ae9a4d3e60e3d918d15a4c2e08a6c3c3e38fa59b92d94"}, - {file = "aiohttp-3.10.10-cp311-cp311-win_amd64.whl", hash = "sha256:362f641f9071e5f3ee6f8e7d37d5ed0d95aae656adf4ef578313ee585b585959"}, - {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9294bbb581f92770e6ed5c19559e1e99255e4ca604a22c5c6397b2f9dd3ee42c"}, - {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a8fa23fe62c436ccf23ff930149c047f060c7126eae3ccea005f0483f27b2e28"}, - {file = "aiohttp-3.10.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c6a5b8c7926ba5d8545c7dd22961a107526562da31a7a32fa2456baf040939f"}, - {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:007ec22fbc573e5eb2fb7dec4198ef8f6bf2fe4ce20020798b2eb5d0abda6138"}, - {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9627cc1a10c8c409b5822a92d57a77f383b554463d1884008e051c32ab1b3742"}, - {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50edbcad60d8f0e3eccc68da67f37268b5144ecc34d59f27a02f9611c1d4eec7"}, - {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a45d85cf20b5e0d0aa5a8dca27cce8eddef3292bc29d72dcad1641f4ed50aa16"}, - {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b00807e2605f16e1e198f33a53ce3c4523114059b0c09c337209ae55e3823a8"}, - {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f2d4324a98062be0525d16f768a03e0bbb3b9fe301ceee99611dc9a7953124e6"}, - {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:438cd072f75bb6612f2aca29f8bd7cdf6e35e8f160bc312e49fbecab77c99e3a"}, - {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:baa42524a82f75303f714108fea528ccacf0386af429b69fff141ffef1c534f9"}, - {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a7d8d14fe962153fc681f6366bdec33d4356f98a3e3567782aac1b6e0e40109a"}, - {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205"}, - {file = "aiohttp-3.10.10-cp312-cp312-win32.whl", hash = "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628"}, - {file = "aiohttp-3.10.10-cp312-cp312-win_amd64.whl", hash = "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf"}, - {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ad7593bb24b2ab09e65e8a1d385606f0f47c65b5a2ae6c551db67d6653e78c28"}, - {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1eb89d3d29adaf533588f209768a9c02e44e4baf832b08118749c5fad191781d"}, - {file = "aiohttp-3.10.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3fe407bf93533a6fa82dece0e74dbcaaf5d684e5a51862887f9eaebe6372cd79"}, - {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50aed5155f819873d23520919e16703fc8925e509abbb1a1491b0087d1cd969e"}, - {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f05e9727ce409358baa615dbeb9b969db94324a79b5a5cea45d39bdb01d82e6"}, - {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dffb610a30d643983aeb185ce134f97f290f8935f0abccdd32c77bed9388b42"}, - {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa6658732517ddabe22c9036479eabce6036655ba87a0224c612e1ae6af2087e"}, - {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:741a46d58677d8c733175d7e5aa618d277cd9d880301a380fd296975a9cdd7bc"}, - {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e00e3505cd80440f6c98c6d69269dcc2a119f86ad0a9fd70bccc59504bebd68a"}, - {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ffe595f10566f8276b76dc3a11ae4bb7eba1aac8ddd75811736a15b0d5311414"}, - {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdfcf6443637c148c4e1a20c48c566aa694fa5e288d34b20fcdc58507882fed3"}, - {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d183cf9c797a5291e8301790ed6d053480ed94070637bfaad914dd38b0981f67"}, - {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:77abf6665ae54000b98b3c742bc6ea1d1fb31c394bcabf8b5d2c1ac3ebfe7f3b"}, - {file = "aiohttp-3.10.10-cp313-cp313-win32.whl", hash = "sha256:4470c73c12cd9109db8277287d11f9dd98f77fc54155fc71a7738a83ffcc8ea8"}, - {file = "aiohttp-3.10.10-cp313-cp313-win_amd64.whl", hash = "sha256:486f7aabfa292719a2753c016cc3a8f8172965cabb3ea2e7f7436c7f5a22a151"}, - {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1b66ccafef7336a1e1f0e389901f60c1d920102315a56df85e49552308fc0486"}, - {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:acd48d5b80ee80f9432a165c0ac8cbf9253eaddb6113269a5e18699b33958dbb"}, - {file = "aiohttp-3.10.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3455522392fb15ff549d92fbf4b73b559d5e43dc522588f7eb3e54c3f38beee7"}, - {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c3b868724137f713a38376fef8120c166d1eadd50da1855c112fe97954aed8"}, - {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:da1dee8948d2137bb51fbb8a53cce6b1bcc86003c6b42565f008438b806cccd8"}, - {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5ce2ce7c997e1971b7184ee37deb6ea9922ef5163c6ee5aa3c274b05f9e12fa"}, - {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28529e08fde6f12eba8677f5a8608500ed33c086f974de68cc65ab218713a59d"}, - {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7db54c7914cc99d901d93a34704833568d86c20925b2762f9fa779f9cd2e70f"}, - {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03a42ac7895406220124c88911ebee31ba8b2d24c98507f4a8bf826b2937c7f2"}, - {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:7e338c0523d024fad378b376a79faff37fafb3c001872a618cde1d322400a572"}, - {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:038f514fe39e235e9fef6717fbf944057bfa24f9b3db9ee551a7ecf584b5b480"}, - {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:64f6c17757251e2b8d885d728b6433d9d970573586a78b78ba8929b0f41d045a"}, - {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:93429602396f3383a797a2a70e5f1de5df8e35535d7806c9f91df06f297e109b"}, - {file = "aiohttp-3.10.10-cp38-cp38-win32.whl", hash = "sha256:c823bc3971c44ab93e611ab1a46b1eafeae474c0c844aff4b7474287b75fe49c"}, - {file = "aiohttp-3.10.10-cp38-cp38-win_amd64.whl", hash = "sha256:54ca74df1be3c7ca1cf7f4c971c79c2daf48d9aa65dea1a662ae18926f5bc8ce"}, - {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01948b1d570f83ee7bbf5a60ea2375a89dfb09fd419170e7f5af029510033d24"}, - {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9fc1500fd2a952c5c8e3b29aaf7e3cc6e27e9cfc0a8819b3bce48cc1b849e4cc"}, - {file = "aiohttp-3.10.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f614ab0c76397661b90b6851a030004dac502e48260ea10f2441abd2207fbcc7"}, - {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00819de9e45d42584bed046314c40ea7e9aea95411b38971082cad449392b08c"}, - {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05646ebe6b94cc93407b3bf34b9eb26c20722384d068eb7339de802154d61bc5"}, - {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:998f3bd3cfc95e9424a6acd7840cbdd39e45bc09ef87533c006f94ac47296090"}, - {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9010c31cd6fa59438da4e58a7f19e4753f7f264300cd152e7f90d4602449762"}, - {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ea7ffc6d6d6f8a11e6f40091a1040995cdff02cfc9ba4c2f30a516cb2633554"}, - {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ef9c33cc5cbca35808f6c74be11eb7f5f6b14d2311be84a15b594bd3e58b5527"}, - {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ce0cdc074d540265bfeb31336e678b4e37316849d13b308607efa527e981f5c2"}, - {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:597a079284b7ee65ee102bc3a6ea226a37d2b96d0418cc9047490f231dc09fe8"}, - {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7789050d9e5d0c309c706953e5e8876e38662d57d45f936902e176d19f1c58ab"}, - {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e7f8b04d83483577fd9200461b057c9f14ced334dcb053090cea1da9c8321a91"}, - {file = "aiohttp-3.10.10-cp39-cp39-win32.whl", hash = "sha256:c02a30b904282777d872266b87b20ed8cc0d1501855e27f831320f471d54d983"}, - {file = "aiohttp-3.10.10-cp39-cp39-win_amd64.whl", hash = "sha256:edfe3341033a6b53a5c522c802deb2079eee5cbfbb0af032a55064bd65c73a23"}, - {file = "aiohttp-3.10.10.tar.gz", hash = "sha256:0631dd7c9f0822cc61c88586ca76d5b5ada26538097d0f1df510b082bad3411a"}, + {file = "aiohttp-3.10.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5077b1a5f40ffa3ba1f40d537d3bec4383988ee51fbba6b74aa8fb1bc466599e"}, + {file = "aiohttp-3.10.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8d6a14a4d93b5b3c2891fca94fa9d41b2322a68194422bef0dd5ec1e57d7d298"}, + {file = "aiohttp-3.10.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffbfde2443696345e23a3c597049b1dd43049bb65337837574205e7368472177"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20b3d9e416774d41813bc02fdc0663379c01817b0874b932b81c7f777f67b217"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b943011b45ee6bf74b22245c6faab736363678e910504dd7531a58c76c9015a"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48bc1d924490f0d0b3658fe5c4b081a4d56ebb58af80a6729d4bd13ea569797a"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e12eb3f4b1f72aaaf6acd27d045753b18101524f72ae071ae1c91c1cd44ef115"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f14ebc419a568c2eff3c1ed35f634435c24ead2fe19c07426af41e7adb68713a"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:72b191cdf35a518bfc7ca87d770d30941decc5aaf897ec8b484eb5cc8c7706f3"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5ab2328a61fdc86424ee540d0aeb8b73bbcad7351fb7cf7a6546fc0bcffa0038"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa93063d4af05c49276cf14e419550a3f45258b6b9d1f16403e777f1addf4519"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:30283f9d0ce420363c24c5c2421e71a738a2155f10adbb1a11a4d4d6d2715cfc"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e5358addc8044ee49143c546d2182c15b4ac3a60be01c3209374ace05af5733d"}, + {file = "aiohttp-3.10.11-cp310-cp310-win32.whl", hash = "sha256:e1ffa713d3ea7cdcd4aea9cddccab41edf6882fa9552940344c44e59652e1120"}, + {file = "aiohttp-3.10.11-cp310-cp310-win_amd64.whl", hash = "sha256:778cbd01f18ff78b5dd23c77eb82987ee4ba23408cbed233009fd570dda7e674"}, + {file = "aiohttp-3.10.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:80ff08556c7f59a7972b1e8919f62e9c069c33566a6d28586771711e0eea4f07"}, + {file = "aiohttp-3.10.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c8f96e9ee19f04c4914e4e7a42a60861066d3e1abf05c726f38d9d0a466e695"}, + {file = "aiohttp-3.10.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fb8601394d537da9221947b5d6e62b064c9a43e88a1ecd7414d21a1a6fba9c24"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ea224cf7bc2d8856d6971cea73b1d50c9c51d36971faf1abc169a0d5f85a382"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db9503f79e12d5d80b3efd4d01312853565c05367493379df76d2674af881caa"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0f449a50cc33f0384f633894d8d3cd020e3ccef81879c6e6245c3c375c448625"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82052be3e6d9e0c123499127782a01a2b224b8af8c62ab46b3f6197035ad94e9"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20063c7acf1eec550c8eb098deb5ed9e1bb0521613b03bb93644b810986027ac"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:489cced07a4c11488f47aab1f00d0c572506883f877af100a38f1fedaa884c3a"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ea9b3bab329aeaa603ed3bf605f1e2a6f36496ad7e0e1aa42025f368ee2dc07b"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ca117819d8ad113413016cb29774b3f6d99ad23c220069789fc050267b786c16"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2dfb612dcbe70fb7cdcf3499e8d483079b89749c857a8f6e80263b021745c730"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9b615d3da0d60e7d53c62e22b4fd1c70f4ae5993a44687b011ea3a2e49051b8"}, + {file = "aiohttp-3.10.11-cp311-cp311-win32.whl", hash = "sha256:29103f9099b6068bbdf44d6a3d090e0a0b2be6d3c9f16a070dd9d0d910ec08f9"}, + {file = "aiohttp-3.10.11-cp311-cp311-win_amd64.whl", hash = "sha256:236b28ceb79532da85d59aa9b9bf873b364e27a0acb2ceaba475dc61cffb6f3f"}, + {file = "aiohttp-3.10.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7480519f70e32bfb101d71fb9a1f330fbd291655a4c1c922232a48c458c52710"}, + {file = "aiohttp-3.10.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f65267266c9aeb2287a6622ee2bb39490292552f9fbf851baabc04c9f84e048d"}, + {file = "aiohttp-3.10.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7400a93d629a0608dc1d6c55f1e3d6e07f7375745aaa8bd7f085571e4d1cee97"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f34b97e4b11b8d4eb2c3a4f975be626cc8af99ff479da7de49ac2c6d02d35725"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e7b825da878464a252ccff2958838f9caa82f32a8dbc334eb9b34a026e2c636"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9f92a344c50b9667827da308473005f34767b6a2a60d9acff56ae94f895f385"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f1ab987a27b83c5268a17218463c2ec08dbb754195113867a27b166cd6087"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1dc0f4ca54842173d03322793ebcf2c8cc2d34ae91cc762478e295d8e361e03f"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7ce6a51469bfaacff146e59e7fb61c9c23006495d11cc24c514a455032bcfa03"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:aad3cd91d484d065ede16f3cf15408254e2469e3f613b241a1db552c5eb7ab7d"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f4df4b8ca97f658c880fb4b90b1d1ec528315d4030af1ec763247ebfd33d8b9a"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2e4e18a0a2d03531edbc06c366954e40a3f8d2a88d2b936bbe78a0c75a3aab3e"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ce66780fa1a20e45bc753cda2a149daa6dbf1561fc1289fa0c308391c7bc0a4"}, + {file = "aiohttp-3.10.11-cp312-cp312-win32.whl", hash = "sha256:a919c8957695ea4c0e7a3e8d16494e3477b86f33067478f43106921c2fef15bb"}, + {file = "aiohttp-3.10.11-cp312-cp312-win_amd64.whl", hash = "sha256:b5e29706e6389a2283a91611c91bf24f218962717c8f3b4e528ef529d112ee27"}, + {file = "aiohttp-3.10.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:703938e22434d7d14ec22f9f310559331f455018389222eed132808cd8f44127"}, + {file = "aiohttp-3.10.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9bc50b63648840854e00084c2b43035a62e033cb9b06d8c22b409d56eb098413"}, + {file = "aiohttp-3.10.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f0463bf8b0754bc744e1feb61590706823795041e63edf30118a6f0bf577461"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6c6dec398ac5a87cb3a407b068e1106b20ef001c344e34154616183fe684288"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcaf2d79104d53d4dcf934f7ce76d3d155302d07dae24dff6c9fffd217568067"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25fd5470922091b5a9aeeb7e75be609e16b4fba81cdeaf12981393fb240dd10e"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbde2ca67230923a42161b1f408c3992ae6e0be782dca0c44cb3206bf330dee1"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:249c8ff8d26a8b41a0f12f9df804e7c685ca35a207e2410adbd3e924217b9006"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:878ca6a931ee8c486a8f7b432b65431d095c522cbeb34892bee5be97b3481d0f"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8663f7777ce775f0413324be0d96d9730959b2ca73d9b7e2c2c90539139cbdd6"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6cd3f10b01f0c31481fba8d302b61603a2acb37b9d30e1d14e0f5a58b7b18a31"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e8d8aad9402d3aa02fdc5ca2fe68bcb9fdfe1f77b40b10410a94c7f408b664d"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:38e3c4f80196b4f6c3a85d134a534a56f52da9cb8d8e7af1b79a32eefee73a00"}, + {file = "aiohttp-3.10.11-cp313-cp313-win32.whl", hash = "sha256:fc31820cfc3b2863c6e95e14fcf815dc7afe52480b4dc03393c4873bb5599f71"}, + {file = "aiohttp-3.10.11-cp313-cp313-win_amd64.whl", hash = "sha256:4996ff1345704ffdd6d75fb06ed175938c133425af616142e7187f28dc75f14e"}, + {file = "aiohttp-3.10.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:74baf1a7d948b3d640badeac333af581a367ab916b37e44cf90a0334157cdfd2"}, + {file = "aiohttp-3.10.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:473aebc3b871646e1940c05268d451f2543a1d209f47035b594b9d4e91ce8339"}, + {file = "aiohttp-3.10.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c2f746a6968c54ab2186574e15c3f14f3e7f67aef12b761e043b33b89c5b5f95"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d110cabad8360ffa0dec8f6ec60e43286e9d251e77db4763a87dcfe55b4adb92"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0099c7d5d7afff4202a0c670e5b723f7718810000b4abcbc96b064129e64bc7"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0316e624b754dbbf8c872b62fe6dcb395ef20c70e59890dfa0de9eafccd2849d"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a5f7ab8baf13314e6b2485965cbacb94afff1e93466ac4d06a47a81c50f9cca"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c891011e76041e6508cbfc469dd1a8ea09bc24e87e4c204e05f150c4c455a5fa"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9208299251370ee815473270c52cd3f7069ee9ed348d941d574d1457d2c73e8b"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:459f0f32c8356e8125f45eeff0ecf2b1cb6db1551304972702f34cd9e6c44658"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:14cdc8c1810bbd4b4b9f142eeee23cda528ae4e57ea0923551a9af4820980e39"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:971aa438a29701d4b34e4943e91b5e984c3ae6ccbf80dd9efaffb01bd0b243a9"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9a309c5de392dfe0f32ee57fa43ed8fc6ddf9985425e84bd51ed66bb16bce3a7"}, + {file = "aiohttp-3.10.11-cp38-cp38-win32.whl", hash = "sha256:9ec1628180241d906a0840b38f162a3215114b14541f1a8711c368a8739a9be4"}, + {file = "aiohttp-3.10.11-cp38-cp38-win_amd64.whl", hash = "sha256:9c6e0ffd52c929f985c7258f83185d17c76d4275ad22e90aa29f38e211aacbec"}, + {file = "aiohttp-3.10.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cdc493a2e5d8dc79b2df5bec9558425bcd39aff59fc949810cbd0832e294b106"}, + {file = "aiohttp-3.10.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b3e70f24e7d0405be2348da9d5a7836936bf3a9b4fd210f8c37e8d48bc32eca6"}, + {file = "aiohttp-3.10.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:968b8fb2a5eee2770eda9c7b5581587ef9b96fbdf8dcabc6b446d35ccc69df01"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deef4362af9493d1382ef86732ee2e4cbc0d7c005947bd54ad1a9a16dd59298e"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:686b03196976e327412a1b094f4120778c7c4b9cff9bce8d2fdfeca386b89829"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3bf6d027d9d1d34e1c2e1645f18a6498c98d634f8e373395221121f1c258ace8"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:099fd126bf960f96d34a760e747a629c27fb3634da5d05c7ef4d35ef4ea519fc"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c73c4d3dae0b4644bc21e3de546530531d6cdc88659cdeb6579cd627d3c206aa"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0c5580f3c51eea91559db3facd45d72e7ec970b04528b4709b1f9c2555bd6d0b"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fdf6429f0caabfd8a30c4e2eaecb547b3c340e4730ebfe25139779b9815ba138"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d97187de3c276263db3564bb9d9fad9e15b51ea10a371ffa5947a5ba93ad6777"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:0acafb350cfb2eba70eb5d271f55e08bd4502ec35e964e18ad3e7d34d71f7261"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c13ed0c779911c7998a58e7848954bd4d63df3e3575f591e321b19a2aec8df9f"}, + {file = "aiohttp-3.10.11-cp39-cp39-win32.whl", hash = "sha256:22b7c540c55909140f63ab4f54ec2c20d2635c0289cdd8006da46f3327f971b9"}, + {file = "aiohttp-3.10.11-cp39-cp39-win_amd64.whl", hash = "sha256:7b26b1551e481012575dab8e3727b16fe7dd27eb2711d2e63ced7368756268fb"}, + {file = "aiohttp-3.10.11.tar.gz", hash = "sha256:9dc2b8f3dcab2e39e0fa309c8da50c3b55e6f34ab25f1a71d3288f24924d33a7"}, ] [package.dependencies] aiohappyeyeballs = ">=2.3.0" aiosignal = ">=1.1.2" -async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" From 9f728833a78a9d7962d5c586314408dc2921547c Mon Sep 17 00:00:00 2001 From: Sergio Garcia Date: Tue, 19 Nov 2024 04:21:20 -0400 Subject: [PATCH 48/55] chore(iam): add missing service catalog permissions (#5816) --- permissions/create_role_to_assume_cfn.yaml | 2 ++ permissions/prowler-additions-policy.json | 2 ++ 2 files changed, 4 insertions(+) diff --git a/permissions/create_role_to_assume_cfn.yaml b/permissions/create_role_to_assume_cfn.yaml index 14c4f97e4e..2f1def4cc4 100644 --- a/permissions/create_role_to_assume_cfn.yaml +++ b/permissions/create_role_to_assume_cfn.yaml @@ -91,6 +91,8 @@ Resources: - 'shield:GetSubscriptionState' - 'securityhub:BatchImportFindings' - 'securityhub:GetFindings' + - 'servicecatalog:Describe*' + - 'servicecatalog:List*' - 'ssm:GetDocument' - 'ssm-incidents:List*' - 'support:Describe*' diff --git a/permissions/prowler-additions-policy.json b/permissions/prowler-additions-policy.json index 6b463320d2..74c6ba8e6d 100644 --- a/permissions/prowler-additions-policy.json +++ b/permissions/prowler-additions-policy.json @@ -39,6 +39,8 @@ "shield:GetSubscriptionState", "securityhub:BatchImportFindings", "securityhub:GetFindings", + "servicecatalog:Describe*", + "servicecatalog:List*", "ssm:GetDocument", "ssm-incidents:List*", "support:Describe*", From 456f79d80cab292b7bcde2376c43d31cb273ff4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 09:52:56 +0100 Subject: [PATCH 49/55] chore(deps): bump botocore from 1.35.63 to 1.35.64 (#5818) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index cfef7fd382..554f01b815 100644 --- a/poetry.lock +++ b/poetry.lock @@ -778,13 +778,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.63" +version = "1.35.64" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.63-py3-none-any.whl", hash = "sha256:0ca1200694a4c0a3fa846795d8e8a08404c214e21195eb9e010c4b8a4ca78a4a"}, - {file = "botocore-1.35.63.tar.gz", hash = "sha256:2b8196bab0a997d206c3d490b52e779ef47dffb68c57c685443f77293aca1589"}, + {file = "botocore-1.35.64-py3-none-any.whl", hash = "sha256:bbd96bf7f442b1d5e35b36f501076e4a588c83d8d84a1952e9ee1d767e5efb3e"}, + {file = "botocore-1.35.64.tar.gz", hash = "sha256:2f95c83f31c9e38a66995c88810fc638c829790e125032ba00ab081a2cf48cb9"}, ] [package.dependencies] @@ -5176,4 +5176,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "221f02866e4ad60405ac3bfd61f98d2eab084e82478be7ac8f42439bf37afb3e" +content-hash = "25d2af2d36f5243a025f40be403adad646b4b6d9a48f3caed9dc48f8dd5f7ce6" diff --git a/pyproject.toml b/pyproject.toml index 8a4c17a382..3b52f93663 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ azure-mgmt-subscription = "3.1.1" azure-mgmt-web = "7.3.1" azure-storage-blob = "12.24.0" boto3 = "1.35.63" -botocore = "1.35.63" +botocore = "1.35.64" colorama = "0.4.6" cryptography = "43.0.1" dash = "2.18.2" From f5e71db5e04a1fc91ae17fd546ecb06488738919 Mon Sep 17 00:00:00 2001 From: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com> Date: Tue, 19 Nov 2024 14:23:52 +0100 Subject: [PATCH 50/55] fix(rds): fix typo error in `rds_snapshots_public_access_fixer` test (#5826) --- .../rds_snapshots_public_access_fixer_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/providers/aws/services/rds/rds_snapshots_public_access/rds_snapshots_public_access_fixer_test.py b/tests/providers/aws/services/rds/rds_snapshots_public_access/rds_snapshots_public_access_fixer_test.py index 6a915bfb1c..e45d11958d 100644 --- a/tests/providers/aws/services/rds/rds_snapshots_public_access/rds_snapshots_public_access_fixer_test.py +++ b/tests/providers/aws/services/rds/rds_snapshots_public_access/rds_snapshots_public_access_fixer_test.py @@ -6,7 +6,7 @@ from moto import mock_aws from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider -class Test_rds_snapshots_public_access: +class Test_rds_snapshots_public_access_fixer: @mock_aws def test_rds_private_snapshot(self): conn = client("rds", region_name=AWS_REGION_US_EAST_1) From 8a491bcf7ded4d08e0e4eaf433d36b0164ffd763 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 09:24:32 -0400 Subject: [PATCH 51/55] chore(deps): bump trufflesecurity/trufflehog from 3.83.6 to 3.83.7 (#5819) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/find-secrets.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/find-secrets.yml b/.github/workflows/find-secrets.yml index 2e8b7eeb9a..0051e71a46 100644 --- a/.github/workflows/find-secrets.yml +++ b/.github/workflows/find-secrets.yml @@ -11,7 +11,7 @@ jobs: with: fetch-depth: 0 - name: TruffleHog OSS - uses: trufflesecurity/trufflehog@v3.83.6 + uses: trufflesecurity/trufflehog@v3.83.7 with: path: ./ base: ${{ github.event.repository.default_branch }} From c8fdaa392333b481a25a89ab999acbf0afc77d7b Mon Sep 17 00:00:00 2001 From: Prowler Bot Date: Tue, 19 Nov 2024 14:24:49 +0100 Subject: [PATCH 52/55] chore(regions_update): Changes in regions for AWS services (#5824) Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com> --- prowler/providers/aws/aws_regions_by_service.json | 1 + 1 file changed, 1 insertion(+) diff --git a/prowler/providers/aws/aws_regions_by_service.json b/prowler/providers/aws/aws_regions_by_service.json index df3f67950d..533785e17e 100644 --- a/prowler/providers/aws/aws_regions_by_service.json +++ b/prowler/providers/aws/aws_regions_by_service.json @@ -2627,6 +2627,7 @@ "connectcampaigns": { "regions": { "aws": [ + "af-south-1", "ap-southeast-2", "ca-central-1", "eu-central-1", From e33b081dc6ac53c7e6ab52ef021f8c2fc14369f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Tue, 19 Nov 2024 14:42:56 +0100 Subject: [PATCH 53/55] chore(iam): add exception for public policy in EKS service (#4759) --- ...e_confused_deputy_prevention.metadata.json | 2 +- .../providers/aws/services/iam/lib/policy.py | 25 +++++++++++-------- .../aws/services/iam/lib/policy_test.py | 16 ++++++++++++ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/prowler/providers/aws/services/iam/iam_role_cross_service_confused_deputy_prevention/iam_role_cross_service_confused_deputy_prevention.metadata.json b/prowler/providers/aws/services/iam/iam_role_cross_service_confused_deputy_prevention/iam_role_cross_service_confused_deputy_prevention.metadata.json index 54f30ccbcb..0a3cfa4153 100644 --- a/prowler/providers/aws/services/iam/iam_role_cross_service_confused_deputy_prevention/iam_role_cross_service_confused_deputy_prevention.metadata.json +++ b/prowler/providers/aws/services/iam/iam_role_cross_service_confused_deputy_prevention/iam_role_cross_service_confused_deputy_prevention.metadata.json @@ -28,5 +28,5 @@ ], "DependsOn": [], "RelatedTo": [], - "Notes": "CAF Security Epic: IAM" + "Notes": "" } diff --git a/prowler/providers/aws/services/iam/lib/policy.py b/prowler/providers/aws/services/iam/lib/policy.py index f4293bc407..b45dd1bd30 100644 --- a/prowler/providers/aws/services/iam/lib/policy.py +++ b/prowler/providers/aws/services/iam/lib/policy.py @@ -168,17 +168,22 @@ def is_policy_public( in principal.get("CanonicalUser", "") or check_cross_service_confused_deputy and ( - ( # Check if function can be invoked by other AWS services if check_cross_service_confused_deputy is True - ( - ".amazonaws.com" in principal.get("Service", "") - or ".amazon.com" in principal.get("Service", "") - or "*" in principal.get("Service", "") - ) + # Check if function can be invoked by other AWS services if check_cross_service_confused_deputy is True + ( + ".amazonaws.com" in principal.get("Service", "") + or ".amazon.com" in principal.get("Service", "") + or "*" in principal.get("Service", "") + ) + and ( + "secretsmanager.amazonaws.com" + not in principal.get( + "Service", "" + ) # AWS ensures that resources called by SecretsManager are executed in the same AWS account + or "eks.amazonaws.com" + not in principal.get( + "Service", "" + ) # AWS ensures that resources called by EKS are executed in the same AWS account ) - and "secretsmanager.amazonaws.com" - not in principal.get( - "Service", "" - ) # AWS ensures that resources called by SecretsManager are executed in the same AWS account ) ) ) diff --git a/tests/providers/aws/services/iam/lib/policy_test.py b/tests/providers/aws/services/iam/lib/policy_test.py index f07582000b..bac84355ac 100644 --- a/tests/providers/aws/services/iam/lib/policy_test.py +++ b/tests/providers/aws/services/iam/lib/policy_test.py @@ -1923,6 +1923,22 @@ class Test_Policy: } assert not is_policy_public(policy, TRUSTED_AWS_ACCOUNT_NUMBER) + def test_is_policy_public_eks( + self, + ): + policy = { + "Statement": [ + { + "Sid": "test", + "Effect": "Allow", + "Principal": {"Service": "eks.amazonaws.com"}, + "Action": "lambda:GetFunction", + "Resource": "*", + } + ] + } + assert not is_policy_public(policy, TRUSTED_AWS_ACCOUNT_NUMBER) + def test_is_policy_public_cross_cross_service_confused_deputy( self, ): From 7a4f5f34f79ae5ed67e74f276042e3bff53b80fa Mon Sep 17 00:00:00 2001 From: Sergio Garcia Date: Tue, 19 Nov 2024 10:04:30 -0400 Subject: [PATCH 54/55] fix(kubernetes): validate seccomp profile at pod and container levels (#5814) --- .../core_seccomp_profile_docker_default.py | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/prowler/providers/kubernetes/services/core/core_seccomp_profile_docker_default/core_seccomp_profile_docker_default.py b/prowler/providers/kubernetes/services/core/core_seccomp_profile_docker_default/core_seccomp_profile_docker_default.py index 71c4a7f28a..20a9c16b2f 100644 --- a/prowler/providers/kubernetes/services/core/core_seccomp_profile_docker_default/core_seccomp_profile_docker_default.py +++ b/prowler/providers/kubernetes/services/core/core_seccomp_profile_docker_default/core_seccomp_profile_docker_default.py @@ -10,18 +10,38 @@ class core_seccomp_profile_docker_default(Check): report.namespace = pod.namespace report.resource_name = pod.name report.resource_id = pod.uid - if ( + + pod_seccomp_correct = ( pod.security_context and pod.security_context.seccomp_profile and pod.security_context.seccomp_profile.type == "RuntimeDefault" - ): + ) + containers_seccomp_correct = True + + # Check container-level seccomp profile + for container in pod.containers.values(): + if not ( + container.security_context + and container.security_context.seccomp_profile + and container.security_context.seccomp_profile.type + == "RuntimeDefault" + ): + containers_seccomp_correct = False + break + + # Determine the report status + if pod_seccomp_correct or containers_seccomp_correct: report.status = "PASS" - report.status_extended = ( - f"Pod {pod.name} has docker/default seccomp profile enabled." - ) + report.status_extended = f"Pod {pod.name} and its containers have docker/default seccomp profile enabled." else: report.status = "FAIL" - report.status_extended = f"Pod {pod.name} does not have docker/default seccomp profile enabled." + if not pod_seccomp_correct and not containers_seccomp_correct: + report.status_extended = f"Pod {pod.name} does not have docker/default seccomp profile enabled at both pod and container levels." + elif not pod_seccomp_correct: + report.status_extended = f"Pod {pod.name} does not have docker/default seccomp profile enabled at pod level." + else: + report.status_extended = f"Pod {pod.name} does not have docker/default seccomp profile enabled at container level." + findings.append(report) return findings From a5f5967bb25e81d5205b1c313145bb6d14eb0895 Mon Sep 17 00:00:00 2001 From: Sergio Garcia Date: Tue, 19 Nov 2024 10:06:12 -0400 Subject: [PATCH 55/55] fix(aws): exclude member accounts in IAM Root Credentials check (#5813) --- .../iam_no_root_access_key.py | 5 ++++- ...t_credentials_management_enabled.metadata.json | 5 +++-- .../iam_root_credentials_management_enabled.py | 1 + .../iam_root_hardware_mfa_enabled.py | 5 ++++- .../iam_root_mfa_enabled/iam_root_mfa_enabled.py | 5 ++++- prowler/providers/aws/services/iam/iam_service.py | 15 +++++++++++++++ 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/prowler/providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key.py b/prowler/providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key.py index 365a9f2fda..12666439b2 100644 --- a/prowler/providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key.py +++ b/prowler/providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key.py @@ -6,7 +6,10 @@ class iam_no_root_access_key(Check): def execute(self) -> Check_Report_AWS: findings = [] # Check if the root credentials are managed by AWS Organizations - if "RootCredentialsManagement" not in iam_client.organization_features: + if ( + iam_client.organization_features is not None + and "RootCredentialsManagement" not in iam_client.organization_features + ): for user in iam_client.credential_report: if user["user"] == "": report = Check_Report_AWS(self.metadata()) diff --git a/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.metadata.json b/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.metadata.json index 69a71f9510..530f211ec6 100644 --- a/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.metadata.json +++ b/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.metadata.json @@ -27,7 +27,8 @@ "DependsOn": [], "RelatedTo": [ "iam_root_hardware_mfa_enabled", - "iam_root_mfa_enabled" + "iam_root_mfa_enabled", + "iam_no_root_access_key" ], - "Notes": "" + "Notes": "This check skips findings for member accounts as they cannot execute the ListOrganizationsFeatures API call, which is restricted to the management account or delegated administrators." } diff --git a/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.py b/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.py index 5895c0e08a..c5fcc0896e 100644 --- a/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.py +++ b/prowler/providers/aws/services/iam/iam_root_credentials_management_enabled/iam_root_credentials_management_enabled.py @@ -11,6 +11,7 @@ class iam_root_credentials_management_enabled(Check): if ( organizations_client.organization and organizations_client.organization.status == "ACTIVE" + and iam_client.organization_features is not None ): report = Check_Report_AWS(self.metadata()) report.region = iam_client.region diff --git a/prowler/providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled.py b/prowler/providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled.py index 284ad86b98..03d14d15cf 100644 --- a/prowler/providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled.py +++ b/prowler/providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled.py @@ -8,7 +8,10 @@ class iam_root_hardware_mfa_enabled(Check): # This check is only available in Commercial Partition if iam_client.audited_partition == "aws": # Check if the root credentials are managed by AWS Organizations - if "RootCredentialsManagement" not in iam_client.organization_features: + if ( + iam_client.organization_features is not None + and "RootCredentialsManagement" not in iam_client.organization_features + ): if iam_client.account_summary: virtual_mfa = False report = Check_Report_AWS(self.metadata()) diff --git a/prowler/providers/aws/services/iam/iam_root_mfa_enabled/iam_root_mfa_enabled.py b/prowler/providers/aws/services/iam/iam_root_mfa_enabled/iam_root_mfa_enabled.py index 2db6c4bfff..4710d47689 100644 --- a/prowler/providers/aws/services/iam/iam_root_mfa_enabled/iam_root_mfa_enabled.py +++ b/prowler/providers/aws/services/iam/iam_root_mfa_enabled/iam_root_mfa_enabled.py @@ -6,7 +6,10 @@ class iam_root_mfa_enabled(Check): def execute(self) -> Check_Report_AWS: findings = [] # Check if the root credentials are managed by AWS Organizations - if "RootCredentialsManagement" not in iam_client.organization_features: + if ( + iam_client.organization_features is not None + and "RootCredentialsManagement" not in iam_client.organization_features + ): if iam_client.credential_report: for user in iam_client.credential_report: if user["user"] == "": diff --git a/prowler/providers/aws/services/iam/iam_service.py b/prowler/providers/aws/services/iam/iam_service.py index 2fc4ef4795..ae6eba3b86 100644 --- a/prowler/providers/aws/services/iam/iam_service.py +++ b/prowler/providers/aws/services/iam/iam_service.py @@ -982,6 +982,21 @@ class IAM(AWSService): logger.warning( f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) + elif ( + error.response["Error"]["Code"] + == "OrganizationNotInAllFeaturesModeException" + ): + logger.warning( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + elif ( + error.response["Error"]["Code"] + == "AccountNotManagementOrDelegatedAdministratorException" + ): + logger.warning( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + self.organization_features = None else: logger.error( f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"