Using Grype to Scan Container Images for Vulnerabilities
Grype is a vulnerability scanner for container images and filesystems developed and maintained by Anchore and written in the Go programming language. Grype can scan from Docker, OCI, Singularity, podman, image archives, and local directory. Grype is compatible with SBOMs generated by Syft, and Grype’s vulnerability database draws from a wide variety of sources, including Wolfi SecDB.
Grype is appropriate for one-off detection for manual CVE mitigation and in automated use in CI pipelines. Chainguard maintains a low-to-no CVE Chainguard Image for Grype based on our lightweight Wolfi distribution.
Installation
Container Images
Grype is readily available as a container image. To pull the low-to-no-CVE Chainguard Image for Grype and perform a scan on the official Docker nginx image, run the following:
docker run -it cgr.dev/chainguard/grype nginx
Alternatively, you can scan using the official Grype Docker image:
docker run -it anchore/grype:latest nginx
Binary Installation
Grype provides an installation script. To use it, change the path following the -b
flag to a preferred installation location on your system path, such as /usr/bin
.
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
Alternatively, find and download the appropriate package file or binary from the official releases page. Place the binary on your system path or install the package using the conventions for your OS and distribution.
Note that Grype can also be built from source.
Install via Package Manager
Grype can be installed using the following commands specific to your OS and distribution:
Homebrew
brew tap anchore/grype
brew install grype
Chocolatey
choco install grype -y
Basic Usage
Throughout this tutorial, we’ll use the grype
command to run Grype. If you’re running Grype as a container image, replace this command with the appropriate docker run
command, such as docker run -it cgr.dev/chainguard/grype
.
Scan an Image in a Registry
To run Grype on an image on Docker Hub, pass the image name as an argument:
grype nginx
For images on other registries:
grype cgr.dev/chainguard/nginx
Scan a .tar File
To scan an image stored to .tar
, pass the path to the archive file as an argument:
docker pull cgr.dev/chainguard/nginx
docker save cgr.dev/chainguard/nginx > nginx_chainguard_image.tar.gz
grype nginx_chainguard_image.tar.gz
Scan a Local Directory
Grype can scan local directories, such as Python virtual environments (venv) or node_modules
folders.
To try it out, start by creating a Python virtual environment:
python -m venv venv
Add a few out-of-date packages that will show vulnerabilities:
./venv/bin/pip install WTForms==2.3.3 Werkzeug==2.0.1
Scan the virtual environment folder by passing the folder path to Grype as an argument:
grype venv
We can do the same with node modules in a node_modules
folder. First, create an empty project folder and change the working directory to that folder:
mkdir node_project && cd node_project
Next, initialize npm with a default configuration:
npm init -y
Install a package with known vulnerabilities. The 6.5.2 version of the qs
query string parser has a known vulnerability allowing for prototype poisoning.
npm install qs@6.5.2
Finally, use Grype to scan the current working directory:
grype .
Scan from an SBOM
Grype can read vulnerabilities from SBOMs generated by Syft. SBOMs can be piped into Grype using stdin:
syft -o syft-json python > sbom.json
cat sbom.json | grype
You should see Grype results based on the packages itemized in the SBOM.
Comprehending Grype Output
By default, Grype output is divided into two sections: a summary of information on the scanned artifact and an itemized list of CVEs.
In this section, we’ll use an Alpine version of the official Python image as an example. Since we’re specifying an older version, you may encounter more CVEs when following the examples than are shown here, as CVEs will accumulate on an image over time.
Scan the image with the following command:
grype python:3.10.14-alpine3.20
You will receive output similar to the following:
✔ Vulnerability DB [no update av
✔ Parsed image sha256:f48490
✔ Cataloged contents 09feb83998b9d
├── ✔ Packages [47 pac
│ └── ⠹ Linux kernel cataloger
├── ✔ File digests [659 fi
├── ✔ File metadata [659 lo
└── ✔ Executables [144 ex
✔ Scanned for vulnerabilities [10 vulnerabi
├── by severity: 0 critical, 1 high, 8 medium
└── by status: 7 fixed, 3 not-fixed, 0 igno
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
busybox 1.36.1-r28 1.36.1-r29 apk CVE-2023-42365 Medium
busybox 1.36.1-r28 1.36.1-r29 apk CVE-2023-42364 Medium
busybox-binsh 1.36.1-r28 1.36.1-r29 apk CVE-2023-42365 Medium
busybox-binsh 1.36.1-r28 1.36.1-r29 apk CVE-2023-42364 Medium
pip 23.0.1 23.3 python GHSA-mq26-g339-26xf Medium
python 3.10.14 binary CVE-2023-36632 High
python 3.10.14 binary CVE-2023-27043 Medium
python 3.10.14 binary CVE-2024-4030 Unknown
ssl_client 1.36.1-r28 1.36.1-r29 apk CVE-2023-42365 Medium
ssl_client 1.36.1-r28 1.36.1-r29 apk CVE-2023-42364 Medium
Interpreting the Summary
In the initial portion of its results output, Grype summarizes information on the scanned artifact and gives an overview of known vulnerabilities. In the case of a scanned image, the output includes the image digest, a unique hash of the image that can be used as an identifier.
Overview output includes the number of packages, files, and executables found in the artifact. Generally speaking, CVEs are detected against packages, but the number of executables detected can also give you an idea of the attack surface of the scanned image or filesystem.
Finally, this portion gives a count of the number of CVEs detected by severity and fixed status. Severity categorization sorts CVEs into four categories based on the Common Vulnerability Scoring System (CVSS).
What is CVSS??
CVEs are assigned numerical scores according to the Common Vulnerability Scoring System (CVSS), an open framework for communicating the severity of software vulnerabilities.
CVSS scores correspond to four categories
- Critical (9.0-10.0)
- High (7.0-8.9)
- Medium (4.0-6.9)
- Low (0.1-3.9)
In general, the higher the score, the more serious the vulnerability. Higher scores should be prioritized during CVE remediation.
In our output, we can see that we have 0 critical, 1 high, and 8 medium CVEs:
├── by severity: 0 critical, 1 high, 8 medium, 0 low, 0 negligible
Grype also counts the number of CVEs by fixed status. If a CVE is marked as fixed, it can be resolved by updating to a newer version of the package. Our output suggests that 7 packages have been fixed and can be remediated with updates:
└── by status: 7 fixed, 3 not-fixed, 0 ignored
Itemized CVEs
In addition to the summary, Grype provides an itemized list of CVEs. By default, these are in table format, and list the package name, current version, severity, and package type (such as apt
, apk
, or binary
). If the package is fixed, Grype will also indicate the package version where the fix was introduced.
Grype writes itemized CVEs to stdout, so you can redirect the report of itemized CVEs to a file:
grype python:3.10.14-alpine3.20 > report.txt
Alternatively, you can use the --file
flag to write to a file:
grype --file report.txt python:3.10.14-alpine3.20
Redirecting output can also be useful to suppress a long list of CVEs, making the summary more immediately accessible.
Output Formats
Standard Formats
You can use Grype to write itemized CVEs to a number of formats, including the XML- or JSON-based cyclonedx SBOM standard and the SARIF static analysis format. To maximize the information provided by Grype, use the JSON output type:
grype -o json python:3.10.14-alpine3.20 > report.json
When using these more detailed formats, Grype provides additional useful fields, such as the data source of the CVE, URLs to information on the CVE, advisories, related vulnerabilities, and details on how the vulnerability was detected.
Output Templates
Additional output formats are available as Hugo templates. These include output templates for HTML and CSV, and a full list can be found at the Grype GitHub repository.
To generate a HTML file of the itemized CVEs, first clone the Grype repository from GitHub. Then provide the path to the template file in your grype
command:
git clone git@github.com:anchore/grype.git ~/.grype
grype -o template -t ~/.grype/templates/html.tmpl python:3.10.14-alpine3.20 > report.html
To generate a CSV:
git clone git@github.com:anchore/grype.git ~/.grype
grype -o template -t ~/.grype/templates/csv.tmpl python:3.10.14-alpine3.20 > report.csv
grype explain
Grype provides an explain subcommand that gives information on the nature of a specific CVE, how it was matched, and the locations of files associated with the vulnerability. The output of this command suggests a useful starting point for remediation.
To use grype explain
, generate JSON output from Grype and pipe it into the grype explain
subcommand. Indicate the CVE you’d like information on using the --id
flag. The -q
flag in the following example suppresses the summary output.
grype -q python:3.10.14-alpine3.20 -o json | grype explain --id CVE-2023-36632
Grype uses information from the JSON output to generate a human-readable report on the specific CVE that includes match information, file locations, and links to information on the vulnerability.
Additional Resources
The following resources may also be useful while working with Grype:
Tools
- Syft - A Grype-compatible tool for generating SBOMs from images and filesystems.
- Grype-DB - A tool to build Grype databases from specific upstream vulnerability database providers
- Vunnel - A tool for collating vulnerability provider data
- Grype Chainguard Image — A low-to-no CVE container image maintained by Chainguard
More on Grype
- Chainguard Deep Dive 🤿: Where Does Grype Data Come From?
- Grype on the Anchore blog - Blog posts from Anchore related to Grype
- Why Chainguard uses Grype - Why Chainguard contributes to and recommends Grype for vulnerability scanning in container images
Last updated: 2024-06-06 20:00