Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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 @@
`elasticbeanstalk_environment_no_secrets_in_configuration` check for AWS provider, scanning the option settings of every Elastic Beanstalk environment for hardcoded secrets
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": "compute",
"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": "https://hub.prowler.com/check/elasticbeanstalk_environment_no_secrets_in_configuration"
}
},
"Categories": [
"secrets"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": "False positives can be suppressed with the `secrets_ignore_patterns` configuration option. When `secrets_validate` is enabled and a detected secret is confirmed to be live, the finding is escalated to critical."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import json

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.elasticbeanstalk_client import (
elasticbeanstalk_client,
)


class elasticbeanstalk_environment_no_secrets_in_configuration(Check):
"""Check that Elastic Beanstalk environment configurations contain no hardcoded secrets."""

def execute(self) -> list[Check_Report_AWS]:
"""Scan the option settings of each Elastic Beanstalk environment for secrets.

Every option setting is scanned as ``{OptionName: Value}`` so the scanner
gets the same name context the other secrets checks provide. Findings are
keyed by ``(environment index, option setting index)`` because the same
namespace and option name can appear more than once per environment (one
entry per ``ResourceName``).

Returns:
list[Check_Report_AWS]: A report for each Elastic Beanstalk environment.
"""
findings = []
secrets_ignore_patterns = elasticbeanstalk_client.audit_config.get(
"secrets_ignore_patterns", []
)
validate = elasticbeanstalk_client.audit_config.get("secrets_validate", False)
environments = list(elasticbeanstalk_client.environments.values())

# Phase 1: collect — build the payload strings only, no scan yet.
def payloads():
for environment_index, environment in enumerate(environments):
for option_index, option_setting in enumerate(
environment.option_settings or []
):
value = option_setting.get("Value")
if not value:
continue
yield (environment_index, option_index), json.dumps(
{option_setting.get("OptionName", ""): value}
)

# Phase 2: batch — one scan for every environment.
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

# Phase 3: report — one finding per environment.
for environment_index, environment in enumerate(environments):
report = Check_Report_AWS(metadata=self.metadata(), resource=environment)

if environment.option_settings is None:
report.status = "MANUAL"
report.status_extended = (
f"Could not retrieve the configuration of Elastic Beanstalk "
f"environment {environment.name}; manual review is required."
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
findings.append(report)
continue

if scan_error and any(
option_setting.get("Value")
for option_setting in environment.option_settings
):
report.status = "MANUAL"
report.status_extended = (
f"Could not scan the configuration of Elastic Beanstalk "
f"environment {environment.name} for secrets; manual review is required."
)
findings.append(report)
continue

report.status = "PASS"
report.status_extended = f"No secrets found in the configuration of Elastic Beanstalk environment {environment.name}."

secret_settings = []
all_secrets = []
for option_index, option_setting in enumerate(environment.option_settings):
detect_secrets_output = batch_results.get(
(environment_index, option_index)
)
if detect_secrets_output:
all_secrets.extend(detect_secrets_output)
secret_settings.append(
f"{option_setting.get('Namespace', '')}/{option_setting.get('OptionName', '')}"
)

if secret_settings:
report.status = "FAIL"
report.status_extended = (
f"Potential {'secrets' if len(secret_settings) > 1 else 'secret'} "
f"found in the configuration of Elastic Beanstalk environment "
f"{environment.name} -> {', '.join(secret_settings)}."
)
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: Optional[list] = None
Loading
Loading