fix(ecr): Refactor service (#2302)

Co-authored-by: Gabriel Soltz <thegaby@gmail.com>
Co-authored-by: Kay Agahd <kagahd@users.noreply.github.com>
Co-authored-by: Nacho Rivera <nachor1992@gmail.com>
Co-authored-by: Kevin Pullin <kevin.pullin@gmail.com>
Co-authored-by: Sergio Garcia <sergargar1@gmail.com>
This commit is contained in:
Pepe Fagoaga
2023-05-09 17:04:21 +02:00
committed by GitHub
parent d344318dd4
commit 6f48012234
13 changed files with 894 additions and 454 deletions

View File

@@ -5,24 +5,27 @@ from prowler.providers.aws.services.ecr.ecr_client import ecr_client
class ecr_registry_scan_images_on_push_enabled(Check): class ecr_registry_scan_images_on_push_enabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
for registry in ecr_client.registries: for registry in ecr_client.registries.values():
report = Check_Report_AWS(self.metadata()) # We want to check the registry if it is in use, hence there are repositories
report.region = registry.region if len(registry.repositories) != 0:
report.resource_id = registry.id report = Check_Report_AWS(self.metadata())
report.resource_tags = registry.tags report.region = registry.region
report.status = "FAIL" report.resource_id = registry.id
report.status_extended = f"ECR registry {registry.id} has {registry.scan_type} scanning without scan on push" # A registry cannot have tags
if registry.rules: report.resource_tags = []
report.status = "PASS" report.status = "FAIL"
report.status_extended = f"ECR registry {registry.id} has {registry.scan_type} scan with scan on push" report.status_extended = f"ECR registry {registry.id} has {registry.scan_type} scanning without scan on push enabled"
filters = True if registry.rules:
for rule in registry.rules: report.status = "PASS"
if not rule.scan_filters or "'*'" in str(rule.scan_filters): report.status_extended = f"ECR registry {registry.id} has {registry.scan_type} scan with scan on push enabled"
filters = False filters = True
if filters: for rule in registry.rules:
report.status = "FAIL" if not rule.scan_filters or "'*'" in str(rule.scan_filters):
report.status_extended = f"ECR registry {registry.id} has {registry.scan_type} scanning with scan on push but with repository filters" filters = False
if filters:
report.status = "FAIL"
report.status_extended = f"ECR registry {registry.id} has {registry.scan_type} scanning with scan on push but with repository filters"
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -5,22 +5,19 @@ from prowler.providers.aws.services.ecr.ecr_client import ecr_client
class ecr_repositories_lifecycle_policy_enabled(Check): class ecr_repositories_lifecycle_policy_enabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
for repository in ecr_client.repositories: for registry in ecr_client.registries.values():
report = Check_Report_AWS(self.metadata()) for repository in registry.repositories:
report.region = repository.region report = Check_Report_AWS(self.metadata())
report.resource_id = repository.name report.region = repository.region
report.resource_arn = repository.arn report.resource_id = repository.name
report.resource_tags = repository.tags report.resource_arn = repository.arn
report.status = "FAIL" report.resource_tags = repository.tags
report.status_extended = ( report.status = "FAIL"
f"Repository {repository.name} has no lifecycle policy" report.status_extended = f"Repository {repository.name} has not a lifecycle policy configured"
) if repository.lifecycle_policy:
if repository.lyfecicle_policy: report.status = "PASS"
report.status = "PASS" report.status_extended = f"Repository {repository.name} has a lifecycle policy configured"
report.status_extended = (
f"Repository {repository.name} has lifecycle policy"
)
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -5,25 +5,28 @@ from prowler.providers.aws.services.ecr.ecr_client import ecr_client
class ecr_repositories_not_publicly_accessible(Check): class ecr_repositories_not_publicly_accessible(Check):
def execute(self): def execute(self):
findings = [] findings = []
for repository in ecr_client.repositories: for registry in ecr_client.registries.values():
report = Check_Report_AWS(self.metadata()) for repository in registry.repositories:
report.region = repository.region report = Check_Report_AWS(self.metadata())
report.resource_id = repository.name report.region = repository.region
report.resource_arn = repository.arn report.resource_id = repository.name
report.resource_tags = repository.tags report.resource_arn = repository.arn
report.status = "PASS" report.resource_tags = repository.tags
report.status_extended = f"Repository {repository.name} is not open" report.status = "PASS"
if repository.policy: report.status_extended = (
for statement in repository.policy["Statement"]: f"Repository {repository.name} is not publicly accesible"
if statement["Effect"] == "Allow": )
if "*" in statement["Principal"] or ( if repository.policy:
"AWS" in statement["Principal"] for statement in repository.policy["Statement"]:
and "*" in statement["Principal"]["AWS"] if statement["Effect"] == "Allow":
): if "*" in statement["Principal"] or (
report.status = "FAIL" "AWS" in statement["Principal"]
report.status_extended = f"Repository {repository.name} policy may allow anonymous users to perform actions (Principal: '*')" and "*" in statement["Principal"]["AWS"]
break ):
report.status = "FAIL"
report.status_extended = f"Repository {repository.name} policy may allow anonymous users to perform actions (Principal: '*')"
break
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -5,22 +5,23 @@ from prowler.providers.aws.services.ecr.ecr_client import ecr_client
class ecr_repositories_scan_images_on_push_enabled(Check): class ecr_repositories_scan_images_on_push_enabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
for repository in ecr_client.repositories: for registry in ecr_client.registries.values():
report = Check_Report_AWS(self.metadata()) for repository in registry.repositories:
report.region = repository.region report = Check_Report_AWS(self.metadata())
report.resource_id = repository.name report.region = repository.region
report.resource_arn = repository.arn report.resource_id = repository.name
report.resource_tags = repository.tags report.resource_arn = repository.arn
report.status = "PASS" report.resource_tags = repository.tags
report.status_extended = ( report.status = "PASS"
f"ECR repository {repository.name} has scan on push enabled"
)
if not repository.scan_on_push:
report.status = "FAIL"
report.status_extended = ( report.status_extended = (
f"ECR repository {repository.name} has scan on push disabled" f"ECR repository {repository.name} has scan on push enabled"
) )
if not repository.scan_on_push:
report.status = "FAIL"
report.status_extended = (
f"ECR repository {repository.name} has scan on push disabled"
)
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -5,32 +5,37 @@ from prowler.providers.aws.services.ecr.ecr_client import ecr_client
class ecr_repositories_scan_vulnerabilities_in_latest_image(Check): class ecr_repositories_scan_vulnerabilities_in_latest_image(Check):
def execute(self): def execute(self):
findings = [] findings = []
for repository in ecr_client.repositories: for registry in ecr_client.registries.values():
for image in repository.images_details: for repository in registry.repositories:
report = Check_Report_AWS(self.metadata()) # First check if the repository has images
report.region = repository.region if len(repository.images_details) > 0:
report.resource_id = repository.name # We only want to check the latest image pushed
report.resource_arn = repository.arn image = repository.images_details[-1]
report.resource_tags = repository.tags
report.status = "PASS"
report.status_extended = f"ECR repository {repository.name} has imageTag {image.latest_tag} scanned without findings"
if not image.scan_findings_status:
report.status = "FAIL"
report.status_extended = f"ECR repository {repository.name} has imageTag {image.latest_tag} without a scan"
elif image.scan_findings_status == "FAILED":
report.status = "FAIL"
report.status_extended = (
f"ECR repository {repository.name} with scan status FAILED"
)
elif image.scan_findings_status != "FAILED":
if image.scan_findings_severity_count and (
image.scan_findings_severity_count.critical
or image.scan_findings_severity_count.high
or image.scan_findings_severity_count.medium
):
report.status = "FAIL"
report.status_extended = f"ECR repository {repository.name} has imageTag {image.latest_tag} scanned with findings: CRITICAL->{image.scan_findings_severity_count.critical}, HIGH->{image.scan_findings_severity_count.high}, MEDIUM->{image.scan_findings_severity_count.medium} "
findings.append(report) report = Check_Report_AWS(self.metadata())
report.region = repository.region
report.resource_id = repository.name
report.resource_arn = repository.arn
report.resource_tags = repository.tags
report.status = "PASS"
report.status_extended = f"ECR repository {repository.name} has imageTag {image.latest_tag} scanned without findings"
if not image.scan_findings_status:
report.status = "FAIL"
report.status_extended = f"ECR repository {repository.name} has imageTag {image.latest_tag} without a scan"
elif image.scan_findings_status == "FAILED":
report.status = "FAIL"
report.status_extended = (
f"ECR repository {repository.name} with scan status FAILED"
)
elif image.scan_findings_status != "FAILED":
if image.scan_findings_severity_count and (
image.scan_findings_severity_count.critical
or image.scan_findings_severity_count.high
or image.scan_findings_severity_count.medium
):
report.status = "FAIL"
report.status_extended = f"ECR repository {repository.name} has imageTag {image.latest_tag} scanned with findings: CRITICAL->{image.scan_findings_severity_count.critical}, HIGH->{image.scan_findings_severity_count.high}, MEDIUM->{image.scan_findings_severity_count.medium} "
findings.append(report)
return findings return findings

View File

@@ -1,4 +1,5 @@
import threading import threading
from datetime import datetime
from json import loads from json import loads
from typing import Optional from typing import Optional
@@ -17,14 +18,14 @@ class ECR:
self.session = audit_info.audit_session self.session = audit_info.audit_session
self.audit_resources = audit_info.audit_resources self.audit_resources = audit_info.audit_resources
self.regional_clients = generate_regional_clients(self.service, audit_info) self.regional_clients = generate_regional_clients(self.service, audit_info)
self.repositories = [] self.registry_id = audit_info.audited_account
self.registries = [] self.registries = {}
self.__threading_call__(self.__describe_repositories__) self.__threading_call__(self.__describe_registries_and_repositories__)
self.__describe_repository_policies__() self.__threading_call__(self.__describe_repository_policies__)
self.__get_image_details__() self.__threading_call__(self.__get_image_details__)
self.__get_repository_lifecycle_policy__() self.__threading_call__(self.__get_repository_lifecycle_policy__)
self.__threading_call__(self.__get_registry_scanning_configuration__) self.__threading_call__(self.__get_registry_scanning_configuration__)
self.__list_tags_for_resource__() self.__threading_call__(self.__list_tags_for_resource__)
def __get_session__(self): def __get_session__(self):
return self.session return self.session
@@ -38,8 +39,9 @@ class ECR:
for t in threads: for t in threads:
t.join() t.join()
def __describe_repositories__(self, regional_client): def __describe_registries_and_repositories__(self, regional_client):
logger.info("ECR - Describing repositories...") logger.info("ECR - Describing registries and repositories...")
regional_registry_repositories = []
try: try:
describe_ecr_paginator = regional_client.get_paginator( describe_ecr_paginator = regional_client.get_paginator(
"describe_repositories" "describe_repositories"
@@ -51,126 +53,157 @@ class ECR:
repository["repositoryArn"], self.audit_resources repository["repositoryArn"], self.audit_resources
) )
): ):
self.repositories.append( regional_registry_repositories.append(
Repository( Repository(
name=repository["repositoryName"], name=repository["repositoryName"],
arn=repository["repositoryArn"], arn=repository["repositoryArn"],
registry_id=repository["registryId"],
region=regional_client.region, region=regional_client.region,
scan_on_push=repository["imageScanningConfiguration"][ scan_on_push=repository["imageScanningConfiguration"][
"scanOnPush" "scanOnPush"
], ],
policy=None, policy=None,
images_details=[], images_details=[],
lyfecicle_policy=None, lifecycle_policy=None,
) )
) )
# The default ECR registry is assumed
self.registries[regional_client.region] = Registry(
id=self.registry_id,
region=regional_client.region,
repositories=regional_registry_repositories,
)
except Exception as error: except Exception as error:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
) )
def __describe_repository_policies__(self): def __describe_repository_policies__(self, regional_client):
logger.info("ECR - Describing repository policies...") logger.info("ECR - Describing repository policies...")
try: try:
for repository in self.repositories: if regional_client.region in self.registries:
client = self.regional_clients[repository.region] for repository in self.registries[regional_client.region].repositories:
policy = client.get_repository_policy(repositoryName=repository.name) client = self.regional_clients[repository.region]
if "policyText" in policy: policy = client.get_repository_policy(
repository.policy = loads(policy["policyText"]) repositoryName=repository.name
)
if "policyText" in policy:
repository.policy = loads(policy["policyText"])
except Exception as error: except Exception as error:
if "RepositoryPolicyNotFoundException" not in str(error): if "RepositoryPolicyNotFoundException" not in str(error):
logger.error( logger.error(
f"-- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
) )
def __get_repository_lifecycle_policy__(self): def __get_repository_lifecycle_policy__(self, regional_client):
logger.info("ECR - Getting repository lifecycle policy...") logger.info("ECR - Getting repository lifecycle policy...")
try: try:
for repository in self.repositories: if regional_client.region in self.registries:
client = self.regional_clients[repository.region] for repository in self.registries[regional_client.region].repositories:
policy = client.get_lifecycle_policy(repositoryName=repository.name) client = self.regional_clients[repository.region]
if "lifecyclePolicyText" in policy: policy = client.get_lifecycle_policy(repositoryName=repository.name)
repository.lyfecicle_policy = policy["lifecyclePolicyText"] if "lifecyclePolicyText" in policy:
repository.lifecycle_policy = policy["lifecyclePolicyText"]
except Exception as error: except Exception as error:
if "LifecyclePolicyNotFoundException" not in str(error): if "LifecyclePolicyNotFoundException" not in str(error):
logger.error( logger.error(
f"-- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
) )
def __get_image_details__(self): def __get_image_details__(self, regional_client):
logger.info("ECR - Getting images details...") logger.info("ECR - Getting images details...")
try: try:
for repository in self.repositories: if regional_client.region in self.registries:
# if the repo is not scanning pushed images there is nothing to do for repository in self.registries[regional_client.region].repositories:
if repository.scan_on_push: # There is nothing to do if the repository is not scanning pushed images
client = self.regional_clients[repository.region] if repository.scan_on_push:
describe_images_paginator = client.get_paginator("describe_images") client = self.regional_clients[repository.region]
for page in describe_images_paginator.paginate( describe_images_paginator = client.get_paginator(
repositoryName=repository.name "describe_images"
): )
for image in page["imageDetails"]: for page in describe_images_paginator.paginate(
severity_counts = None registryId=self.registries[regional_client.region].id,
last_scan_status = None repositoryName=repository.name,
if "imageScanStatus" in image: PaginationConfig={"PageSize": 1000},
last_scan_status = image["imageScanStatus"]["status"] ):
for image in page["imageDetails"]:
# The following condition is required since sometimes
# the AWS ECR API returns None using the iterator
if image is not None:
severity_counts = None
last_scan_status = None
if "imageScanStatus" in image:
last_scan_status = image["imageScanStatus"][
"status"
]
if "imageScanFindingsSummary" in image: if "imageScanFindingsSummary" in image:
severity_counts = FindingSeverityCounts( severity_counts = FindingSeverityCounts(
critical=0, high=0, medium=0 critical=0, high=0, medium=0
) )
finding_severity_counts = image[ finding_severity_counts = image[
"imageScanFindingsSummary" "imageScanFindingsSummary"
]["findingSeverityCounts"] ]["findingSeverityCounts"]
if "CRITICAL" in finding_severity_counts: if "CRITICAL" in finding_severity_counts:
severity_counts.critical = finding_severity_counts[ severity_counts.critical = (
"CRITICAL" finding_severity_counts["CRITICAL"]
] )
if "HIGH" in finding_severity_counts: if "HIGH" in finding_severity_counts:
severity_counts.high = finding_severity_counts[ severity_counts.high = (
"HIGH" finding_severity_counts["HIGH"]
] )
if "MEDIUM" in finding_severity_counts: if "MEDIUM" in finding_severity_counts:
severity_counts.medium = finding_severity_counts[ severity_counts.medium = (
"MEDIUM" finding_severity_counts["MEDIUM"]
] )
latest_tag = "None" latest_tag = "None"
if image.get("imageTags"): if image.get("imageTags"):
latest_tag = image["imageTags"][0] latest_tag = image["imageTags"][0]
repository.images_details.append( repository.images_details.append(
ImageDetails( ImageDetails(
latest_tag=latest_tag, latest_tag=latest_tag,
latest_digest=image["imageDigest"], image_pushed_at=image["imagePushedAt"],
scan_findings_status=last_scan_status, latest_digest=image["imageDigest"],
scan_findings_severity_count=severity_counts, scan_findings_status=last_scan_status,
) scan_findings_severity_count=severity_counts,
) )
)
# Sort the repository images by date pushed
repository.images_details.sort(
key=lambda image: image.image_pushed_at
)
except Exception as error: except Exception as error:
logger.error( logger.error(
f"-- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {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, regional_client):
logger.info("ECR - List Tags...") logger.info("ECR - List Tags...")
try: try:
for repository in self.repositories: if regional_client.region in self.registries:
try: for repository in self.registries[regional_client.region].repositories:
regional_client = self.regional_clients[repository.region] try:
response = regional_client.list_tags_for_resource( regional_client = self.regional_clients[repository.region]
resourceArn=repository.arn response = regional_client.list_tags_for_resource(
)["tags"] resourceArn=repository.arn
repository.tags = response )["tags"]
repository.tags = response
except ClientError as error: except ClientError as error:
if error.response["Error"]["Code"] == "RepositoryNotFoundException": if (
logger.warning( error.response["Error"]["Code"]
f"{regional_client.region} --" == "RepositoryNotFoundException"
f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:" ):
f" {error}" logger.warning(
) f"{regional_client.region} --"
continue f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
f" {error}"
)
continue
except Exception as error: except Exception as error:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -179,25 +212,34 @@ class ECR:
def __get_registry_scanning_configuration__(self, regional_client): def __get_registry_scanning_configuration__(self, regional_client):
logger.info("ECR - Getting Registry Scanning Configuration...") logger.info("ECR - Getting Registry Scanning Configuration...")
try: try:
response = regional_client.get_registry_scanning_configuration() if regional_client.region in self.registries:
rules = [] response = regional_client.get_registry_scanning_configuration()
for rule in response.get("scanningConfiguration").get("rules", []): rules = []
rules.append( for rule in response.get("scanningConfiguration").get("rules", []):
ScanningRule( rules.append(
scan_frequency=rule.get("scanFrequency"), ScanningRule(
scan_filters=rule.get("repositoryFilters"), scan_frequency=rule.get("scanFrequency"),
scan_filters=rule.get("repositoryFilters", []),
)
) )
self.registries[regional_client.region].scan_type = response.get(
"scanningConfiguration"
).get("scanType", "BASIC")
self.registries[regional_client.region].rules = rules
except ClientError as error:
if error.response["Error"][
"Code"
] == "ValidationException" and "GetRegistryScanningConfiguration operation: This feature is disabled" in str(
error
):
self.registries[regional_client.region].scan_type = "BASIC"
self.registries[regional_client.region].rules = []
else:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
) )
self.registries.append(
Registry(
id=response.get("registryId", ""),
scan_type=response.get("scanningConfiguration").get(
"scanType", "BASIC"
),
region=regional_client.region,
rules=rules,
)
)
except Exception as error: except Exception as error:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -213,6 +255,7 @@ class FindingSeverityCounts(BaseModel):
class ImageDetails(BaseModel): class ImageDetails(BaseModel):
latest_tag: str latest_tag: str
latest_digest: str latest_digest: str
image_pushed_at: datetime
scan_findings_status: Optional[str] scan_findings_status: Optional[str]
scan_findings_severity_count: Optional[FindingSeverityCounts] scan_findings_severity_count: Optional[FindingSeverityCounts]
@@ -221,10 +264,11 @@ class Repository(BaseModel):
name: str name: str
arn: str arn: str
region: str region: str
registry_id = str
scan_on_push: bool scan_on_push: bool
policy: Optional[dict] policy: Optional[dict]
images_details: Optional[list[ImageDetails]] images_details: Optional[list[ImageDetails]]
lyfecicle_policy: Optional[str] lifecycle_policy: Optional[str]
tags: Optional[list] = [] tags: Optional[list] = []
@@ -236,6 +280,6 @@ class ScanningRule(BaseModel):
class Registry(BaseModel): class Registry(BaseModel):
id: str id: str
region: str region: str
scan_type: str repositories: list[Repository]
rules: list[ScanningRule] scan_type: Optional[str]
tags: Optional[list] = [] rules: Optional[list[ScanningRule]]

View File

@@ -1,17 +1,25 @@
from re import search from re import search
from unittest import mock from unittest import mock
from prowler.providers.aws.services.ecr.ecr_service import Registry, ScanningRule from prowler.providers.aws.services.ecr.ecr_service import (
Registry,
Repository,
ScanningRule,
)
# Mock Test Region # Mock Test Region
AWS_REGION = "eu-west-1" AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012" AWS_ACCOUNT_NUMBER = "123456789012"
repository_name = "test_repo"
repository_arn = (
f"arn:aws:ecr:eu-west-1:{AWS_ACCOUNT_NUMBER}:repository/{repository_name}"
)
class Test_ecr_registry_scan_images_on_push_enabled: class Test_ecr_registry_scan_images_on_push_enabled:
def test_no_registries(self): def test_no_registries(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.registries = [] ecr_client.registries = {}
with mock.patch( with mock.patch(
"prowler.providers.aws.services.ecr.ecr_service.ECR", "prowler.providers.aws.services.ecr.ecr_service.ECR",
@@ -25,21 +33,53 @@ class Test_ecr_registry_scan_images_on_push_enabled:
result = check.execute() result = check.execute()
assert len(result) == 0 assert len(result) == 0
def test_scan_on_push_enabled(self): def test_registry_no_repositories(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.registries = [] ecr_client.registries = {}
ecr_client.registries.append( ecr_client.registries[AWS_REGION] = Registry(
Registry( id=AWS_ACCOUNT_NUMBER,
id=AWS_ACCOUNT_NUMBER, region=AWS_REGION,
region=AWS_REGION, scan_type="BASIC",
scan_type="BASIC", repositories=[],
rules=[ rules=[],
ScanningRule( )
scan_frequency="SCAN_ON_PUSH",
scan_filters=[{"filter": "*", "filterType": "WILDCARD"}], with mock.patch(
) "prowler.providers.aws.services.ecr.ecr_service.ECR",
], 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) == 0
def test_registry_scan_on_push_enabled(self):
ecr_client = mock.MagicMock
ecr_client.registries = {}
ecr_client.registries[AWS_REGION] = Registry(
id=AWS_ACCOUNT_NUMBER,
region=AWS_REGION,
scan_type="BASIC",
repositories=[
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION,
scan_on_push=True,
policy="",
images_details=None,
lifecycle_policy="",
)
],
rules=[
ScanningRule(
scan_frequency="SCAN_ON_PUSH",
scan_filters=[{"filter": "*", "filterType": "WILDCARD"}],
)
],
) )
with mock.patch( with mock.patch(
@@ -60,19 +100,28 @@ class Test_ecr_registry_scan_images_on_push_enabled:
def test_scan_on_push_enabled_with_filters(self): def test_scan_on_push_enabled_with_filters(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.registries = [] ecr_client.registries = {}
ecr_client.registries.append( ecr_client.registries[AWS_REGION] = Registry(
Registry( id=AWS_ACCOUNT_NUMBER,
id=AWS_ACCOUNT_NUMBER, region=AWS_REGION,
region=AWS_REGION, scan_type="BASIC",
scan_type="BASIC", repositories=[
rules=[ Repository(
ScanningRule( name=repository_name,
scan_frequency="SCAN_ON_PUSH", arn=repository_arn,
scan_filters=[{"filter": "test", "filterType": "WILDCARD"}], region=AWS_REGION,
) scan_on_push=True,
], policy="",
) images_details=None,
lifecycle_policy="",
)
],
rules=[
ScanningRule(
scan_frequency="SCAN_ON_PUSH",
scan_filters=[{"filter": "test", "filterType": "WILDCARD"}],
)
],
) )
with mock.patch( with mock.patch(
@@ -96,14 +145,23 @@ class Test_ecr_registry_scan_images_on_push_enabled:
def test_scan_on_push_disabled(self): def test_scan_on_push_disabled(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.registries = [] ecr_client.registries = {}
ecr_client.registries.append( ecr_client.registries[AWS_REGION] = Registry(
Registry( id=AWS_ACCOUNT_NUMBER,
id=AWS_ACCOUNT_NUMBER, region=AWS_REGION,
region=AWS_REGION, scan_type="BASIC",
scan_type="BASIC", repositories=[
rules=[], Repository(
) name=repository_name,
arn=repository_arn,
region=AWS_REGION,
scan_on_push=True,
policy="",
images_details=None,
lifecycle_policy="",
)
],
rules=[],
) )
with mock.patch( with mock.patch(

View File

@@ -1,7 +1,6 @@
from re import search
from unittest import mock from unittest import mock
from prowler.providers.aws.services.ecr.ecr_service import Repository from prowler.providers.aws.services.ecr.ecr_service import Registry, Repository
# Mock Test Region # Mock Test Region
AWS_REGION = "eu-west-1" AWS_REGION = "eu-west-1"
@@ -24,19 +23,64 @@ repo_policy_public = {
class Test_ecr_repositories_lifecycle_policy_enabled: class Test_ecr_repositories_lifecycle_policy_enabled:
def test_no_lyfecicle_policy(self): def test_no_registries(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.repositories = [] ecr_client.registries = {}
ecr_client.repositories.append(
Repository( with mock.patch(
name=repository_name, "prowler.providers.aws.services.ecr.ecr_service.ECR",
arn=repository_arn, ecr_client,
region=AWS_REGION, ):
scan_on_push=True, from prowler.providers.aws.services.ecr.ecr_repositories_lifecycle_policy_enabled.ecr_repositories_lifecycle_policy_enabled import (
policy=repo_policy_public, ecr_repositories_lifecycle_policy_enabled,
images_details=None,
lyfecicle_policy="test-policy",
) )
check = ecr_repositories_lifecycle_policy_enabled()
result = check.execute()
assert len(result) == 0
def test_registry_no_repositories(self):
ecr_client = mock.MagicMock
ecr_client.registries = {}
ecr_client.registries[AWS_REGION] = Registry(
id=AWS_ACCOUNT_NUMBER,
region=AWS_REGION,
scan_type="BASIC",
repositories=[],
rules=[],
)
with mock.patch(
"prowler.providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from prowler.providers.aws.services.ecr.ecr_repositories_lifecycle_policy_enabled.ecr_repositories_lifecycle_policy_enabled import (
ecr_repositories_lifecycle_policy_enabled,
)
check = ecr_repositories_lifecycle_policy_enabled()
result = check.execute()
assert len(result) == 0
def test_lifecycle_policy(self):
ecr_client = mock.MagicMock
ecr_client.registries = {}
ecr_client.registries[AWS_REGION] = Registry(
id=AWS_ACCOUNT_NUMBER,
region=AWS_REGION,
scan_type="BASIC",
rules=[],
repositories=[
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION,
scan_on_push=True,
policy=repo_policy_public,
images_details=None,
lifecycle_policy="test-policy",
)
],
) )
with mock.patch( with mock.patch(
@@ -51,23 +95,33 @@ class Test_ecr_repositories_lifecycle_policy_enabled:
result = check.execute() result = check.execute()
assert len(result) == 1 assert len(result) == 1
assert result[0].status == "PASS" assert result[0].status == "PASS"
assert search("has lifecycle policy", result[0].status_extended) assert (
result[0].status_extended
== f"Repository {repository_name} has a lifecycle policy configured"
)
assert result[0].resource_id == repository_name assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn assert result[0].resource_arn == repository_arn
assert result[0].resource_tags == []
def test_lifecycle_policy(self): def test_no_lifecycle_policy(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.repositories = [] ecr_client.registries = {}
ecr_client.repositories.append( ecr_client.registries[AWS_REGION] = Registry(
Repository( id=AWS_ACCOUNT_NUMBER,
name=repository_name, region=AWS_REGION,
arn=repository_arn, scan_type="BASIC",
region=AWS_REGION, rules=[],
scan_on_push=False, repositories=[
policy=repo_policy_public, Repository(
images_details=None, name=repository_name,
lyfecicle_policy=None, arn=repository_arn,
) region=AWS_REGION,
scan_on_push=False,
policy=repo_policy_public,
images_details=None,
lifecycle_policy=None,
)
],
) )
with mock.patch( with mock.patch(
@@ -82,6 +136,10 @@ class Test_ecr_repositories_lifecycle_policy_enabled:
result = check.execute() result = check.execute()
assert len(result) == 1 assert len(result) == 1
assert result[0].status == "FAIL" assert result[0].status == "FAIL"
assert search("has no lifecycle policy", result[0].status_extended) assert (
result[0].status_extended
== f"Repository {repository_name} has not a lifecycle policy configured"
)
assert result[0].resource_id == repository_name assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn assert result[0].resource_arn == repository_arn
assert result[0].resource_tags == []

View File

@@ -1,7 +1,6 @@
from re import search
from unittest import mock from unittest import mock
from prowler.providers.aws.services.ecr.ecr_service import Repository from prowler.providers.aws.services.ecr.ecr_service import Registry, Repository
# Mock Test Region # Mock Test Region
AWS_REGION = "eu-west-1" AWS_REGION = "eu-west-1"
@@ -36,19 +35,64 @@ repo_policy_public = {
class Test_ecr_repositories_not_publicly_accessible: class Test_ecr_repositories_not_publicly_accessible:
def test_no_registries(self):
ecr_client = mock.MagicMock
ecr_client.registries = {}
with mock.patch(
"prowler.providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible import (
ecr_repositories_not_publicly_accessible,
)
check = ecr_repositories_not_publicly_accessible()
result = check.execute()
assert len(result) == 0
def test_registry_no_repositories(self):
ecr_client = mock.MagicMock
ecr_client.registries = {}
ecr_client.registries[AWS_REGION] = Registry(
id=AWS_ACCOUNT_NUMBER,
region=AWS_REGION,
scan_type="BASIC",
repositories=[],
rules=[],
)
with mock.patch(
"prowler.providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible import (
ecr_repositories_not_publicly_accessible,
)
check = ecr_repositories_not_publicly_accessible()
result = check.execute()
assert len(result) == 0
def test_repository_not_public(self): def test_repository_not_public(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.repositories = [] ecr_client.registries = {}
ecr_client.repositories.append( ecr_client.registries[AWS_REGION] = Registry(
Repository( id=AWS_ACCOUNT_NUMBER,
name=repository_name, region=AWS_REGION,
arn=repository_arn, scan_type="BASIC",
region=AWS_REGION, repositories=[
scan_on_push=True, Repository(
policy=repo_policy_not_public, name=repository_name,
images_details=None, arn=repository_arn,
lyfecicle_policy=None, region=AWS_REGION,
) scan_on_push=True,
policy=repo_policy_not_public,
images_details=None,
lifecycle_policy=None,
)
],
rules=[],
) )
with mock.patch( with mock.patch(
@@ -63,23 +107,32 @@ class Test_ecr_repositories_not_publicly_accessible:
result = check.execute() result = check.execute()
assert len(result) == 1 assert len(result) == 1
assert result[0].status == "PASS" assert result[0].status == "PASS"
assert search("is not open", result[0].status_extended) assert (
result[0].status_extended
== f"Repository {repository_name} is not publicly accesible"
)
assert result[0].resource_id == repository_name assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn assert result[0].resource_arn == repository_arn
def test_repository_public(self): def test_repository_public(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.repositories = [] ecr_client.registries = {}
ecr_client.repositories.append( ecr_client.registries[AWS_REGION] = Registry(
Repository( id=AWS_ACCOUNT_NUMBER,
name=repository_name, region=AWS_REGION,
arn=repository_arn, scan_type="BASIC",
region=AWS_REGION, repositories=[
scan_on_push=True, Repository(
policy=repo_policy_public, name=repository_name,
images_details=None, arn=repository_arn,
lyfecicle_policy=None, region=AWS_REGION,
) scan_on_push=True,
policy=repo_policy_public,
images_details=None,
lifecycle_policy=None,
)
],
rules=[],
) )
with mock.patch( with mock.patch(
@@ -94,8 +147,9 @@ class Test_ecr_repositories_not_publicly_accessible:
result = check.execute() result = check.execute()
assert len(result) == 1 assert len(result) == 1
assert result[0].status == "FAIL" assert result[0].status == "FAIL"
assert search( assert (
"policy may allow anonymous users to", result[0].status_extended result[0].status_extended
== f"Repository {repository_name} policy may allow anonymous users to perform actions (Principal: '*')"
) )
assert result[0].resource_id == repository_name assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn assert result[0].resource_arn == repository_arn

View File

@@ -1,7 +1,6 @@
from re import search
from unittest import mock from unittest import mock
from prowler.providers.aws.services.ecr.ecr_service import Repository from prowler.providers.aws.services.ecr.ecr_service import Registry, Repository
# Mock Test Region # Mock Test Region
AWS_REGION = "eu-west-1" AWS_REGION = "eu-west-1"
@@ -24,19 +23,64 @@ repo_policy_public = {
class Test_ecr_repositories_scan_images_on_push_enabled: class Test_ecr_repositories_scan_images_on_push_enabled:
def test_no_registries(self):
ecr_client = mock.MagicMock
ecr_client.registries = {}
with mock.patch(
"prowler.providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from prowler.providers.aws.services.ecr.ecr_repositories_scan_images_on_push_enabled.ecr_repositories_scan_images_on_push_enabled import (
ecr_repositories_scan_images_on_push_enabled,
)
check = ecr_repositories_scan_images_on_push_enabled()
result = check.execute()
assert len(result) == 0
def test_registry_no_repositories(self):
ecr_client = mock.MagicMock
ecr_client.registries = {}
ecr_client.registries[AWS_REGION] = Registry(
id=AWS_ACCOUNT_NUMBER,
region=AWS_REGION,
scan_type="BASIC",
repositories=[],
rules=[],
)
with mock.patch(
"prowler.providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from prowler.providers.aws.services.ecr.ecr_repositories_scan_images_on_push_enabled.ecr_repositories_scan_images_on_push_enabled import (
ecr_repositories_scan_images_on_push_enabled,
)
check = ecr_repositories_scan_images_on_push_enabled()
result = check.execute()
assert len(result) == 0
def test_scan_on_push_disabled(self): def test_scan_on_push_disabled(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.repositories = [] ecr_client.registries = {}
ecr_client.repositories.append( ecr_client.registries[AWS_REGION] = Registry(
Repository( id=AWS_ACCOUNT_NUMBER,
name=repository_name, region=AWS_REGION,
arn=repository_arn, scan_type="BASIC",
region=AWS_REGION, repositories=[
scan_on_push=True, Repository(
policy=repo_policy_public, name=repository_name,
images_details=None, arn=repository_arn,
lyfecicle_policy=None, region=AWS_REGION,
) scan_on_push=True,
policy=repo_policy_public,
images_details=None,
lifecycle_policy=None,
)
],
rules=[],
) )
with mock.patch( with mock.patch(
@@ -51,23 +95,32 @@ class Test_ecr_repositories_scan_images_on_push_enabled:
result = check.execute() result = check.execute()
assert len(result) == 1 assert len(result) == 1
assert result[0].status == "PASS" assert result[0].status == "PASS"
assert search("has scan on push enabled", result[0].status_extended) assert (
result[0].status_extended
== f"ECR repository {repository_name} has scan on push enabled"
)
assert result[0].resource_id == repository_name assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn assert result[0].resource_arn == repository_arn
def test_scan_on_push_enabled(self): def test_scan_on_push_enabled(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.repositories = [] ecr_client.registries = {}
ecr_client.repositories.append( ecr_client.registries[AWS_REGION] = Registry(
Repository( id=AWS_ACCOUNT_NUMBER,
name=repository_name, region=AWS_REGION,
arn=repository_arn, scan_type="BASIC",
region=AWS_REGION, repositories=[
scan_on_push=False, Repository(
policy=repo_policy_public, name=repository_name,
images_details=None, arn=repository_arn,
lyfecicle_policy=None, region=AWS_REGION,
) scan_on_push=False,
policy=repo_policy_public,
images_details=None,
lifecycle_policy=None,
)
],
rules=[],
) )
with mock.patch( with mock.patch(
@@ -82,6 +135,9 @@ class Test_ecr_repositories_scan_images_on_push_enabled:
result = check.execute() result = check.execute()
assert len(result) == 1 assert len(result) == 1
assert result[0].status == "FAIL" assert result[0].status == "FAIL"
assert search("has scan on push disabled", result[0].status_extended) assert (
result[0].status_extended
== f"ECR repository {repository_name} has scan on push disabled"
)
assert result[0].resource_id == repository_name assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn assert result[0].resource_arn == repository_arn

View File

@@ -1,9 +1,11 @@
from datetime import datetime
from re import search from re import search
from unittest import mock from unittest import mock
from prowler.providers.aws.services.ecr.ecr_service import ( from prowler.providers.aws.services.ecr.ecr_service import (
FindingSeverityCounts, FindingSeverityCounts,
ImageDetails, ImageDetails,
Registry,
Repository, Repository,
) )
@@ -28,20 +30,66 @@ repo_policy_public = {
class Test_ecr_repositories_scan_vulnerabilities_in_latest_image: class Test_ecr_repositories_scan_vulnerabilities_in_latest_image:
def test_no_registries(self):
ecr_client = mock.MagicMock
ecr_client.registries = {}
with mock.patch(
"prowler.providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from prowler.providers.aws.services.ecr.ecr_repositories_scan_vulnerabilities_in_latest_image.ecr_repositories_scan_vulnerabilities_in_latest_image import (
ecr_repositories_scan_vulnerabilities_in_latest_image,
)
check = ecr_repositories_scan_vulnerabilities_in_latest_image()
result = check.execute()
assert len(result) == 0
def test_registry_no_repositories(self):
ecr_client = mock.MagicMock
ecr_client.registries = {}
ecr_client.registries[AWS_REGION] = Registry(
id=AWS_ACCOUNT_NUMBER,
region=AWS_REGION,
scan_type="BASIC",
repositories=[],
rules=[],
)
with mock.patch(
"prowler.providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from prowler.providers.aws.services.ecr.ecr_repositories_scan_vulnerabilities_in_latest_image.ecr_repositories_scan_vulnerabilities_in_latest_image import (
ecr_repositories_scan_vulnerabilities_in_latest_image,
)
check = ecr_repositories_scan_vulnerabilities_in_latest_image()
result = check.execute()
assert len(result) == 0
def test_empty_repository(self): def test_empty_repository(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.repositories = [] ecr_client.registries = {}
ecr_client.repositories.append( ecr_client.registries[AWS_REGION] = Registry(
Repository( id=AWS_ACCOUNT_NUMBER,
name=repository_name, region=AWS_REGION,
arn=repository_arn, scan_type="BASIC",
region=AWS_REGION, repositories=[
scan_on_push=True, Repository(
policy=repo_policy_public, name=repository_name,
images_details=[], arn=repository_arn,
lyfecicle_policy=None, region=AWS_REGION,
) scan_on_push=True,
policy=repo_policy_public,
images_details=[],
lifecycle_policy=None,
)
],
rules=[],
) )
with mock.patch( with mock.patch(
"prowler.providers.aws.services.ecr.ecr_service.ECR", "prowler.providers.aws.services.ecr.ecr_service.ECR",
ecr_client, ecr_client,
@@ -56,28 +104,35 @@ class Test_ecr_repositories_scan_vulnerabilities_in_latest_image:
def test_image_scaned_without_findings(self): def test_image_scaned_without_findings(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.repositories = [] ecr_client.registries = {}
ecr_client.repositories.append( ecr_client.registries[AWS_REGION] = Registry(
Repository( id=AWS_ACCOUNT_NUMBER,
name=repository_name, region=AWS_REGION,
arn=repository_arn, scan_type="BASIC",
region=AWS_REGION, repositories=[
scan_on_push=True, Repository(
policy=repo_policy_public, name=repository_name,
images_details=[], arn=repository_arn,
lyfecicle_policy=None, region=AWS_REGION,
) scan_on_push=True,
policy=repo_policy_public,
images_details=[
ImageDetails(
latest_tag="test-tag",
latest_digest="test-digest",
image_pushed_at=datetime(2023, 1, 1),
scan_findings_status="COMPLETE",
scan_findings_severity_count=FindingSeverityCounts(
critical=0, high=0, medium=0
),
),
],
lifecycle_policy=None,
)
],
rules=[],
) )
ecr_client.repositories[0].images_details.append(
ImageDetails(
latest_tag="test-tag",
latest_digest="test-digest",
scan_findings_status="COMPLETE",
scan_findings_severity_count=FindingSeverityCounts(
critical=0, high=0, medium=0
),
),
),
with mock.patch( with mock.patch(
"prowler.providers.aws.services.ecr.ecr_service.ECR", "prowler.providers.aws.services.ecr.ecr_service.ECR",
ecr_client, ecr_client,
@@ -96,28 +151,35 @@ class Test_ecr_repositories_scan_vulnerabilities_in_latest_image:
def test_image_scanned_with_findings(self): def test_image_scanned_with_findings(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.repositories = [] ecr_client.registries = {}
ecr_client.repositories.append( ecr_client.registries[AWS_REGION] = Registry(
Repository( id=AWS_ACCOUNT_NUMBER,
name=repository_name, region=AWS_REGION,
arn=repository_arn, scan_type="BASIC",
region=AWS_REGION, repositories=[
scan_on_push=True, Repository(
policy=repo_policy_public, name=repository_name,
images_details=[], arn=repository_arn,
lyfecicle_policy=None, region=AWS_REGION,
) scan_on_push=True,
policy=repo_policy_public,
images_details=[
ImageDetails(
latest_tag="test-tag",
latest_digest="test-digest",
image_pushed_at=datetime(2023, 1, 1),
scan_findings_status="COMPLETE",
scan_findings_severity_count=FindingSeverityCounts(
critical=12, high=34, medium=7
),
)
],
lifecycle_policy=None,
)
],
rules=[],
) )
ecr_client.repositories[0].images_details.append(
ImageDetails(
latest_tag="test-tag",
latest_digest="test-digest",
scan_findings_status="COMPLETE",
scan_findings_severity_count=FindingSeverityCounts(
critical=12, high=34, medium=7
),
),
),
with mock.patch( with mock.patch(
"prowler.providers.aws.services.ecr.ecr_service.ECR", "prowler.providers.aws.services.ecr.ecr_service.ECR",
ecr_client, ecr_client,
@@ -136,28 +198,35 @@ class Test_ecr_repositories_scan_vulnerabilities_in_latest_image:
def test_image_scanned_fail_scan(self): def test_image_scanned_fail_scan(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.repositories = [] ecr_client.registries = {}
ecr_client.repositories.append( ecr_client.registries[AWS_REGION] = Registry(
Repository( id=AWS_ACCOUNT_NUMBER,
name=repository_name, region=AWS_REGION,
arn=repository_arn, scan_type="BASIC",
region=AWS_REGION, repositories=[
scan_on_push=True, Repository(
policy=repo_policy_public, name=repository_name,
images_details=[], arn=repository_arn,
lyfecicle_policy=None, region=AWS_REGION,
) scan_on_push=True,
policy=repo_policy_public,
images_details=[
ImageDetails(
latest_tag="test-tag",
latest_digest="test-digest",
image_pushed_at=datetime(2023, 1, 1),
scan_findings_status="FAILED",
scan_findings_severity_count=FindingSeverityCounts(
critical=0, high=0, medium=0
),
)
],
lifecycle_policy=None,
)
],
rules=[],
) )
ecr_client.repositories[0].images_details.append(
ImageDetails(
latest_tag="test-tag",
latest_digest="test-digest",
scan_findings_status="FAILED",
scan_findings_severity_count=FindingSeverityCounts(
critical=0, high=0, medium=0
),
),
),
with mock.patch( with mock.patch(
"prowler.providers.aws.services.ecr.ecr_service.ECR", "prowler.providers.aws.services.ecr.ecr_service.ECR",
ecr_client, ecr_client,
@@ -176,28 +245,35 @@ class Test_ecr_repositories_scan_vulnerabilities_in_latest_image:
def test_image_not_scanned(self): def test_image_not_scanned(self):
ecr_client = mock.MagicMock ecr_client = mock.MagicMock
ecr_client.repositories = [] ecr_client.registries = {}
ecr_client.repositories.append( ecr_client.registries[AWS_REGION] = Registry(
Repository( id=AWS_ACCOUNT_NUMBER,
name=repository_name, region=AWS_REGION,
arn=repository_arn, scan_type="BASIC",
region=AWS_REGION, repositories=[
scan_on_push=True, Repository(
policy=repo_policy_public, name=repository_name,
images_details=[], arn=repository_arn,
lyfecicle_policy=None, region=AWS_REGION,
) scan_on_push=True,
policy=repo_policy_public,
images_details=[
ImageDetails(
latest_tag="test-tag",
latest_digest="test-digest",
image_pushed_at=datetime(2023, 1, 1),
scan_findings_status="",
scan_findings_severity_count=FindingSeverityCounts(
critical=0, high=0, medium=0
),
)
],
lifecycle_policy=None,
)
],
rules=[],
) )
ecr_client.repositories[0].images_details.append(
ImageDetails(
latest_tag="test-tag",
latest_digest="test-digest",
scan_findings_status="",
scan_findings_severity_count=FindingSeverityCounts(
critical=0, high=0, medium=0
),
),
),
with mock.patch( with mock.patch(
"prowler.providers.aws.services.ecr.ecr_service.ECR", "prowler.providers.aws.services.ecr.ecr_service.ECR",
ecr_client, ecr_client,

View File

@@ -1,3 +1,4 @@
from datetime import datetime
from unittest.mock import patch from unittest.mock import patch
import botocore import botocore
@@ -24,8 +25,9 @@ def mock_make_api_call(self, operation_name, kwarg):
{ {
"imageDigest": "sha256:d8868e50ac4c7104d2200d42f432b661b2da8c1e417ccfae217e6a1e04bb9295", "imageDigest": "sha256:d8868e50ac4c7104d2200d42f432b661b2da8c1e417ccfae217e6a1e04bb9295",
"imageTags": [ "imageTags": [
"test-tag", "test-tag1",
], ],
"imagePushedAt": datetime(2023, 1, 1),
"imageScanStatus": { "imageScanStatus": {
"status": "COMPLETE", "status": "COMPLETE",
}, },
@@ -38,6 +40,13 @@ def mock_make_api_call(self, operation_name, kwarg):
"imageTags": [ "imageTags": [
"test-tag2", "test-tag2",
], ],
"imagePushedAt": datetime(2023, 1, 2),
"imageScanStatus": {
"status": "COMPLETE",
},
"imageScanFindingsSummary": {
"findingSeverityCounts": {"CRITICAL": 1, "HIGH": 2, "MEDIUM": 3}
},
}, },
], ],
} }
@@ -68,6 +77,7 @@ def mock_make_api_call(self, operation_name, kwarg):
], ],
}, },
} }
return make_api_call(self, operation_name, kwarg) return make_api_call(self, operation_name, kwarg)
@@ -128,7 +138,7 @@ class Test_ECR_Service:
# Test describe ECR repositories # Test describe ECR repositories
@mock_ecr @mock_ecr
def test__describe_repositories__(self): def test__describe_registries_and_repositories__(self):
ecr_client = client("ecr", region_name=AWS_REGION) ecr_client = client("ecr", region_name=AWS_REGION)
ecr_client.create_repository( ecr_client.create_repository(
repositoryName=repo_name, repositoryName=repo_name,
@@ -139,11 +149,16 @@ class Test_ECR_Service:
) )
audit_info = self.set_mocked_audit_info() audit_info = self.set_mocked_audit_info()
ecr = ECR(audit_info) ecr = ECR(audit_info)
assert len(ecr.repositories) == 1
assert ecr.repositories[0].name == repo_name assert len(ecr.registries) == 1
assert ecr.repositories[0].arn == repo_arn assert ecr.registries[AWS_REGION].id == AWS_ACCOUNT_NUMBER
assert ecr.repositories[0].scan_on_push assert ecr.registries[AWS_REGION].region == AWS_REGION
assert ecr.repositories[0].tags == [ assert len(ecr.registries[AWS_REGION].repositories) == 1
assert ecr.registries[AWS_REGION].repositories[0].name == repo_name
assert ecr.registries[AWS_REGION].repositories[0].arn == repo_arn
assert ecr.registries[AWS_REGION].repositories[0].scan_on_push
assert ecr.registries[AWS_REGION].repositories[0].tags == [
{"Key": "test", "Value": "test"}, {"Key": "test", "Value": "test"},
] ]
@@ -157,28 +172,39 @@ class Test_ECR_Service:
) )
audit_info = self.set_mocked_audit_info() audit_info = self.set_mocked_audit_info()
ecr = ECR(audit_info) ecr = ECR(audit_info)
assert len(ecr.repositories) == 1 assert len(ecr.registries) == 1
assert ecr.repositories[0].name == repo_name assert len(ecr.registries[AWS_REGION].repositories) == 1
assert ecr.repositories[0].arn == repo_arn assert ecr.registries[AWS_REGION].repositories[0].name == repo_name
assert ecr.repositories[0].scan_on_push assert ecr.registries[AWS_REGION].repositories[0].arn == repo_arn
assert ecr.registries[AWS_REGION].repositories[0].scan_on_push
assert ( assert (
ecr.repositories[0].policy["Statement"][0]["Sid"] == "Allow Describe Images" ecr.registries[AWS_REGION].repositories[0].policy["Statement"][0]["Sid"]
== "Allow Describe Images"
) )
assert ecr.repositories[0].policy["Statement"][0]["Effect"] == "Allow"
assert ( assert (
ecr.repositories[0].policy["Statement"][0]["Principal"]["AWS"][0] ecr.registries[AWS_REGION].repositories[0].policy["Statement"][0]["Effect"]
== "Allow"
)
assert (
ecr.registries[AWS_REGION]
.repositories[0]
.policy["Statement"][0]["Principal"]["AWS"][0]
== f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root" == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
) )
assert ( assert (
ecr.repositories[0].policy["Statement"][0]["Action"][0] ecr.registries[AWS_REGION]
.repositories[0]
.policy["Statement"][0]["Action"][0]
== "ecr:DescribeImages" == "ecr:DescribeImages"
) )
assert ( assert (
ecr.repositories[0].policy["Statement"][0]["Action"][1] ecr.registries[AWS_REGION]
.repositories[0]
.policy["Statement"][0]["Action"][1]
== "ecr:DescribeRepositories" == "ecr:DescribeRepositories"
) )
# Test describe ECR repository policies # Test describe ECR repository lifecycle policies
@mock_ecr @mock_ecr
def test__get_lifecycle_policies__(self): def test__get_lifecycle_policies__(self):
ecr_client = client("ecr", region_name=AWS_REGION) ecr_client = client("ecr", region_name=AWS_REGION)
@@ -188,11 +214,12 @@ class Test_ECR_Service:
) )
audit_info = self.set_mocked_audit_info() audit_info = self.set_mocked_audit_info()
ecr = ECR(audit_info) ecr = ECR(audit_info)
assert len(ecr.repositories) == 1 assert len(ecr.registries) == 1
assert ecr.repositories[0].name == repo_name assert len(ecr.registries[AWS_REGION].repositories) == 1
assert ecr.repositories[0].arn == repo_arn assert ecr.registries[AWS_REGION].repositories[0].name == repo_name
assert ecr.repositories[0].scan_on_push assert ecr.registries[AWS_REGION].repositories[0].arn == repo_arn
assert ecr.repositories[0].lyfecicle_policy assert ecr.registries[AWS_REGION].repositories[0].scan_on_push
assert ecr.registries[AWS_REGION].repositories[0].lifecycle_policy
# Test get image details # Test get image details
@mock_ecr @mock_ecr
@@ -204,45 +231,103 @@ class Test_ECR_Service:
) )
audit_info = self.set_mocked_audit_info() audit_info = self.set_mocked_audit_info()
ecr = ECR(audit_info) ecr = ECR(audit_info)
assert len(ecr.repositories) == 1 assert len(ecr.registries) == 1
assert ecr.repositories[0].name == repo_name assert len(ecr.registries[AWS_REGION].repositories) == 1
assert ecr.repositories[0].arn == repo_arn assert ecr.registries[AWS_REGION].repositories[0].name == repo_name
assert ecr.repositories[0].scan_on_push assert ecr.registries[AWS_REGION].repositories[0].arn == repo_arn
assert len(ecr.repositories[0].images_details) == 2 assert ecr.registries[AWS_REGION].repositories[0].scan_on_push
assert ecr.repositories[0].images_details[0].latest_tag == "test-tag" assert len(ecr.registries[AWS_REGION].repositories[0].images_details) == 2
# First image pushed
assert ecr.registries[AWS_REGION].repositories[0].images_details[
0
].image_pushed_at == datetime(2023, 1, 1)
assert ( assert (
ecr.repositories[0].images_details[0].latest_digest ecr.registries[AWS_REGION].repositories[0].images_details[0].latest_tag
== "test-tag1"
)
assert (
ecr.registries[AWS_REGION].repositories[0].images_details[0].latest_digest
== "sha256:d8868e50ac4c7104d2200d42f432b661b2da8c1e417ccfae217e6a1e04bb9295" == "sha256:d8868e50ac4c7104d2200d42f432b661b2da8c1e417ccfae217e6a1e04bb9295"
) )
assert ecr.repositories[0].images_details[0].scan_findings_status == "COMPLETE"
assert ( assert (
ecr.repositories[0].images_details[0].scan_findings_severity_count.critical ecr.registries[AWS_REGION]
.repositories[0]
.images_details[0]
.scan_findings_status
== "COMPLETE"
)
assert (
ecr.registries[AWS_REGION]
.repositories[0]
.images_details[0]
.scan_findings_severity_count.critical
== 1 == 1
) )
assert ( assert (
ecr.repositories[0].images_details[0].scan_findings_severity_count.high == 2 ecr.registries[AWS_REGION]
.repositories[0]
.images_details[0]
.scan_findings_severity_count.high
== 2
) )
assert ( assert (
ecr.repositories[0].images_details[0].scan_findings_severity_count.medium ecr.registries[AWS_REGION]
.repositories[0]
.images_details[0]
.scan_findings_severity_count.medium
== 3 == 3
) )
assert ecr.repositories[0].images_details[1].latest_tag == "test-tag2"
# Second image pushed
assert ecr.registries[AWS_REGION].repositories[0].images_details[
1
].image_pushed_at == datetime(2023, 1, 2)
assert ( assert (
ecr.repositories[0].images_details[1].latest_digest ecr.registries[AWS_REGION].repositories[0].images_details[1].latest_tag
== "test-tag2"
)
assert (
ecr.registries[AWS_REGION].repositories[0].images_details[1].latest_digest
== "sha256:83251ac64627fc331584f6c498b3aba5badc01574e2c70b2499af3af16630eed" == "sha256:83251ac64627fc331584f6c498b3aba5badc01574e2c70b2499af3af16630eed"
) )
assert not ecr.repositories[0].images_details[1].scan_findings_status assert (
assert not ecr.repositories[0].images_details[1].scan_findings_severity_count ecr.registries[AWS_REGION]
.repositories[0]
.images_details[1]
.scan_findings_status
== "COMPLETE"
)
assert (
ecr.registries[AWS_REGION]
.repositories[0]
.images_details[1]
.scan_findings_severity_count.critical
== 1
)
assert (
ecr.registries[AWS_REGION]
.repositories[0]
.images_details[1]
.scan_findings_severity_count.high
== 2
)
assert (
ecr.registries[AWS_REGION]
.repositories[0]
.images_details[1]
.scan_findings_severity_count.medium
== 3
)
# Test get ECR Registries # Test get ECR Registries Scanning Configuration
@mock_ecr @mock_ecr
def test__get_registry_scanning_configuration__(self): def test__get_registry_scanning_configuration__(self):
audit_info = self.set_mocked_audit_info() audit_info = self.set_mocked_audit_info()
ecr = ECR(audit_info) ecr = ECR(audit_info)
assert len(ecr.registries) == 1 assert len(ecr.registries) == 1
assert ecr.registries[0].id == AWS_ACCOUNT_NUMBER assert ecr.registries[AWS_REGION].id == AWS_ACCOUNT_NUMBER
assert ecr.registries[0].scan_type == "BASIC" assert ecr.registries[AWS_REGION].scan_type == "BASIC"
assert ecr.registries[0].rules == [ assert ecr.registries[AWS_REGION].rules == [
ScanningRule( ScanningRule(
scan_frequency="SCAN_ON_PUSH", scan_frequency="SCAN_ON_PUSH",
scan_filters=[{"filter": "*", "filterType": "WILDCARD"}], scan_filters=[{"filter": "*", "filterType": "WILDCARD"}],

View File

@@ -271,8 +271,8 @@ class Test_iam_role_cross_account_readonlyaccess_policy:
) )
with mock.patch( with mock.patch(
"prowler.providers.aws.services.iam.iam_service.IAM", "prowler.providers.aws.services.iam.iam_role_cross_account_readonlyaccess_policy.iam_role_cross_account_readonlyaccess_policy.iam_client",
iam_client, new=iam_client,
): ):
# Test Check # Test Check
from prowler.providers.aws.services.iam.iam_role_cross_account_readonlyaccess_policy.iam_role_cross_account_readonlyaccess_policy import ( from prowler.providers.aws.services.iam.iam_role_cross_account_readonlyaccess_policy.iam_role_cross_account_readonlyaccess_policy import (