Working with JSON
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:
{
"matches": [
{
"vulnerability": { ... },
"relatedVulnerabilities": [ ... ],
"matchDetails": [ ... ],
"artifact": { ... }
}
]
}
Ultimately, matches are the core results of a Grype scan. Matches are composed of:
- vulnerability - Primary vulnerability information
- matchDetails - How Grype found the match
- artifact - The package/artifact that was matched against the vulnerability
Vulnerability fields
The vulnerability object contains the primary vulnerability information:
- id (string) - The vulnerability identifier (CVE, GHSA, ALAS, ELSA, etc.)
- dataSource (string) - URL to the vulnerability record in the data feed
- namespace (string) - The data source namespace (e.g.,
alpine:distro:alpine:3.10,debian:distro:debian:10,github:language:javascript,nvd:cpe) - severity (string) - Severity rating from the data source
- urls (array) - Reference URLs for the vulnerability
- description (string) - Human-readable vulnerability description
- 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
A typical vulnerability object looks like:
{
"vulnerability": {
"id": "CVE-2021-36159",
"dataSource": "https://security.alpinelinux.org/vuln/CVE-2021-36159",
"namespace": "alpine:distro:alpine:3.10",
"severity": "Critical",
"urls": [],
"fix": {
"versions": ["2.10.7-r0"],
"state": "fixed"
},
"advisories": [],
"risk": 0.92
}
}
Match detail fields
The matchDetails array contains information about how Grype found the match. Each detail object includes:
- type (string) - Match type:
exact-direct-match,exact-indirect-match, orcpe-match - matcher (string) - The matcher that produced this result (e.g.,
apk-matcher,github-matcher,stock-matcher) - searchedBy (object) - The specific attributes used to search (package name, version, etc.)
- found (object) - The specific attributes in the vulnerability data that matched
- fix (object) - Fix details specific to this match (may differ from vulnerability-level fix)
Here’s what matchDetails looks like:
{
"matchDetails": [
{
"type": "exact-direct-match",
"matcher": "apk-matcher",
"searchedBy": {
"distro": {
"type": "alpine",
"version": "3.10.9"
},
"package": {
"name": "apk-tools",
"version": "2.10.6-r0"
},
"namespace": "alpine:distro:alpine:3.10"
},
"found": {
"vulnerabilityID": "CVE-2021-36159",
"versionConstraint": "< 2.10.7-r0 (apk)"
}
}
]
}
Understanding match types
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.
See View filtered results to learn how to inspect filtered vulnerabilities.
Filtering and querying results
Use jq to filter and analyze JSON output based on match type, severity, or data source.
Filter by match type
Show only high-confidence exact matches:
grype <image> -o json | jq '.matches[] | select(.matchDetails[0].type == "exact-direct-match")'
Exclude CPE matches:
grype <image> -o json | jq '.matches[] | select(.matchDetails[0].type != "cpe-match")'
Filter by data source
Show only matches from Alpine security data:
grype <image> -o json | jq '.matches[] | select(.vulnerability.namespace | startswith("alpine:"))'
Show only GitHub Security Advisories:
grype <image> -o json | jq '.matches[] | select(.vulnerability.namespace | startswith("github:"))'
Filter by severity
Show only Critical and High severity vulnerabilities:
grype <image> -o json | jq '.matches[] | select(.vulnerability.severity == "Critical" or .vulnerability.severity == "High")'
Combine filters
Show Critical/High severity vulnerabilities with exact matches only:
grype <image> -o json | jq '.matches[] | select(
(.vulnerability.severity == "Critical" or .vulnerability.severity == "High") and
(.matchDetails[0].type == "exact-direct-match" or .matchDetails[0].type == "exact-indirect-match")
)'
Count matches by type
grype <image> -o json | jq '[.matches[].matchDetails[0].type] | group_by(.) | map({type: .[0], count: length})'
Understanding a match
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:
grype <image> -o json | jq '.matches[].vulnerability | {id, dataSource, urls}'
- dataSource - URL to the vulnerability record in Grype’s data feed
- urls - Reference URLs from the original vulnerability disclosure (CVE details, vendor advisories, etc.)
These URLs point to the original vulnerability information that Grype used.
What Grype searched for
The matchDetails[].searchedBy field shows what Grype looked at when searching for vulnerabilities:
grype <image> -o json | jq '.matches[].matchDetails[].searchedBy'
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:
grype <image> -o json | jq '.matches[].matchDetails[] | {found, type}'
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.
Next steps
Continue the guide
Next: Learn how to filter scan results to control which vulnerabilities Grype reports.Additional resources:
- Filter results: Use result filtering for severity thresholds and ignore rules
- Supported ecosystems: Understand data source selection for different package types
- Configuration: See Grype configuration reference for customizing behavior