pip package versions are not pinned

Installing Python packages without versions can change the libraries included in a Docker build.

Description

A command such as pip install connexion can download different versions as the package repository changes. Pin actual package arguments in both shell and JSON exec forms of pip and pip3 install.

Python packages often have transitive dependencies, so even small updates can change behavior. Values for options such as --index-url and --trusted-host, including URLs, are not package names. The packages supplied alongside those options still need package==version specifications.

Potential impact

  • Different Python packages can be installed between builds.
  • Dependency changes can cause runtime errors.
  • Test and production environments can contain different libraries.

Remediation

  • Specify versions, as in pip install connexion==3.1.0.
  • Manage a reviewed requirements file with pinned versions, including transitive dependencies.
  • Test the application before applying version updates.

Examples

The versions below illustrate pinning syntax; they are not current security recommendations. Manage the base image and transitive dependencies too, and continue applying security updates.

Before

dockerfile
FROM python:3.12-slim
RUN pip install connexion
RUN pip3 install requests

After

dockerfile
FROM python:3.12-slim
RUN pip install --upgrade pip==24.2 connexion==3.1.0
RUN pip3 install --no-cache-dir requests==2.32.3

Explanation:

  • Before: Unspecified versions allow repository changes to affect image contents.
  • After: == specifies the versions installed directly. These commands do not pin every transitive dependency.

References