Scanning Dependencies and Containers in CI
The vulnerability you ship today is the incident you fight next month.
Most of your application is code you did not write: open-source dependencies and base images. Scanning catches known vulnerabilities (CVEs) in that code before it reaches production. This lesson adds dependency and container scanning to a pipeline, sets sensible failure thresholds, and manages the inevitable noise.
Two layers to scan
- Dependency scanning: checks your declared packages (and their transitive dependencies) against vulnerability databases.
- Container scanning: inspects the built image, including the OS packages in the base image, not just your app dependencies.
- You want both: a clean dependency tree can still sit on a base image full of unpatched OS CVEs.
Scanning a container image
A scanner like Trivy inspects the built image and reports CVEs. Fail the build on high-severity findings so they cannot be ignored.
- name: Scan image
uses: aquasecurity/trivy-action@0.24.0
with:
image-ref: ghcr.io/${{ github.repository }}:${{ github.sha }}
severity: CRITICAL,HIGH
exit-code: '1'Set a sensible threshold
Failing on every finding, including unfixable low-severity noise, trains people to ignore the scanner. Start by failing only on CRITICAL and HIGH severities that have a fix available, and tighten over time. The goal is a signal people act on, not an alarm they mute.
Reduce findings at the source
- Use a slim, minimal base image (fewer packages means fewer CVEs), pairing well with multi-stage builds.
- Keep dependencies current with Dependabot so fixes land continuously instead of in a big scary batch.
- Allow-list accepted, unfixable findings explicitly with an expiry, rather than disabling the gate.
Key takeaways
- Scan both dependencies and the container image; a slim base image still needs OS-package scanning.
- Fail the build on fixable CRITICAL/HIGH findings, not on every low-severity finding.
- Cut findings at the source with slim base images and continuous dependency updates.