Scanning Images with rad-image-scanner (CLI)
Scan container images locally with rad-image-scanner — a Grype wrapper that enriches reports with how the image is currently deployed in your fleet.
rad-image-scanner is a container image vulnerability scanner. Without RAD credentials it behaves identically to Grype. With RAD credentials, it queries the RAD Security inventory for every configured account, compares severity counts against the image as it is currently deployed in your fleet, and writes an enrichment report with vulnerability count deltas, regression detection, and distro EOL warnings.
Source: github.com/rad-security/image-scanner. For CI usage see Scanning Images in CI with the GitHub Action.
Installation
brew install rad-security/tap/rad-image-scanner
VERSION=$(curl -fsSL https://api.github.com/repos/rad-security/image-scanner/releases/latest | grep -oE '"tag_name": "v[^"]+"' | cut -d'"' -f4 | sed 's/^v//')
ARCH=$(uname -m); case "$ARCH" in x86_64) ARCH=amd64;; aarch64|arm64) ARCH=arm64;; esac
curl -fsSL "https://github.com/rad-security/image-scanner/releases/download/v${VERSION}/rad-image-scanner_${VERSION}_linux_${ARCH}.tar.gz" \
| sudo tar -xz -C /usr/local/bin rad-image-scanner
rad-image-scanner --version
For macOS, swap linux for darwin in the URL.
docker run --rm public.ecr.aws/n8h5y2v5/rad-security/rad-image-scanner:latest <image>
go install github.com/rad-security/image-scanner@latest
Pure passthrough mode (no RAD env)
If RAD_ACCESS_KEY_ID and RAD_SECRET_KEY are unset, rad-image-scanner is a transparent wrapper around grype. Every argument is forwarded, output is unchanged, exit code is grype's.
rad-image-scanner alpine:3.23 -o sarif --file alpine.sarif
RAD-enriched mode
Set the credentials below and the scanner additionally queries the RAD Security inventory for every configured account, compares severity counts, and writes an enrichment report.
| Variable | Required | Description |
|---|---|---|
RAD_ACCESS_KEY_ID | yes | Access key ID from RAD Security. |
RAD_SECRET_KEY | yes | Secret matching the access key. |
RAD_ACCOUNT_IDS | yes | Comma-separated list of RAD account IDs to query. |
RAD_API_URL | no | Defaults to https://api.rad.security. |
export RAD_ACCESS_KEY_ID=...
export RAD_SECRET_KEY=...
export RAD_ACCOUNT_IDS=acct_1,acct_2
rad-image-scanner nginx:stable-alpine3.23 \
-o sarif --file svc.sarif \
--rad-annotate-sarif \
--rad-fail-on-regression=critical \
--rad-fail-on-eol
The terminal summary shows where the image runs today (clusters, namespaces, workloads) and a verdict (improvement, same, or regression) per deployed instance. The full structured report is written to a per-run JSON file (rad-report-<image>-<YYYYMMDD-HHMMSS>.json by default).
RAD-specific flags
| Flag | Description |
|---|---|
--rad-report PATH | Write the standalone RAD enrichment JSON. Defaults to rad-report-<image>-<YYYYMMDD-HHMMSS>.json so successive scans never overwrite each other. |
--rad-annotate-sarif | When Grype emits SARIF, inject the RAD report under runs[].properties.rad. |
--rad-fail-on-regression critical|high|medium|low|any | Exit non-zero if the new scan adds vulnerabilities at this severity or higher vs any deployed instance. |
--rad-fail-on-eol | Exit non-zero if the scanned image is built on an end-of-life distro (detected by Grype). |
--rad-account-ids id1,id2 | Override RAD_ACCOUNT_IDS. |
--rad-api-url URL | Override the RAD API base URL. |
--rad-image-name NAME | Force the image name used for RAD lookup (useful when parsing is ambiguous). |
--rad-image-repo REPO/ | Force the image repo used for RAD lookup. |
--rad-grype-version VER | Pin a different Grype version at runtime. |
--rad-grype-help | Print Grype's full, unmodified help and exit. |
--rad-skip | Disable RAD enrichment even if env is set. |
All flags not recognised as --rad-* are passed through to Grype unchanged.
How matching works
For each scan target the scanner extracts name and repo from the image reference (e.g. registry/foo/bar/baz:v1 → repo=registry/foo/bar/, name=baz) and queries:
GET /accounts/{id}/data/scanned_images
?filters_query=name:"<name>" AND repo:"<repo>"
across every configured account in parallel. All returned deployments are merged; each one becomes a row in the enrichment report. For every deployed image the scanner then queries the container inventory to aggregate running containers into a placement block (container count, clusters, namespaces, workloads).
Behaviour when RAD is unreachable
If RAD_ACCESS_KEY_ID and RAD_SECRET_KEY are set but authentication fails or the inventory API is unreachable, the scanner exits non-zero. There is no silent fallback to pure-Grype mode — misconfigured CI should surface a loud failure rather than quietly drop the enrichment guarantee.
Grype binary management
rad-image-scanner does not embed Grype. At runtime it:
- Honours
$RAD_GRYPE_PATHif set. - Looks for
grypein$PATHand uses it if the version matches the pinned one. - Looks in
$XDG_CACHE_HOME/rad-image-scanner/grype-v<version>/grype. - Downloads the official Grype release tarball from GitHub, verifies the SHA256 against the published checksum file, extracts the binary into the cache, and uses it.
--rad-grype-version overrides the pinned version.