diff --git a/Dockerfile b/Dockerfile index 3b9d3d185..67de92ad9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -50,7 +50,11 @@ WORKDIR /app # Install runtime system dependencies (ffmpeg and nodejs v22) # gnupg is required for the Nodesource setup script -RUN apt-get update && apt-get install -y --no-install-recommends \ +# `apt-get upgrade` applies Debian security updates the pinned base layer +# predates (e.g. zlib1g 1:1.3.dfsg+really1.3.1-1+deb13u1, DSA-6613-1). +# Refs #2396: fixable OS findings must not ride in from a stale base layer. +RUN apt-get update && apt-get upgrade -y --no-install-recommends \ + && apt-get install -y --no-install-recommends \ ffmpeg \ curl \ gnupg \ @@ -72,6 +76,13 @@ RUN rm -rf /usr/local/lib/python3.11/site-packages/* COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages COPY --from=builder /usr/local/bin /usr/local/bin +# pip is an install-time tool. Its wheel ships pip/_vendor/bom.cdx.json, which +# declares the vendored pkg_resources as setuptools 70.3.0, so image scanners +# report CVE-2025-47273 against it even on the newest pip (26.3). The served +# app (uvicorn) never invokes pip, so drop it from the runtime image. +# Refs #2396. +RUN python -m pip uninstall --yes pip + # Copy installed Node.js packages from builder COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/apps/web/node_modules ./apps/web/node_modules diff --git a/tests/unit/test_merge_policy_security_scan_wording.py b/tests/unit/test_merge_policy_security_scan_wording.py index c1fc023b2..736a1f865 100644 --- a/tests/unit/test_merge_policy_security_scan_wording.py +++ b/tests/unit/test_merge_policy_security_scan_wording.py @@ -100,6 +100,27 @@ def test_image_build_constrains_python_and_runtime_npm(self): self.assertIn("npm install --global npm@12.2.2", dockerfile) self.assertIn("rm -rf /usr/local/lib/python3.11/site-packages/*", dockerfile) + def test_runtime_stage_applies_debian_security_updates(self): + root = Path(__file__).resolve().parents[2] + dockerfile = (root / "Dockerfile").read_text() + runtime = dockerfile.split("AS runtime", 1)[1] + update = runtime.index("apt-get update") + upgrade = runtime.index("apt-get upgrade -y --no-install-recommends") + install = runtime.index("apt-get install -y --no-install-recommends") + self.assertLess(update, upgrade) + self.assertLess(upgrade, install) + self.assertNotIn("dist-upgrade", runtime) + + def test_runtime_stage_drops_pip_after_copying_builder_env(self): + root = Path(__file__).resolve().parents[2] + dockerfile = (root / "Dockerfile").read_text() + runtime = dockerfile.split("AS runtime", 1)[1] + copy_bin = runtime.index("COPY --from=builder /usr/local/bin /usr/local/bin") + uninstall = runtime.index("RUN python -m pip uninstall --yes pip") + self.assertLess(copy_bin, uninstall) + self.assertLess(uninstall, runtime.index("USER appuser")) + self.assertNotIn("pip install", runtime) + if __name__ == "__main__": unittest.main()