#!/bin/bash # Trivy CVE scanner integration — probed by the WebUI updater generator. # --------------------------------------------------------------------------- # Trivy (containers/trivy) runs as a long-lived server whose vulnerability DB # is cached in ./trivy-cache. On first start — and periodically after — that DB # is downloaded (tens of MB). Until it lands NO scan can produce results, so the # WebUI must not paint a green "no known vulnerabilities" all-clear yet. # # These helpers expose the scanner's state so the updater generator can write an # honest cves.json: # absent — Trivy isn't installed/running (CVE scanning unavailable) # db_updating — installed, but the vulnerability DB is still downloading # ready — DB present; real per-image scans can run # Every helper degrades to the safe answer (absent / [] ) on any error so the # generator never breaks. # Echoes: absent | db_updating | ready trivyScannerState() { # Not in `docker ps` (never installed, stopped, or still starting) -> absent. dockerCommandRun "docker ps --format '{{.Names}}'" 2>/dev/null \ | grep -qx trivy-service || { echo absent; return; } # Running: the DB is ready once `trivy version` reports a VulnerabilityDB # block (null/absent while it's still being fetched on first boot). local ver; ver="$(dockerCommandRun "docker exec trivy-service trivy version -f json" 2>/dev/null)" [ -n "$ver" ] || { echo db_updating; return; } if command -v jq >/dev/null 2>&1; then [ -n "$(printf '%s' "$ver" | jq -r '.VulnerabilityDB // empty' 2>/dev/null)" ] \ && echo ready || echo db_updating else # No jq: DownloadedAt only appears once the DB is present (and, unlike a # nullable VulnerabilityDB key, avoids a false positive when it's null). printf '%s' "$ver" | grep -q '"DownloadedAt"' \ && echo ready || echo db_updating fi } # Echoes the DB's UpdatedAt timestamp (ISO8601) if available, else nothing. trivyDbUpdatedAt() { command -v jq >/dev/null 2>&1 || return 0 local ver; ver="$(dockerCommandRun "docker exec trivy-service trivy version -f json" 2>/dev/null)" [ -n "$ver" ] || return 0 printf '%s' "$ver" | jq -r '.VulnerabilityDB.UpdatedAt // empty' 2>/dev/null } # trivyScanImageCves — scan one image against the cached DB and echo a # JSON array of normalized CVE objects for the WebUI: # [ { id, severity, package, installed, fixed_in, url }, ... ] # Echoes [] on any failure so a single bad image never aborts the whole scan. # Requires jq (the generator already guards on it). trivyScanImageCves() { local image="$1" [ -n "$image" ] || { echo '[]'; return; } command -v jq >/dev/null 2>&1 || { echo '[]'; return; } # Scan runs inside the server container, reusing its cached DB. --quiet keeps # the progress spinner out of stdout; we only want CRITICAL..LOW findings. local raw raw="$(dockerCommandRun "docker exec trivy-service trivy image --quiet --scanners vuln --format json --severity CRITICAL,HIGH,MEDIUM,LOW '$image'" 2>/dev/null)" [ -n "$raw" ] || { echo '[]'; return; } printf '%s' "$raw" | jq -c ' [ (.Results // [])[] | (.Vulnerabilities // [])[] | { id: .VulnerabilityID, severity: ((.Severity // "UNKNOWN") | ascii_downcase), package: .PkgName, installed: (.InstalledVersion // ""), fixed_in: (.FixedVersion // ""), url: (.PrimaryURL // "") } ] | unique_by(.id + "|" + (.package // "")) ' 2>/dev/null || echo '[]' }