From 1c72536eb7f515c0fd466c35558cad47ab7122b5 Mon Sep 17 00:00:00 2001 From: MarioRgzLpz Date: Tue, 8 Oct 2024 09:23:01 +0200 Subject: [PATCH 1/9] feat(elasticbeanstalk): Add new service elasticbeanstalk --- .../aws/services/elasticbeanstalk/__init__.py | 0 .../elasticbeanstalk_client.py | 6 + .../elasticbeanstalk_service.py | 87 +++++++++ .../elasticbeanstalk_service_test.py | 184 ++++++++++++++++++ 4 files changed, 277 insertions(+) create mode 100644 prowler/providers/aws/services/elasticbeanstalk/__init__.py create mode 100644 prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_client.py create mode 100644 prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py create mode 100644 tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py diff --git a/prowler/providers/aws/services/elasticbeanstalk/__init__.py b/prowler/providers/aws/services/elasticbeanstalk/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_client.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_client.py new file mode 100644 index 0000000000..bea628d978 --- /dev/null +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_client.py @@ -0,0 +1,6 @@ +from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_service import ( + ElasticBeanstalk, +) +from prowler.providers.common.provider import Provider + +elasticbeanstalk_client = ElasticBeanstalk(Provider.get_global_provider()) diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py new file mode 100644 index 0000000000..ab26dd65be --- /dev/null +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py @@ -0,0 +1,87 @@ +from typing import Optional + +from pydantic import BaseModel + +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 + + +################## EMR +class ElasticBeanstalk(AWSService): + def __init__(self, provider): + # Call AWSService's __init__ + super().__init__(__class__.__name__, provider) + self.environments = {} + self.__threading_call__(self._describe_environments) + self.__threading_call__( + self._describe_configuration_settings, self.environments.values() + ) + + def _describe_environments(self, regional_client): + logger.info("ElasticBeanstalk - Describing environments...") + try: + describe_environment_paginator = regional_client.get_paginator( + "describe_environments" + ) + for page in describe_environment_paginator.paginate(): + for environment in page["Environments"]: + environment_arn = environment["EnvironmentArn"] + if not self.audit_resources or ( + is_resource_filtered(environment_arn, self.audit_resources) + ): + self.environments[environment_arn] = Environment( + id=environment["EnvironmentId"], + arn=environment_arn, + application_name=environment["ApplicationName"], + name=environment["EnvironmentName"], + region=regional_client.region, + ) + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + def _describe_configuration_settings(self, environment): + logger.info("ElasticBeanstalk - Describing configuration settings...") + try: + regional_client = self.regional_clients[environment.region] + configuration_settings = regional_client.describe_configuration_settings( + ApplicationName=environment.application_name, + EnvironmentName=environment.name, + ) + option_settings = configuration_settings["ConfigurationSettings"][0].get( + "OptionSettings", {} + ) + for option in option_settings: + if ( + option["Namespace"] == "aws:elasticbeanstalk:healthreporting:system" + and option["OptionName"] == "SystemType" + ): + environment.health_option = option + elif ( + option["Namespace"] == "aws:elasticbeanstalk:managedactions" + and option["OptionName"] == "ManagedActionsEnabled" + ): + environment.managed_actions_option = option + elif ( + option["Namespace"] == "aws:elasticbeanstalk:cloudwatch:logs" + and option["OptionName"] == "StreamLogs" + ): + environment.cloudwatch_option = option + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + +class Environment(BaseModel): + id: str + name: str + arn: str + region: str + application_name: str + health_option: Optional[dict] + managed_actions_option: Optional[dict] + cloudwatch_option: Optional[dict] + tags: Optional[list] = [] diff --git a/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py b/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py new file mode 100644 index 0000000000..cde75d834e --- /dev/null +++ b/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py @@ -0,0 +1,184 @@ +from unittest.mock import patch + +import botocore +from boto3 import client +from moto import mock_aws + +from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_service import ( + ElasticBeanstalk, +) +from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider + +# Mocking Access Analyzer Calls +make_api_call = botocore.client.BaseClient._make_api_call + + +def mock_make_api_call(self, operation_name, kwarg): + if operation_name == "DescribeConfigurationSettings": + return { + "ConfigurationSettings": [ + { + "OptionSettings": [ + { + "Namespace": "aws:elasticbeanstalk:healthreporting:system", + "OptionName": "SystemType", + "Value": "enhanced", + }, + { + "Namespace": "aws:elasticbeanstalk:managedactions", + "OptionName": "ManagedActionsEnabled", + "Value": "true", + }, + { + "Namespace": "aws:elasticbeanstalk:cloudwatch:logs", + "OptionName": "StreamLogs", + "Value": "true", + }, + ], + } + ] + } + + return make_api_call(self, operation_name, kwarg) + + +# Mock generate_regional_clients() +def mock_generate_regional_clients(provider, service): + regional_client = provider._session.current_session.client( + service, region_name=AWS_REGION_EU_WEST_1 + ) + regional_client.region = AWS_REGION_EU_WEST_1 + return {AWS_REGION_EU_WEST_1: regional_client} + + +@patch( + "prowler.providers.aws.aws_provider.AwsProvider.generate_regional_clients", + new=mock_generate_regional_clients, +) +@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call) +class Test_ElasticBeanstalk_Service: + # Test ElasticBeanstalk Client + @mock_aws + def test_get_client(self): + elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) + assert ( + elasticbeanstalk.regional_clients[AWS_REGION_EU_WEST_1].__class__.__name__ + == "ElasticBeanstalk" + ) + + # Test ElasticBeanstalk Session + @mock_aws + def test__get_session__(self): + elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) + assert elasticbeanstalk.session.__class__.__name__ == "Session" + + # Test ElasticBeanstalk Service + @mock_aws + def test__get_service__(self): + elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) + assert elasticbeanstalk.service == "elasticbeanstalk" + + # Test _describe_environments + @mock_aws + def test_describe_environments(self): + # Create ElasticBeanstalk app and env + elasticbeanstalk_client = client( + "elasticbeanstalk", region_name=AWS_REGION_EU_WEST_1 + ) + elasticbeanstalk_client.create_application(ApplicationName="test-app") + environment = elasticbeanstalk_client.create_environment( + ApplicationName="test-app", + EnvironmentName="test-env", + ) + # ElasticBeanstalk Class + elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) + + assert len(elasticbeanstalk.environments) == 1 + assert ( + elasticbeanstalk.environments[environment["EnvironmentArn"]].id + == environment["EnvironmentId"] + ) + assert ( + elasticbeanstalk.environments[environment["EnvironmentArn"]].name + == "test-env" + ) + assert ( + elasticbeanstalk.environments[environment["EnvironmentArn"]].region + == AWS_REGION_EU_WEST_1 + ) + assert ( + elasticbeanstalk.environments[ + environment["EnvironmentArn"] + ].application_name + == "test-app" + ) + + # Test _describe_configuration_settings + @mock_aws + def test_describe_configuration_settings(self): + # Create ElasticBeanstalk app and env + elasticbeanstalk_client = client( + "elasticbeanstalk", region_name=AWS_REGION_EU_WEST_1 + ) + elasticbeanstalk_client.create_application(ApplicationName="test-app") + environment = elasticbeanstalk_client.create_environment( + ApplicationName="test-app", + EnvironmentName="test-env", + ) + # ElasticBeanstalk Class + elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) + + assert ( + elasticbeanstalk.environments[environment["EnvironmentArn"]].health_option[ + "Namespace" + ] + == "aws:elasticbeanstalk:healthreporting:system" + ) + assert ( + elasticbeanstalk.environments[environment["EnvironmentArn"]].health_option[ + "OptionName" + ] + == "SystemType" + ) + assert ( + elasticbeanstalk.environments[environment["EnvironmentArn"]].health_option[ + "Value" + ] + == "enhanced" + ) + assert ( + elasticbeanstalk.environments[ + environment["EnvironmentArn"] + ].managed_actions_option["Namespace"] + == "aws:elasticbeanstalk:managedactions" + ) + assert ( + elasticbeanstalk.environments[ + environment["EnvironmentArn"] + ].managed_actions_option["OptionName"] + == "ManagedActionsEnabled" + ) + assert ( + elasticbeanstalk.environments[ + environment["EnvironmentArn"] + ].managed_actions_option["Value"] + == "true" + ) + assert ( + elasticbeanstalk.environments[ + environment["EnvironmentArn"] + ].cloudwatch_option["Namespace"] + == "aws:elasticbeanstalk:cloudwatch:logs" + ) + assert ( + elasticbeanstalk.environments[ + environment["EnvironmentArn"] + ].cloudwatch_option["OptionName"] + == "StreamLogs" + ) + assert ( + elasticbeanstalk.environments[ + environment["EnvironmentArn"] + ].cloudwatch_option["Value"] + == "true" + ) From 7d329c93bd7abb585c8875a0edfb3255e8d07efd Mon Sep 17 00:00:00 2001 From: Sergio Date: Tue, 8 Oct 2024 10:26:47 -0400 Subject: [PATCH 2/9] fix: typo --- .../aws/services/elasticbeanstalk/elasticbeanstalk_service.py | 1 - 1 file changed, 1 deletion(-) diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py index ab26dd65be..d849567738 100644 --- a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py @@ -7,7 +7,6 @@ from prowler.providers.aws.lib.service.service import AWSService -################## EMR class ElasticBeanstalk(AWSService): def __init__(self, provider): # Call AWSService's __init__ From 7f64d1fe6669249b70116abd73c310335c4cda4d Mon Sep 17 00:00:00 2001 From: MarioRgzLpz Date: Tue, 8 Oct 2024 17:34:15 +0200 Subject: [PATCH 3/9] chore(elasticbeanstalk): Add list tags for resource method --- .../elasticbeanstalk_service.py | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py index ab26dd65be..06d5818597 100644 --- a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py @@ -7,7 +7,7 @@ from prowler.providers.aws.lib.service.service import AWSService -################## EMR +################## ElasticBeanstalk class ElasticBeanstalk(AWSService): def __init__(self, provider): # Call AWSService's __init__ @@ -58,17 +58,36 @@ def _describe_configuration_settings(self, environment): option["Namespace"] == "aws:elasticbeanstalk:healthreporting:system" and option["OptionName"] == "SystemType" ): - environment.health_option = option + environment.health_option = option.get("Value", "basic") elif ( option["Namespace"] == "aws:elasticbeanstalk:managedactions" and option["OptionName"] == "ManagedActionsEnabled" ): - environment.managed_actions_option = option + environment.managed_actions_option = option.get("Value", "false") elif ( option["Namespace"] == "aws:elasticbeanstalk:cloudwatch:logs" and option["OptionName"] == "StreamLogs" ): - environment.cloudwatch_option = option + environment.cloudwatch_option = option.get("Value", "false") + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + def _list_tags_for_resource(self): + logger.info("ElasticBeanstalk - List Tags...") + try: + for environment_arn, environment in self.environments.items(): + try: + regional_client = self.regional_clients[environment.region] + response = regional_client.list_tags_for_resource( + ResourceArn=environment_arn + )["TagList"] + environment.tags = response + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) except Exception as error: logger.error( f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -81,7 +100,7 @@ class Environment(BaseModel): arn: str region: str application_name: str - health_option: Optional[dict] - managed_actions_option: Optional[dict] - cloudwatch_option: Optional[dict] + health_option: str + managed_actions_option: str + cloudwatch_option: str tags: Optional[list] = [] From 65028291a048f07b3f03f5ef3b5b8c41ef8d86b9 Mon Sep 17 00:00:00 2001 From: MarioRgzLpz Date: Tue, 8 Oct 2024 17:40:52 +0200 Subject: [PATCH 4/9] fix(elasticbeanstalk): Run precommit and fix typo --- .../aws/services/elasticbeanstalk/elasticbeanstalk_service.py | 1 - 1 file changed, 1 deletion(-) diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py index c49d9fdbff..06d5818597 100644 --- a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py @@ -7,7 +7,6 @@ from prowler.providers.aws.lib.service.service import AWSService - ################## ElasticBeanstalk class ElasticBeanstalk(AWSService): def __init__(self, provider): From 8799389860d64c5b9f098920ef074c839293cd20 Mon Sep 17 00:00:00 2001 From: MarioRgzLpz Date: Tue, 8 Oct 2024 18:09:33 +0200 Subject: [PATCH 5/9] fix(elasticbeanstalk): Add call to list tags for resource --- .../aws/services/elasticbeanstalk/elasticbeanstalk_service.py | 1 + 1 file changed, 1 insertion(+) diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py index 06d5818597..d35a20ad6b 100644 --- a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py @@ -17,6 +17,7 @@ def __init__(self, provider): self.__threading_call__( self._describe_configuration_settings, self.environments.values() ) + self.__threading_call__(self._list_tags_for_resource) def _describe_environments(self, regional_client): logger.info("ElasticBeanstalk - Describing environments...") From f6bb72cfd890b1402af6fd0de4e6a3fe00df96f7 Mon Sep 17 00:00:00 2001 From: MarioRgzLpz Date: Tue, 8 Oct 2024 18:16:31 +0200 Subject: [PATCH 6/9] fix(elasticbeanstalk): Undo the Optional changes in environment model attributes and fix test to adjust to the changes --- .../elasticbeanstalk_service.py | 6 +-- .../elasticbeanstalk_service_test.py | 45 ++----------------- 2 files changed, 6 insertions(+), 45 deletions(-) diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py index d35a20ad6b..fe15ad12e9 100644 --- a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py @@ -101,7 +101,7 @@ class Environment(BaseModel): arn: str region: str application_name: str - health_option: str - managed_actions_option: str - cloudwatch_option: str + health_option: Optional[str] + managed_actions_option: Optional[str] + cloudwatch_option: Optional[str] tags: Optional[list] = [] diff --git a/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py b/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py index cde75d834e..83a6ba7b19 100644 --- a/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py +++ b/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py @@ -127,58 +127,19 @@ def test_describe_configuration_settings(self): ) # ElasticBeanstalk Class elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) - - assert ( - elasticbeanstalk.environments[environment["EnvironmentArn"]].health_option[ - "Namespace" - ] - == "aws:elasticbeanstalk:healthreporting:system" - ) - assert ( - elasticbeanstalk.environments[environment["EnvironmentArn"]].health_option[ - "OptionName" - ] - == "SystemType" - ) assert ( - elasticbeanstalk.environments[environment["EnvironmentArn"]].health_option[ - "Value" - ] + elasticbeanstalk.environments[environment["EnvironmentArn"]].health_option == "enhanced" ) assert ( elasticbeanstalk.environments[ environment["EnvironmentArn"] - ].managed_actions_option["Namespace"] - == "aws:elasticbeanstalk:managedactions" - ) - assert ( - elasticbeanstalk.environments[ - environment["EnvironmentArn"] - ].managed_actions_option["OptionName"] - == "ManagedActionsEnabled" - ) - assert ( - elasticbeanstalk.environments[ - environment["EnvironmentArn"] - ].managed_actions_option["Value"] + ].managed_actions_option == "true" ) assert ( elasticbeanstalk.environments[ environment["EnvironmentArn"] - ].cloudwatch_option["Namespace"] - == "aws:elasticbeanstalk:cloudwatch:logs" - ) - assert ( - elasticbeanstalk.environments[ - environment["EnvironmentArn"] - ].cloudwatch_option["OptionName"] - == "StreamLogs" - ) - assert ( - elasticbeanstalk.environments[ - environment["EnvironmentArn"] - ].cloudwatch_option["Value"] + ].cloudwatch_option == "true" ) From fab3060655f85faf207c4da73ebd848442a01ea6 Mon Sep 17 00:00:00 2001 From: MarioRgzLpz Date: Wed, 9 Oct 2024 14:42:25 +0200 Subject: [PATCH 7/9] chore(elasticbeanstalk): Rename attributes and add new test for method list_tags_for_resource --- .../elasticbeanstalk_service.py | 39 +++++++++---------- .../elasticbeanstalk_service_test.py | 26 +++++++++++-- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py index fe15ad12e9..3f8faa0e64 100644 --- a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py @@ -7,7 +7,6 @@ from prowler.providers.aws.lib.service.service import AWSService -################## ElasticBeanstalk class ElasticBeanstalk(AWSService): def __init__(self, provider): # Call AWSService's __init__ @@ -17,7 +16,9 @@ def __init__(self, provider): self.__threading_call__( self._describe_configuration_settings, self.environments.values() ) - self.__threading_call__(self._list_tags_for_resource) + self.__threading_call__( + self._list_tags_for_resource, self.environments.values() + ) def _describe_environments(self, regional_client): logger.info("ElasticBeanstalk - Describing environments...") @@ -59,36 +60,34 @@ def _describe_configuration_settings(self, environment): option["Namespace"] == "aws:elasticbeanstalk:healthreporting:system" and option["OptionName"] == "SystemType" ): - environment.health_option = option.get("Value", "basic") + environment.health_reporting = option.get("Value", "basic") elif ( option["Namespace"] == "aws:elasticbeanstalk:managedactions" and option["OptionName"] == "ManagedActionsEnabled" ): - environment.managed_actions_option = option.get("Value", "false") + environment.managed_platform_updates = option.get("Value", "false") elif ( option["Namespace"] == "aws:elasticbeanstalk:cloudwatch:logs" and option["OptionName"] == "StreamLogs" ): - environment.cloudwatch_option = option.get("Value", "false") + environment.cloudwatch_stream_logs = option.get("Value", "false") except Exception as error: logger.error( f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - def _list_tags_for_resource(self): + def _list_tags_for_resource(self, resource: any): logger.info("ElasticBeanstalk - List Tags...") try: - for environment_arn, environment in self.environments.items(): - try: - regional_client = self.regional_clients[environment.region] - response = regional_client.list_tags_for_resource( - ResourceArn=environment_arn - )["TagList"] - environment.tags = response - except Exception as error: - logger.error( - f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" - ) + regional_client = self.regional_clients[resource.region] + response = regional_client.list_tags_for_resource(ResourceArn=resource.arn)[ + "ResourceTags" + ] + resource.tags = response + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) except Exception as error: logger.error( f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -101,7 +100,7 @@ class Environment(BaseModel): arn: str region: str application_name: str - health_option: Optional[str] - managed_actions_option: Optional[str] - cloudwatch_option: Optional[str] + health_reporting: Optional[str] + managed_platform_updates: Optional[str] + cloudwatch_stream_logs: Optional[str] tags: Optional[list] = [] diff --git a/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py b/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py index 83a6ba7b19..e435bfec49 100644 --- a/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py +++ b/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py @@ -128,18 +128,38 @@ def test_describe_configuration_settings(self): # ElasticBeanstalk Class elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) assert ( - elasticbeanstalk.environments[environment["EnvironmentArn"]].health_option + elasticbeanstalk.environments[ + environment["EnvironmentArn"] + ].health_reporting == "enhanced" ) assert ( elasticbeanstalk.environments[ environment["EnvironmentArn"] - ].managed_actions_option + ].managed_platform_updates == "true" ) assert ( elasticbeanstalk.environments[ environment["EnvironmentArn"] - ].cloudwatch_option + ].cloudwatch_stream_logs == "true" ) + + @mock_aws + def test_list_tags_for_resource(self): + # Create ElasticBeanstalk app and env + elasticbeanstalk_client = client( + "elasticbeanstalk", region_name=AWS_REGION_EU_WEST_1 + ) + elasticbeanstalk_client.create_application(ApplicationName="test-app") + environment = elasticbeanstalk_client.create_environment( + ApplicationName="test-app", + EnvironmentName="test-env", + Tags=[{"Key": "test-key", "Value": "test-value"}], + ) + # ElasticBeanstalk Class + elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) + assert elasticbeanstalk.environments[environment["EnvironmentArn"]].tags == [ + {"Key": "test-key", "Value": "test-value"} + ] From 67f5f05d2b87c0f54d4c4667b7701ac71fdd0d87 Mon Sep 17 00:00:00 2001 From: MarioRgzLpz Date: Wed, 9 Oct 2024 15:05:36 +0200 Subject: [PATCH 8/9] feat(elasticbeanstalk): Add check logic with respective unit tests. Add metadata too --- .../__init__.py | 0 ...ced_health_reporting_enabled.metadata.json | 34 ++++ ...stalk_enhanced_health_reporting_enabled.py | 28 ++++ ..._enhanced_health_reporting_enabled_test.py | 146 ++++++++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/__init__.py create mode 100644 prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.metadata.json create mode 100644 prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.py create mode 100644 tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled_test.py diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/__init__.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.metadata.json b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.metadata.json new file mode 100644 index 0000000000..a86d31e47e --- /dev/null +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.metadata.json @@ -0,0 +1,34 @@ +{ + "Provider": "aws", + "CheckID": "elasticbeanstalk_enhanced_health_reporting_enabled", + "CheckTitle": "Elastic Beanstalk environments should have enhanced health reporting enabled", + "CheckType": [ + "Software and Configuration Checks/AWS Security Best Practices" + ], + "ServiceName": "elasticbeanstalk", + "SubServiceName": "", + "ResourceIdTemplate": "arn:aws:elasticbeanstalk:{region}:{account-id}:environment/{environment-id}", + "Severity": "low", + "ResourceType": "AwsElasticBeanstalkEnvironment", + "Description": "This control checks whether enhanced health reporting is enabled for your AWS Elastic Beanstalk environments.", + "Risk": "Without enhanced health reporting, you may face delays in detecting and responding to issues in your Elastic Beanstalk environment, affecting application availability and performance.", + "RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/beanstalk-enhanced-health-reporting-enabled.html", + "Remediation": { + "Code": { + "CLI": "aws elasticbeanstalk update-environment --environment-id --option-settings Namespace=aws:elasticbeanstalk:healthreporting:system,OptionName=EnhancedHealthReporting,Value=enabled", + "NativeIaC": "", + "Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/elasticbeanstalk-controls.html#elasticbeanstalk-1", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable enhanced health reporting in your Elastic Beanstalk environments for better monitoring and faster issue detection.", + "Url": "https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/health-enhanced-enable.html#health-enhanced-enable-console" + } + }, + "Categories": [ + "logging" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.py new file mode 100644 index 0000000000..d8a48b1318 --- /dev/null +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.py @@ -0,0 +1,28 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_client import ( + elasticbeanstalk_client, +) + + +class elasticbeanstalk_enhanced_health_reporting_enabled(Check): + def execute(self): + findings = [] + for environment in elasticbeanstalk_client.environments.values(): + report = Check_Report_AWS(self.metadata()) + report.region = environment.region + report.resource_id = environment.name + report.resource_arn = environment.arn + report.resource_tags = environment.tags + report.status = "PASS" + report.status_extended = f"Elastic Beanstalk environment {environment.name} has enhanced health reporting enabled." + + if ( + environment.health_reporting is None + or environment.health_reporting != "enhanced" + ): + report.status = "FAIL" + report.status_extended = f"Elastic Beanstalk environment {environment.name} does not have enhanced health reporting enabled." + + findings.append(report) + + return findings diff --git a/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled_test.py b/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled_test.py new file mode 100644 index 0000000000..2fc0d8b76a --- /dev/null +++ b/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled_test.py @@ -0,0 +1,146 @@ +from unittest import mock + +import botocore +from boto3 import client +from moto import mock_aws + +from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_service import ( + ElasticBeanstalk, +) +from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider + +make_api_call = botocore.client.BaseClient._make_api_call + + +def mock_make_api_call(self, operation_name, kwarg): + if operation_name == "DescribeConfigurationSettings": + if kwarg["EnvironmentName"] == "test-env-using-basic-health-reporting": + return { + "ConfigurationSettings": [ + { + "OptionSettings": [ + { + "Namespace": "aws:elasticbeanstalk:healthreporting:system", + "OptionName": "SystemType", + "Value": "basic", + }, + ], + } + ] + } + if kwarg["EnvironmentName"] == "test-env-using-enhanced-health-reporting": + return { + "ConfigurationSettings": [ + { + "OptionSettings": [ + { + "Namespace": "aws:elasticbeanstalk:healthreporting:system", + "OptionName": "SystemType", + "Value": "enhanced", + }, + ], + } + ] + } + + return make_api_call(self, operation_name, kwarg) + + +class Test_elasticbeanstalk_enhanced_health_reporting_enabled: + @mock_aws + def test_elasticbeanstalk_no_environments(self): + elasticbeanstalk_client = client( + "elasticbeanstalk", region_name=AWS_REGION_EU_WEST_1 + ) + elasticbeanstalk_client.create_application(ApplicationName="test-app") + + 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.elasticbeanstalk.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_client", + new=ElasticBeanstalk(aws_provider), + ): + from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_enhanced_health_reporting_enabled import ( + elasticbeanstalk_enhanced_health_reporting_enabled, + ) + + check = elasticbeanstalk_enhanced_health_reporting_enabled() + result = check.execute() + assert len(result) == 0 + + @mock_aws + @mock.patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call) + def test_elasticbeanstalk_environment_cloudwatch_not_enabled(self): + elasticbeanstalk_client = client( + "elasticbeanstalk", region_name=AWS_REGION_EU_WEST_1 + ) + elasticbeanstalk_client.create_application(ApplicationName="test-app") + environment = elasticbeanstalk_client.create_environment( + ApplicationName="test-app", + EnvironmentName="test-env-using-enhanced-health-reporting", + ) + + 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.elasticbeanstalk.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_client", + new=ElasticBeanstalk(aws_provider), + ): + from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_enhanced_health_reporting_enabled import ( + elasticbeanstalk_enhanced_health_reporting_enabled, + ) + + check = elasticbeanstalk_enhanced_health_reporting_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Elastic Beanstalk environment test-env-using-enhanced-health-reporting has enhanced health reporting enabled." + ) + assert result[0].resource_id == environment["EnvironmentName"] + assert result[0].resource_arn == environment["EnvironmentArn"] + assert result[0].region == AWS_REGION_EU_WEST_1 + + @mock_aws + @mock.patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call) + def test_elasticbeanstalk_environment_cloudwatch_enabled(self): + elasticbeanstalk_client = client( + "elasticbeanstalk", region_name=AWS_REGION_EU_WEST_1 + ) + elasticbeanstalk_client.create_application(ApplicationName="test-app") + environment = elasticbeanstalk_client.create_environment( + ApplicationName="test-app", + EnvironmentName="test-env-using-basic-health-reporting", + ) + + 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.elasticbeanstalk.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_client", + new=ElasticBeanstalk(aws_provider), + ): + from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_enhanced_health_reporting_enabled import ( + elasticbeanstalk_enhanced_health_reporting_enabled, + ) + + check = elasticbeanstalk_enhanced_health_reporting_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Elastic Beanstalk environment test-env-using-basic-health-reporting does not have enhanced health reporting enabled." + ) + assert result[0].resource_id == environment["EnvironmentName"] + assert result[0].resource_arn == environment["EnvironmentArn"] + assert result[0].region == AWS_REGION_EU_WEST_1 From 3441cccfa6d165358c4e00f6bf84b5e225487013 Mon Sep 17 00:00:00 2001 From: Sergio Date: Fri, 11 Oct 2024 09:45:44 -0400 Subject: [PATCH 9/9] chore: revision --- .../__init__.py | 0 ...t_enhanced_health_reporting.metadata.json} | 2 +- ..._environment_enhanced_health_reporting.py} | 7 ++--- ...ronment_enhanced_health_reporting_test.py} | 26 +++++++++---------- 4 files changed, 16 insertions(+), 19 deletions(-) rename prowler/providers/aws/services/elasticbeanstalk/{elasticbeanstalk_enhanced_health_reporting_enabled => elasticbeanstalk_environment_enhanced_health_reporting}/__init__.py (100%) rename prowler/providers/aws/services/elasticbeanstalk/{elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.metadata.json => elasticbeanstalk_environment_enhanced_health_reporting/elasticbeanstalk_environment_enhanced_health_reporting.metadata.json} (96%) rename prowler/providers/aws/services/elasticbeanstalk/{elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.py => elasticbeanstalk_environment_enhanced_health_reporting/elasticbeanstalk_environment_enhanced_health_reporting.py} (81%) rename tests/providers/aws/services/elasticbeanstalk/{elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled_test.py => elasticbeanstalk_environment_enhanced_health_reporting/elasticbeanstalk_environment_enhanced_health_reporting_test.py} (81%) diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/__init__.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_environment_enhanced_health_reporting/__init__.py similarity index 100% rename from prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/__init__.py rename to prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_environment_enhanced_health_reporting/__init__.py diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.metadata.json b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_environment_enhanced_health_reporting/elasticbeanstalk_environment_enhanced_health_reporting.metadata.json similarity index 96% rename from prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.metadata.json rename to prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_environment_enhanced_health_reporting/elasticbeanstalk_environment_enhanced_health_reporting.metadata.json index a86d31e47e..9f8c71ae28 100644 --- a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.metadata.json +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_environment_enhanced_health_reporting/elasticbeanstalk_environment_enhanced_health_reporting.metadata.json @@ -1,6 +1,6 @@ { "Provider": "aws", - "CheckID": "elasticbeanstalk_enhanced_health_reporting_enabled", + "CheckID": "elasticbeanstalk_environment_enhanced_health_reporting", "CheckTitle": "Elastic Beanstalk environments should have enhanced health reporting enabled", "CheckType": [ "Software and Configuration Checks/AWS Security Best Practices" diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_environment_enhanced_health_reporting/elasticbeanstalk_environment_enhanced_health_reporting.py similarity index 81% rename from prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.py rename to prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_environment_enhanced_health_reporting/elasticbeanstalk_environment_enhanced_health_reporting.py index d8a48b1318..0e6b0f570c 100644 --- a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled.py +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_environment_enhanced_health_reporting/elasticbeanstalk_environment_enhanced_health_reporting.py @@ -4,7 +4,7 @@ ) -class elasticbeanstalk_enhanced_health_reporting_enabled(Check): +class elasticbeanstalk_environment_enhanced_health_reporting(Check): def execute(self): findings = [] for environment in elasticbeanstalk_client.environments.values(): @@ -16,10 +16,7 @@ def execute(self): report.status = "PASS" report.status_extended = f"Elastic Beanstalk environment {environment.name} has enhanced health reporting enabled." - if ( - environment.health_reporting is None - or environment.health_reporting != "enhanced" - ): + if environment.health_reporting != "enhanced": report.status = "FAIL" report.status_extended = f"Elastic Beanstalk environment {environment.name} does not have enhanced health reporting enabled." diff --git a/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled_test.py b/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_environment_enhanced_health_reporting/elasticbeanstalk_environment_enhanced_health_reporting_test.py similarity index 81% rename from tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled_test.py rename to tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_environment_enhanced_health_reporting/elasticbeanstalk_environment_enhanced_health_reporting_test.py index 2fc0d8b76a..2716bb9c03 100644 --- a/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_enhanced_health_reporting_enabled/elasticbeanstalk_enhanced_health_reporting_enabled_test.py +++ b/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_environment_enhanced_health_reporting/elasticbeanstalk_environment_enhanced_health_reporting_test.py @@ -46,7 +46,7 @@ def mock_make_api_call(self, operation_name, kwarg): return make_api_call(self, operation_name, kwarg) -class Test_elasticbeanstalk_enhanced_health_reporting_enabled: +class Test_elasticbeanstalk_environment_enhanced_health_reporting: @mock_aws def test_elasticbeanstalk_no_environments(self): elasticbeanstalk_client = client( @@ -60,14 +60,14 @@ def test_elasticbeanstalk_no_environments(self): "prowler.providers.common.provider.Provider.get_global_provider", return_value=aws_provider, ), mock.patch( - "prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_client", + "prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_enhanced_health_reporting.elasticbeanstalk_environment_enhanced_health_reporting.elasticbeanstalk_client", new=ElasticBeanstalk(aws_provider), ): - from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_enhanced_health_reporting_enabled import ( - elasticbeanstalk_enhanced_health_reporting_enabled, + from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_enhanced_health_reporting.elasticbeanstalk_environment_enhanced_health_reporting import ( + elasticbeanstalk_environment_enhanced_health_reporting, ) - check = elasticbeanstalk_enhanced_health_reporting_enabled() + check = elasticbeanstalk_environment_enhanced_health_reporting() result = check.execute() assert len(result) == 0 @@ -89,14 +89,14 @@ def test_elasticbeanstalk_environment_cloudwatch_not_enabled(self): "prowler.providers.common.provider.Provider.get_global_provider", return_value=aws_provider, ), mock.patch( - "prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_client", + "prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_enhanced_health_reporting.elasticbeanstalk_environment_enhanced_health_reporting.elasticbeanstalk_client", new=ElasticBeanstalk(aws_provider), ): - from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_enhanced_health_reporting_enabled import ( - elasticbeanstalk_enhanced_health_reporting_enabled, + from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_enhanced_health_reporting.elasticbeanstalk_environment_enhanced_health_reporting import ( + elasticbeanstalk_environment_enhanced_health_reporting, ) - check = elasticbeanstalk_enhanced_health_reporting_enabled() + check = elasticbeanstalk_environment_enhanced_health_reporting() result = check.execute() assert len(result) == 1 assert result[0].status == "PASS" @@ -126,14 +126,14 @@ def test_elasticbeanstalk_environment_cloudwatch_enabled(self): "prowler.providers.common.provider.Provider.get_global_provider", return_value=aws_provider, ), mock.patch( - "prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_client", + "prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_enhanced_health_reporting.elasticbeanstalk_environment_enhanced_health_reporting.elasticbeanstalk_client", new=ElasticBeanstalk(aws_provider), ): - from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_enhanced_health_reporting_enabled.elasticbeanstalk_enhanced_health_reporting_enabled import ( - elasticbeanstalk_enhanced_health_reporting_enabled, + from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_enhanced_health_reporting.elasticbeanstalk_environment_enhanced_health_reporting import ( + elasticbeanstalk_environment_enhanced_health_reporting, ) - check = elasticbeanstalk_enhanced_health_reporting_enabled() + check = elasticbeanstalk_environment_enhanced_health_reporting() result = check.execute() assert len(result) == 1 assert result[0].status == "FAIL"