From 38646fdac2481ffc48b22c70a715c793fba19c33 Mon Sep 17 00:00:00 2001 From: Adil Hafeez Date: Fri, 13 Feb 2026 19:53:49 -0800 Subject: [PATCH] Add Trivy Docker security scan to CI (#755) * Add Trivy Docker image security scan workflow Scans the Docker image for CRITICAL and HIGH vulnerabilities using Trivy. Blocks PRs on failures; runs non-blocking on main for visibility. Results are uploaded to the GitHub Security tab via SARIF. * Add explicit permissions to Docker security scan workflow Set minimal permissions: contents read for checkout, security-events write for SARIF upload to the GitHub Security tab. Co-Authored-By: Claude Opus 4.6 * Fix 27 HIGH vulnerabilities found by Trivy Docker scan - Install supervisor via pip instead of apt to eliminate 22 Debian python3.13 package vulnerabilities - Pin urllib3>=2.6.3 to fix CVE-2025-66418, CVE-2025-66471, CVE-2026-21441 - Add ignore-unfixed to Trivy scan to suppress unfixable glibc CVE-2026-0861 Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- .github/workflows/docker-security-scan.yml | 56 ++++++++++++++++++++++ Dockerfile | 7 ++- cli/pyproject.toml | 1 + cli/uv.lock | 8 ++-- 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/docker-security-scan.yml diff --git a/.github/workflows/docker-security-scan.yml b/.github/workflows/docker-security-scan.yml new file mode 100644 index 00000000..03150064 --- /dev/null +++ b/.github/workflows/docker-security-scan.yml @@ -0,0 +1,56 @@ +name: Docker Security Scan + +env: + DOCKER_IMAGE: katanemo/plano + +on: + push: + branches: + - main + pull_request: + +permissions: + contents: read + security-events: write + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Build Docker Image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64 + push: false + tags: ${{ env.DOCKER_IMAGE }}:scan + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ env.DOCKER_IMAGE }}:scan + format: table + # Fail on PRs so vulnerabilities block merge; on main just report + exit-code: ${{ github.event_name == 'pull_request' && '1' || '0' }} + ignore-unfixed: true + severity: CRITICAL,HIGH + + - name: Run Trivy scanner (SARIF for GitHub Security tab) + if: always() + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ env.DOCKER_IMAGE }}:scan + format: sarif + output: trivy-results.sarif + ignore-unfixed: true + severity: CRITICAL,HIGH + + - name: Upload Trivy results to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: trivy-results.sarif diff --git a/Dockerfile b/Dockerfile index c6f9b7ce..4f49f4a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,9 +46,11 @@ FROM python:3.13.11-slim AS arch RUN set -eux; \ apt-get update; \ - apt-get install -y --no-install-recommends supervisor gettext-base curl; \ + apt-get install -y --no-install-recommends gettext-base curl; \ apt-get clean; rm -rf /var/lib/apt/lists/* +RUN pip install --no-cache-dir supervisor + # Remove PAM packages (CVE-2025-6020) RUN set -eux; \ dpkg -r --force-depends libpam-modules libpam-modules-bin libpam-runtime libpam0g || true; \ @@ -70,6 +72,7 @@ RUN uv run pip install --no-cache-dir . COPY cli/planoai planoai/ COPY config/envoy.template.yaml . COPY config/plano_config_schema.yaml . +RUN mkdir -p /etc/supervisor/conf.d COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf COPY --from=wasm-builder /arch/target/wasm32-wasip1/release/prompt_gateway.wasm /etc/envoy/proxy-wasm-plugins/prompt_gateway.wasm @@ -81,4 +84,4 @@ RUN mkdir -p /var/log/supervisor && \ /var/log/access_ingress.log /var/log/access_ingress_prompt.log \ /var/log/access_internal.log /var/log/access_llm.log /var/log/access_agent.log -ENTRYPOINT ["/usr/bin/supervisord"] +ENTRYPOINT ["/usr/local/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/cli/pyproject.toml b/cli/pyproject.toml index 85e35713..84af6ca3 100644 --- a/cli/pyproject.toml +++ b/cli/pyproject.toml @@ -14,6 +14,7 @@ dependencies = [ "questionary>=2.1.1,<3.0.0", "pyyaml>=6.0.2,<7.0.0", "requests>=2.31.0,<3.0.0", + "urllib3>=2.6.3", "rich>=14.2.0", "rich-click>=1.9.5", ] diff --git a/cli/uv.lock b/cli/uv.lock index f8f72721..5f18604b 100644 --- a/cli/uv.lock +++ b/cli/uv.lock @@ -350,6 +350,7 @@ dependencies = [ { name = "requests" }, { name = "rich" }, { name = "rich-click" }, + { name = "urllib3" }, ] [package.optional-dependencies] @@ -375,6 +376,7 @@ requires-dist = [ { name = "requests", specifier = ">=2.31.0,<3.0.0" }, { name = "rich", specifier = ">=14.2.0" }, { name = "rich-click", specifier = ">=1.9.5" }, + { name = "urllib3", specifier = ">=2.6.3" }, ] provides-extras = ["dev"] @@ -742,11 +744,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.5.0" +version = "2.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] [[package]]