Scanning Container Images with Grype
Overview
Grype is an open-source vulnerability scanner from Anchore that inspects container images, filesystems, and SBOMs for known CVEs. It leverages Syft-generated SBOMs to match packages against multiple vulnerability databases including NVD, GitHub Advisories, and OS-specific feeds.
When to Use
- When conducting security assessments that involve scanning container images with grype
- When following incident response procedures for related security events
- When performing scheduled security testing or auditing activities
- When validating security controls through hands-on testing
Coverage Gaps & Validation
Grype matches packages it can discover against a DB it has cached - both are sources of false negatives:
- Catalogers see what's installed, not what's vendored: Grype finds OS packages and language deps that leave a manifest/lockfile, but statically linked Go/Rust binaries, vendored libs, and
FROM scratch/distroless images often expose little - a near-empty result is "couldn't enumerate", not "no CVEs".
--scope squashed skips intermediate layers: a secret or vulnerable package deleted in a later layer still ships in the image; use --scope all-layers.
- Severity cutoff hides findings:
--fail-on high / severity-cutoff only gates the build - lower-rated but exploitable CVEs and --only-fixed filtering drop items from view.
.grype.yaml ignore drift: stale ignore: entries (especially fix-state: unknown blanket rules) silently suppress real CVEs over time.
Validate completeness: check the DB is fresh (grype db status - watch max-allowed-built-age) and update it (grype db update) before trusting a clean run. Drive the scan from an explicit SBOM (syft <img> -o spdx-json | grype sbom:-) so you can see exactly which components were catalogued; if the SBOM is thin, the scan is thin. Pin to the image digest not latest, scan all-layers, and diff vulnerability counts against the previously deployed digest to catch regressions.
Prerequisites
- Docker or Podman installed
- Grype CLI installed (
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin)
- Syft CLI (optional, for SBOM generation)
- Network access to pull vulnerability databases
Core Commands
Install Grype
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
grype version
brew install grype
Scan Container Images
grype nginx:latest
grype docker:myapp:1.0
grype docker-archive:image.tar
grype oci-dir:path/to/oci/
grype sif:image.sif
grype dir:/path/to/project
Output Formats
grype alpine:3.18
grype alpine:3.18 -o json > results.json
grype alpine:3.18 -o cyclonedx
grype alpine:3.18 -o sarif > grype.sarif
grype alpine:3.18 -o template -t /path/to/template.tmpl
Filtering and Thresholds
grype nginx:latest --fail-on critical
grype nginx:latest --only-fixed
grype nginx:latest --only-notfixed
grype nginx:latest --only-fixed -o json | jq '[.matches[] | select(.vulnerability.severity == "High")]'
grype nginx:latest --explain --id CVE-2024-1234
Working with SBOMs
syft nginx:latest -o spdx-json > nginx-sbom.json
grype sbom:nginx-sbom.json
grype sbom:bom.json
Configuration File (.grype.yaml)
check-for-app-update: false
fail-on-severity: "high"
output: "json"
scope: "squashed"
quiet: false
ignore:
- vulnerability: CVE-2023-12345
reason: "False positive - not exploitable in our context"
- vulnerability: CVE-2023-67890
fix-state: unknown
db:
auto-update: true
cache-dir: "/tmp/grype-db"
max-allowed-built-age: 120h
match:
java:
using-cpes: true
python:
using-cpes: true
javascript:
using-cpes: false
CI/CD Integration
- name: Scan image with Grype
uses: anchore/scan-action@v4
with:
image: "myregistry/myapp:${{ github.sha }}"
fail-build: true
severity-cutoff: high
output-format: sarif
id: scan
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ steps.scan.outputs.sarif }}
container_scan:
stage: test
image: anchore/grype:latest
script:
- grype ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA} --fail-on high -o json > grype-report.json
artifacts:
reports:
container_scanning: grype-report.json
Database Management
grype db status
grype db update
grype db delete
grype db list
Key Vulnerability Sources
| Source | Coverage |
|---|
| NVD | CVEs across all ecosystems |
| GitHub Advisories | Open source package vulnerabilities |
| Alpine SecDB | Alpine Linux packages |
| Amazon Linux ALAS | Amazon Linux AMI |
| Debian Security Tracker | Debian packages |
| Red Hat OVAL | RHEL, CentOS |
| Ubuntu Security | Ubuntu packages |
| Wolfi SecDB | Wolfi/Chainguard images |
Best Practices
- Pin image tags - Always scan specific digests, not
latest
- Fail on severity - Set
--fail-on high or critical in CI gates
- Use SBOMs - Generate SBOMs with Syft for reproducible scanning
- Suppress false positives - Use
.grype.yaml ignore rules with documented reasons
- Scan all layers - Use
--scope all-layers to catch vulnerabilities in intermediate layers
- Automate database updates - Keep the vulnerability database current in CI runners
- Compare scans - Track vulnerability count over time for regression detection