litellm/.github/workflows/image-scan.yml
yuneng-jiang ae2f276d19
ci(image-scan): match Python packages against CPE data (#34136)
grype defaults match.python.using-cpes to false, so PyPI packages are
matched only against the GitHub Advisory Database. When a CVE is
published to NVD but its GHSA has not propagated to the global advisory
database, the scan reports clean even though grype's own database
already carries the NVD record with the correct version ranges.

The pypdf CVEs (CVE-2026-59935 / 59936 / 59937 / 59938, analyzed in NVD
since 2026-07-08) are the case that exposed this; their GHSA IDs are
still repo-level and return 404 from the global advisory API, so the
ecosystem matcher has nothing to match on.

Enabling CPE matching for Python closes that gap. Measured against a
v1.91.1 build the finding count goes from 28 to 38; the additions are
mostly actionable, and the few cross-product CPE collisions cannot fail
the build because --only-fixed drops the ones carrying no fix version
and the remainder land below the --fail-on high threshold.
2026-07-21 12:56:56 -07:00

67 lines
2.4 KiB
YAML

name: Image Scan
on:
pull_request:
branches:
- main
- litellm_internal_staging
- litellm_oss_branch
- "litellm_**"
paths:
- docker/Dockerfile.non_root
- uv.lock
- ui/litellm-dashboard/package-lock.json
- .github/workflows/image-scan.yml
schedule:
- cron: "41 6 * * *"
workflow_dispatch:
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
image-scan:
name: image-scan
runs-on: ubuntu-latest
if: >-
github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name == github.repository
timeout-minutes: 30
permissions:
contents: read
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
persist-credentials: false
- name: Download Grype v0.114.0
run: |
curl -fsSL --retry 3 -o "$RUNNER_TEMP/grype.tar.gz" \
https://github.com/anchore/grype/releases/download/v0.114.0/grype_0.114.0_linux_amd64.tar.gz
echo "edda0968d8827daab01d32b3cd7de192ae0915005e7bbfcfef9e68e79bc43343 $RUNNER_TEMP/grype.tar.gz" | sha256sum -c -
tar xzf "$RUNNER_TEMP/grype.tar.gz" -C "$RUNNER_TEMP" grype
chmod +x "$RUNNER_TEMP/grype"
# Dockerfile.non_root is the rootless variant we ship. The other
# Dockerfiles share the same wolfi base and apk set, so OS-layer coverage
# is the same; matrix-scan if those variants ever diverge.
- name: Build runtime image
run: docker build -f docker/Dockerfile.non_root -t litellm-image-scan:${{ github.sha }} .
# Scans the whole shipped artifact: OS/apk plus every language package
# baked into the image, including ones no lockfile declares (e.g. prisma's
# vendored node engine) that osv-scan cannot see. osv-scan stays the fast
# source-level gate; this is the customer's-eye-view backstop. Credential-
# free OSS, run as a pinned, checksum-verified binary; no GitHub Action
# dependency and no vendor SaaS callout.
- name: Scan image for fixable HIGH/CRITICAL CVEs
env:
GRYPE_MATCH_PYTHON_USING_CPES: "true"
run: |
"$RUNNER_TEMP/grype" litellm-image-scan:${{ github.sha }} \
--only-fixed \
--fail-on high \
--output table