> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rad.security/llms.txt
> Use this file to discover all available pages before exploring further.

# Scanning Images in CI with the GitHub Action

> Use the rad-security/image-scan-action GitHub Action to scan container images on pull requests, upload SARIF to code scanning, and gate merges on regressions vs deployed images.

The [`rad-security/image-scan-action`](https://github.com/rad-security/image-scan-action) action wraps [`rad-image-scanner`](/rad-security/platform/tutorials/rad-image-scanner-cli) 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

```yaml theme={null}
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

```yaml theme={null}
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 }}
```

<Note>
  `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.
</Note>

## Inputs

| 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-security/platform/tutorials/managing-api-keys). |
| `RAD_SECRET_KEY`    | Secret matching the access key.                                                                               |

## Common recipes

### Upload SARIF to Code Scanning

```yaml theme={null}
- 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

```yaml theme={null}
- 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.

```yaml theme={null}
- 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

```yaml theme={null}
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

<CardGroup cols={2}>
  <Card title="Use the CLI locally" href="/rad-security/platform/tutorials/rad-image-scanner-cli">
    Run the same scanner outside CI to reproduce findings or iterate on a fix.
  </Card>

  <Card title="Manage API keys" href="/rad-security/platform/tutorials/managing-api-keys">
    Generate the `RAD_ACCESS_KEY_ID` / `RAD_SECRET_KEY` pair used by the action.
  </Card>
</CardGroup>
