Filter scan results
TL;DR
- Use
--fail-on <severity>to set exit code thresholds for CI/CD pipelines - Filter by fix availability with
--only-fixedor--only-notfixed - Create ignore rules in
.grype.yamlto exclude specific vulnerabilities or packages - Use VEX documents with
--vexto 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 yetwont-fix- Maintainers won’t fix this vulnerabilityunknown- 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 states
grype 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 vulnerabilities
grype 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.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
apk-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 exist
grype 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 vulnerabilities
grype 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.9
zlib 1.2.11-r1 apk CVE-2018-25032 High < 0.1% (26th) < 0.1 (suppressed)
libcrypto1.1 1.1.1k-r0 apk CVE-2021-3711 Critical 2.7% (85th) 2.4 (suppressed)
libssl1.1 1.1.1k-r0 apk CVE-2021-3711 Critical 2.7% (85th) 2.4 (suppressed)
libcrypto1.1 1.1.1k-r0 apk CVE-2021-3712 High 0.5% (66th) 0.4 (suppressed)
libssl1.1 1.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.
View the structure:
grype alpine:3.10 --only-fixed -o json | jq '{matches, ignoredMatches}'
The structure separates matched from ignored vulnerabilities:
{
"matches": [
{
"vulnerability": {...},
"artifact": {...},
...
}
],
"ignoredMatches": [
{
"vulnerability": {...},
"artifact": {...},
...
},
...
]
}
Inspect a specific ignored vulnerability:
grype alpine:3.10 --only-fixed -o json | jq '.ignoredMatches[0] | {vulnerability: .vulnerability.id, package: .artifact.name, reason: .appliedIgnoreRules}'
Each ignored match shows why it was filtered:
{
"vulnerability": "CVE-2018-25032",
"package": "zlib",
"reason": [
{
"namespace": "",
"fix-state": "unknown"
}
]
}
The appliedIgnoreRules field shows why each vulnerability was filtered.
Ignore specific vulnerabilities or packages
You can create ignore rules in your .grype.yaml configuration file to exclude specific vulnerabilities or packages from scan results.
Use ignore rules
Create a .grype.yaml file with ignore rules:
ignore:
# Ignore specific CVEs
- vulnerability: CVE-2008-4318
- vulnerability: GHSA-1234-5678-90ab
# Ignore all vulnerabilities in a package
- package:
name: libcurl
# Ignore vulnerabilities in a specific version
- package:
name: openssl
version: 1.1.1g
# Ignore by package type
- package:
type: npm
name: lodash
# Ignore by package location (supports glob patterns)
- package:
location: "/usr/local/lib/node_modules/**"
# Ignore by fix state
- vulnerability: CVE-2020-1234
fix-state: not-fixed
# Combine multiple criteria
- vulnerability: CVE-2008-4318
fix-state: unknown
package:
name: libcurl
version: 1.5.1
Valid fix-state values are: fixed, not-fixed, wont-fix, unknown.
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-suppressedflag and marked as(suppressed by VEX) - JSON output: Moved to the
ignoredMatchesarray with VEX rules listed inappliedIgnoreRules
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:
# Single VEX document
grype alpine:latest --vex vex-report.json
# Multiple VEX documents
grype alpine:latest --vex vex-1.json,vex-2.json
You can also specify VEX documents in your configuration file:
# .grype.yaml file
vex-documents:
- vex-report.json
- vex-findings.json
VEX status values
VEX documents use four standard status values:
Filtering statuses (automatically applied):
not_affected- Product is not affected by the vulnerabilityfixed- Vulnerability has been remediated
Augmenting statuses (require explicit configuration):
affected- Product is affected by the vulnerabilityunder_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 image
vexctl 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 Grype
grype 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:
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "https://openvex.dev/docs/public/vex-07f09249682f6d9d2924be146078475538731fa0ee6a50ad3c9f33617e4a0be4",
"author": "Alex Goodman",
"version": 1,
"statements": [
{
"vulnerability": {
"name": "CVE-2024-58251"
},
"products": [
{
"@id": "pkg:oci/alpine@sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412",
"subcomponents": [
{
"@id": "pkg:apk/alpine/busybox@1.37.0-r19"
}
]
}
],
"status": "not_affected",
"justification": "vulnerable_code_not_present",
"timestamp": "2025-11-21T20:30:11.725672Z"
}
],
"timestamp": "2025-11-21T20:30:11Z"
}
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 productvulnerable_code_not_present- The vulnerable code is not presentvulnerable_code_not_in_execute_path- The vulnerable code cannot be executedvulnerable_code_cannot_be_controlled_by_adversary- The vulnerability cannot be exploitedinline_mitigations_already_exist- Mitigations prevent exploitation
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:
Container images (most reliable):
"products": [
{ "@id": "pkg:oci/alpine@sha256:124c7d2707a0ee..." }
]
Image tags (less reliable, can change):
"products": [
{ "@id": "alpine:3.17" }
]
Individual packages via PURLs:
"products": [
{
"@id": "pkg:oci/alpine@sha256:124c7d...",
"subcomponents": [
{ "@id": "pkg:apk/alpine/libssl3@3.0.8-r3" }
]
}
]
Use container digests for the most reliable matching, as tags can move to different images over time.
Next steps
Continue the guide
Next: Learn about the vulnerability database to understand how Grype keeps vulnerability data up to date.Additional resources:
- Interpret results: Learn how to understand scan output and assess match reliability
- Configuration: See Grype configuration reference for all configuration options
- Data sources: Explore vulnerability data sources for details on each feed