Grype
Overview
Grype is an open-source vulnerability scanner from Anchore. It finds known CVEs in container images, filesystems, and SBOMs (Software Bill of Materials) by matching packages against databases including NVD, GitHub Security Advisories, OS vendor feeds (Alpine, Debian, Ubuntu, RHEL, etc.), and language ecosystem advisories (PyPI, npm, Maven, Go, Rust). Grype pairs naturally with Syft (SBOM generation) and integrates into CI/CD pipelines, Kubernetes admission controllers, and Anchore Enterprise.
Installation
Linux (curl installer)
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
grype version
Homebrew (macOS / Linux)
brew install grype
Docker (no install required)
docker run --rm anchore/grype:latest alpine:3.19
Go install
go install github.com/anchore/grype/cmd/grype@latest
Verify and update DB
grype version
grype db update # Pull latest vulnerability database
grype db status # Show current DB version and age
Configuration
Config file locations
Grype checks these paths in order:
.grype.yaml(project root)~/.grype.yaml~/.config/grype/config.yaml
.grype.yaml reference
# Output format: table | json | cyclonedx | sarif | template
output: table
# Fail on severity: negligible | low | medium | high | critical
fail-on-severity: high
# Severity filter (only report these and above)
severity: medium
# Ignore specific vulnerabilities
ignore:
- vulnerability: CVE-2023-12345
reason: "Not exploitable in our configuration"
- vulnerability: CVE-2022-99999
package:
name: openssl
version: 1.1.1k
type: rpm
- fix-state: not-fixed # Ignore all unfixed vulns (risky)
- package:
language: python
name: requests
# Database configuration
db:
cache-dir: ~/.cache/grype
update-url: https://toolbox-data.anchore.io/grype/databases/listing.json
auto-update: true
validate-age: true
max-allowed-built-age-hours: 120
# Scope for directory scans: squashed | all-layers
scope: squashed
# Add CPE matching
add-cpes-if-none: true
# External sources (GitHub token for GHSA)
external-sources:
enable: true
maven:
search-upstream-by-sha1: true
base-url: https://search.maven.org/solrsearch/select
Core Commands
| Command | Description |
|---|---|
grype alpine:3.19 | Scan a Docker Hub image |
grype nginx:latest | Scan nginx image from registry |
grype dir:. | Scan current filesystem directory |
grype dir:/app | Scan specific directory |
grype sbom:./bom.json | Scan a CycloneDX or SPDX SBOM |
grype file:./binary | Scan a single binary file |
grype registry:myregistry/image:tag | Scan from private registry |
grype oci-dir:./extracted-image/ | Scan an OCI layout directory |
grype docker:./image.tar | Scan a saved Docker image tar |
grype . --fail-on high | Exit non-zero if high+ CVEs found |
grype . -o json | Output as JSON |
grype . -o sarif | Output SARIF for GitHub Advanced Security |
grype . -o cyclonedx | Output CycloneDX VEX |
grype . --severity high | Only show high and critical |
grype . --add-cpes-if-none | Enhance matching with CPE generation |
grype . --scope all-layers | Scan all image layers, not just squashed |
grype db update | Update vulnerability database |
grype db list | List available database versions |
grype db check | Check if DB update is available |
grype config | Show current effective configuration |
grype version | Show version and DB info |
Advanced Usage
Severity filtering and CI gating
# Fail the pipeline on critical vulnerabilities
grype myapp:latest --fail-on critical
echo "Exit code: $?" # 0 = pass, 1 = vulnerabilities found
# Fail on high and above
grype myapp:latest --fail-on high
# Only report medium and above (suppress noise)
grype myapp:latest --severity medium
# Combine: report medium+, fail on critical
grype myapp:latest --severity medium --fail-on critical
Working with SBOMs (Syft integration)
# Generate SBOM with Syft, then scan with Grype
syft alpine:3.19 -o json > sbom.json
grype sbom:sbom.json
# Generate and pipe directly
syft alpine:3.19 -o json | grype
# Use CycloneDX format
syft myapp:latest -o cyclonedx-json > sbom-cyclonedx.json
grype sbom:sbom-cyclonedx.json -o sarif > results.sarif
Ignore rules in config
# .grype.yaml
ignore:
# Ignore specific CVE everywhere
- vulnerability: CVE-2023-44487
# Ignore CVE only for specific package
- vulnerability: CVE-2021-44228
package:
name: log4j-core
version: 2.14.1
# Ignore all unfixed vulnerabilities
- fix-state: not-fixed
# Ignore by namespace
- namespace: github:language:python
# Ignore by package type
- package:
type: java
# Ignore with expiry (manual enforcement needed)
- vulnerability: CVE-2022-1234
reason: "Tracked in JIRA-567, expires 2024-01-01"
Output formats
# Table (default, human-readable)
grype myapp:latest
# JSON (machine-readable, full detail)
grype myapp:latest -o json | jq '.matches[] | {name:.artifact.name, cve:.vulnerability.id, severity:.vulnerability.severity}'
# SARIF (GitHub Code Scanning)
grype myapp:latest -o sarif > grype-results.sarif
# CycloneDX (VEX, vulnerability exchange)
grype myapp:latest -o cyclonedx
# Template (custom output)
grype myapp:latest -o template -t my-template.tmpl
Private registry scanning
# Docker credential helpers are used automatically
# Or set explicitly:
export GRYPE_REGISTRY_AUTH_USERNAME=myuser
export GRYPE_REGISTRY_AUTH_PASSWORD=mytoken
grype registry:my-registry.example.com/myapp:1.0.0
# With TLS verification disabled (use carefully)
grype --insecure-skip-tls-verify registry:my.internal-registry.com/app:v1
Comparison: Grype vs Trivy
| Feature | Grype | Trivy |
|---|---|---|
| Language | Go | Go |
| Primary focus | Container + SBOM scanning | Multi-target scanning |
| SBOM input | CycloneDX, SPDX | CycloneDX, SPDX |
| SBOM output | Via Syft | Built-in |
| Misconfig scanning | No | Yes |
| Secret scanning | No | Yes |
| Kubernetes scanning | Via Anchore Enterprise | Built-in |
| DB update frequency | Daily | Daily |
| Anchore integration | Native | Third-party |
| GitHub Actions | anchore/scan-action | aquasecurity/trivy-action |
Common Workflows
GitHub Actions integration
name: Container Security Scan
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Scan with Grype
uses: anchore/scan-action@v4
id: scan
with:
image: myapp:${{ github.sha }}
fail-build: true
severity-cutoff: high
output-format: sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: ${{ steps.scan.outputs.sarif }}
Dockerfile scanning in CI
# Build and immediately scan
docker build -t myapp:latest .
grype myapp:latest --fail-on high -o json | tee grype-output.json
# Parse results
CRITICAL=$(jq '[.matches[] | select(.vulnerability.severity=="Critical")] | length' grype-output.json)
echo "Critical vulnerabilities: $CRITICAL"
[ "$CRITICAL" -eq 0 ] || exit 1
Offline / air-gapped environments
# On internet-connected machine: download DB archive
grype db list # Find latest listing URL
# Download the DB tarball manually from listing.json URL
# Transfer to air-gapped machine
# On air-gapped machine:
grype db import grype-db-5.tar.gz
grype --offline myapp:latest # Skip DB update check
Scheduled vulnerability monitoring
#!/bin/bash
# scan-images.sh — run nightly in cron
IMAGES=("myapp:production" "myapp:staging")
for img in "${IMAGES[@]}"; do
echo "Scanning $img"
grype "$img" --fail-on critical -o json \
> "grype-$(echo $img | tr '/:' '-').json"
done
# Send report via email or Slack webhook
Tips and Best Practices
Update the database before every scan. The vulnerability database is updated daily. Run grype db update at the start of your CI job or before local scans to avoid missing recent CVEs.
Use --fail-on critical in CI gates. Block deployments on critical vulnerabilities but use --severity medium for reporting so you see the full picture without failing on every medium-severity issue.
Combine with Syft for SBOM-first workflows. Generate a SBOM with Syft once, store it as a build artifact, then scan the SBOM with Grype in separate jobs. This decouples image building from scanning and enables re-scanning stored SBOMs later.
Do not use fix-state: not-fixed ignores in production gates. Ignoring unfixed vulnerabilities entirely defeats the purpose of scanning. Instead, track them in your issue tracker and set reminder dates.
Scope to all-layers for thorough audits. The default squashed scope is faster but can miss packages installed and then deleted in intermediate layers. Use --scope all-layers for security audits.
Pin the Grype version in CI. Use anchore/scan-action@v4 with a specific version tag to avoid behavior changes from automatic updates breaking your pipeline.
Use SARIF output for GitHub Advanced Security. Uploading SARIF results surfaces vulnerabilities directly in pull requests and the repository Security tab, making review much easier.
Separate ignore files per environment. Maintain different .grype.yaml files for development (lenient) and production (strict) scanning, committed alongside Dockerfiles for each deployment target.