diff --git a/poetry.lock b/poetry.lock index c48e14d23f..b3fb2a314c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.1 and should not be changed by hand. [[package]] name = "about-time" @@ -1511,34 +1511,34 @@ files = [ [[package]] name = "boto3" -version = "1.42.39" +version = "1.40.61" description = "The AWS SDK for Python" optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "boto3-1.42.39-py3-none-any.whl", hash = "sha256:d9d6ce11df309707b490d2f5f785b761cfddfd6d1f665385b78c9d8ed097184b"}, - {file = "boto3-1.42.39.tar.gz", hash = "sha256:d03f82363314759eff7f84a27b9e6428125f89d8119e4588e8c2c1d79892c956"}, + {file = "boto3-1.40.61-py3-none-any.whl", hash = "sha256:6b9c57b2a922b5d8c17766e29ed792586a818098efe84def27c8f582b33f898c"}, + {file = "boto3-1.40.61.tar.gz", hash = "sha256:d6c56277251adf6c2bdd25249feae625abe4966831676689ff23b4694dea5b12"}, ] [package.dependencies] -botocore = ">=1.42.39,<1.43.0" +botocore = ">=1.40.61,<1.41.0" jmespath = ">=0.7.1,<2.0.0" -s3transfer = ">=0.16.0,<0.17.0" +s3transfer = ">=0.14.0,<0.15.0" [package.extras] crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.42.39" +version = "1.40.61" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "botocore-1.42.39-py3-none-any.whl", hash = "sha256:9e0d0fed9226449cc26fcf2bbffc0392ac698dd8378e8395ce54f3ec13f81d58"}, - {file = "botocore-1.42.39.tar.gz", hash = "sha256:0f00355050821e91a5fe6d932f7bf220f337249b752899e3e4cf6ed54326249e"}, + {file = "botocore-1.40.61-py3-none-any.whl", hash = "sha256:17ebae412692fd4824f99cde0f08d50126dc97954008e5ba2b522eb049238aa7"}, + {file = "botocore-1.40.61.tar.gz", hash = "sha256:a2487ad69b090f9cccd64cf07c7021cd80ee9c0655ad974f87045b02f3ef52cd"}, ] [package.dependencies] @@ -1550,7 +1550,7 @@ urllib3 = [ ] [package.extras] -crt = ["awscrt (==0.29.2)"] +crt = ["awscrt (==0.27.6)"] [[package]] name = "cachetools" @@ -5913,14 +5913,14 @@ files = [ [[package]] name = "s3transfer" -version = "0.16.0" +version = "0.14.0" description = "An Amazon S3 Transfer Manager" optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe"}, - {file = "s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920"}, + {file = "s3transfer-0.14.0-py3-none-any.whl", hash = "sha256:ea3b790c7077558ed1f02a3072fb3cb992bbbd253392f4b6e9e8976941c7d456"}, + {file = "s3transfer-0.14.0.tar.gz", hash = "sha256:eff12264e7c8b4985074ccce27a3b38a485bb7f7422cc8046fee9be4983e4125"}, ] [package.dependencies] @@ -6853,4 +6853,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">3.9.1,<3.13" -content-hash = "e8e046df80223f2dcf8249bd03e3744868728dccb1ee7a9d3bef25bf0f60fb7b" +content-hash = "f9ff21ae57caa3ddcd27f3753c29c1b3be2966709baed52e1bbc24e7bdc33f3c" diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index aabb2315dd..57f3ae5b22 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -15,7 +15,6 @@ All notable changes to the **Prowler SDK** are documented in this file. - `rds_instance_extended_support` check for AWS provider [(#9865)](https://github.com/prowler-cloud/prowler/pull/9865) - `OpenStack` provider support with Compute service including 1 security check [(#9811)](https://github.com/prowler-cloud/prowler/pull/9811) - `OpenStack` documentation for the support in the CLI [(#9848)](https://github.com/prowler-cloud/prowler/pull/9848) -- `autoscaling_group_deletion_protection_enabled` check for AWS provider [(#9928)](https://github.com/prowler-cloud/prowler/pull/9928) - Cloudflare provider credentials as constructor parameters (`api_token`, `api_key`, `api_email`) [(#9907)](https://github.com/prowler-cloud/prowler/pull/9907) ### Changed diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_group_deletion_protection_enabled/__init__.py b/prowler/providers/aws/services/autoscaling/autoscaling_group_deletion_protection_enabled/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_group_deletion_protection_enabled/autoscaling_group_deletion_protection_enabled.metadata.json b/prowler/providers/aws/services/autoscaling/autoscaling_group_deletion_protection_enabled/autoscaling_group_deletion_protection_enabled.metadata.json deleted file mode 100644 index 9f4fa1faea..0000000000 --- a/prowler/providers/aws/services/autoscaling/autoscaling_group_deletion_protection_enabled/autoscaling_group_deletion_protection_enabled.metadata.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "Provider": "aws", - "CheckID": "autoscaling_group_deletion_protection_enabled", - "CheckTitle": "Auto Scaling group has deletion protection enabled", - "CheckType": [ - "Software and Configuration Checks/AWS Security Best Practices" - ], - "ServiceName": "autoscaling", - "SubServiceName": "", - "ResourceIdTemplate": "", - "Severity": "medium", - "ResourceType": "AwsAutoScalingAutoScalingGroup", - "ResourceGroup": "compute", - "Description": "**EC2 Auto Scaling groups** are evaluated for having **deletion protection** enabled. AWS ASGs support three levels of deletion protection: `none` (default), `prevent-force-deletion`, and `prevent-all-deletion`. Groups without deletion protection (`none`) can be accidentally deleted, potentially causing service disruptions.", - "Risk": "Without deletion protection, Auto Scaling groups can be accidentally or maliciously deleted, leading to immediate loss of capacity, service outages, and potential data loss. This is especially critical for production workloads where unplanned capacity removal can cascade into wider failures.", - "RelatedUrl": "", - "AdditionalURLs": [ - "https://docs.aws.amazon.com/autoscaling/ec2/APIReference/API_UpdateAutoScalingGroup.html", - "https://docs.aws.amazon.com/autoscaling/ec2/userguide/resource-deletion-protection.html" - ], - "Remediation": { - "Code": { - "CLI": "aws autoscaling update-auto-scaling-group --auto-scaling-group-name --deletion-protection prevent-all-deletion", - "NativeIaC": "```yaml\n# CloudFormation: Enable deletion protection on an Auto Scaling group\nResources:\n :\n Type: AWS::AutoScaling::AutoScalingGroup\n Properties:\n DeletionProtection: prevent-all-deletion\n MinSize: \"1\"\n MaxSize: \"3\"\n VPCZoneIdentifier:\n - \n - \n LaunchTemplate:\n LaunchTemplateId: \n Version: $Latest\n```", - "Other": "1. In the AWS Console, go to EC2 > Auto Scaling Groups and select \n2. Click Edit\n3. Under Details, find Deletion protection\n4. Select prevent-all-deletion\n5. Click Update to save", - "Terraform": "```hcl\n# Terraform: Enable deletion protection on an Auto Scaling group\nresource \"aws_autoscaling_group\" \"\" {\n name = \"\"\n min_size = 1\n max_size = 3\n vpc_zone_identifier = [\"\", \"\"]\n deletion_protection = \"prevent-all-deletion\"\n\n launch_template {\n id = \"\"\n version = \"$Latest\"\n }\n}\n```" - }, - "Recommendation": { - "Text": "Enable **deletion protection** on Auto Scaling groups to prevent accidental or unauthorized removal:\n- Use `prevent-all-deletion` to block all deletion attempts\n- Use `prevent-force-deletion` to block force deletion while allowing normal deletion after scaling to zero\nApply deletion protection to all production Auto Scaling groups as a safeguard against operational errors.", - "Url": "https://hub.prowler.com/check/autoscaling_group_deletion_protection_enabled" - } - }, - "Categories": [ - "resilience" - ], - "DependsOn": [], - "RelatedTo": [], - "Notes": "" -} diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_group_deletion_protection_enabled/autoscaling_group_deletion_protection_enabled.py b/prowler/providers/aws/services/autoscaling/autoscaling_group_deletion_protection_enabled/autoscaling_group_deletion_protection_enabled.py deleted file mode 100644 index 9036f76959..0000000000 --- a/prowler/providers/aws/services/autoscaling/autoscaling_group_deletion_protection_enabled/autoscaling_group_deletion_protection_enabled.py +++ /dev/null @@ -1,38 +0,0 @@ -from prowler.lib.check.models import Check, Check_Report_AWS -from prowler.providers.aws.services.autoscaling.autoscaling_client import ( - autoscaling_client, -) - - -class autoscaling_group_deletion_protection_enabled(Check): - """ - Auto Scaling groups should have deletion protection enabled to prevent accidental or unauthorized deletion. - This check verifies if Auto Scaling groups have deletion protection configured. - AWS ASGs support three levels of deletion protection: 'none', 'prevent-force-deletion', and 'prevent-all-deletion'. - If deletion protection is set to 'none', it will be marked as FAIL. - If deletion protection is set to 'prevent-force-deletion' or 'prevent-all-deletion', it will be marked as PASS. - """ - - def execute(self): - """ - Execute the Auto Scaling group deletion protection check. - - Iterate over all Auto Scaling groups and check if deletion protection is enabled. - - Returns: - List[Check_Report_AWS]: A list of report objects with the results of the check. - """ - findings = [] - for group in autoscaling_client.groups: - report = Check_Report_AWS(metadata=self.metadata(), resource=group) - - if group.deletion_protection == "none": - report.status = "FAIL" - report.status_extended = f"Autoscaling group {group.name} does not have deletion protection enabled." - else: - report.status = "PASS" - report.status_extended = f"Autoscaling group {group.name} has deletion protection set to {group.deletion_protection}." - - findings.append(report) - - return findings diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_service.py b/prowler/providers/aws/services/autoscaling/autoscaling_service.py index 9936d44fc5..2cafc84056 100644 --- a/prowler/providers/aws/services/autoscaling/autoscaling_service.py +++ b/prowler/providers/aws/services/autoscaling/autoscaling_service.py @@ -100,9 +100,6 @@ class AutoScaling(AWSService): launch_configuration_name=group.get( "LaunchConfigurationName", "" ), - deletion_protection=group.get( - "DeletionProtection", "none" - ), ) ) @@ -181,7 +178,6 @@ class Group(BaseModel): load_balancers: list = [] target_groups: list = [] launch_configuration_name: str - deletion_protection: str class ScalableTarget(BaseModel): diff --git a/pyproject.toml b/pyproject.toml index 567e2d296e..8f8a74f992 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,8 +41,8 @@ dependencies = [ "azure-monitor-query==2.0.0", "azure-storage-blob==12.24.1", "cloudflare==4.3.1", - "boto3==1.42.39", - "botocore==1.42.39", + "boto3==1.40.61", + "botocore==1.40.61", "colorama==0.4.6", "cryptography==44.0.1", "dash==3.1.1", diff --git a/tests/providers/aws/services/autoscaling/autoscaling_group_deletion_protection_enabled/autoscaling_group_deletion_protection_enabled_test.py b/tests/providers/aws/services/autoscaling/autoscaling_group_deletion_protection_enabled/autoscaling_group_deletion_protection_enabled_test.py deleted file mode 100644 index de9494c384..0000000000 --- a/tests/providers/aws/services/autoscaling/autoscaling_group_deletion_protection_enabled/autoscaling_group_deletion_protection_enabled_test.py +++ /dev/null @@ -1,266 +0,0 @@ -from unittest import mock - -import botocore -from boto3 import client -from moto import mock_aws - -from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider - -make_api_call = botocore.client.BaseClient._make_api_call - -ASG_NAME = "my-autoscaling-group" -ASG_ARN = f"arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:uuid:autoScalingGroupName/{ASG_NAME}" - - -def mock_make_api_call_no_protection(self, operation_name, kwarg): - if operation_name == "DescribeAutoScalingGroups": - return { - "AutoScalingGroups": [ - { - "AutoScalingGroupName": ASG_NAME, - "AutoScalingGroupARN": ASG_ARN, - "AvailabilityZones": ["us-east-1a"], - "Tags": [], - "Instances": [], - "DeletionProtection": "none", - } - ] - } - return make_api_call(self, operation_name, kwarg) - - -def mock_make_api_call_prevent_force_deletion(self, operation_name, kwarg): - if operation_name == "DescribeAutoScalingGroups": - return { - "AutoScalingGroups": [ - { - "AutoScalingGroupName": ASG_NAME, - "AutoScalingGroupARN": ASG_ARN, - "AvailabilityZones": ["us-east-1a"], - "Tags": [], - "Instances": [], - "DeletionProtection": "prevent-force-deletion", - } - ] - } - return make_api_call(self, operation_name, kwarg) - - -def mock_make_api_call_prevent_all_deletion(self, operation_name, kwarg): - if operation_name == "DescribeAutoScalingGroups": - return { - "AutoScalingGroups": [ - { - "AutoScalingGroupName": ASG_NAME, - "AutoScalingGroupARN": ASG_ARN, - "AvailabilityZones": ["us-east-1a"], - "Tags": [], - "Instances": [], - "DeletionProtection": "prevent-all-deletion", - } - ] - } - return make_api_call(self, operation_name, kwarg) - - -class Test_autoscaling_group_deletion_protection_enabled: - @mock_aws - def test_no_autoscaling(self): - autoscaling_client = client("autoscaling", region_name=AWS_REGION_US_EAST_1) - autoscaling_client.groups = [] - - from prowler.providers.aws.services.autoscaling.autoscaling_service import ( - AutoScaling, - ) - - 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.autoscaling.autoscaling_group_deletion_protection_enabled.autoscaling_group_deletion_protection_enabled.autoscaling_client", - new=AutoScaling(aws_provider), - ), - ): - from prowler.providers.aws.services.autoscaling.autoscaling_group_deletion_protection_enabled.autoscaling_group_deletion_protection_enabled import ( - autoscaling_group_deletion_protection_enabled, - ) - - check = autoscaling_group_deletion_protection_enabled() - result = check.execute() - - assert len(result) == 0 - - @mock_aws - def test_deletion_protection_none(self): - with mock.patch( - "botocore.client.BaseClient._make_api_call", - new=mock_make_api_call_no_protection, - ): - autoscaling_client = client("autoscaling", region_name=AWS_REGION_US_EAST_1) - autoscaling_client.create_launch_configuration( - LaunchConfigurationName="test", - ImageId="ami-12c6146b", - InstanceType="t1.micro", - KeyName="the_keys", - SecurityGroups=["default", "default2"], - ) - autoscaling_client.create_auto_scaling_group( - AutoScalingGroupName=ASG_NAME, - LaunchConfigurationName="test", - MinSize=0, - MaxSize=0, - DesiredCapacity=0, - AvailabilityZones=["us-east-1a"], - ) - - from prowler.providers.aws.services.autoscaling.autoscaling_service import ( - AutoScaling, - ) - - 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.autoscaling.autoscaling_group_deletion_protection_enabled.autoscaling_group_deletion_protection_enabled.autoscaling_client", - new=AutoScaling(aws_provider), - ), - ): - from prowler.providers.aws.services.autoscaling.autoscaling_group_deletion_protection_enabled.autoscaling_group_deletion_protection_enabled import ( - autoscaling_group_deletion_protection_enabled, - ) - - check = autoscaling_group_deletion_protection_enabled() - result = check.execute() - - assert len(result) == 1 - assert result[0].status == "FAIL" - assert ( - result[0].status_extended - == f"Autoscaling group {ASG_NAME} does not have deletion protection enabled." - ) - assert result[0].resource_id == ASG_NAME - assert result[0].resource_arn == ASG_ARN - assert result[0].region == AWS_REGION_US_EAST_1 - assert result[0].resource_tags == [] - - @mock_aws - def test_deletion_protection_prevent_force_deletion(self): - with mock.patch( - "botocore.client.BaseClient._make_api_call", - new=mock_make_api_call_prevent_force_deletion, - ): - autoscaling_client = client("autoscaling", region_name=AWS_REGION_US_EAST_1) - autoscaling_client.create_launch_configuration( - LaunchConfigurationName="test", - ImageId="ami-12c6146b", - InstanceType="t1.micro", - KeyName="the_keys", - SecurityGroups=["default", "default2"], - ) - autoscaling_client.create_auto_scaling_group( - AutoScalingGroupName=ASG_NAME, - LaunchConfigurationName="test", - MinSize=0, - MaxSize=0, - DesiredCapacity=0, - AvailabilityZones=["us-east-1a"], - ) - - from prowler.providers.aws.services.autoscaling.autoscaling_service import ( - AutoScaling, - ) - - 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.autoscaling.autoscaling_group_deletion_protection_enabled.autoscaling_group_deletion_protection_enabled.autoscaling_client", - new=AutoScaling(aws_provider), - ), - ): - from prowler.providers.aws.services.autoscaling.autoscaling_group_deletion_protection_enabled.autoscaling_group_deletion_protection_enabled import ( - autoscaling_group_deletion_protection_enabled, - ) - - check = autoscaling_group_deletion_protection_enabled() - result = check.execute() - - assert len(result) == 1 - assert result[0].status == "PASS" - assert ( - result[0].status_extended - == f"Autoscaling group {ASG_NAME} has deletion protection set to prevent-force-deletion." - ) - assert result[0].resource_id == ASG_NAME - assert result[0].resource_arn == ASG_ARN - assert result[0].region == AWS_REGION_US_EAST_1 - assert result[0].resource_tags == [] - - @mock_aws - def test_deletion_protection_prevent_all_deletion(self): - with mock.patch( - "botocore.client.BaseClient._make_api_call", - new=mock_make_api_call_prevent_all_deletion, - ): - autoscaling_client = client("autoscaling", region_name=AWS_REGION_US_EAST_1) - autoscaling_client.create_launch_configuration( - LaunchConfigurationName="test", - ImageId="ami-12c6146b", - InstanceType="t1.micro", - KeyName="the_keys", - SecurityGroups=["default", "default2"], - ) - autoscaling_client.create_auto_scaling_group( - AutoScalingGroupName=ASG_NAME, - LaunchConfigurationName="test", - MinSize=0, - MaxSize=0, - DesiredCapacity=0, - AvailabilityZones=["us-east-1a"], - ) - - from prowler.providers.aws.services.autoscaling.autoscaling_service import ( - AutoScaling, - ) - - 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.autoscaling.autoscaling_group_deletion_protection_enabled.autoscaling_group_deletion_protection_enabled.autoscaling_client", - new=AutoScaling(aws_provider), - ), - ): - from prowler.providers.aws.services.autoscaling.autoscaling_group_deletion_protection_enabled.autoscaling_group_deletion_protection_enabled import ( - autoscaling_group_deletion_protection_enabled, - ) - - check = autoscaling_group_deletion_protection_enabled() - result = check.execute() - - assert len(result) == 1 - assert result[0].status == "PASS" - assert ( - result[0].status_extended - == f"Autoscaling group {ASG_NAME} has deletion protection set to prevent-all-deletion." - ) - assert result[0].resource_id == ASG_NAME - assert result[0].resource_arn == ASG_ARN - assert result[0].region == AWS_REGION_US_EAST_1 - assert result[0].resource_tags == []