Back to Blog

AWS ECR Image Scanning: Vulnerability Detection, Trivy CI, SBOM, and Image Signing with Cosign

Secure your container images with AWS ECR enhanced scanning, Trivy in CI/CD pipelines, Software Bill of Materials (SBOM) generation, and Cosign image signing. Covers Inspector integration, CVE reporting, and Terraform setup.

Viprasol Tech Team
May 13, 2027
13 min read

Shipping a container image without scanning it is like deploying code without running tests. CVEs in base images and third-party packages are a common attack vector — and they're fixable before deployment. AWS ECR provides two scanning tiers, Trivy adds CI-gate capability, and Cosign ensures images haven't been tampered with between build and deploy.

This guide covers the full supply chain security stack for containers.

ECR Scanning: Basic vs Enhanced

FeatureBasic ScanningEnhanced Scanning
EngineClair (open source)Amazon Inspector + Snyk
OS packages
Language packages (npm, pip, go)
Scan on push
Continuous scanning (after push)✅ (rescans as new CVEs discovered)
CostFree$0.09/image/month after first 500
EventBridge integration

Terraform: ECR with Enhanced Scanning

# terraform/ecr.tf

# Enable enhanced scanning at the registry level
resource "aws_ecr_registry_scanning_configuration" "main" {
  scan_type = "ENHANCED"  # Or "BASIC"

  rule {
    scan_frequency = "SCAN_ON_PUSH"
    repository_filter {
      filter      = "*"           # Scan all repos
      filter_type = "WILDCARD"
    }
  }

  rule {
    scan_frequency = "CONTINUOUS_SCAN"  # Enhanced only
    repository_filter {
      filter      = "prod-*"      # Continuous scan for production images
      filter_type = "WILDCARD"
    }
  }
}

resource "aws_ecr_repository" "app" {
  name                 = "${var.app_name}-app"
  image_tag_mutability = "IMMUTABLE"  # Prevent tag overwriting (security baseline)

  image_scanning_configuration {
    scan_on_push = true
  }

  encryption_configuration {
    encryption_type = "KMS"
    kms_key         = aws_kms_key.ecr.arn
  }

  tags = local.tags
}

# Lifecycle policy: keep last 10 tagged images, expire untagged after 7 days
resource "aws_ecr_lifecycle_policy" "app" {
  repository = aws_ecr_repository.app.name

  policy = jsonencode({
    rules = [
      {
        rulePriority = 1
        description  = "Keep last 10 production images"
        selection = {
          tagStatus     = "tagged"
          tagPrefixList = ["v"]
          countType     = "imageCountMoreThan"
          countNumber   = 10
        }
        action = { type = "expire" }
      },
      {
        rulePriority = 2
        description  = "Expire untagged images after 7 days"
        selection = {
          tagStatus   = "untagged"
          countType   = "sinceImagePushed"
          countUnit   = "days"
          countNumber = 7
        }
        action = { type = "expire" }
      }
    ]
  })
}

# Alert when CRITICAL vulnerabilities are found
resource "aws_cloudwatch_event_rule" "ecr_critical_cve" {
  name = "${var.app_name}-ecr-critical-cve"
  event_pattern = jsonencode({
    source      = ["aws.inspector2"]
    detail-type = ["Inspector2 Finding"]
    detail = {
      severity = [{ exists = true }]
      resources = {
        type = ["AWS_ECR_CONTAINER_IMAGE"]
      }
      severity = ["CRITICAL"]
    }
  })
}

resource "aws_cloudwatch_event_target" "ecr_cve_sns" {
  rule = aws_cloudwatch_event_rule.ecr_critical_cve.name
  arn  = aws_sns_topic.security_alerts.arn
}

☁️ Is Your Cloud Costing Too Much?

Most teams overspend 30–40% on cloud — wrong instance types, no reserved pricing, bloated storage. We audit, right-size, and automate your infrastructure.

  • AWS, GCP, Azure certified engineers
  • Infrastructure as Code (Terraform, CDK)
  • Docker, Kubernetes, GitHub Actions CI/CD
  • Typical audit recovers $500–$3,000/month in savings

Trivy in CI: Block on Critical CVEs

# .github/workflows/build-and-scan.yml
name: Build, Scan, and Push

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions:
  contents:      read
  id-token:      write   # For OIDC → AWS
  security-events: write # For SARIF upload to GitHub Security

env:
  ECR_REGISTRY: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com
  IMAGE_NAME:   ${{ secrets.APP_NAME }}-app

jobs:
  build-scan-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # Build image with build args
      - name: Build Docker image
        run: |
          docker build \
            --build-arg BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
            --build-arg GIT_SHA=${{ github.sha }} \
            --tag $IMAGE_NAME:${{ github.sha }} \
            --tag $IMAGE_NAME:latest \
            .

      # Scan with Trivy — fail on CRITICAL CVEs
      - name: Scan with Trivy (fail on CRITICAL)
        uses: aquasecurity/trivy-action@master
        with:
          image-ref:     "${{ env.IMAGE_NAME }}:${{ github.sha }}"
          format:        "table"
          exit-code:     "1"            # Fail CI on vulnerabilities found
          severity:      "CRITICAL"     # Only fail on CRITICAL (not HIGH)
          ignore-unfixed: true          # Skip CVEs with no fix available

      # Also output SARIF for GitHub Security tab
      - name: Scan with Trivy (SARIF output)
        uses: aquasecurity/trivy-action@master
        with:
          image-ref:  "${{ env.IMAGE_NAME }}:${{ github.sha }}"
          format:     "sarif"
          output:     "trivy-results.sarif"
          severity:   "HIGH,CRITICAL"
          ignore-unfixed: true

      - name: Upload SARIF to GitHub Security
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-results.sarif

      # Only push if scan passes
      - name: Configure AWS credentials (OIDC)
        if: github.ref == 'refs/heads/main'
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
          aws-region:     ${{ secrets.AWS_REGION }}

      - name: Login to ECR
        if: github.ref == 'refs/heads/main'
        uses: aws-actions/amazon-ecr-login@v2

      - name: Push to ECR
        if: github.ref == 'refs/heads/main'
        run: |
          docker tag $IMAGE_NAME:${{ github.sha }} \
            $ECR_REGISTRY/$IMAGE_NAME:${{ github.sha }}
          docker tag $IMAGE_NAME:${{ github.sha }} \
            $ECR_REGISTRY/$IMAGE_NAME:latest
          docker push $ECR_REGISTRY/$IMAGE_NAME:${{ github.sha }}
          docker push $ECR_REGISTRY/$IMAGE_NAME:latest

SBOM Generation with Syft

A Software Bill of Materials lists every package in your image — required for compliance in many industries.

      # After build, before push
      - name: Generate SBOM with Syft
        uses: anchore/sbom-action@v0
        with:
          image:     "${{ env.IMAGE_NAME }}:${{ github.sha }}"
          format:    spdx-json           # Or cyclonedx-json
          output-file: sbom.spdx.json

      # Upload SBOM as build artifact
      - name: Upload SBOM artifact
        uses: actions/upload-artifact@v4
        with:
          name: sbom-${{ github.sha }}
          path: sbom.spdx.json
          retention-days: 90

      # Optionally: attach SBOM to ECR image
      - name: Attach SBOM to ECR image
        run: |
          oras attach \
            --artifact-type application/vnd.syft+json \
            $ECR_REGISTRY/$IMAGE_NAME:${{ github.sha }} \
            sbom.spdx.json

⚙️ DevOps Done Right — Zero Downtime, Full Automation

Ship faster without breaking things. We build CI/CD pipelines, monitoring stacks, and auto-scaling infrastructure that your team can actually maintain.

  • Staging + production environments with feature flags
  • Automated security scanning in the pipeline
  • Uptime monitoring + alerting + runbook automation
  • On-call support handover docs included

Image Signing with Cosign

Cosign ensures that the image deployed to ECS/EKS is exactly what was built in CI — no tampering in transit.

      # Install Cosign
      - name: Install Cosign
        uses: sigstore/cosign-installer@v3

      # Sign image with keyless signing (OIDC-based, no private key to manage)
      - name: Sign image with Cosign (keyless)
        if: github.ref == 'refs/heads/main'
        env:
          COSIGN_EXPERIMENTAL: "1"
        run: |
          cosign sign --yes \
            $ECR_REGISTRY/$IMAGE_NAME:${{ github.sha }}

      # Verify before deployment (in deploy job or admission webhook)
      - name: Verify image signature
        if: github.ref == 'refs/heads/main'
        env:
          COSIGN_EXPERIMENTAL: "1"
        run: |
          cosign verify \
            --certificate-identity-regexp="https://github.com/${{ github.repository }}.*" \
            --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
            $ECR_REGISTRY/$IMAGE_NAME:${{ github.sha }}

Dockerfile Best Practices for Low CVE Surface

# Use minimal base images — fewer packages = fewer CVEs
FROM node:22-alpine AS base

# ✅ Alpine base images have ~50 packages vs ~200 in debian-slim
# ✅ Always pin to a specific digest for reproducibility
# FROM node:22-alpine@sha256:<digest>

# Don't run as root
RUN addgroup -S app && adduser -S app -G app

# Build stage
FROM base AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --ignore-scripts      # --ignore-scripts: prevents malicious postinstall scripts
COPY . .
RUN npm run build

# Production stage
FROM base AS runner
WORKDIR /app

# Only copy production artifacts
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static     ./.next/static
COPY --from=builder /app/public           ./public

# Drop to non-root user
USER app

# Health check for container orchestrators
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD wget -qO- http://localhost:3000/api/health || exit 1

EXPOSE 3000
CMD ["node", "server.js"]

Check Scan Results via AWS CLI

# Get scan findings for the latest image in a repository
aws ecr describe-image-scan-findings \
  --repository-name myapp-app \
  --image-id imageTag=latest \
  --query 'imageScanFindings.findingSeverityCounts' \
  --output table

# Output example:
# +-----------+-------+
# | CRITICAL  |     0 |
# | HIGH      |     3 |
# | MEDIUM    |    12 |
# | LOW       |    24 |
# | INFO      |     8 |
# +-----------+-------+

# List CRITICAL findings with CVE IDs
aws ecr describe-image-scan-findings \
  --repository-name myapp-app \
  --image-id imageTag=latest \
  --query 'imageScanFindings.findings[?severity==`CRITICAL`].[name,description,uri]' \
  --output table

Cost Estimates

ComponentCost
ECR Basic scanningFree
ECR Enhanced scanning (Amazon Inspector)$0.09/image/month after first 500
Trivy in CIFree (open source)
Cosign keyless signingFree (Sigstore public good)
SBOM storage (S3)~$0.023/GB/month
CloudWatch Events + SNS alerts~$1–2/month

See Also


Working With Viprasol

Container vulnerabilities are preventable but require deliberate tooling. Our team sets up ECR with enhanced scanning, blocks CI on CRITICAL CVEs with Trivy, generates SBOM artifacts for compliance, and signs images with Cosign so deployments can verify image provenance before pulling.

What we deliver:

  • ECR Terraform with IMMUTABLE tags, KMS encryption, lifecycle policy (10 tagged, 7-day untagged expiry)
  • Enhanced scanning with CONTINUOUS_SCAN for production repositories
  • Trivy GitHub Action: exit-code: 1 on CRITICAL + SARIF upload to GitHub Security tab
  • Multi-stage Dockerfile: Alpine base, --ignore-scripts npm install, non-root USER
  • Syft SBOM generation in SPDX-JSON format, uploaded as build artifact
  • Cosign keyless signing + verification step before deploy

Talk to our team about container security for your platform →

Or explore our cloud infrastructure services.

Share this article:

About the Author

V

Viprasol Tech Team

Custom Software Development Specialists

The Viprasol Tech team specialises in algorithmic trading software, AI agent systems, and SaaS development. With 100+ projects delivered across MT4/MT5 EAs, fintech platforms, and production AI systems, the team brings deep technical experience to every engagement. Based in India, serving clients globally.

MT4/MT5 EA DevelopmentAI Agent SystemsSaaS DevelopmentAlgorithmic Trading

Need DevOps & Cloud Expertise?

Scale your infrastructure with confidence. AWS, GCP, Azure certified team.

Free consultation • No commitment • Response within 24 hours

Viprasol · Big Data & Analytics

Making sense of your data at scale?

Viprasol builds end-to-end big data analytics solutions — ETL pipelines, data warehouses on Snowflake or BigQuery, and self-service BI dashboards. One reliable source of truth for your entire organisation.