The rad-security/image-scan-action action wraps rad-image-scanner for use in GitHub workflows. It scans an image (or a pre-built Syft SBOM), optionally enriches the report with currently-deployed inventory from RAD Security, and can fail the workflow on a severity threshold, a regression vs deployed instances, or an end-of-life base distro.
Minimal example — plain vulnerability scan
name: image-scan
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: rad-security/image-scan-action@v1
with:
image: ghcr.io/${{ github.repository }}:${{ github.sha }}
fail_on_severity: high
Without RAD credentials this is a transparent Grype scan: the workflow fails if Grype finds a high or critical vulnerability.
RAD-enriched example — gate on regression vs deployed images
name: image-scan
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: rad-security/image-scan-action@v1
with:
image: ghcr.io/${{ github.repository }}:${{ github.sha }}
format: sarif
rad_account_ids: acct_1,acct_2
rad_fail_on_regression: critical
rad_fail_on_eol: "true"
env:
RAD_ACCESS_KEY_ID: ${{ secrets.RAD_ACCESS_KEY_ID }}
RAD_SECRET_KEY: ${{ secrets.RAD_SECRET_KEY }}
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: ${{ steps.scan.outputs.sarif }}
RAD_ACCESS_KEY_ID and RAD_SECRET_KEY are passed via the workflow’s env: block, not as inputs:. GitHub Actions inputs are visible in workflow logs; secrets in env: are masked.
| Input | Default | Description |
|---|
image | — | Image to scan (e.g. registry.example.com/foo/bar:v1). Required unless sbom is set. |
sbom | — | Path to a Syft JSON SBOM to scan instead of an image. |
format | table | Output format passed to Grype: table, json, sarif, or cyclonedx. |
fail_on_severity | — | Fail the workflow if any Grype finding at this severity or higher exists. Values: negligible, low, medium, high, critical. |
ignore_cves | — | Multiline list of CVE IDs to ignore, one per line. |
rad_account_ids | — | Comma-separated RAD account IDs to query. Required for RAD enrichment. Without this the action runs as plain Grype. |
rad_fail_on_regression | — | Fail the workflow if the scan adds vulnerabilities at this severity or higher vs any deployed instance. Values: critical, high, medium, low, any. |
rad_fail_on_eol | — | Set to "true" to fail the workflow if the scanned image is built on an end-of-life distro. |
rad_api_url | https://api.rad.security | Override the RAD API base URL. |
rad_report | rad-report.json | Path where the RAD enrichment JSON report is written. |
rad_annotate_sarif | "true" | When format=sarif, inject the RAD report under runs[].properties.rad. |
Outputs
| Output | When set | Description |
|---|
sarif | format: sarif | Path to the SARIF report. |
rad_report | RAD credentials configured | Path to the RAD enrichment JSON report. |
Required secrets
For RAD-enriched scans, add the credential pair to your repository (or organisation) secrets:
| Secret | Description |
|---|
RAD_ACCESS_KEY_ID | Access key ID from RAD Security. See Managing API keys. |
RAD_SECRET_KEY | Secret matching the access key. |
Common recipes
Upload SARIF to Code Scanning
- id: scan
uses: rad-security/image-scan-action@v1
with:
image: ghcr.io/${{ github.repository }}:${{ github.sha }}
format: sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: ${{ steps.scan.outputs.sarif }}
The Code Scanning UI then shows each finding as an alert tied to the image layer.
Ignore noisy CVEs
- uses: rad-security/image-scan-action@v1
with:
image: ghcr.io/${{ github.repository }}:${{ github.sha }}
fail_on_severity: high
ignore_cves: |
CVE-2024-12345
CVE-2024-67890
Scan a pre-built SBOM instead of an image
Useful when you already build a Syft SBOM earlier in the pipeline and want to avoid re-pulling the image.
- uses: anchore/sbom-action@v0
with:
image: ghcr.io/${{ github.repository }}:${{ github.sha }}
format: syft-json
output-file: image.sbom.json
- uses: rad-security/image-scan-action@v1
with:
sbom: image.sbom.json
fail_on_severity: high
Matrix across multiple images
strategy:
matrix:
image:
- ghcr.io/${{ github.repository }}/api:${{ github.sha }}
- ghcr.io/${{ github.repository }}/worker:${{ github.sha }}
steps:
- uses: rad-security/image-scan-action@v1
with:
image: ${{ matrix.image }}
rad_account_ids: ${{ vars.RAD_ACCOUNT_IDS }}
rad_fail_on_regression: critical
env:
RAD_ACCESS_KEY_ID: ${{ secrets.RAD_ACCESS_KEY_ID }}
RAD_SECRET_KEY: ${{ secrets.RAD_SECRET_KEY }}
How the action behaves on failure
| Situation | Outcome |
|---|
Grype finds a vulnerability ≥ fail_on_severity | Workflow fails. |
RAD scan adds vulnerabilities ≥ rad_fail_on_regression vs deployed | Workflow fails. |
rad_fail_on_eol: "true" and base distro is EOL | Workflow fails. |
| RAD credentials set but auth or API unreachable | Workflow fails. No silent fallback to pure-Grype. |
Neither image nor sbom provided | Workflow fails with input error. |
Next steps
Use the CLI locally
Run the same scanner outside CI to reproduce findings or iterate on a fix.
Manage API keys
Generate the RAD_ACCESS_KEY_ID / RAD_SECRET_KEY pair used by the action.