Learn how to scan container images, filesystems, and SBOMs for known software vulnerabilities.
Vulnerability scanning is the automated process of proactively identifying security weaknesses and known exploits within software and systems.
This is crucial because it helps developers and organizations find and fix potential security holes before malicious actors can discover and exploit them, thus protecting data and maintaining system integrity.
Grype is an open-source vulnerability scanner specifically designed to analyze container images and filesystems.
It works by comparing the software components it finds against a database of known vulnerabilities, providing a report of potential risks so they can be addressed.
1 - Getting Started
Use Grype to scan your container images, directories, or archives for known vulnerabilities.
What is Vulnerability Scanning?
Vulnerability scanning is the process of identifying known security vulnerabilities in software packages and dependencies.
For developers, it helps catch security issues early in development, before they reach production.
For organizations, it’s essential for maintaining security posture and meeting compliance requirements.
Grype is a CLI tool for scanning container images, filesystems, and SBOMs for known vulnerabilities.
Installation
Grype is provided as a single compiled executable and requires no external dependencies to run.
Run the command for your platform to download the latest release.
curl -sSfL https://get.anchore.io/grype | sudo sh -s -- -b /usr/local/bin
brew install grype
nuget install Anchore.Grype
Check out installation guide for full list of official and community-maintained packaging options.
Scan a container image for vulnerabilities
Run grype against a small container image. Grype will download the latest vulnerability database
and output simple human-readable table of packages that are vulnerable:
Grype can scan container images directly, but it can also scan an existing SBOM document.
Note
This presumes you already created alpine_latest-spdx.json using Syft, or some other tool. If not, go to SBOM Generation Getting Started and create it now.
grype alpine_latest-spdx.json
Create a vulnerability report in JSON format
The JSON-formatted output from Grype can be processed or visualized by other tools.
Create the vulnerability report using the --output flag:
Only for downloading container images and the vulnerability database. After the initial database download, scanning works offline until you update the database.
What about private container registries?
Grype supports authentication for private registries. See Private Registries.
Can I use Grype in CI/CD pipelines?
Absolutely! Grype is designed for automation. Scan images or SBOMs during builds and fail pipelines based on severity thresholds.
What data does Grype send externally?
Nothing. Grype runs entirely locally and doesn’t send any data to external services.
Explore the different scan targets Grype supports including container images, directories, SBOMs, and individual packages
TL;DR
Grype automatically detects scan target type, simply pass it as an argument: grype <target>
Supports container images (Docker/Podman/Containerd/registries), directories, files, archives, and SBOMs
Scan individual packages via PURLs or CPE identifiers
Use --from <type> to explicitly specify scan target type
Grype can scan a variety of scan targets including container images, directories, files, archives, SBOMs, and individual packages.
In most cases, you can simply point Grype at what you want to analyze and it will automatically detect and scan it correctly.
Scan a container image from your local daemon or a remote registry:
grype alpine:latest
Scan a directory or file:
grype /path/to/project
Scan an SBOM:
grype sbom.json
To explicitly specify the scan target type, use the --from flag:
--from ARG
Description
docker
Use images from the Docker daemon
podman
Use images from the Podman daemon
containerd
Use images from the Containerd daemon
docker-archive
Use a tarball from disk for archives created from docker save
oci-archive
Use a tarball from disk for OCI archives
oci-dir
Read directly from a path on disk for OCI layout directories
singularity
Read directly from a Singularity Image Format (SIF) container file on disk
dir
Read directly from a path on disk (any directory)
file
Read directly from a path on disk (any single file)
registry
Pull image directly from a registry (bypass any container runtimes)
sbom
Read SBOM from file (supports Syft JSON, SPDX, CycloneDX formats)
purl
Scan individual packages via Package URL identifiers
Instead of using the --from flag explicitly, you can instead:
provide no hint and let Grype automatically detect the scan target type implicitly based on the input provided
provide the scan target type as a URI scheme in the target argument (e.g., docker:alpine:latest, oci-archive:/path/to/image.tar, dir:/path/to/dir)
For scan targets that are uniquely supported by Grype, see the sections below.
SBOM Scan Targets
You can scan pre-generated SBOMs instead of scanning the scan target directly. This approach offers several benefits:
Faster scans since package cataloging is already complete
Ability to cache and reuse SBOMs
Standardized vulnerability scanning across different tools
Scan an SBOM file
Grype scans SBOM files in multiple formats. You can provide an explicit sbom: prefix or just provide the file path:
Explicit SBOM prefix:
grype sbom:sbom.json
Implicit detection:
grype sbom.json
Grype automatically detects the SBOM format. Supported formats include:
Syft JSON
SPDX JSON, XML, and tag-value
CycloneDX JSON and XML
Use the explicit sbom: prefix when the file path might be ambiguous or when you want to be clear about the input type.
Scan an SBOM from stdin
You can pipe SBOM output directly from Syft or other SBOM generation tools:
Syft → Grype pipeline:
syft alpine:latest -o json | grype
Read SBOM from file via stdin:
Grype detects stdin input automatically when no command-line argument is provided and stdin is piped:
cat sbom.json | grype
Note
Grype will not attempt to read from redirected stdin in interactive terminal sessions when the use of a pipe is not detected.
Thus grype < sbom.json will not work in an interactive terminal session.
Package scan targets
You can scan specific packages without scanning an entire image or directory. This is useful for:
Testing whether a specific package has vulnerabilities
Lightweight vulnerability checks
Compliance scanning for specific dependencies
Grype supports two formats for individual package scanning: Package URLs (PURLs) and Common Platform Enumerations (CPEs).
When Grype receives input, it checks for PURL format first, then CPE format, before trying other scan target types.
Scan Package URLs (PURLs)
Package URLs (PURLs) provide a standardized way to identify software packages.
Without either the distro qualifier or the --distro flag hint, Grype may not find distribution-specific vulnerabilities.
Other qualifiers include:
upstream: The upstream package name or version. Vulnerability information tends to be tracked with the source or origin
package instead of the installed package itself (e.g. libcrypto
might be installed but the pacakge it was built from is openssl
which is where vulnerabilities are attributed to)
epoch: The epoch value for RPM packages. This is necessary when the package in question has changed the methodology for versioning
(e.g., switching from date-based versions to semantic versions) and the epoch is used to indicate that change.
You can scan multiple packages from a file. The file contains one PURL per line:
# contents of packages.txt follow, which must be a text file with one PURL per line
pkg:npm/lodash@4.17.20
pkg:pypi/requests@2.25.1
pkg:maven/org.apache.commons/commons-lang3@3.12.0
grype ./packages.txt
Grype scans all the packages in the file:
NAME INSTALLED FIXED IN TYPE VULNERABILITY SEVERITY
lodash 4.17.20 4.17.21 npm GHSA-35jh-r3h4-6jhm High
requests 2.25.1 2.31.0 python GHSA-j8r2-6x86-q33q Medium
commons-lang3 3.12.0 3.18.0 java-archive GHSA-j288-q9x7-2f5v Medium
...
Filter vulnerabilities: Use result filtering to focus on specific findings
Private registries: Set up authentication for private images
3 - Supported package ecosystems
Learn how Grype selects vulnerability data for different package types and what level of accuracy to expect
TL;DR
OS packages use distribution-specific security feeds (Alpine, Debian, Ubuntu, RHEL, etc.)
Language packages use GitHub Security Advisories (npm, PyPI, Maven, Go, etc.)
Other packages fall back to CPE matching against NVD (may have false positives)
Grype automatically selects the right data source based on package type
Grype automatically selects the right vulnerability data source based on the package type and distribution information in your SBOM. This guide explains how Grype chooses which vulnerability feed to use and what level of accuracy to expect.
How Grype chooses vulnerability data
Grype selects vulnerability feeds based on package type:
OS packages (apk, deb, rpm, portage, alpm) use vulnerability data sourced from distribution-specific security feeds.
Language packages (npm, PyPI, Maven, Go modules, etc.) use GitHub Security Advisories.
Other packages (binaries, Homebrew, Jenkins plugins, etc.) fall back to CPE matching against the NVD.
OS packages
When Grype scans an OS package, it uses vulnerability data sourced from distribution security feeds.
Distribution maintainers curate these feeds and provide authoritative information about vulnerabilities affecting specific distribution versions.
For example, when you scan Debian 10, Grype looks for vulnerabilities affecting Debian 10 packages:
$ grype debian:10
NAME INSTALLED FIXED IN TYPE VULNERABILITY SEVERITY
libgcrypt20 1.8.4-5+deb10u1 (won't fix) deb CVE-2021-33560 High
bash 5.0-4 deb CVE-2019-18276 Negligible
libidn2-0 2.0.5-1+deb10u1 (won't fix) deb CVE-2019-12290 High
OS distributions
Grype supports major Linux distributions with dedicated vulnerability feeds,
including Alpine, Debian, Ubuntu, RHEL, SUSE, and many others.
Some distributions have mature security tracking programs that report both fixed and unfixed vulnerabilities, providing comprehensive coverage.
Derivative distributions automatically use their parent distribution’s vulnerability feed.
Grype maps derivative distributions to their upstream source using the ID_LIKE field from /etc/os-release.
For example, Rocky Linux and AlmaLinux use the RHEL vulnerability feed, while Raspbian uses Debian’s feed.
When scanning Rocky Linux, Grype uses Red Hat security data:
The distro type shows rockylinux, but Grype searches the RHEL vulnerability feed automatically.
You don’t need to configure this mapping –it happens transparently based on the distribution’s ID_LIKE field.
Learn more
For a complete list of supported Linux distributions and their versions, see the OS support reference.
Language packages use vulnerability data from GitHub Security Advisories (GHSA).
GitHub maintains security advisories for major package ecosystems, sourced from package maintainers, security researchers, and automated scanning.
When you scan a JavaScript package, Grype searches GHSA for npm advisories:
$ grype node:18-alpine
NAME INSTALLED FIXED IN TYPE VULNERABILITY SEVERITY
cross-spawn 7.0.3 7.0.5 npm GHSA-3xgq-45jj-v275 High
Supported language ecosystems
Grype supports these language ecosystems through GHSA:
For language packages, Grype searches GHSA by package name and version, applying ecosystem-specific version comparison rules to determine if your package version falls within the vulnerable range.
In addition to language packages, Bitnami packages are searched against Bitnami’s vulnerability feed in a similar manner.
Other packages
Packages without dedicated feeds use CPE fallback matching
Packages using CPE matching
These package types rely on Common Platform Enumeration (CPE) matching against the National Vulnerability Database (NVD):
Binary executables
Homebrew packages
Jenkins plugins
Conda packages
WordPress plugins
CPE matching constructs a CPE string from the package name and version, then searches the NVD for matching vulnerability entries.
Understanding CPE match accuracy
CPE matching has important limitations:
May produce false positives - CPEs often do not distinguish between package ecosystems. For example, the PyPI package docker (a Python library for talking to the Docker daemon) can match vulnerabilities for Docker the container runtime because they share similar CPE identifiers.
May miss vulnerabilities - Not all vulnerabilities have CPE entries in the NVD.
Requires CPE metadata - Packages must have CPE information for matching to work.
You should verify CPE matches against the actual vulnerability details to confirm they apply to your specific package. Here’s a CPE match example:
Notice the version constraint shows (unknown) format rather than ecosystem-specific semantics, and the match type is cpe-match instead of exact-direct-match.
For more details on interpreting match types, confidence levels, and result reliability, see Understanding Grype results.
Filter results: Use result filtering to focus on specific vulnerabilities
Data sources: Explore vulnerability data sources for details on each data source and supported operating systems
4 - Understanding Grype results
Learn how to read and interpret Grype’s vulnerability scan output, including match types, confidence levels, and result reliability
TL;DR
Default table output shows package, vulnerability, severity, and fix info
Match types in JSON output indicate reliability: exact-direct-match and exact-indirect-match are high confidence, cpe-match requires verification
Use --by-cve to normalize vulnerability IDs to CVE format
Filter results with jq for analysis by match type, severity, or data source
This guide explains how to read and interpret Grype’s vulnerability scan output.
You’ll learn what different match types mean, how to assess result reliability, and how to filter results based on confidence levels.
Output formats
Grype supports several output formats for scan results:
Table (default) - Human-readable columnar output for terminal viewing
JSON - Complete structured data with all match details
SARIF - Standard format for tool integration and CI/CD pipelines
Template - Custom output using Go templates
This guide focuses on table and JSON formats, which you’ll use most often for understanding scan results.
Reading table output
The table format is Grype’s default output. When you run grype <image>, you see a table displaying one row per
unique vulnerability match, with deduplication of identical rows.
Table columns
The table displays eight standard columns, with an optional ninth column for annotations:
NAME - The package name
INSTALLED - The version of the package
FIXED-IN - The version that fixes the vulnerability (shows (won't fix) if the vendor won’t fix it, or empty if no fix is available). See Filter by fix availability to filter results based on fix states
TYPE - Package type (apk, deb, rpm, npm, python, java-archive, etc.)
VULNERABILITY - The vulnerability identifier (see below)
RISK - Calculated risk score combining CVSS, EPSS, and other severity metrics into a single numeric value (0.0 to 10.0)
Annotations (conditional) - Additional context like KEV (Known Exploited Vulnerability), suppressed status, or distribution version when scanning multi-distro images
The Annotations column appears conditionally to provide additional context:
KEV or (kev) - Indicates the vulnerability is in CISA’s Known Exploited Vulnerabilities catalog
suppressed or suppressed by VEX - Shown when using --show-suppressed flag (see View filtered results)
Distribution version (e.g., ubuntu:20.04) - Shown when scan results include matches from multiple different distributions
Understanding vulnerability IDs
The VULNERABILITY column displays different types of identifiers depending on the data source:
CVE IDs (e.g., CVE-2024-1234) - Common Vulnerabilities and Exposures identifiers used by most Linux distributions (Alpine, Debian, Ubuntu, RHEL, SUSE) and the NVD
GHSA IDs (e.g., GHSA-xxxx-xxxx-xxxx) - GitHub Security Advisory identifiers for language ecosystem packages
ALAS IDs (e.g., ALAS-2023-1234) - Amazon Linux Security Advisory identifiers
By default, Grype displays the vulnerability ID from the original data source. For example, an Alpine package might show CVE-2024-1234
while a GitHub Advisory for the same issue shows GHSA-abcd-1234-efgh. Use the --by-cve flag to normalize results to CVE identifiers:
grype <image> --by-cve
This flag replaces non-CVE vulnerability IDs with their related CVE ID when available, uses CVE metadata instead of the
original advisory metadata, and makes it easier to correlate vulnerabilities across different data sources.
Compare the two approaches:
# Default output - shows GitHub Advisory ID$ grype node:18
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
lodash 4.17.20 4.17.21 npm GHSA-35jh-r3h4-6jhm High
# With --by-cve - converts to CVE$ grype node:18 --by-cve
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
lodash 4.17.20 4.17.21 npm CVE-2021-23337 High
Sorting results
By default, Grype sorts vulnerability results by risk score, which combines multiple factors to help you prioritize remediation efforts.
Understanding how sorting works and when to use alternative methods helps you build effective security workflows.
Why risk-based sorting works best
The default risk score takes a holistic approach by combining:
Threat (likelihood of exploitation) - Based on EPSS (Exploit Prediction Scoring System) scores or presence in CISA’s Known Exploited Vulnerabilities (KEV) catalog
Impact (potential damage) - Based on CVSS scores and severity ratings from multiple sources
Context (exploitation evidence) - Additional weight for vulnerabilities with known ransomware campaigns
This multi-factor approach aligns with security best practices recommended by the EPSS project,
which emphasizes that “CVSS is a useful tool for capturing the fundamental properties of a vulnerability,
but it needs to be used in combination with data-driven threat information, like EPSS.”
Risk-based sorting helps you focus on vulnerabilities that are both likely to be exploited AND have significant business impact,
optimizing your remediation efficiency.
Why single-metric sorting can be misleading
While Grype offers several sorting options via the --sort-by flag, using single metrics can lead to inefficient prioritization:
Severity-only sorting (--sort-by severity) focuses solely on potential impact:
You may waste effort patching Critical severity vulnerabilities that are unlikely to ever be exploited in the wild
No consideration for whether attackers are actively targeting the vulnerability
Ignores real-world threat intelligence
EPSS-only sorting (--sort-by epss) focuses solely on exploitation likelihood:
You may prioritize vulnerabilities with high exploitation probability but low business impact
EPSS is not a risk score – it only addresses the threat component, not the complete risk picture
Missing context like asset criticality, network exposure, or available compensating controls
The EPSS documentation explicitly states that EPSS scores should be combined with severity information to make informed prioritization decisions,
which is exactly what Grype’s risk score does.
Understanding EPSS in Grype
EPSS (Exploit Prediction Scoring System) is a data-driven scoring model that estimates the probability a vulnerability will be exploited in the next 30 days.
Grype displays EPSS data in the table output showing both the raw score and percentile, such as 94.4% (99th), which means:
94.4% - The raw EPSS score indicating a 94.4% probability of exploitation within 30 days
99th - The percentile rank, meaning this score is higher than 99% of all EPSS scores
EPSS percentiles help normalize the heavily skewed distribution of EPSS scores, making it easier to set thresholds.
For example, a vulnerability in the 90th percentile is more concerning than one in the 50th percentile, even if the raw likelihood values appear to be similar.
Grype incorporates EPSS as the threat component of its risk calculation. When a vulnerability appears in the KEV catalog, Grype automatically treats it as maximum threat (overriding EPSS) since observed exploitation is more significant than predicted exploitation.
While risk-based sorting is recommended for most remediation workflows, alternative sorting methods serve specific use cases:
Sort by KEV status (--sort-by kev):
When you need to comply with regulatory requirements like CISA BOD 22-01
For incident response scenarios focusing on actively exploited vulnerabilities
Sort by severity (--sort-by severity):
When organizational SLAs or compliance frameworks specify severity-based remediation timeframes (e.g., “patch all Critical within 7 days”)
Sort by EPSS (--sort-by epss):
For threat landscape analysis and security research
Sort by package (--sort-by package):
When organizing remediation work by team ownership (different teams maintain different packages)
For coordinating updates across multiple instances of the same package
Sort by vulnerability ID (--sort-by vulnerability):
When tracking specific CVE campaigns across your environment
For correlating findings with external threat intelligence reports
For most security and remediation workflows, stick with the default risk-based sorting.
It provides the best balance of threat intelligence and impact assessment to help you prioritize effectively.
Grype’s native JSON output format provides a comprehensive representation of vulnerability scan results,
including detailed information about each vulnerability, how it was matched, and the affected packages.
This guide explains the structure of the JSON output and how to interpret its contents effectively.
Data shapes
The JSON output contains a top-level matches array. Each match has this structure:
cvss (array) - CVSS score information from various sources
fix (object) - Fix information including available versions and fix state (fixed, not-fixed, wont-fix, unknown). See Understanding fix states for details
advisories (array) - Related security advisories (where RHSAs appear)
risk (float64) - Calculated risk score combining CVSS, EPSS, and other severity metrics
Grype determines how it matched a package to a vulnerability based on the available data sources.
The match type indicates how the match was made:
exact-direct-match means the package name matched directly in a dedicated vulnerability feed.
Grype searched the feed using the package name from your scan and found a matching vulnerability entry.
exact-indirect-match means the source package name matched in a dedicated vulnerability feed.
This occurs when you scan a binary package (e.g., libcrypto1.1) but the feed tracks vulnerabilities under the source package (e.g., openssl).
Grype searches the feed using the source package name and maps the results to the binary package.
cpe-match means Grype used Common Platform Enumeration (CPE) matching as a fallback when no exact match was found in ecosystem-specific feeds.
CPE matching relies on CPE identifiers derived from package metadata and is less precise.
You can loosely think of the match type as a proxy for confidence level in the match, where exact-direct-match has the highest confidence,
followed by exact-indirect-match, and finally cpe-match.
A cpe-match means Grype used Common Platform Enumeration (CPE) matching as a fallback.
CPE matching occurs when:
No exact package match exists in ecosystem-specific feeds
Grype falls back to the NVD database
The match is based on CPE identifiers derived from package metadata
This match type has lower confidence because:
CPE matching is generic and not package-ecosystem aware
Package naming may not match CPE naming conventions exactly
Version ranges may be broader or less precise
Understanding version constraints
The found.versionConstraint field shows the version range (found on the vulnerability) which the
package version was found to be within (thus, the package is affected by the vulnerability).
The format indicates the constraint type and the comparison logic used:
< 1.2.3 (apk) - Alpine package version constraint using apk version comparison
< 1.2.3 (deb) - Debian package version constraint using dpkg version comparison
< 1.2.3 (rpm) - RPM package version constraint using rpm version comparison
< 1.2.3 (python) - Python package version constraint using PEP 440 comparison
< 1.2.3 (semantic) - Semantic versioning constraint using semver comparison
< 1.2.3 (unknown) - Unknown version format (lower reliability)
The constraint type tells you how Grype compared versions. Ecosystem-specific formats (apk, deb, rpm) use that
ecosystem’s version comparison rules, which handle epoch numbers, release tags, and other format-specific details correctly.
Generic formats like unknown may have less precise matching.
Tip
When you use filtering flags or ignore rules, filtered vulnerabilities appear in the ignoredMatches array instead of matches.
Each match in JSON output contains information about how Grype found the vulnerability and links to the original sources. This lets you examine what Grype looked at and verify the match yourself.
Reference URLs
The vulnerability object includes reference URLs from the vulnerability data:
For distro packages, this shows the distro, package name, and version. For CPE matches, this shows the CPE strings Grype constructed. This lets you see exactly what Grype queried.
What Grype found
The matchDetails[].found field shows what matched in the vulnerability data:
This shows the vulnerability ID and version constraint that matched, along with the match type. Comparing searchedBy and found shows how Grype connected your package to the vulnerability.
Control which vulnerabilities Grype reports using filtering flags, configuration rules, and VEX documents
TL;DR
Use --fail-on <severity> to set exit code thresholds for CI/CD pipelines
Filter by fix availability with --only-fixed or --only-notfixed
Create ignore rules in .grype.yaml to exclude specific vulnerabilities or packages
Use VEX documents with --vex to filter based on exploitability information
Learn how to control which vulnerabilities Grype reports using filtering flags and configuration options.
Set failure thresholds
Use the --fail-on flag to control Grype’s exit code based on vulnerability severity. This can be helpful for integrating Grype into CI/CD pipelines.
The --fail-on flag (alias: -f) sets a severity threshold. When scanning completes, Grype exits with code 2 if it found vulnerabilities at or above the specified severity:
grype alpine:3.10 --fail-on high
You’ll see vulnerabilities at or above the threshold:
NAME INSTALLED FIXED IN TYPE VULNERABILITY SEVERITY EPSS RISK
zlib 1.2.11-r1 apk CVE-2022-37434 Critical 92.7% (99th) 87.1
libcrypto1.1 1.1.1k-r0 apk CVE-2023-0286 High 89.1% (99th) 66.4
libssl1.1 1.1.1k-r0 apk CVE-2023-0286 High 89.1% (99th) 66.4
...
[0026] ERROR discovered vulnerabilities at or above the severity threshold
# Exit code: 2
Valid severity values, from lowest to highest:
negligible < low < medium < high < critical
When you set a threshold, Grype fails if it finds vulnerabilities at that severity or higher. For example, --fail-on high fails on both high and critical vulnerabilities.
Filter by fix availability
Grype provides flags to filter vulnerabilities based on whether fixes are available.
Show only vulnerabilities with fixes available
The --only-fixed flag filters scan results to show only vulnerabilities that have fixes available:
grype alpine:latest --only-fixed
This flag filters out vulnerabilities with these fix states:
not-fixed - No fix is available yet
wont-fix - Maintainers won’t fix this vulnerability
unknown - No fix state information is available
This is useful when you want to focus on actionable vulnerabilities that you can remediate by updating packages.
Note
Do not use --only-fixed and --only-notfixed together. These flags are mutually exclusive and filter out all vulnerabilities.
Show only vulnerabilities without fixes available
The --only-notfixed flag filters scan results to show only vulnerabilities that do not have fixes available:
grype alpine:3.10 --only-notfixed
These vulnerabilities don’t have fixes available yet:
NAME INSTALLED TYPE VULNERABILITY SEVERITY EPSS RISK
zlib 1.2.11-r1 apk CVE-2022-37434 Critical 92.7% (99th) 87.1
libcrypto1.1 1.1.1k-r0 apk CVE-2023-0286 High 89.1% (99th) 66.4
libssl1.1 1.1.1k-r0 apk CVE-2023-0286 High 89.1% (99th) 66.4
libcrypto1.1 1.1.1k-r0 apk CVE-2023-2650 Medium 92.0% (99th) 52.9
libssl1.1 1.1.1k-r0 apk CVE-2023-2650 Medium 92.0% (99th) 52.9
...
This flag filters out vulnerabilities with fix state fixed. Notice the FIXED-IN column is empty for these vulnerabilities.
This is useful when you want to identify vulnerabilities that require alternative mitigation strategies, such as:
Accepting the risk
Implementing compensating controls
Waiting for a fix to become available
Switching to a different package
Understanding fix states
Grype assigns one of four fix states to each vulnerability based on information from vulnerability data sources:
Fix State
Description
fixed
A fix is available for this vulnerability
not-fixed
No fix is available yet, but maintainers may release one
wont-fix
Package maintainers have decided not to fix this vulnerability
unknown
No fix state information is available
Vulnerabilities with no fix state information are treated as unknown. This ensures Grype handles missing data consistently.
Ignore specific fix states
The --ignore-states flag gives you fine-grained control over which fix states to filter out. You can ignore one or more fix states by specifying them as a comma-separated list:
# Ignore vulnerabilities with unknown fix statesgrype alpine:3.10 --ignore-states unknown
Only vulnerabilities with known fix states appear:
NAME INSTALLED FIXED IN TYPE VULNERABILITY SEVERITY EPSS RISK
apk-tools 2.10.6-r0 2.10.7-r0 apk CVE-2021-36159 Critical 1.0% (76th) 0.9
# Ignore both wont-fix and not-fixed vulnerabilitiesgrype alpine:3.10 --ignore-states wont-fix,not-fixed
This leaves only fixed vulnerabilities and those with unknown states:
NAME INSTALLED FIXED IN TYPE VULNERABILITY SEVERITY EPSS RISK
zlib 1.2.11-r1 apk CVE-2022-37434 Critical 92.7% (99th) 87.1libcrypto1.11.1.1k-r0 apk CVE-2023-0286 High 89.1% (99th) 66.4libssl1.11.1.1k-r0 apk CVE-2023-0286 High 89.1% (99th) 66.4apk-tools 2.10.6-r0 2.10.7-r0 apk CVE-2021-36159 Critical 1.0% (76th) 0.9...
Valid fix state values are: fixed, not-fixed, wont-fix, unknown.
If you specify an invalid fix state, Grype returns an error:
grype alpine:latest --ignore-states invalid-state
# Error: unknown fix state invalid-state was supplied for --ignore-states
Combining severity with fix filtering
You can combine --fail-on with fix state filtering to create sophisticated CI/CD policies:
# Fail only if fixable critical or high vulnerabilities existgrype alpine:3.10 --fail-on high --only-fixed
Grype now only fails on fixable critical/high vulnerabilities:
NAME INSTALLED FIXED IN TYPE VULNERABILITY SEVERITY EPSS RISK
apk-tools 2.10.6-r0 2.10.7-r0 apk CVE-2021-36159 Critical 1.0% (76th) 0.9[0026] ERROR discovered vulnerabilities at or above the severity threshold
# Exit code: 2
# Fail on medium or higher, but ignore wont-fix vulnerabilitiesgrype alpine:latest --fail-on medium --ignore-states wont-fix
The --fail-on check runs after vulnerability matching and filtering. Grype converts all filtering options (--only-fixed, --only-notfixed, --ignore-states, configuration ignore rules, and VEX documents) into ignore rules and applies them during matching. The severity threshold check then evaluates only the remaining vulnerabilities.
View filtered results
By default, Grype hides filtered vulnerabilities from output. You can view them in table output with --show-suppressed or in JSON output by inspecting the ignoredMatches field.
In table output
The --show-suppressed flag displays filtered vulnerabilities in table output with a (suppressed) label:
grype alpine:3.10 --only-fixed --show-suppressed
Filtered vulnerabilities now appear with a (suppressed) label:
NAME INSTALLED FIXED IN TYPE VULNERABILITY SEVERITY EPSS RISK
apk-tools 2.10.6-r0 2.10.7-r0 apk CVE-2021-36159 Critical 1.0% (76th) 0.9zlib 1.2.11-r1 apk CVE-2018-25032 High <0.1% (26th) <0.1 (suppressed)
libcrypto1.11.1.1k-r0 apk CVE-2021-3711 Critical 2.7% (85th) 2.4 (suppressed)
libssl1.11.1.1k-r0 apk CVE-2021-3711 Critical 2.7% (85th) 2.4 (suppressed)
libcrypto1.11.1.1k-r0 apk CVE-2021-3712 High 0.5% (66th) 0.4 (suppressed)
libssl1.11.1.1k-r0 apk CVE-2021-3712 High 0.5% (66th) 0.4 (suppressed)
...
Note
The --show-suppressed flag only applies to table output format. It has no effect on JSON, SARIF, or other output formats.
In JSON output
When you use JSON output (-o json), Grype places filtered vulnerabilities in the ignoredMatches array. Non-filtered vulnerabilities appear in the matches array.
For details on the complete JSON structure and all fields, see Reading JSON output.
When you combine multiple criteria in a rule, all criteria must match for the rule to apply.
Use VEX documents
Grype supports Vulnerability Exploitability eXchange (VEX) documents to provide information about which vulnerabilities affect your software.
VEX allows you to communicate vulnerability status in a machine-readable format that follows CISA minimum requirements.
Grype supports two VEX formats as input:
OpenVEX - Compact JSON format with minimal required fields
CSAF VEX - Comprehensive format with rich advisory metadata (OASIS standard)
VEX-filtered vulnerabilities behave like other filtered results:
Table output: Hidden by default, shown with --show-suppressed flag and marked as (suppressed by VEX)
JSON output: Moved to the ignoredMatches array with VEX rules listed in appliedIgnoreRules
This guide uses OpenVEX examples for simplicity, but both formats work identically with Grype.
The core concepts (status values, product identification, filtering behavior) apply to both formats.
Basic usage
Use the --vex flag to provide one or more VEX documents:
affected - Product is affected by the vulnerability
under_investigation - Impact is still being assessed
By default, Grype moves vulnerabilities with not_affected or fixed status to the ignored list.
Vulnerabilities with affected or under_investigation status are only added to results when you enable augmentation:
vex-add: ["affected", "under_investigation"]
Creating VEX documents with vexctl
The easiest way to create OpenVEX documents is with vexctl:
# Create a VEX statement marking a CVE as not affecting your imagevexctl create \
--product="pkg:oci/alpine@sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412"\
--subcomponents="pkg:apk/alpine/busybox@1.37.0-r19"\
--vuln="CVE-2024-58251"\
--status="not_affected"\
--justification="vulnerable_code_not_present"\
--file="vex.json"# Use the VEX document with Grypegrype alpine:3.22.2 --vex vex.json
Note
vexctl creates OpenVEX documents only.
For CSAF VEX, you’ll need to create documents manually or use CSAF-specific tooling. Both formats work the same way with Grype.
You can also create VEX documents manually. Here’s an OpenVEX example:
CSAF VEX documents have a more complex structure with product trees, branches, and vulnerability arrays. See the CSAF specification for complete structure details.
Justifications for not_affected
OpenVEX provides standardized justification values when marking vulnerabilities as not_affected:
component_not_present - The component is not included in the product
vulnerable_code_not_present - The vulnerable code is not present
vulnerable_code_not_in_execute_path - The vulnerable code cannot be executed
vulnerable_code_cannot_be_controlled_by_adversary - The vulnerability cannot be exploited
CSAF VEX uses a richer product status model with categories like known_not_affected that Grype maps to the standard VEX statuses. See the CSAF specification for details on CSAF-specific fields.
These justifications help security teams understand the rationale behind VEX statements.
Product identification
Grype matches VEX statements to scan results using several identification methods:
Grype uses a locally cached database of known vulnerabilities
Database auto-updates on each Grype launch when newer version is available
Manage manually with grype db check and grype db update
Database published by Anchore at no cost from multiple upstream feeds
Grype uses a locally cached database of known vulnerabilities when searching a container, directory, or SBOM for security vulnerabilities.
Anchore collates vulnerability data from common feeds, and publishes that data online, at no cost to users.
When Grype is launched, it checks for an existing vulnerability database, and looks for an updated one online.
If available, Grype will automatically download the new database.
Database age validation
Grype will automatically fail scans if the vulnerability database is more than 5 days old.
You can disable this behavior or adjust the age threshold in your configuration:
Set db.validate-age: false to disable age validation
Adjust db.max-allowed-built-age to change the threshold (e.g., 168h for 7 days)
To update the database manually, use the following command:
grype db update
If instead, you would like to simply check if a new DB is available without actually updating, use:
grype db check
This will return 0 if the database is up to date, and 1 if an update is available.
Or, you can delete the local database entirely:
grype db delete
Searching the database
The Grype vulnerability database contains detailed information about vulnerabilities and affected packages across all supported ecosystems.
While you can examine the raw SQLite database directly (use grype db status to find the local storage path),
the grype db search commands provide a much easier way to explore what’s in the database.
Search tips
For both affected package and vulnerability searches, keep these tips in mind:
Result limit: By default, searches return up to 5,000 results. Use --limit 0 for unlimited results.
JSON output: Add --output json for programmatic processing of results.
Search for affected packages
Use grype db search to find packages affected by vulnerabilities. This is useful when you want to understand
what packages are impacted by a specific CVE, or when you want to see all vulnerabilities affecting a particular package.
For example, to find all packages affected by Log4Shell across all ecosystems:
grype db search --vuln CVE-2021-44228
To find all vulnerable versions of the log4j package:
grype db search --pkg log4j
To search by PURL or CPE formats:
grype db search --pkg 'pkg:rpm/redhat/openssl'grype db search --pkg 'cpe:2.3:a:jetty:jetty_http_server:*:*:*:*:*:*:*:*'
Any version value provided will be ignored entirely.
You can also use these options in combination to filter results further (finding the common intersection);
in this example, finding packages named “openssl” in Alpine Linux 3.18 that have fixes available:
grype db search --pkg openssl --distro alpine-3.18 --fixed-state fixed
Search for vulnerabilities
Use grype db search vuln to look up vulnerability details directly, including descriptions, severity ratings, and data sources.
This is subtly different from searching for affected packages, as it focuses on the vulnerabilities themselves, so you can find information
about vulnerabilities that may not affect any packages (there are a few reasons why this could happen.)
To view full metadata for a specific CVE:
grype db search vuln CVE-2021-44228
To filter by data provider:
grype db search vuln CVE-2021-44228 --provider nvd