feat(): ECR service and checks (#1475)

Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
Co-authored-by: sergargar <sergio@verica.io>
This commit is contained in:
Nacho Rivera
2022-11-17 19:41:03 +01:00
committed by GitHub
parent 9ff9b68d91
commit c83c4d0892
23 changed files with 1129 additions and 246 deletions

View File

View File

@@ -1,64 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# Remediation:
#
# https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html
#
# aws ecr put-image-scanning-configuration \
# --region <value> \
# --repository-name <value> \
# --image-scanning-configuration scanOnPush=true
CHECK_ID_extra765="7.65"
CHECK_TITLE_extra765="[extra765] Check if ECR image scan on push is enabled "
CHECK_SCORED_extra765="NOT_SCORED"
CHECK_CIS_LEVEL_extra765="EXTRA"
CHECK_SEVERITY_extra765="Medium"
CHECK_ALTERNATE_check765="extra765"
CHECK_SERVICENAME_extra765="ecr"
CHECK_RISK_extra765='Amazon ECR image scanning helps in identifying software vulnerabilities in your container images. Amazon ECR uses the Common Vulnerabilities and Exposures (CVEs) database from the open-source Clair project and provides a list of scan findings. '
CHECK_REMEDIATION_extra765='Enable ECR image scanning and review the scan findings for information about the security of the container images that are being deployed.'
CHECK_DOC_extra765='https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html'
CHECK_CAF_EPIC_extra765='Infrastructure Security'
extra765(){
for region in $REGIONS; do
LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $region --query "repositories[*].[repositoryName]" --output text 2>&1)
if [[ $(echo "$LIST_ECR_REPOS" | grep AccessDenied) ]]; then
textInfo "$region: Access Denied Trying to describe ECR repositories" "$region"
continue
fi
if [[ ! -z "$LIST_ECR_REPOS" ]]; then
for repo in $LIST_ECR_REPOS; do
SCAN_ENABLED=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $region --query "repositories[?repositoryName==\`$repo\`].[imageScanningConfiguration.scanOnPush]" --output text 2>&1)
case "$SCAN_ENABLED" in
"True")
textPass "$region: ECR repository $repo has scan on push enabled" "$region" "$repo"
;;
"False")
textFail "$region: ECR repository $repo has scan on push disabled!" "$region" "$repo"
;;
"None")
textInfo "$region: ECR repository $repo has no scanOnPush status: newer awscli needed" "$region" "$repo"
;;
"*")
textInfo "$region: ECR repository $repo has unknown scanOnPush status \"$SCAN_ENABLED\"" "$region" "$repo"
;;
esac
done
else
textInfo "$region: No ECR repositories found" "$region"
fi
done
}

View File

@@ -1,68 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra77="7.7"
CHECK_TITLE_extra77="[extra77] Ensure there are no ECR repositories set as Public"
CHECK_SCORED_extra77="NOT_SCORED"
CHECK_CIS_LEVEL_extra77="EXTRA"
CHECK_SEVERITY_extra77="Critical"
CHECK_ALTERNATE_extra707="extra77"
CHECK_ALTERNATE_check77="extra77"
CHECK_ALTERNATE_check707="extra77"
CHECK_SERVICENAME_extra77="ecr"
CHECK_RISK_extra77='Policy may allow Anonymous users to perform actions.'
CHECK_REMEDIATION_extra77='Ensure this repository and its contents should be publicly accessible.'
CHECK_DOC_extra77='https://docs.aws.amazon.com/AmazonECR/latest/public/security_iam_service-with-iam.html'
CHECK_CAF_EPIC_extra77='Data Protection'
extra77(){
# "Ensure there are no ECR repositories set as Public "
for regx in $REGIONS; do
LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query "repositories[*].[repositoryName]" --output text 2>&1)
if [[ $(echo "$LIST_ECR_REPOS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied Trying to describe ECR repositories" "$regx" "$repo"
continue
fi
if [[ $(echo "$LIST_ECR_REPOS" | grep SubscriptionRequiredException) ]]; then
textInfo "$regx: Subscription Required Exception trying to describe ECR repositories" "$regx" "$repo"
continue
fi
if [[ ! -z "$LIST_ECR_REPOS" ]]; then
for repo in $LIST_ECR_REPOS; do
TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-ecr-repo.policy.XXXXXXXXXX)
$AWSCLI ecr get-repository-policy $PROFILE_OPT --region $regx --repository-name $repo --query "policyText" --output text > $TEMP_POLICY_FILE 2>&1
if [[ $(grep AccessDenied $TEMP_POLICY_FILE) ]]; then
textInfo "$regx: Access Denied to get repository policy for repo $repo" "$regx" "$repo"
rm -f $TEMP_POLICY_FILE
continue
fi
# https://docs.aws.amazon.com/AmazonECR/latest/userguide/repository-policies.html - "By default, only the repository owner has access to a repository."
if [[ $(grep RepositoryPolicyNotFoundException $TEMP_POLICY_FILE) ]]; then
textPass "$regx: $repo is not open" "$regx" "$repo"
rm -f $TEMP_POLICY_FILE
continue
fi
# check if the policy has Principal as *
CHECK_ECR_REPO_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | jq '.Statement[]|select(.Effect=="Allow" and (((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")))')
if [[ $CHECK_ECR_REPO_ALLUSERS_POLICY ]]; then
textFail "$regx: $repo policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$regx"
else
textPass "$regx: $repo is not open" "$regx" "$repo"
fi
rm -f $TEMP_POLICY_FILE
done
else
textInfo "$regx: No ECR repositories found" "$regx" "$repo"
fi
done
}

View File

@@ -1,114 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# Remediation:
#
# https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html
#
# aws ecr put-image-scanning-configuration \
# --region <value> \
# --repository-name <value> \
# --image-scanning-configuration scanOnPush=true
#
# aws ecr describe-image-scan-findings \
# --region <value> \
# --repository-name <value>
# --image-id imageTag=<value>
CHECK_ID_extra776="7.76"
CHECK_TITLE_extra776="[extra776] Check if ECR image scan found vulnerabilities in the newest image version"
CHECK_SCORED_extra776="NOT_SCORED"
CHECK_CIS_LEVEL_extra776="EXTRA"
CHECK_SEVERITY_extra776="Medium"
CHECK_ALTERNATE_check776="extra776"
CHECK_SERVICENAME_extra776="ecr"
CHECK_ASFF_RESOURCE_TYPE_extra776="AwsEcrRepository"
CHECK_RISK_extra776='Amazon ECR image scanning helps in identifying software vulnerabilities in your container images. Amazon ECR uses the Common Vulnerabilities and Exposures (CVEs) database from the open-source Clair project and provides a list of scan findings. '
CHECK_REMEDIATION_extra776='Open the Amazon ECR console. Then look for vulnerabilities and fix them.'
CHECK_DOC_extra776='https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html#describe-scan-findings'
CHECK_CAF_EPIC_extra776='Logging and Monitoring'
extra776(){
for region in ${REGIONS}; do
# List ECR repositories
LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region "${region}" --query "repositories[*].[repositoryName]" --output text 2>&1)
# Handle authorization errors
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "$LIST_ECR_REPOS"; then
textInfo "$region: Access Denied trying to describe ECR repositories" "$region"
continue
fi
if [[ -n "$LIST_ECR_REPOS" ]]; then
for repo in $LIST_ECR_REPOS; do
# Check if the repository has scanOnPush enabled
SCAN_ENABLED=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region "${region}" --query "repositories[?repositoryName=='${repo}'].[imageScanningConfiguration.scanOnPush]" --output text 2>&1)
if [[ "${SCAN_ENABLED}" == "True" ]]; then
# Recover newest image digest
NEWEST_IMAGE_DIGEST=$($AWSCLI ecr describe-images $PROFILE_OPT --region "${region}" --repository-name "${repo}" --query "sort_by(imageDetails,& imagePushedAt)[-1].imageDigest" --output text | head -n 1 2>&1)
if [[ "${NEWEST_IMAGE_DIGEST}" != *"None"* ]]; then
# Recover newest image tag
NEWEST_IMAGE_TAG=$($AWSCLI ecr describe-images $PROFILE_OPT --region "${region}" --repository-name "${repo}" --query "sort_by(imageDetails,& imagePushedAt)[-1].imageTags[0]" --output text | head -n 1 2>&1)
if [[ -n "${LIST_ECR_REPOS}" ]]; then
# For this newest digest, recover the last scan status
IMAGE_SCAN_STATUS=$($AWSCLI ecr describe-image-scan-findings $PROFILE_OPT --region "${region}" --repository-name "${repo}" --image-id imageDigest="${NEWEST_IMAGE_DIGEST}" --query "imageScanStatus.status" --output text 2>&1)
if [[ "${IMAGE_SCAN_STATUS}" == *"ScanNotFoundException"* ]]; then
textFail "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} without a scan" "${region}" "${repo}"
else
if [[ "${IMAGE_SCAN_STATUS}" == *"FAILED"* ]]; then
textFail "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with scan status ${IMAGE_SCAN_STATUS}" "${region}" "${repo}"
else
# For this newest digest, recover the number of findings found
# This search needs a JSON response to match against severity
FINDINGS_COUNT=$($AWSCLI ecr describe-image-scan-findings $PROFILE_OPT --region "${region}" --repository-name "${repo}" --image-id imageDigest="${NEWEST_IMAGE_DIGEST}" --query "imageScanFindings.findingSeverityCounts" --output json 2>&1)
if [[ -n "${FINDINGS_COUNT}" ]]; then
SEVERITY_CRITICAL=$(jq -r '.CRITICAL' <<< "${FINDINGS_COUNT}")
if [[ "${SEVERITY_CRITICAL}" != "null" ]]; then
textFail "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with CRITICAL ($SEVERITY_CRITICAL) findings" "${region}" "${repo}"
fi
SEVERITY_HIGH=$(jq -r '.HIGH' <<< "${FINDINGS_COUNT}")
if [[ "${SEVERITY_HIGH}" != "null" ]]; then
textFail "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with HIGH ($SEVERITY_HIGH) findings" "${region}" "${repo}"
fi
SEVERITY_MEDIUM=$(jq -r '.MEDIUM' <<< "${FINDINGS_COUNT}")
if [[ "${SEVERITY_MEDIUM}" != "null" ]]; then
textFail "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with MEDIUM ($SEVERITY_MEDIUM) findings" "${region}" "${repo}"
fi
SEVERITY_LOW=$(jq -r '.LOW' <<< "${FINDINGS_COUNT}")
if [[ "${SEVERITY_LOW}" != "null" ]]; then
textInfo "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with LOW ($SEVERITY_LOW) findings" "${region}" "${repo}"
fi
SEVERITY_INFORMATIONAL=$(jq -r '.INFORMATIONAL' <<< "${FINDINGS_COUNT}")
if [[ "${SEVERITY_INFORMATIONAL}" != "null" ]]; then
textInfo "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with INFORMATIONAL ($SEVERITY_INFORMATIONAL) findings" "${region}" "${repo}"
fi
SEVERITY_UNDEFINED=$(jq -r '.UNDEFINED' <<< "${FINDINGS_COUNT}")
if [[ "${SEVERITY_UNDEFINED}" != "null" ]]; then
textInfo "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with UNDEFINED ($SEVERITY_UNDEFINED) findings" "${region}" "${repo}"
fi
else
textPass "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} without findings" "${region}" "${repo}"
fi
fi
fi
fi
else
textInfo "${region}: ECR repository ${repo} has no images" "${region}"
fi
else
textInfo "${region}: ECR repository ${repo} has scanOnPush disabled" "${region}" "${repo}"
fi
done
else
textInfo "${region}: No ECR repositories found" "${region}"
fi
done
}

View File

@@ -0,0 +1,4 @@
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.ecr.ecr_service import ECR
ecr_client = ECR(current_audit_info)

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "ecr_repositories_lifecycle_policy_enabled",
"CheckTitle": "Check if ECR repositories have lifecycle policies enabled",
"CheckType": ["Identify", "Resource configuration"],
"ServiceName": "ecr",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "low",
"ResourceType": "AwsEcrRepository",
"Description": "Check if ECR repositories have lifecycle policies enabled",
"Risk": "Amazon ECR repositories run the risk of retaining huge volumes of images, increasing unnecessary cost.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ECR/lifecycle-policy-in-use.html",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Open the Amazon ECR console. Create an ECR lifecycle policy.",
"Url": "https://docs.aws.amazon.com/AmazonECR/latest/userguide/LifecyclePolicies.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,25 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.ecr.ecr_client import ecr_client
class ecr_repositories_lifecycle_policy_enabled(Check):
def execute(self):
findings = []
for repository in ecr_client.repositories:
report = Check_Report(self.metadata)
report.region = repository.region
report.resource_id = repository.name
report.resource_arn = repository.arn
report.status = "FAIL"
report.status_extended = (
f"Repository {repository.name} has no lifecycle policy"
)
if repository.lyfecicle_policy:
report.status = "PASS"
report.status_extended = (
f"Repository {repository.name} has lifecycle policy"
)
findings.append(report)
return findings

View File

@@ -0,0 +1,87 @@
from re import search
from unittest import mock
from providers.aws.services.ecr.ecr_service import Repository
# Mock Test Region
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
repository_name = "test_repo"
repository_arn = (
f"arn:aws:ecr:eu-west-1:{AWS_ACCOUNT_NUMBER}:repository/{repository_name}"
)
repo_policy_public = {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECRRepositoryPolicy",
"Effect": "Allow",
"Principal": {"AWS": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/username"},
"Action": ["ecr:DescribeImages", "ecr:DescribeRepositories"],
}
],
}
class Test_ecr_repositories_lifecycle_policy_enabled:
def test_no_lyfecicle_policy(self):
ecr_client = mock.MagicMock
ecr_client.repositories = []
ecr_client.repositories.append(
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION,
scan_on_push=True,
policy=repo_policy_public,
images_details=None,
lyfecicle_policy="test-policy",
)
)
with mock.patch(
"providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from 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) == 1
assert result[0].status == "PASS"
assert search("has lifecycle policy", result[0].status_extended)
assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn
def test_lifecycle_policy(self):
ecr_client = mock.MagicMock
ecr_client.repositories = []
ecr_client.repositories.append(
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION,
scan_on_push=False,
policy=repo_policy_public,
images_details=None,
lyfecicle_policy=None,
)
)
with mock.patch(
"providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from 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) == 1
assert result[0].status == "FAIL"
assert search("has no lifecycle policy", result[0].status_extended)
assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "ecr_repositories_not_publicly_accessible",
"CheckTitle": "Ensure there are no ECR repositories set as Public",
"CheckType": ["Protect", "Secure Access Management"],
"ServiceName": "ecr",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "critical",
"ResourceType": "AwsEcrRepository",
"Description": "Ensure there are no ECR repositories set as Public",
"Risk": "A repository policy that allows anonymous access may allow anonymous users to perform actions.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "https://docs.bridgecrew.io/docs/public_1-ecr-repositories-not-public#cloudformation",
"Other": "https://docs.bridgecrew.io/docs/public_1-ecr-repositories-not-public#aws-console",
"Terraform": ""
},
"Recommendation": {
"Text": "Ensure the repository and its contents are not publicly accessible",
"Url": "https://docs.aws.amazon.com/AmazonECR/latest/public/security_iam_service-with-iam.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

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

View File

@@ -0,0 +1,101 @@
from re import search
from unittest import mock
from providers.aws.services.ecr.ecr_service import Repository
# Mock Test Region
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
repository_name = "test_repo"
repository_arn = (
f"arn:aws:ecr:eu-west-1:{AWS_ACCOUNT_NUMBER}:repository/{repository_name}"
)
repo_policy_not_public = {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECRRepositoryPolicy",
"Effect": "Allow",
"Principal": {"AWS": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/username"},
"Action": ["ecr:DescribeImages", "ecr:DescribeRepositories"],
}
],
}
repo_policy_public = {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECRRepositoryPolicy",
"Effect": "Allow",
"Principal": {"AWS": "*"},
"Action": ["ecr:DescribeImages", "ecr:DescribeRepositories"],
}
],
}
class Test_ecr_repositories_not_publicly_accessible:
def test_repository_not_public(self):
ecr_client = mock.MagicMock
ecr_client.repositories = []
ecr_client.repositories.append(
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION,
scan_on_push=True,
policy=repo_policy_not_public,
images_details=None,
lyfecicle_policy=None,
)
)
with mock.patch(
"providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from 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) == 1
assert result[0].status == "PASS"
assert search("is not open", result[0].status_extended)
assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn
def test_repository_public(self):
ecr_client = mock.MagicMock
ecr_client.repositories = []
ecr_client.repositories.append(
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION,
scan_on_push=True,
policy=repo_policy_public,
images_details=None,
lyfecicle_policy=None,
)
)
with mock.patch(
"providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from 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) == 1
assert result[0].status == "FAIL"
assert search(
"policy may allow anonymous users to", result[0].status_extended
)
assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "ecr_repositories_scan_images_on_push_enabled",
"CheckTitle": "Check if ECR image scan on push is enabled",
"CheckType": ["Identify", "Vulnerability, patch, and version management"],
"ServiceName": "ecr",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsEcrRepository",
"Description": "Check if ECR image scan on push is enabled",
"Risk": "Amazon ECR image scanning helps in identifying software vulnerabilities in your container images. Amazon ECR uses the Common Vulnerabilities and Exposures (CVEs) database from the open-source Clair project and provides a list of scan findings. ",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws ecr create-repository --repository-name <repo_name> --image-scanning-configuration scanOnPush=true--region <region_name>",
"NativeIaC": "https://docs.bridgecrew.io/docs/general_8#cli-command",
"Other": "",
"Terraform": "https://docs.bridgecrew.io/docs/general_8#fix---buildtime"
},
"Recommendation": {
"Text": "Enable ECR image scanning and review the scan findings for information about the security of the container images that are being deployed.",
"Url": "https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,25 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.ecr.ecr_client import ecr_client
class ecr_repositories_scan_images_on_push_enabled(Check):
def execute(self):
findings = []
for repository in ecr_client.repositories:
report = Check_Report(self.metadata)
report.region = repository.region
report.resource_id = repository.name
report.resource_arn = repository.arn
report.status = "PASS"
report.status_extended = (
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)
return findings

View File

@@ -0,0 +1,87 @@
from re import search
from unittest import mock
from providers.aws.services.ecr.ecr_service import Repository
# Mock Test Region
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
repository_name = "test_repo"
repository_arn = (
f"arn:aws:ecr:eu-west-1:{AWS_ACCOUNT_NUMBER}:repository/{repository_name}"
)
repo_policy_public = {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECRRepositoryPolicy",
"Effect": "Allow",
"Principal": {"AWS": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/username"},
"Action": ["ecr:DescribeImages", "ecr:DescribeRepositories"],
}
],
}
class Test_ecr_repositories_scan_images_on_push_enabled:
def test_scan_on_push_disabled(self):
ecr_client = mock.MagicMock
ecr_client.repositories = []
ecr_client.repositories.append(
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION,
scan_on_push=True,
policy=repo_policy_public,
images_details=None,
lyfecicle_policy=None,
)
)
with mock.patch(
"providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from 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) == 1
assert result[0].status == "PASS"
assert search("has scan on push enabled", result[0].status_extended)
assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn
def test_scan_on_push_enabled(self):
ecr_client = mock.MagicMock
ecr_client.repositories = []
ecr_client.repositories.append(
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION,
scan_on_push=False,
policy=repo_policy_public,
images_details=None,
lyfecicle_policy=None,
)
)
with mock.patch(
"providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from 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) == 1
assert result[0].status == "FAIL"
assert search("has scan on push disabled", result[0].status_extended)
assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "ecr_repositories_scan_vulnerabilities_in_latest_image",
"CheckTitle": "Check if ECR image scan found vulnerabilities in the newest image version",
"CheckType": ["Identify", "Vulnerability, patch, and version management"],
"ServiceName": "ecr",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsEcrRepository",
"Description": "Check if ECR image scan found vulnerabilities in the newest image version",
"Risk": "Amazon ECR image scanning helps in identifying software vulnerabilities in your container images. Amazon ECR uses the Common Vulnerabilities and Exposures (CVEs) database from the open-source Clair project and provides a list of scan findings.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Open the Amazon ECR console. Then look for vulnerabilities and fix them.",
"Url": "https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html#describe-scan-findings"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,35 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.ecr.ecr_client import ecr_client
class ecr_repositories_scan_vulnerabilities_in_latest_image(Check):
def execute(self):
findings = []
for repository in ecr_client.repositories:
for image in repository.images_details:
report = Check_Report(self.metadata)
report.region = repository.region
report.resource_id = repository.name
report.resource_arn = repository.arn
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

View File

@@ -0,0 +1,189 @@
from re import search
from unittest import mock
from providers.aws.services.ecr.ecr_service import (
FindingSeverityCounts,
ImageDetails,
Repository,
)
# Mock Test Region
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
repository_name = "test_repo"
repository_arn = (
f"arn:aws:ecr:eu-west-1:{AWS_ACCOUNT_NUMBER}:repository/{repository_name}"
)
repo_policy_public = {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECRRepositoryPolicy",
"Effect": "Allow",
"Principal": {"AWS": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/username"},
"Action": ["ecr:DescribeImages", "ecr:DescribeRepositories"],
}
],
}
class Test_ecr_repositories_scan_vulnerabilities_in_latest_image:
def test_image_scaned_without_findings(self):
ecr_client = mock.MagicMock
ecr_client.repositories = []
ecr_client.repositories.append(
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION,
scan_on_push=True,
policy=repo_policy_public,
images_details=[],
lyfecicle_policy=None,
)
)
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(
"providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from 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) == 1
assert result[0].status == "PASS"
assert search("scanned without findings", result[0].status_extended)
assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn
def test_image_scanned_with_findings(self):
ecr_client = mock.MagicMock
ecr_client.repositories = []
ecr_client.repositories.append(
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION,
scan_on_push=True,
policy=repo_policy_public,
images_details=[],
lyfecicle_policy=None,
)
)
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(
"providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from 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) == 1
assert result[0].status == "FAIL"
assert search("scanned with findings:", result[0].status_extended)
assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn
def test_image_scanned_fail_scan(self):
ecr_client = mock.MagicMock
ecr_client.repositories = []
ecr_client.repositories.append(
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION,
scan_on_push=True,
policy=repo_policy_public,
images_details=[],
lyfecicle_policy=None,
)
)
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(
"providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from 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) == 1
assert result[0].status == "FAIL"
assert search("with scan status FAILED", result[0].status_extended)
assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn
def test_image_not_scanned(self):
ecr_client = mock.MagicMock
ecr_client.repositories = []
ecr_client.repositories.append(
Repository(
name=repository_name,
arn=repository_arn,
region=AWS_REGION,
scan_on_push=True,
policy=repo_policy_public,
images_details=[],
lyfecicle_policy=None,
)
)
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(
"providers.aws.services.ecr.ecr_service.ECR",
ecr_client,
):
from 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) == 1
assert result[0].status == "FAIL"
assert search("without a scan", result[0].status_extended)
assert result[0].resource_id == repository_name
assert result[0].resource_arn == repository_arn

View File

@@ -0,0 +1,196 @@
import threading
from dataclasses import dataclass
from json import loads
from lib.logger import logger
from providers.aws.aws_provider import generate_regional_clients
################################ ECR
class ECR:
def __init__(self, audit_info):
self.service = "ecr"
self.session = audit_info.audit_session
self.regional_clients = generate_regional_clients(self.service, audit_info)
self.repositories = []
self.__threading_call__(self.__describe_repositories__)
self.__describe_repository_policies__()
self.__get_image_details__()
self.__get_repository_lifecycle_policy__()
def __get_session__(self):
return self.session
def __threading_call__(self, call):
threads = []
for regional_client in self.regional_clients.values():
threads.append(threading.Thread(target=call, args=(regional_client,)))
for t in threads:
t.start()
for t in threads:
t.join()
def __describe_repositories__(self, regional_client):
logger.info("ECR - Describing repositories...")
try:
describe_ecr_paginator = regional_client.get_paginator(
"describe_repositories"
)
for page in describe_ecr_paginator.paginate():
for repository in page["repositories"]:
self.repositories.append(
Repository(
name=repository["repositoryName"],
arn=repository["repositoryArn"],
region=regional_client.region,
scan_on_push=repository["imageScanningConfiguration"][
"scanOnPush"
],
policy=None,
images_details=[],
lyfecicle_policy=None,
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __describe_repository_policies__(self):
logger.info("ECR - Describing repository policies...")
try:
for repository in self.repositories:
client = self.regional_clients[repository.region]
policy = client.get_repository_policy(repositoryName=repository.name)
if "policyText" in policy:
repository.policy = loads(policy["policyText"])
except Exception as error:
logger.error(
f"-- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __get_repository_lifecycle_policy__(self):
logger.info("ECR - Getting repository lifecycle policy...")
try:
for repository in self.repositories:
client = self.regional_clients[repository.region]
policy = client.get_lifecycle_policy(repositoryName=repository.name)
if "lifecyclePolicyText" in policy:
repository.lyfecicle_policy = policy["lifecyclePolicyText"]
except Exception as error:
logger.error(
f"-- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __get_image_details__(self):
logger.info("ECR - Getting images details...")
try:
for repository in self.repositories:
# if the repo is not scanning pushed images there is nothing to do
if repository.scan_on_push:
client = self.regional_clients[repository.region]
describe_images_paginator = client.get_paginator("describe_images")
for page in describe_images_paginator.paginate(
repositoryName=repository.name
):
for image in page["imageDetails"]:
severity_counts = None
last_scan_status = None
if "imageScanStatus" in image:
last_scan_status = image["imageScanStatus"]["status"]
if "imageScanFindingsSummary" in image:
severity_counts = FindingSeverityCounts(
critical=0, high=0, medium=0
)
finding_severity_counts = image[
"imageScanFindingsSummary"
]["findingSeverityCounts"]
if "CRITICAL" in finding_severity_counts:
severity_counts.critical = finding_severity_counts[
"CRITICAL"
]
if "HIGH" in finding_severity_counts:
severity_counts.high = finding_severity_counts[
"HIGH"
]
if "MEDIUM" in finding_severity_counts:
severity_counts.medium = finding_severity_counts[
"MEDIUM"
]
repository.images_details.append(
ImageDetails(
latest_tag=image["imageTags"][0],
latest_digest=image["imageDigest"],
scan_findings_status=last_scan_status,
scan_findings_severity_count=severity_counts,
)
)
except Exception as error:
logger.error(
f"-- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
@dataclass
class FindingSeverityCounts:
critical: int
high: int
medium: int
def __init__(
self,
critical,
high,
medium,
):
self.critical = critical
self.high = high
self.medium = medium
@dataclass
class ImageDetails:
latest_tag: str
latest_digest: str
scan_findings_status: str
scan_findings_severity_count: FindingSeverityCounts
def __init__(
self,
latest_tag,
latest_digest,
scan_findings_status,
scan_findings_severity_count,
):
self.latest_tag = latest_tag
self.latest_digest = latest_digest
self.scan_findings_status = scan_findings_status
self.scan_findings_severity_count = scan_findings_severity_count
@dataclass
class Repository:
name: str
arn: str
region: str
scan_on_push: bool
policy: dict
images_details: list[ImageDetails]
lyfecicle_policy: str
def __init__(
self, name, arn, region, scan_on_push, policy, images_details, lyfecicle_policy
):
self.name = name
self.arn = arn
self.region = region
self.scan_on_push = scan_on_push
self.policy = policy
self.images_details = images_details
self.lyfecicle_policy = lyfecicle_policy

View File

@@ -0,0 +1,212 @@
from unittest.mock import patch
import botocore
from boto3 import client, session
from moto import mock_ecr
from providers.aws.lib.audit_info.models import AWS_Audit_Info
from providers.aws.services.ecr.ecr_service import ECR
AWS_ACCOUNT_NUMBER = 123456789012
AWS_REGION = "eu-west-1"
repo_arn = f"arn:aws:ecr:eu-west-1:{AWS_ACCOUNT_NUMBER}:repository/test-repo"
repo_name = "test-repo"
# Mocking Access Analyzer Calls
make_api_call = botocore.client.BaseClient._make_api_call
def mock_make_api_call(self, operation_name, kwarg):
if operation_name == "DescribeImages":
return {
"imageDetails": [
{
"imageDigest": "sha256:d8868e50ac4c7104d2200d42f432b661b2da8c1e417ccfae217e6a1e04bb9295",
"imageTags": [
"test-tag",
],
"imageScanStatus": {
"status": "COMPLETE",
},
"imageScanFindingsSummary": {
"findingSeverityCounts": {"CRITICAL": 1, "HIGH": 2, "MEDIUM": 3}
},
},
{
"imageDigest": "sha256:83251ac64627fc331584f6c498b3aba5badc01574e2c70b2499af3af16630eed",
"imageTags": [
"test-tag2",
],
},
],
}
if operation_name == "GetRepositoryPolicy":
return {
"registryId": "string",
"repositoryName": "string",
"policyText": '{\n "Version" : "2012-10-17",\n "Statement" : [ {\n "Sid" : "Allow Describe Images",\n "Effect" : "Allow",\n "Principal" : {\n "AWS" : [ "arn:aws:iam::123456789012:root" ]\n },\n "Action" : [ "ecr:DescribeImages", "ecr:DescribeRepositories" ]\n } ]\n}',
}
if operation_name == "GetLifecyclePolicy":
return {
"registryId": "string",
"repositoryName": "string",
"lifecyclePolicyText": "test-policy",
}
return make_api_call(self, operation_name, kwarg)
def mock_generate_regional_clients(service, audit_info):
regional_client = audit_info.audit_session.client(service, region_name=AWS_REGION)
regional_client.region = AWS_REGION
return {AWS_REGION: regional_client}
# Patch every AWS call using Boto3 and generate_regional_clients to have 1 client
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@patch(
"providers.aws.services.ecr.ecr_service.generate_regional_clients",
new=mock_generate_regional_clients,
)
class Test_ECR_Service:
# Mocked Audit Info
def set_mocked_audit_info(self):
audit_info = AWS_Audit_Info(
original_session=None,
audit_session=session.Session(
profile_name=None,
botocore_session=None,
),
audited_account=AWS_ACCOUNT_NUMBER,
audited_user_id=None,
audited_partition="aws",
audited_identity_arn=None,
profile=None,
profile_region=None,
credentials=None,
assumed_role_info=None,
audited_regions=None,
organizations_metadata=None,
)
return audit_info
# Test ECR Service
def test_service(self):
audit_info = self.set_mocked_audit_info()
ecr = ECR(audit_info)
assert ecr.service == "ecr"
# Test ECR client
def test_client(self):
audit_info = self.set_mocked_audit_info()
ecr = ECR(audit_info)
for regional_client in ecr.regional_clients.values():
assert regional_client.__class__.__name__ == "ECR"
# Test ECR session
def test__get_session__(self):
audit_info = self.set_mocked_audit_info()
ecr = ECR(audit_info)
assert ecr.session.__class__.__name__ == "Session"
# Test describe ECR repositories
@mock_ecr
def test__describe_repositories__(self):
ecr_client = client("ecr", region_name=AWS_REGION)
ecr_client.create_repository(
repositoryName=repo_name,
imageScanningConfiguration={"scanOnPush": True},
)
audit_info = self.set_mocked_audit_info()
ecr = ECR(audit_info)
assert len(ecr.repositories) == 1
assert ecr.repositories[0].name == repo_name
assert ecr.repositories[0].arn == repo_arn
assert ecr.repositories[0].scan_on_push
# Test describe ECR repository policies
@mock_ecr
def test__describe_repository_policies__(self):
ecr_client = client("ecr", region_name=AWS_REGION)
ecr_client.create_repository(
repositoryName=repo_name,
imageScanningConfiguration={"scanOnPush": True},
)
audit_info = self.set_mocked_audit_info()
ecr = ECR(audit_info)
assert len(ecr.repositories) == 1
assert ecr.repositories[0].name == repo_name
assert ecr.repositories[0].arn == repo_arn
assert ecr.repositories[0].scan_on_push
assert (
ecr.repositories[0].policy["Statement"][0]["Sid"] == "Allow Describe Images"
)
assert ecr.repositories[0].policy["Statement"][0]["Effect"] == "Allow"
assert (
ecr.repositories[0].policy["Statement"][0]["Principal"]["AWS"][0]
== f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
)
assert (
ecr.repositories[0].policy["Statement"][0]["Action"][0]
== "ecr:DescribeImages"
)
assert (
ecr.repositories[0].policy["Statement"][0]["Action"][1]
== "ecr:DescribeRepositories"
)
# Test describe ECR repository policies
@mock_ecr
def test__get_lifecycle_policies__(self):
ecr_client = client("ecr", region_name=AWS_REGION)
ecr_client.create_repository(
repositoryName=repo_name,
imageScanningConfiguration={"scanOnPush": True},
)
audit_info = self.set_mocked_audit_info()
ecr = ECR(audit_info)
assert len(ecr.repositories) == 1
assert ecr.repositories[0].name == repo_name
assert ecr.repositories[0].arn == repo_arn
assert ecr.repositories[0].scan_on_push
assert ecr.repositories[0].lyfecicle_policy
# Test get image details
@mock_ecr
def test__get_image_details__(self):
ecr_client = client("ecr", region_name=AWS_REGION)
ecr_client.create_repository(
repositoryName=repo_name,
imageScanningConfiguration={"scanOnPush": True},
)
audit_info = self.set_mocked_audit_info()
ecr = ECR(audit_info)
assert len(ecr.repositories) == 1
assert ecr.repositories[0].name == repo_name
assert ecr.repositories[0].arn == repo_arn
assert ecr.repositories[0].scan_on_push
assert len(ecr.repositories[0].images_details) == 2
assert ecr.repositories[0].images_details[0].latest_tag == "test-tag"
assert (
ecr.repositories[0].images_details[0].latest_digest
== "sha256:d8868e50ac4c7104d2200d42f432b661b2da8c1e417ccfae217e6a1e04bb9295"
)
assert ecr.repositories[0].images_details[0].scan_findings_status == "COMPLETE"
assert (
ecr.repositories[0].images_details[0].scan_findings_severity_count.critical
== 1
)
assert (
ecr.repositories[0].images_details[0].scan_findings_severity_count.high == 2
)
assert (
ecr.repositories[0].images_details[0].scan_findings_severity_count.medium
== 3
)
assert ecr.repositories[0].images_details[1].latest_tag == "test-tag2"
assert (
ecr.repositories[0].images_details[1].latest_digest
== "sha256:83251ac64627fc331584f6c498b3aba5badc01574e2c70b2499af3af16630eed"
)
assert not ecr.repositories[0].images_details[1].scan_findings_status
assert not ecr.repositories[0].images_details[1].scan_findings_severity_count