feat(awslambda): add secrets_ignore_files to skip false-positive files (e.g. *.deps.json) in no-secrets-in-code check (#11222)

Co-authored-by: Daniel Barranquero <danielbo2001@gmail.com>
This commit is contained in:
Hugo Pereira Brito
2026-07-01 09:03:25 +01:00
committed by GitHub
parent af6918d57b
commit fd38a0ac03
6 changed files with 246 additions and 12 deletions
+23
View File
@@ -176,6 +176,29 @@ class Test_AWS_Enums:
assert _validate({"ecr_repository_vulnerability_minimum_severity": level}) == {}
class Test_AWS_Secrets_Ignore_Files:
def test_valid_file_patterns_round_trip(self):
files = ["*.deps.json", "vendor/*.js"]
assert _validate({"secrets_ignore_files": files}) == {
"secrets_ignore_files": files
}
def test_empty_list_is_valid(self):
assert _validate({"secrets_ignore_files": []}) == {"secrets_ignore_files": []}
def test_exposed_in_scan_config_schema(self):
aws_properties = SCAN_CONFIG_SCHEMA["properties"]["aws"]["properties"]
assert aws_properties["secrets_ignore_files"] == {
"anyOf": [
{"items": {"type": "string"}, "type": "array"},
{"type": "null"},
],
"default": None,
"title": "Secrets Ignore Files",
}
class Test_AWS_Booleans:
@pytest.mark.parametrize(
"key",
@@ -1,3 +1,4 @@
import os
import zipfile
from unittest import mock
@@ -53,6 +54,57 @@ def get_lambda_code_with_secrets(code):
)
LAMBDA_DEPS_JSON_WITH_SECRET = """
{
"runtimeTarget": { "name": ".NETCoreApp,Version=v8.0" },
"libraries": {
"AWSSDK.SecretsManager/3.7.0": {
"type": "package",
"password": "test-deps-json-password"
}
}
}
"""
LAMBDA_VENDOR_JS_WITH_SECRET = """
const dbPassword = "test-vendor-password";
"""
def get_lambda_code_from_files(files: dict) -> LambdaCode:
# The check only calls code_zip.extractall(dir); mock it to drop the
# given files into the temporary directory the check creates, so no
# real archive needs to be built.
code_zip = mock.MagicMock()
def _extractall(path):
for name, content in files.items():
os.makedirs(os.path.dirname(f"{path}/{name}"), exist_ok=True)
with open(f"{path}/{name}", "w") as fd:
fd.write(content)
code_zip.extractall.side_effect = _extractall
return LambdaCode(location="", code_zip=code_zip)
def mock_get_function_code_with_deps_json_secret():
yield create_lambda_function(), get_lambda_code_from_files(
{
"lambda_function.py": LAMBDA_FUNCTION_CODE_WITHOUT_SECRETS,
"myapp.deps.json": LAMBDA_DEPS_JSON_WITH_SECRET,
}
)
def mock_get_function_code_with_nested_vendor_secret():
yield create_lambda_function(), get_lambda_code_from_files(
{
"lambda_function.py": LAMBDA_FUNCTION_CODE_WITHOUT_SECRETS,
"vendor/package.js": LAMBDA_VENDOR_JS_WITH_SECRET,
}
)
def mock_get_function_codewith_secrets():
yield create_lambda_function(), get_lambda_code_with_secrets(
LAMBDA_FUNCTION_CODE_WITH_SECRETS
@@ -202,6 +254,133 @@ class Test_awslambda_function_no_secrets_in_code:
)
assert result[0].resource_tags == []
def test_function_code_deps_json_secret_not_ignored(self):
lambda_client = mock.MagicMock
lambda_client.functions = {LAMBDA_FUNCTION_ARN: create_lambda_function()}
lambda_client._get_function_code = mock_get_function_code_with_deps_json_secret
lambda_client.audit_config = {
"secrets_ignore_patterns": [],
"secrets_ignore_files": None,
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.awslambda.awslambda_function_no_secrets_in_code.awslambda_function_no_secrets_in_code.awslambda_client",
new=lambda_client,
),
):
from prowler.providers.aws.services.awslambda.awslambda_function_no_secrets_in_code.awslambda_function_no_secrets_in_code import (
awslambda_function_no_secrets_in_code,
)
check = awslambda_function_no_secrets_in_code()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert "myapp.deps.json" in result[0].status_extended
def test_function_code_nested_vendor_secret_not_ignored(self):
lambda_client = mock.MagicMock
lambda_client.functions = {LAMBDA_FUNCTION_ARN: create_lambda_function()}
lambda_client._get_function_code = (
mock_get_function_code_with_nested_vendor_secret
)
lambda_client.audit_config = {"secrets_ignore_patterns": []}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.awslambda.awslambda_function_no_secrets_in_code.awslambda_function_no_secrets_in_code.awslambda_client",
new=lambda_client,
),
):
from prowler.providers.aws.services.awslambda.awslambda_function_no_secrets_in_code.awslambda_function_no_secrets_in_code import (
awslambda_function_no_secrets_in_code,
)
check = awslambda_function_no_secrets_in_code()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert "vendor/package.js" in result[0].status_extended
def test_function_code_nested_vendor_secret_ignored_by_file_pattern(self):
lambda_client = mock.MagicMock
lambda_client.functions = {LAMBDA_FUNCTION_ARN: create_lambda_function()}
lambda_client._get_function_code = (
mock_get_function_code_with_nested_vendor_secret
)
lambda_client.audit_config = {
"secrets_ignore_patterns": [],
"secrets_ignore_files": ["vendor/*.js"],
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.awslambda.awslambda_function_no_secrets_in_code.awslambda_function_no_secrets_in_code.awslambda_client",
new=lambda_client,
),
):
from prowler.providers.aws.services.awslambda.awslambda_function_no_secrets_in_code.awslambda_function_no_secrets_in_code import (
awslambda_function_no_secrets_in_code,
)
check = awslambda_function_no_secrets_in_code()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
def test_function_code_deps_json_secret_ignored_by_file_pattern(self):
lambda_client = mock.MagicMock
lambda_client.functions = {LAMBDA_FUNCTION_ARN: create_lambda_function()}
lambda_client._get_function_code = mock_get_function_code_with_deps_json_secret
lambda_client.audit_config = {
"secrets_ignore_patterns": [],
"secrets_ignore_files": ["*.deps.json"],
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.awslambda.awslambda_function_no_secrets_in_code.awslambda_function_no_secrets_in_code.awslambda_client",
new=lambda_client,
),
):
from prowler.providers.aws.services.awslambda.awslambda_function_no_secrets_in_code.awslambda_function_no_secrets_in_code import (
awslambda_function_no_secrets_in_code,
)
check = awslambda_function_no_secrets_in_code()
result = check.execute()
assert len(result) == 1
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_id == LAMBDA_FUNCTION_NAME
assert result[0].resource_arn == LAMBDA_FUNCTION_ARN
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"No secrets found in Lambda function {LAMBDA_FUNCTION_NAME} code."
)
assert result[0].resource_tags == []
def test_scan_failure_reports_manual_not_pass(self):
from prowler.lib.utils.utils import SecretsScanError
@@ -209,6 +388,7 @@ class Test_awslambda_function_no_secrets_in_code:
lambda_client.functions = {LAMBDA_FUNCTION_ARN: create_lambda_function()}
lambda_client._get_function_code = mock_get_function_codewith_secrets
lambda_client.audit_config = {"secrets_ignore_patterns": []}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",