Skip to content

Scan container images in CI

AlertaVuln scans a built container image for vulnerable OS packages and the application dependencies baked into its layers, tiers every finding RED / YELLOW / GREEN, and lands the results on the project’s Containers page. The natural place to run that is the pipeline that already builds the image.

From a runner that has just built your image, that is a single command:

Terminal window
av image scan "$IMAGE_REF" --project "$ALERTAVULN_PROJECT_ID"

The scan reads the image with the runner’s own registry credentials, uploads the normalised findings and a CycloneDX inventory, and leaves the image itself on the runner. This guide wires that command into GitHub Actions and Azure DevOps, signing in with an organisation API key your platform injects from a secret.

There is no interactive browser login in a pipeline. Instead the CLI reads an organisation API key from the ALERTAVULN_API_KEY environment variable and uses it as the request token for that one invocation. Your CI platform holds the key as a secret and injects it into the job; the CLI never writes it to disk, so it stays ephemeral on the runner.

  1. Create an organisation API key with ReadWrite scope. In the web app, open Settings and the API Keys panel (org-admin only). Uploading a scan writes to the project, so the key must be ReadWrite, not ReadOnly. The key looks like av_live_ followed by 64 hex characters and is shown once, so copy it immediately.

  2. Store the key as a secret in your CI platform (next section). Never commit it or print it in logs.

  3. Find the project ID you want to scan into. Install the CLI locally and run av project list, then copy the ID from the first column (it is also on the project’s page in the web app).

Add a repository (or organisation) secret named ALERTAVULN_API_KEY under Settings -> Secrets and variables -> Actions. Store the project ID as a plain variable (it is not sensitive) named ALERTAVULN_PROJECT_ID.

The job does three things: build the image, install the CLI, and scan the image it just built. Because the scan reads the image locally, it must run after the build, and it uses the runner’s Docker credentials to reach any registry.

.github/workflows/alertavuln-image.yml
name: Scan container image
on:
push:
branches: [main]
jobs:
image-scan:
runs-on: ubuntu-latest
env:
# Injected from the repository/organisation secret; read by the CLI.
ALERTAVULN_API_KEY: ${{ secrets.ALERTAVULN_API_KEY }}
IMAGE_REF: acme/api:${{ github.sha }}
steps:
- uses: actions/checkout@v4
- name: Build the image
run: docker build -t "$IMAGE_REF" .
- name: Install the AlertaVuln CLI
run: |
curl -fsSL https://get.alertavuln.com/cli/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Scan the image
run: av image scan "$IMAGE_REF" --project "${{ vars.ALERTAVULN_PROJECT_ID }}" --fail-on red

On the next push to main the job builds the image, installs the CLI, authenticates with the injected key, and scans. --fail-on red makes the build fail when the worst finding is RED, so a newly vulnerable image blocks the pipeline. Each run prints a summary and the tiered finding counts:

What a run prints
$ av image scan acme/api:8f3c1d2 --project "$ALERTAVULN_PROJECT_ID"
Image: acme/api:8f3c1d2
Digest: sha256:2b1c9f4e7a08...
OS: debian 12
SEVERITY COUNT
RED 2
YELLOW 14
GREEN 7
Uploaded to project. View on the Containers page.

The scanner runs locally and is reported by its neutral capability label, container-scanner. Only the findings and a CycloneDX inventory are uploaded - never your registry credentials.

Once an image is scanned, the project’s Containers page splits its findings into base image layers and your layers, and shows up to three safer base images - a newer patch tag, a slimmer variant, or the next major - each with a concrete delta such as “-2 RED, -14 YELLOW”. When the project has a connected repo, the advice also names the Dockerfile FROM line to change. Often the fastest way to clear a batch of base-layer findings is to take the recommended FROM change rather than to patch packages one by one.

A CI scan is a point-in-time check. With continuous monitoring on (an Enterprise feature), AlertaVuln keeps re-checking a scanned image as new CVEs land - a nightly sweep plus an event-driven re-match whenever a fresh CVE names a package in the image - all from the stored inventory, with no image re-pull. New or reopened findings raise alerts through your configured notification channels. Turn it on per image from the Containers page (see continuous monitoring).

List the project’s findings after a run, from the pipeline or your machine:

av image findings
$ av image findings --project "$ALERTAVULN_PROJECT_ID" --severity red
SEVERITY STATE CVE PACKAGE INSTALLED FIXED LAYER
RED Open CVE-2025-1234 openssl 3.0.11 3.0.13 base
RED Open CVE-2025-5678 libxml2 2.9.14 2.9.15 base

The same findings appear on the project’s Containers page, and any new or reopened RED / YELLOW finding raises an alert through whatever notification channels you have configured.

  • Use a dedicated ReadWrite key for CI, separate from any personal login, so you can rotate or revoke it without disrupting anyone else.
  • Keep it in your platform’s secret store. Do not echo it, and prefer a Key Vault-backed variable group on Azure DevOps.
  • If a key is exposed, revoke it in Settings -> API Keys and issue a new one.