Skip to main content
ship it and sleep

Trivy, OWASP Dependency-Check, and CodeQL Integration

3 min read Chapter 47 of 66

Trivy, OWASP Dependency-Check, and CodeQL Integration

The Failure

The team ran Trivy on the checkout-service container image and found zero vulnerabilities. They celebrated. Then a penetration tester found a critical CVE in a Go dependency that Trivy missed because the binary was statically compiled with CGO_ENABLED=0 and Trivy’s filesystem scanner was not configured for Go binaries. They needed both container scanning and filesystem scanning to get full coverage.

Each scanner has blind spots. Trivy excels at container images and IaC. CodeQL excels at source-level logic bugs. OWASP Dependency-Check catches CVEs that Trivy might miss in certain language ecosystems. Layer them.

The Mechanism

Scanner Coverage Matrix

ScannerGoJavaNode.jsContainerK8s YAML
Trivy (image)-
Trivy (fs)--
Trivy (config)----
CodeQL--
OWASP DC---
Gitleakssecrets in any language

SARIF Integration

All scanners output SARIF (Static Analysis Results Interchange Format). GitHub’s Security tab aggregates SARIF from all sources into a unified view.

The Implementation

Per-Service Trivy Configuration

# catalog-service/.github/workflows/security.yml
# HARDENED: Trivy filesystem scan for Node.js dependencies
- name: Trivy filesystem scan
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: "fs"
    scan-ref: "."
    format: "sarif"
    output: "trivy-fs.sarif"
    severity: "CRITICAL,HIGH"
    exit-code: "1"
    vuln-type: "library"
# checkout-service/.github/workflows/security.yml
# HARDENED: Trivy for Go binaries
- name: Build Go binary
  run: CGO_ENABLED=0 go build -o checkout-service .

- name: Trivy binary scan
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: "rootfs"
    scan-ref: "."
    format: "sarif"
    output: "trivy-rootfs.sarif"
    severity: "CRITICAL,HIGH"
    exit-code: "1"

OWASP Dependency-Check for Java Services

# payments-service/.github/workflows/security.yml
# HARDENED: OWASP Dependency-Check for Java
- name: OWASP Dependency-Check
  uses: dependency-check/Dependency-Check_Action@main
  with:
    project: "payments-service"
    path: "."
    format: "SARIF"
    args: >-
      --failOnCVSS 7
      --suppression suppression.xml
      --nvdApiKey ${{ secrets.NVD_API_KEY }}

- name: Upload OWASP DC SARIF
  uses: github/codeql-action/upload-sarif@v3
  if: always()
  with:
    sarif_file: reports/dependency-check-report.sarif

Suppression File for Known Exceptions

<!-- suppression.xml -->
<!-- HARDENED: Tracked suppressions with expiration -->
<suppressions>
  <suppress>
    <notes>
      False positive: test dependency not in runtime classpath.
      Tracking: JIRA-1234. Expires: 2025-06-01.
    </notes>
    <cve>CVE-2024-12345</cve>
  </suppress>
</suppressions>

Scheduled Full Database Scan

# .github/workflows/scheduled-scan.yml
# HARDENED: Weekly full scan with updated vulnerability databases
name: Scheduled Security Scan
on:
  schedule:
    - cron: "0 6 * * 1"

jobs:
  full-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Trivy full scan (all severities)
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: "fs"
          format: "sarif"
          output: "trivy-full.sarif"
          severity: "CRITICAL,HIGH,MEDIUM"
          exit-code: "0" # Advisory only for scheduled

      - name: Upload results
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: "trivy-full.sarif"

The Gate

PR scans gate on CRITICAL and HIGH. Scheduled scans report on MEDIUM as advisories. This two-tier approach prevents alert fatigue while catching the most dangerous vulnerabilities at merge time.

The Recovery

Scanner misses a vulnerability: No single scanner catches everything. Layer scanners. Run at least two: one for container images and one for source/dependencies.

NVD API rate limiting breaks OWASP DC: Cache the NVD database. Use --nvdDatafeedUrl to point to a locally hosted mirror or use the NVD API key with --nvdApiKey.

SARIF upload fails with “too many results”: Filter by severity before uploading. The GitHub Security tab has a limit of ~5000 results per SARIF file.