Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"Provider": "aws",
"CheckID": "elasticbeanstalk_environment_no_secrets_in_configuration",
"CheckTitle": "Elastic Beanstalk environment configuration has no hardcoded secrets",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices",
"TTPs/Credential Access",
"Effects/Data Exposure",
"Sensitive Data Identifications/Security"
],
"ServiceName": "elasticbeanstalk",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:elasticbeanstalk:region:account-id:environment/environment-name",
"Severity": "high",
"ResourceType": "AwsElasticBeanstalkEnvironment",
"ResourceGroup": "security",
"Description": "AWS Elastic Beanstalk environments are inspected for hardcoded secrets in configuration option settings. Secrets such as API keys, passwords, access tokens, or credentials should not be stored in environment configuration.",
"Risk": "Plaintext secrets stored in Elastic Beanstalk environment configuration can be viewed by users with read access to the environment configuration, increasing the risk of credential exposure and unauthorized access to downstream resources.",
"RelatedUrl": "",
"AdditionalURLs": [
"https://docs.aws.amazon.com/elasticbeanstalk/latest/api/API_DescribeConfigurationSettings.html",
"https://docs.aws.amazon.com/boto3/latest/reference/services/elasticbeanstalk/client/describe_configuration_settings.html",
"https://docs.prowler.com/developer-guide/secret-scanning-checks"
],
"Remediation": {
"Code": {
"CLI": "aws elasticbeanstalk update-environment --environment-name <environment-name> --option-settings Namespace=<namespace>,OptionName=<option-name>,Value=<secure-reference>",
"NativeIaC": "",
"Other": "1. Review the Elastic Beanstalk environment configuration.\n2. Remove hardcoded secrets from OptionSettings.\n3. Store secrets in AWS Secrets Manager or AWS Systems Manager Parameter Store.\n4. Configure the application to retrieve secrets securely at runtime instead of storing them in environment configuration.",
"Terraform": ""
},
"Recommendation": {
"Text": "Avoid storing secrets in Elastic Beanstalk environment configuration. Store sensitive values in AWS Secrets Manager or AWS Systems Manager Parameter Store and retrieve them securely at runtime.",
"Url": ""
}
},
"Categories": [
"secrets"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.lib.utils.utils import (
SecretsScanError,
annotate_verified_secrets,
detect_secrets_scan_batch,
)
from prowler.providers.aws.services.elasticbeanstalk import elasticbeanstalk_client

class elasticbeanstalk_environment_no_secrets_in_configuration(Check):
def execute(self) :
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
findings = []
secrets_ignore_patterns = elasticbeanstalk_client.audit_config.get(
"secrets_ignore_patterns", []
)
validate = elasticbeanstalk_client.audit_config.get("secrets_validate", False)

def payloads():
for environment in elasticbeanstalk_client.environments.values():
for option_setting in environment.option_settings:
value = option_setting.get("Value")
if not value:
continue
yield((environment.arn, option_setting["Namespace"], option_setting["OptionName"]), value)

scan_error = None
try:
batch_results = detect_secrets_scan_batch(
payloads(),
excluded_secrets=secrets_ignore_patterns,
validate=validate,
)
except SecretsScanError as error:
batch_results = {}
scan_error = error

if scan_error:
for environment in elasticbeanstalk_client.environments.values():
report = Check_Report_AWS(metadata=self.metadata(), resource=environment)
report.status = "MANUAL"
report.status_extended = (
f"Could not scan Elastic BeanStalk environment configuration for "
f"{environment.name} environment for secrets: {scan_error}; "
f"manual review is required."
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
findings.append(report)
return findings

for environment in elasticbeanstalk_client.environments.values():
report = Check_Report_AWS(metadata=self.metadata(), resource=environment)
report.status = "PASS"
report.status_extended = (
f"No secrets found in Elastic BeanStalk environment configuration for {environment.name} environment."
)
detected_secret_settings = []
all_secrets = []
for option_setting in environment.option_settings:
detect_secret_outputs = batch_results.get((environment.arn, option_setting["Namespace"], option_setting["OptionName"]))

if detect_secret_outputs:
detected_secret_settings.append((option_setting["Namespace"], option_setting["OptionName"]))
all_secrets.extend(detect_secret_outputs)

if detected_secret_settings:
secret_setting = "; ".join(
f"Namespace: {namespace}, OptionName: {option_name}" for namespace, option_name in detected_secret_settings
)
report.status = "FAIL"
report.status_extended = (
f"Potential "
f"{'secrets' if len(detected_secret_settings) > 1 else 'secret'} "
f"found in Elastic BeanStalk environment configuration for "
f"{environment.name} environment -> {secret_setting}."
)
annotate_verified_secrets(report, all_secrets)

findings.append(report)

return findings
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ def _describe_configuration_settings(self, environment):
option_settings = configuration_settings["ConfigurationSettings"][0].get(
"OptionSettings", {}
)
environment.option_settings = option_settings

for option in option_settings:
if (
option["Namespace"] == "aws:elasticbeanstalk:healthreporting:system"
Expand Down Expand Up @@ -123,3 +125,4 @@ class Environment(BaseModel):
managed_platform_updates: Optional[str]
cloudwatch_stream_logs: Optional[str]
tags: Optional[list] = []
option_settings: list
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
from unittest import mock

from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_service import Environment
from prowler.lib.utils.utils import SecretsScanError
from tests.providers.aws.utils import (
AWS_ACCOUNT_NUMBER,
AWS_REGION_US_EAST_1,
set_mocked_aws_provider,
)

class Test_elasticbeanstalk_environment_no_secrets_in_configuration:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
def test_environment_configuration_with_secrets(self):
elasticbeanstalk_client = mock.MagicMock()
elasticbeanstalk_client.audit_config = {"secrets_ignore_patterns": []}
eb_env_arn = f"arn:partition:elasticbeanstalk:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:environment/production"

environment = Environment(
id = "e-vbxmknpy2z",
name = "production",
arn = eb_env_arn,
region = AWS_REGION_US_EAST_1,
application_name = "test-app",
option_settings = [
{
"Namespace": "aws:elasticbeanstalk:application:environment",
"OptionName": "JSON_WEB_TOKEN",
"Value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U",
},
{
"Namespace": "aws:elasticbeanstalk:application:environment",
"OptionName": "MONGODB_URI",
"Value": "mongodb+srv://admin_prod:Passw0rd99!@cluster-prod-xyz.1a2b3.mongodb.net/enterprise_db?retryWrites=true&w=majority",
}
]
)
elasticbeanstalk_client.environments = {
eb_env_arn : environment
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_no_secrets_in_configuration.elasticbeanstalk_environment_no_secrets_in_configuration.elasticbeanstalk_client",
new=elasticbeanstalk_client,
),
):
from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_no_secrets_in_configuration.elasticbeanstalk_environment_no_secrets_in_configuration import (
elasticbeanstalk_environment_no_secrets_in_configuration
)
check = elasticbeanstalk_environment_no_secrets_in_configuration()
result = check.execute()

assert len(result) == 1
assert result[0].status == "FAIL"
assert environment.name in result[0].status_extended
assert "JSON_WEB_TOKEN" in result[0].status_extended
assert "MONGODB_URI" in result[0].status_extended

def test_environment_configuration_without_secrets(self):
elasticbeanstalk_client = mock.MagicMock()
elasticbeanstalk_client.audit_config = {"secrets_ignore_patterns": []}
eb_env_arn = f"arn:partition:elasticbeanstalk:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:environment/staging"

environment = Environment(
id = "e-icsgecu3wf",
name = "staging",
arn = eb_env_arn,
region = AWS_REGION_US_EAST_1,
application_name = "test-app",
option_settings = [
{
"Namespace": "aws:elasticbeanstalk:application:environment",
"OptionName": "LOG_LEVEL",
"Value": "INFO",
},
{
"Namespace": "aws:elasticbeanstalk:application:environment",
"OptionName": "SystemType",
"Value": "enhanced",
}
]
)
elasticbeanstalk_client.environments = {
eb_env_arn: environment
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_no_secrets_in_configuration.elasticbeanstalk_environment_no_secrets_in_configuration.elasticbeanstalk_client",
new=elasticbeanstalk_client,
),
):
from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_no_secrets_in_configuration.elasticbeanstalk_environment_no_secrets_in_configuration import (
elasticbeanstalk_environment_no_secrets_in_configuration
)
check = elasticbeanstalk_environment_no_secrets_in_configuration()
result = check.execute()

assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"No secrets found in Elastic BeanStalk environment configuration for {environment.name} environment."
)

def test_environment_configuration_scan_error(self):
elasticbeanstalk_client = mock.MagicMock()
elasticbeanstalk_client.audit_config = {"secrets_ignore_patterns": []}
eb_env_arn = f"arn:partition:elasticbeanstalk:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:environment/testing"

environment = Environment(
id = "e-mz7paq4pqp",
name = "testing",
arn = eb_env_arn,
region = AWS_REGION_US_EAST_1,
application_name = "test-app",
option_settings = [
{
"Namespace": "aws:elasticbeanstalk:application:environment",
"OptionName": "DB_SSL_ENABLED",
"Value": "true",
},
{
"Namespace": "aws:elasticbeanstalk:application:environment",
"OptionName": "CACHE_TTL",
"Value": "300",
}
]
)
elasticbeanstalk_client.environments = {
eb_env_arn: environment
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_no_secrets_in_configuration.elasticbeanstalk_environment_no_secrets_in_configuration.elasticbeanstalk_client",
new=elasticbeanstalk_client,
),
mock.patch(
"prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_no_secrets_in_configuration.elasticbeanstalk_environment_no_secrets_in_configuration.detect_secrets_scan_batch",
side_effect=SecretsScanError("Kingfisher exited with code 1"),
),
):
from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_no_secrets_in_configuration.elasticbeanstalk_environment_no_secrets_in_configuration import (
elasticbeanstalk_environment_no_secrets_in_configuration
)
check = elasticbeanstalk_environment_no_secrets_in_configuration()
result = check.execute()

assert len(result) == 1
assert result[0].status == "MANUAL"
assert f"Could not scan Elastic BeanStalk environment configuration for {environment.name} environment" in result[0].status_extended

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
Loading