Clair Integration with DefectDojo
Clair Integration with DefectDojo
Clair is an open source static analysis tool for container images, originally created by CoreOS and now maintained by the Quay team at Red Hat. It indexes the layers of an image, identifies the operating system packages they contain, and matches those packages against vulnerability data from distribution security trackers such as Debian, Ubuntu, Alpine, and Red Hat. Clair runs as a service, so most teams drive it through a client such as clair-scanner or Klar, which produce JSON reports that DefectDojo can import.
Clair Integration with DefectDojo
We run Clair because it gives us a self-hosted way to check every image before it reaches a registry, and DefectDojo is where those results become work. A Clair report on its own is a snapshot of one image at one moment. Imported into DefectDojo, each CVE becomes a Finding attached to the right Asset, deduplicated against earlier scans, assigned to an owner, and measured against our SLAs. When the base image gets patched and we rebuild, the next import closes what was fixed instead of leaving us to diff two JSON files by hand.
Why Clair Matters
Container images carry a full operating system userland, and most of their CVEs come from packages nobody on the application team chose directly. Clair targets exactly that layer.
- It analyzes images layer by layer, so a vulnerable package in a shared base image is caught in every image built on it.
- It is open source and self-hosted, which suits teams that can't send image contents to a third-party service.
- Its findings name the vulnerable package, the installed version, and the version that fixes it, which is the information an engineer needs to act.
- Raw Clair output has no memory. Without a platform behind it, nobody can say whether a CVE is new this week or has been open since the last release.
Advantages of This Integration
What we gained by sending Clair results through DefectDojo:
- Deduplication across rebuilds. DefectDojo hashes each Clair finding on title, vulnerability IDs, description, and severity, so rescanning the same image doesn't create a second copy of every CVE.
- A real lifecycle. Reimporting a new report into the same Test mitigates CVEs that disappeared after a base image update, adds new ones, and reactivates any that came back.
- SLA tracking by severity. Clair severities map onto DefectDojo's Critical through Info scale, so container CVEs fall under the same SLA rules as findings from every other scanner.
- Ownership. Findings land on the Asset that owns the image, where they can be assigned, commented on, risk-accepted, or pushed to Jira.
- One view of risk. Image CVEs sit next to SAST, SCA, and DAST findings for the same Asset, so reporting covers the whole application, not just its code.
How This Integration Works
DefectDojo imports Clair results with the Clair Scan scan type. The parser accepts two JSON layouts and detects which one it has been given automatically.
1. Produce a JSON report. With clair-scanner, point it at your Clair server and write a report file:
clair-scanner --clair=http://clair:6060 --ip=<host-ip> -r clair-report.json myapp:1.4.2
With Klar, enable JSON output:
CLAIR_ADDR=http://clair:6060 JSON_OUTPUT=true klar registry.example.com/myapp:1.4.2 > klar-report.json
The parser treats a file with top-level image and vulnerabilities keys as a clair-scanner style report, and a file with LayerCount and a Vulnerabilities object grouped by severity as a Klar report. If you have moved to newer Clair tooling, confirm your report matches one of these layouts before wiring it into a pipeline.
2. Import it. In the UI, open the Engagement, choose Import Scan Results, select Clair Scan, and upload the file. For automation, use the API in Community Edition or DefectDojo Pro:
curl "https://YOUR_INSTANCE/api/v2/import-scan/" \
-H "Authorization: Token $DD_API_TOKEN" \
-F "scan_type=Clair Scan" \
-F "file=@clair-report.json" \
-F "product_name=myapp" \
-F "engagement_name=Container Builds" \
-F "auto_create_context=true"
DefectDojo Pro users can do the same from a pipeline with Universal Importer:
universal-importer import \
--defectdojo-url "https://YOUR_INSTANCE.cloud.defectdojo.com/" \
--scan-type "Clair Scan" \
--report-path "./clair-report.json" \
--product-name "myapp" \
--engagement-name "Container Builds" \
--auto-create-context
3. Reimport on every build. For an image you scan repeatedly, send later reports to /api/v2/reimport-scan/ against the same Test so fixed CVEs are closed and history is kept in one place.
Data Granularity: What Gets Imported
| DefectDojo Field | Source in Clair Report | Notes |
|---|---|---|
| Title | Vulnerability ID, package name, package version | Formatted as CVE-ID - (package, version) |
| Severity | Clair severity | Negligible becomes Info. Unknown becomes Info in clair-scanner reports; in Klar reports, Unknown and Defcon1 become Critical |
| Description | Clair description plus package details | Includes vulnerable package, installed version, fixed-by version, and namespace (clair-scanner) |
| Mitigation | Fixed-by version | Klar reports read "Please use version X of library Y", or note that no patch was found |
| References | Vulnerability link | Points to the distribution or NVD advisory |
| Vulnerability IDs | Vulnerability ID | Set for clair-scanner reports, enabling CVE-based filtering |
| Component Name / Version | Package name and version | Set for clair-scanner reports |
| CWE | Fixed at 1035 | Klar reports only (Vulnerable Third Party Component) |
| Finding type | Static | All Clair findings are marked static |
| Deduplication | Hashcode | Title, vulnerability IDs, description, severity |
Within a single report, the parser also collapses repeated entries for the same vulnerability and package, so one CVE in one package appears once per import.
Use Cases
In a CI/CD pipeline: Every image build runs clair-scanner and reimports the report into a Test named for the image. Engineers see the delta from the last build, and a release gate can query the DefectDojo API for new Critical findings while older, risk-accepted ones stay out of the way.
When patching base images: A platform team updates a shared base image and rebuilds 30 services. Reimporting each service's report shows which CVEs the update closed across the fleet and which ones remain because they live in application-installed packages.
During an audit: Container CVEs carry their discovery date, SLA status, and remediation history in DefectDojo, so an auditor can see how long Critical image vulnerabilities stayed open without anyone reconstructing it from pipeline logs.
Across many registries: Teams scanning images from several registries import each into the owning Asset, which gives security leads one report of container exposure by team instead of one per registry.
Operational Tips
- Use one Test per image (or per image and tag stream) and reimport into it. Importing every scan as a new Test works, but you lose the clean open-to-mitigated history.
- Know your client's severity mapping. Klar reports promote Unknown and Defcon1 to Critical, while clair-scanner reports drop Unknown to Info. Choose one client per pipeline so SLAs stay consistent.
- Set
minimum_severityon import if Negligible and Low OS package CVEs would bury your team. You can always lower the threshold later. - Tag imports with the image name and tag (for example
tags=myapp,1.4.2) so findings can be filtered by release. - Risk-accept CVEs that have no fix available in your distribution with an expiration date, so they come back for review when a patch is likely to exist.
- Klar reports don't populate vulnerability IDs or component fields, so if CVE-level reporting matters to you, prefer the clair-scanner report layout.