Scanning with Trivy and Grype

Trivy 67,732 from Aqua Security (github.com/aquasecurity/trivy (https://github.com/aquasecurity/trivy 38,101 ), 0.74.0) and Grype 12,938 from Anchore 391,549 (github.com/anchore/grype (https://github.com/anchore/grype 12,938 ), 0.119.0) are Apache-2.0 scanners that need no account: each reads an image's OS packages and language manifests and matches them against a vulnerability database it caches locally. Scan BookNest's two images and the distroless base of Multi-Stage and BuildKit:

Vulnerability counts from Trivy and GrypeCSS
sev() { jq -r "[$1] | group_by(.) | map(\"\(.[0][0:1])=\(length)\") | join(\" \")"; }
T='.Results[].Vulnerabilities[]?.Severity'
for i in l3-booknest-api:latest l3-booknest-web:latest gcr.io/distroless/nodejs24-debian13; do
  printf '%-38s %s\n' "$i" "$(trivy image -q -f json "$i" | sev "$T")"; done
trivy image -q -f json --ignore-unfixed l3-booknest-api:latest | sev "$T"
trivy image -q -f json l3-booknest-api:latest | jq -r '.Results[] | select(.Type=="node-pkg")
  | .Vulnerabilities[].PkgPath' | cut -d/ -f1-5 | sort -u
grype -q l3-booknest-api:latest -o json | sev '.matches[].vulnerability.severity'
Output
l3-booknest-api:latest                 C=4 H=56 L=72 M=101 U=2
l3-booknest-web:latest                 H=1
gcr.io/distroless/nodejs24-debian13    L=7 M=15
H=4 M=5 U=1
usr/local/lib/node_modules/npm
C=7 H=57 L=10 M=80 N=58 U=16

The API image carries over two hundred findings (C, H, M, L and U for critical, high, medium, low and unknown), but only 10 have a fixed version yet, and none sit in BookNest's own express or pg: the Node.js 2,131 findings are all in the copy of npm 2,036 that the node:24-slim base bundles, which the running API never uses. The Alpine-based Nginx 75 image has one, and the distroless base none above medium. Grype's totals differ from Trivy's because the tools use different databases and severity sources (Grype adds Negligible), so pick one per pipeline and track its trend. In CI, fail the build on what you can act on: trivy image --exit-code 1 --severity CRITICAL --ignore-unfixed or grype --fail-on critical --only-fixed, and rebuild regularly so base-image fixes arrive.