back to scripts

cyber-cve-scan.sh

bash 65 lines secrets redacted

Runs Trivy against every running Docker image to surface known CVEs — report-only, against live vulnerability databases.

Note Live script from my home-lab server. Tokens, IDs, phone numbers and other secrets have been replaced with placeholders like <WHATSAPP_GROUP_ID> — everything else is the real, running code.
#!/usr/bin/env bash
set -euo pipefail

REPORT_DIR=/home/lanky/reports
CACHE_DIR=/home/lanky/reports/trivy-cache
GROUP_ID=<WHATSAPP_GROUP_ID>
mkdir -p "$REPORT_DIR" "$CACHE_DIR"
stamp=$(date +%Y%m%d-%H%M%S)
report="$REPORT_DIR/cyber-cve-scan-$stamp.txt"
latest="$REPORT_DIR/cyber-cve-scan-latest.txt"
json_report="$REPORT_DIR/cyber-cve-scan-$stamp.json"

images=$(docker ps --format '{{.Image}}' | sort -u)
{
  echo "Cyber Analyst CVE scan"
  echo "Generated: $(date -Is)"
  echo "Scanner: aquasec/trivy container"
  echo "Scope: currently running Docker images"
  echo "Note: This scan uses Internet CVE databases and is report-only."
  echo
  echo "Images:"
  printf '%s\n' $images | sed 's/^/- /'
  echo
} > "$report"

scan_status=0
: > "$json_report"
for image in $images; do
  safe=$(printf '%s' "$image" | tr '/:@' '___')
  image_json="$REPORT_DIR/trivy-$safe-$stamp.json"
  echo "== $image ==" >> "$report"
  if timeout 20m docker run --rm \
      -v /var/run/docker.sock:/var/run/docker.sock \
      -v "$CACHE_DIR:/root/.cache/" \
      aquasec/trivy:latest image \
      --quiet \
      --scanners vuln \
      --severity HIGH,CRITICAL \
      --format table \
      "$image" >> "$report" 2>&1; then
    :
  else
    scan_status=1
    echo "Scan failed or timed out for $image" >> "$report"
  fi
  docker run --rm \
      -v /var/run/docker.sock:/var/run/docker.sock \
      -v "$CACHE_DIR:/root/.cache/" \
      aquasec/trivy:latest image \
      --quiet \
      --scanners vuln \
      --severity HIGH,CRITICAL \
      --format json \
      "$image" > "$image_json" 2>/dev/null || true
  printf '{"image":%s,"report":%s}\n' "$(printf '%s' "$image" | jq -Rs .)" "$(cat "$image_json" | jq -c . 2>/dev/null || echo null)" >> "$json_report"
  echo >> "$report"
done

cp "$report" "$latest"
crit_high=$(grep -E 'CRITICAL|HIGH' "$report" | wc -l || true)
summary=$(/home/lanky/scripts/person-friendly-report.sh cve "$report")
if [ -z "${SERVICE_DESK_JOB_ID:-}" ]; then
  sudo -u lanky openclaw message send --channel whatsapp --account default --target "$GROUP_ID" --message "$summary" --media "$report" --force-document >/dev/null 2>&1 || true
fi
exit "$scan_status"

back to scripts