Skip to content

Forced pip update raises supply chain safety concerns (and is often useless) #1346

Description

@mara004

Description:

As of v6.3.0, setup-python appears to unconditionally auto-update pip, without a dependency cooldown.
Even when a pip-version input is specified, setup-python seemingly updates pip to the latest version first, then installs the specified version.

This behavior raises supply chain safety concerns: If pip itself were subject to a supply chain attack,1 any callers of setup-python would be immediately affected during the attack window of opportunity.

Also, updating pip on user level seems pointless when virtual environments are used (which you should), because (AFAIK) venvs are initialized with python's bootstrap copy of pip, not site-packages pip.
However, note that merely not calling setup-python's updated pip does not resolve these concerns, because an attacker might upload only an sdist of pip, which would allow for install-time execution of the hypothetical attack.

To be clear, we all hope that this scenario stays entirely hypothetical, but the point is that setup-python is not following safety best practices here, rsp. does not even allow the caller to do so.

Proposed remediation:

  • Add an option to let the caller opt out of pip auto-updating entirely (given that it is pointless when a venv is used).
  • Subject the default behavior of auto-updating pip to a dependency cooldown, ideally configurable through an input.
    Suggested default: 3 days, like dependabot, or anything non-zero really.
  • When a pip-version is specified, install the given version right away without first updating to latest.

Note that updating pip itself with a cooldown is complicated by the fact that pip versions before 26 do not support --uploaded-prior-to, PIP_UPLOADED_PRIOR_TO etc.
This can basically be worked around by updating to a pinned and hash-checked version first, then updating with cooldown.
The following script shows how to do this: https://github.com/pypdfium2-team/pypdfium2/blob/811faae77f8fc90bc57832bc6400c65fd9f4fbee/utils/update_pip.py

Justification:
Supply chain safety, see the description above.

Are you willing able to submit a PR?

No, I am not a typescript programmer and not familiar with setup-python's internals.

Edit: Submitted actions/python-versions#406 after all, a simple patch to avoid possible setup-time code execution when updating pip. This should stuff a key loophole and allow an aware caller to be unaffected, but otherwise the issue still stands.

Footnotes

  1. Hypothetical and hopefully highly unlikely, but no project is per se immune to it, and you have to acknowledge that pip would be a very lucrative target for a supply chain attack, so downstream precautions seem important. ↩

Activity

  1. mara004 commented on Jul 27, 2026

    @mara004
    Author

    @woodruffw Seeing your activity on #1251, I would again be glad to hear your opinion on this matter

  2. woodruffw commented on Jul 27, 2026

    @woodruffw

    Yeah, it seems not ideal that setup-python would auto-update pip. Or rather, it seems like it would be okay to auto-update pip, but more conservatively than latest by default. A cooldown seems like an appropriate way to do that.

    With that said, I'm not sure I can find where that install happens? Is it this?

    async function installPip(pythonLocation: string) {
    const pipVersion = core.getInput('pip-version');
    // Validate pip-version format: major[.minor][.patch]
    const versionRegex = /^\d+(\.\d+)?(\.\d+)?$/;
    if (pipVersion && !versionRegex.test(pipVersion)) {
    throw new Error(
    `Invalid pip-version "${pipVersion}". Please specify a version in the format major[.minor][.patch].`
    );
    }
    if (pipVersion) {
    core.info(
    `pip-version input is specified. Installing pip version ${pipVersion}`
    );
    await exec.exec(
    `${pythonLocation}/python -m pip install --upgrade pip==${pipVersion} --disable-pip-version-check --no-warn-script-location`
    );
    }
    }

    If I'm reading that code correctly it should only install the user's requested version. But perhaps I'm missing something about how --upgrade works.

  3. mara004 commented on Jul 27, 2026

    @mara004
    Author

    I can't seem to find the spot either, but I believe installPip() isn't what auto-updates pip, because this always does pip==${pipVersion}, but the default behavior appears to be an unconditional update, not a pin.

  4. woodruffw commented on Jul 27, 2026

    @woodruffw

    Do you happen to be using PyPy or GraalPy? I think both of those have their own installPip() helpers that do perform an unconditional upgrade:

    async function installPip(pythonLocation: string) {
    core.info('Installing and updating pip');
    const pythonBinary = path.join(pythonLocation, 'python');
    await exec.exec(`${pythonBinary} -m ensurepip`);
    await exec.exec(
    `${pythonLocation}/python -m pip install --ignore-installed pip`
    );
    }

    (Not sure why, but that seems like a plausible source.)

  5. mara004 commented on Jul 27, 2026

    @mara004
    Author

    No, I'm using CPython. But FWIW, on CI I get a print Upgrading pip..., and searching this codebase for "Upgrading" using GH does not yield any findings:
    https://github.com/search?q=repo%3Aactions%2Fsetup-python+Upgrading&type=code

  6. woodruffw commented on Jul 27, 2026

    @woodruffw

    Weird. Can you share those CI logs?

  7. mara004 commented on Jul 27, 2026

    @mara004
    Author

    This, for instance:

    Version 3.8 was not found in the local cache
      Version 3.8 is available for downloading
      Download from "https://github.com/actions/python-versions/releases/download/3.8.10-8879978422/python-3.8.10-darwin-arm64.tar.gz"
      Extract downloaded archive
      /usr/bin/tar xz -C /Users/runner/work/_temp/1def420c-2bfa-49e1-a959-ba773a86af32 -f /Users/runner/work/_temp/df6e4332-99d4-4590-9358-7e86cf7e72e7
      Execute installation script
      Check if Python hostedtoolcache folder exist...
      Install Python binaries from prebuilt package
      installer: Package name is Python
      installer: Upgrading at base path /
      installer: The upgrade was successful.
      Create hostedtoolcach symlinks (Required for the backward compatibility)
      Create Python 3.8.10 folder
      Create additional symlinks (Required for the UsePythonVersion Azure Pipelines task and the setup-python GitHub Action)
      Upgrading pip...
      Looking in links: /var/folders/8j/sfr9qqcj73j4p6nhwcfpr0th0000gn/T/tmp_cntivvt
      Requirement already satisfied: setuptools in /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages (56.0.0)
      Requirement already satisfied: pip in /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages (21.1.1)
      Collecting pip
      Downloading pip-25.0.1-py3-none-any.whl (1.8 MB)
      Installing collected packages: pip
      Successfully installed pip-25.0.1
      Install OpenSSL certificates
      Collecting certifi
      Downloading certifi-2026.7.22-py3-none-any.whl.metadata (2.5 kB)
      Downloading certifi-2026.7.22-py3-none-any.whl ([13](https://github.com/pypdfium2-team/pypdfium2/actions/runs/30127505834/job/89594365423#step:3:14)6 kB)
      Installing collected packages: certifi
      Successfully installed certifi-2026.7.22
      Error: [notice] A new release of pip is available: 21.1.1 -> 25.0.1
      [notice] To update, run: python3.8 -m pip install --upgrade pip
      -- pip install --upgrade certifi
       -- removing any existing file or link
       -- creating symlink to certifi certificate bundle
       -- setting permissions
       -- update complete
      Create complete file
      pip-version input is specified. Installing pip version 25.0.1
      /Users/runner/hostedtoolcache/Python/3.8.10/arm64/bin/python -m pip install --upgrade pip==25.0.1 --disable-pip-version-check --no-warn-script-location
      Collecting pip==25.0.1
        Downloading pip-25.0.1-py3-none-any.whl.metadata (3.7 kB)
      Downloading pip-25.0.1-py3-none-any.whl (1.8 MB)
         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.8/1.8 MB 50.3 MB/s eta 0:00:00
      Installing collected packages: pip
        Attempting uninstall: pip
          Found existing installation: pip [21](https://github.com/pypdfium2-team/pypdfium2/actions/runs/30127505834/job/89594365423#step:3:22).1.1
          Uninstalling pip-21.1.1:
            Successfully uninstalled pip-21.1.1
      Successfully installed pip-25.0.1
      Successfully set up CPython (3.8.10)
    

    from https://github.com/pypdfium2-team/pypdfium2/actions/runs/30127505834/job/89594365423

  8. mara004 commented on Jul 27, 2026

    @mara004
    Author

    As you can see, we first get an Upgrading pip... run, and then a second pip-version input is specified. Installing pip version ... block.

    I shall build a better repro where the versions are not identical.

  9. woodruffw commented on Jul 27, 2026

    @woodruffw

    Ah, that suggests it's coming from a script in actions/python-versions.

    This looks like it:

    https://github.com/actions/python-versions/blob/54f797b5823ba87b833222de8bc48c405d525c72/installers/nix-setup-template.sh#L52-L57

    That's very surprising behavior IMO!

  10. mara004 commented on Jul 27, 2026

    @mara004
    Author
  11. mara004 commented on Jul 28, 2026

    @mara004
    Author

    A very easy change to add some safety might be adding --only-binary :all:1 to the pip upgrade command identified in #1346 (comment). That should prevent setup-time code execution, so workflows that never call setup-python's updated pip would be unaffected.

    Footnotes

    1. i.e. allow wheel distributions only, no sdists ↩

  12. v-HarithaVattikuti commented on Jul 28, 2026

    @v-HarithaVattikuti
    Contributor

    Thanks for the valuable discussion, @mara004 @woodruffw !
    We understand your concerns around supply chain safety and really appreciate the thorough analysis and proposed solutions.

    We'll take a closer look at the current pip auto-update behavior and explore what options are feasible, keeping safety and user experience in mind. We'll follow up here once we have more clarity on the path forward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature requestNew feature or request to improve the current logic

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions