<!-- Copyright (c) 2026 defconxt. All rights reserved. -->
<!-- Licensed under AGPL-3.0 — see LICENSE file for details. -->
---
name: implementing-container-image-scanning
description: >-
  Scan container images for OS and library vulnerabilities using Trivy, Grype, and
  Snyk Container. Integrate into CI/CD build pipelines with quality gates that block
  vulnerable images from reaching registries.
domain: cybersecurity
subdomain: devsecops
tags:
  - container-security
  - trivy
  - grype
  - docker
  - vulnerability-scanning
  - ci-cd
  - image-hardening
version: "1.0"
author: defconxt
license: AGPL-3.0
metadata:
  mitre-attack: ["T1195.002"]
---

# Implementing Container Image Scanning

## Overview

Container images inherit vulnerabilities from base images, OS packages, and application
dependencies. Scanning images before pushing to registries catches CVEs at build time.
This skill covers vulnerability scanning, Dockerfile best practices enforcement, and
quality gates that prevent vulnerable images from reaching production.

Mode: `[MODE: BLUE]` — Container security through build-time vulnerability detection.

## Prerequisites

| Requirement | Details |
|---|---|
| Trivy (`brew install trivy` or Docker) | Required |
| Grype (`brew install grype`) | Required |
| Docker or container build tool | Required |
| Container registry access | Required |

## Key Concepts

### Trivy Image Scanning

```bash
# Scan local image
trivy image app:latest --severity HIGH,CRITICAL --format json --output trivy-image.json

# Scan with exit code (fail pipeline)
trivy image app:latest --severity CRITICAL --exit-code 1

# Scan remote image
trivy image ghcr.io/org/app:v1.2.3 --severity HIGH,CRITICAL

# Ignore unfixed vulnerabilities
trivy image app:latest --ignore-unfixed --severity CRITICAL

# Scan Dockerfile for misconfigurations
trivy config Dockerfile --severity HIGH,CRITICAL
```

### Grype Scanning

```bash
# Scan image
grype app:latest --output json > grype-results.json

# Fail on severity
grype app:latest --fail-on high

# Scan from tar
docker save app:latest -o app.tar
grype app.tar --output json

# Scan SBOM
grype sbom:sbom.cdx.json --output json
```

### GitHub Actions Integration

```yaml
name: Container Security
on:
  push:
    branches: [main]

jobs:
  build-scan-push:
    runs-on: ubuntu-latest
    permissions:
      packages: write
      security-events: write
    steps:
      - uses: actions/checkout@v4

      - name: Build image
        run: docker build -t app:${{ github.sha }} .

      - name: Trivy vulnerability scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'app:${{ github.sha }}'
          severity: 'HIGH,CRITICAL'
          exit-code: '1'
          format: 'sarif'
          output: 'trivy-results.sarif'

      - name: Upload SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-results.sarif

      - name: Push image (only if scan passes)
        run: |
          docker tag app:${{ github.sha }} ghcr.io/org/app:${{ github.sha }}
          docker push ghcr.io/org/app:${{ github.sha }}
```

### Dockerfile Best Practices

```dockerfile
# Use specific digest-pinned base image
FROM python:3.12-slim@sha256:abcdef123456 AS base

# Run as non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser

# Copy only what's needed
COPY --chown=appuser:appuser requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY --chown=appuser:appuser src/ ./src/

# Drop privileges
USER appuser

# No secrets in ENV
# Use runtime secret injection instead

HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:8080/health || exit 1

EXPOSE 8080
CMD ["python", "-m", "src.main"]
```

### Distroless Images

```dockerfile
# Multi-stage build with distroless
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o /server .

FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /server /server
USER nonroot:nonroot
ENTRYPOINT ["/server"]
```

## Workflow

### Step 1: Scan Image

```bash
node scripts/agent.js --action scan --image app:latest --output /tmp/image-scan.json
```

### Step 2: Check Dockerfile

```bash
node scripts/agent.js --action dockerfile --dockerfile Dockerfile --output /tmp/dockerfile-check.json
```

### Step 3: Compare Base Images

```bash
node scripts/agent.js --action base-image --image python:3.12-slim --output /tmp/base-scan.json
```

## Detection

```yaml
title: Container Image Scanning Detection
id: fd13791a-f728-475b-82fa-02399ecfca41
status: experimental
description: Detects suspicious activity related to implementing container image scanning techniques in devsecops context
logsource:
  category: process_creation
  product: linux
detection:
  selection:
    CommandLine: "*implementing*container*"
  condition: selection
level: medium
tags:
  - attack.t1195.002
  - attack.execution
falsepositives:
  - CI/CD pipeline executing authorized security scanning tools
```


**Detection Opportunities**

| Indicator | Source | Detection Logic |
|---|---|---|
| Container Image Scanning Detection | linux/process_creation | Sigma rule (medium) |
| ATT&CK Coverage | MITRE ATT&CK | T1195.002 |

## Verification

- [ ] All images scanned before registry push
- [ ] Critical CVEs block pipeline
- [ ] Base images pinned to digest (not mutable tags)
- [ ] Non-root user in all Dockerfiles
- [ ] No secrets in image layers or ENV
- [ ] Multi-stage builds minimize attack surface
- [ ] Distroless or minimal base images used where possible
- [ ] Image scanning results uploaded to security dashboard

## References

- [Trivy Documentation](https://aquasecurity.github.io/trivy/)
- [Grype Documentation](https://github.com/anchore/grype)
- [Docker Security Best Practices](https://docs.docker.com/develop/security-best-practices/)
- [Distroless Images](https://github.com/GoogleContainerTools/distroless)
