fix(ecr): read each registry scanning rule's frequency instead of assuming scan on push (#12560)

This commit is contained in:
Jonathan Nguyen
2026-09-01 16:53:58 +02:00
committed by GitHub
parent 9c5285adc3
commit 9ffbb4b758
4 changed files with 300 additions and 16 deletions
@@ -72,6 +72,7 @@ class Test_ecr_registry_scan_images_on_push_enabled:
assert len(result) == 0
def test_registry_scan_on_push_enabled(self):
"""A BASIC registry whose one unfiltered rule is SCAN_ON_PUSH passes as scan on push."""
ecr_client = mock.MagicMock
ecr_client.audited_account_arn = AWS_ACCOUNT_ARN
ecr_client.registries = {}
@@ -119,7 +120,7 @@ class Test_ecr_registry_scan_images_on_push_enabled:
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"ECR registry {AWS_ACCOUNT_NUMBER} has BASIC scan with scan on push enabled."
== f"ECR registry {AWS_ACCOUNT_NUMBER} has BASIC scanning with scan on push for all repositories."
)
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
assert (
@@ -186,6 +187,7 @@ class Test_ecr_registry_scan_images_on_push_enabled:
assert result[0].region == AWS_REGION_EU_WEST_1
def test_scan_on_push_disabled(self):
"""A registry with no scanning rules at all fails: no frequency is configured to read."""
ecr_client = mock.MagicMock
ecr_client.audited_account_arn = AWS_ACCOUNT_ARN
ecr_client.registries = {}
@@ -228,7 +230,7 @@ class Test_ecr_registry_scan_images_on_push_enabled:
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"ECR registry {AWS_ACCOUNT_NUMBER} has BASIC scanning without scan on push enabled."
== f"ECR registry {AWS_ACCOUNT_NUMBER} has BASIC scanning without automated scanning enabled."
)
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
assert (
@@ -236,3 +238,242 @@ class Test_ecr_registry_scan_images_on_push_enabled:
== f"arn:aws:ecr:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:registry/{AWS_ACCOUNT_NUMBER}"
)
assert result[0].region == AWS_REGION_EU_WEST_1
def test_enhanced_with_no_rules_fails(self):
"""An ENHANCED registry with an empty rule set fails: nothing is scanned automatically.
Reviewed as a false FAIL, on the grounds that enhanced scanning defaults to continuous
scanning for all repositories when no rules are configured. It does not. Enhanced scanning
is filter-driven, and a repository matching no filter is not scanned at all: "Any
repositories that don't match a filter will have an Off scan frequency and won't be
scanned" (https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning-enhanced.html),
and "Any repositories not matching an enhanced scanning filter will have scanning disabled"
(https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning-filters.html). With
no rules at all, no repository matches, so the whole registry goes unscanned.
Nor is this state a disguised pass. ECR materialises "continuous scanning for all
repositories" as an explicit CONTINUOUS_SCAN rule filtered on '*' -- which the check already
passes, and which an observed ENHANCED registry returns instead of an empty list. So an
empty rule set is either unreachable or genuinely means nothing is scanned, and PASS here
would report a registry that scans nothing as covered.
"""
ecr_client = mock.MagicMock
ecr_client.audited_account_arn = AWS_ACCOUNT_ARN
ecr_client.registries = {}
ecr_client.registries[AWS_REGION_EU_WEST_1] = Registry(
id=AWS_ACCOUNT_NUMBER,
arn=f"arn:aws:ecr:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:registry/{AWS_ACCOUNT_NUMBER}",
region=AWS_REGION_EU_WEST_1,
scan_type="ENHANCED",
repositories=[
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION_EU_WEST_1,
scan_on_push=False,
policy="",
images_details=None,
lifecycle_policy="",
)
],
rules=[],
)
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.ecr.ecr_registry_scan_images_on_push_enabled.ecr_registry_scan_images_on_push_enabled.ecr_client",
ecr_client,
),
):
from prowler.providers.aws.services.ecr.ecr_registry_scan_images_on_push_enabled.ecr_registry_scan_images_on_push_enabled import (
ecr_registry_scan_images_on_push_enabled,
)
check = ecr_registry_scan_images_on_push_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"ECR registry {AWS_ACCOUNT_NUMBER} has ENHANCED scanning without automated scanning enabled."
)
def test_continuous_scan_is_not_reported_as_scan_on_push(self):
"""A CONTINUOUS_SCAN-only registry passes, but must not be described as scan on push.
Observed against a live ENHANCED registry whose single rule was CONTINUOUS_SCAN: the check
reported \"scan with scan on push enabled\", a configuration the registry did not have. The
verdict was right and the sentence was not, because scan-on-push was inferred from the mere
presence of a rule and scanFrequency was never read."""
ecr_client = mock.MagicMock
ecr_client.audited_account_arn = AWS_ACCOUNT_ARN
ecr_client.registries = {}
ecr_client.registries[AWS_REGION_EU_WEST_1] = Registry(
id=AWS_ACCOUNT_NUMBER,
arn=f"arn:aws:ecr:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:registry/{AWS_ACCOUNT_NUMBER}",
region=AWS_REGION_EU_WEST_1,
scan_type="ENHANCED",
repositories=[
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION_EU_WEST_1,
scan_on_push=False,
policy="",
images_details=None,
lifecycle_policy="",
)
],
rules=[
ScanningRule(
scan_frequency="CONTINUOUS_SCAN",
scan_filters=[{"filter": "*", "filterType": "WILDCARD"}],
)
],
)
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.ecr.ecr_registry_scan_images_on_push_enabled.ecr_registry_scan_images_on_push_enabled.ecr_client",
ecr_client,
),
):
from prowler.providers.aws.services.ecr.ecr_registry_scan_images_on_push_enabled.ecr_registry_scan_images_on_push_enabled import (
ecr_registry_scan_images_on_push_enabled,
)
check = ecr_registry_scan_images_on_push_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"ECR registry {AWS_ACCOUNT_NUMBER} has ENHANCED scanning with continuous scanning for all repositories."
)
def test_manual_only_scanning_fails(self):
"""MANUAL is the third frequency the API returns, and nothing scans a pushed image.
The registry is BASIC because that is the only scan type MANUAL occurs under: scanFrequency
documents CONTINUOUS_SCAN and SCAN_ON_PUSH for ENHANCED, and MANUAL as the BASIC default
when scan on push is not specified. Before this was read, any rule at all produced PASS --
so a registry that scans nothing until someone asks reported as scanning on push.
"""
ecr_client = mock.MagicMock
ecr_client.audited_account_arn = AWS_ACCOUNT_ARN
ecr_client.registries = {}
ecr_client.registries[AWS_REGION_EU_WEST_1] = Registry(
id=AWS_ACCOUNT_NUMBER,
arn=f"arn:aws:ecr:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:registry/{AWS_ACCOUNT_NUMBER}",
region=AWS_REGION_EU_WEST_1,
scan_type="BASIC",
repositories=[
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION_EU_WEST_1,
scan_on_push=False,
policy="",
images_details=None,
lifecycle_policy="",
)
],
rules=[
ScanningRule(
scan_frequency="MANUAL",
scan_filters=[{"filter": "*", "filterType": "WILDCARD"}],
)
],
)
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.ecr.ecr_registry_scan_images_on_push_enabled.ecr_registry_scan_images_on_push_enabled.ecr_client",
ecr_client,
),
):
from prowler.providers.aws.services.ecr.ecr_registry_scan_images_on_push_enabled.ecr_registry_scan_images_on_push_enabled import (
ecr_registry_scan_images_on_push_enabled,
)
check = ecr_registry_scan_images_on_push_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"ECR registry {AWS_ACCOUNT_NUMBER} has BASIC scanning set to manual only, so images are not scanned when they are pushed."
)
def test_both_frequencies_are_named_in_a_fixed_order(self):
"""Two rules, one of each frequency: the wording is ordered, not set-iteration order.
ECR allows up to two rules, so this is a real configuration rather than a contrived one. The
frequencies are collected into a set, and rendering a set directly would let the sentence vary
between runs for identical input.
"""
ecr_client = mock.MagicMock
ecr_client.audited_account_arn = AWS_ACCOUNT_ARN
ecr_client.registries = {}
ecr_client.registries[AWS_REGION_EU_WEST_1] = Registry(
id=AWS_ACCOUNT_NUMBER,
arn=f"arn:aws:ecr:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:registry/{AWS_ACCOUNT_NUMBER}",
region=AWS_REGION_EU_WEST_1,
scan_type="ENHANCED",
repositories=[
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION_EU_WEST_1,
scan_on_push=True,
policy="",
images_details=None,
lifecycle_policy="",
)
],
rules=[
ScanningRule(
scan_frequency="CONTINUOUS_SCAN",
scan_filters=[{"filter": "*", "filterType": "WILDCARD"}],
),
ScanningRule(
scan_frequency="SCAN_ON_PUSH",
scan_filters=[{"filter": "*", "filterType": "WILDCARD"}],
),
],
)
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.ecr.ecr_registry_scan_images_on_push_enabled.ecr_registry_scan_images_on_push_enabled.ecr_client",
ecr_client,
),
):
from prowler.providers.aws.services.ecr.ecr_registry_scan_images_on_push_enabled.ecr_registry_scan_images_on_push_enabled import (
ecr_registry_scan_images_on_push_enabled,
)
check = ecr_registry_scan_images_on_push_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"ECR registry {AWS_ACCOUNT_NUMBER} has ENHANCED scanning with scan on push and continuous scanning for all repositories."
)