Remediate a vulnerable Python package

A vulnerable package is a package version that a published security advisory lists as affected. Posit Package Manager gets the advisories from the Open Source Vulnerabilities (OSV) database. It does not scan the code of packages. To fix the problem, update the package in your project to a fixed version. A fixed version is a version that the advisory does not list as affected.

This page shows how to remediate a vulnerable Python package with pip or uv. If your project is also on Posit Connect, follow the steps in Update content on Connect after the update.

For R packages, see Remediate a vulnerable R package.

Before you start

You need this information:

  • The name of the package, for example jinja2
  • The version that your project uses, for example 3.0.3
  • A fixed version, for example 3.1.6

If you do not have a fixed version, see Find a fixed version.

Then find the tool that installs the packages of your project. The steps on this page have a tab for each tool:

  • If the project folder has a uv.lock file, select the uv tab.
  • If you install packages with uv pip, select the pip tab. Use uv pip in place of python -m pip.
  • Otherwise, select the pip tab.

To see the version that your project uses, open a terminal in the project folder. Then run the command for your tool.

If the project has a virtual environment, activate it first. See step 1 in the pip tab of Update the package in your project. Then run this command:

Terminal
python -m pip show jinja2

Run this command:

Terminal
uv pip show jinja2

The Version line shows the version.

Update the package in your project

Complete the following steps in the environment where you work on the project. This can be Posit Workbench, Positron, VS Code, or a terminal on your computer.

  1. Open a terminal in the project folder. Then find out whether the project has a virtual environment. A virtual environment is a folder that holds the packages of one project. Its name is often .venv or venv.

    If the project has a virtual environment, activate it. On macOS or Linux, run this command for a virtual environment in the .venv folder:

    Terminal
    source .venv/bin/activate

    On Windows, run this command in PowerShell:

    PowerShell
    .venv\Scripts\Activate.ps1

    In the Windows Command Prompt, run this command:

    Command Prompt
    .venv\Scripts\activate.bat

    If the folder has a different name, use that name in place of .venv.

    If the project uses a conda environment, activate it with conda activate and the name of the environment. This page does not cover packages that conda installs. If conda installed the package, update it with conda.

    If the project does not have a virtual environment, python -m pip uses the Python that the python command starts. Make sure that this is the Python that runs the project. The output of this command shows the folder where that Python keeps its packages:

    Terminal
    python -m pip --version
  2. Set the fixed version in the requirements.txt file of the project. If the project does not have this file, go to step 3.

    If a tool made the file, do not edit the file. The first lines of the file name the tool, for example pip-compile. These lines also show the command that made the file.

    If the source file, for example requirements.in, has an old version of the package, change that line. Then run the command again, and add --upgrade-package jinja2. For example, for a file that shows pip-compile --generate-hashes requirements.in, run this command:

    Terminal
    pip-compile --generate-hashes --upgrade-package jinja2 requirements.in

    If you wrote the file yourself, edit it:

    • If the file has a line for the package, change the version on that line to the fixed version. Also change a line with no version or with a range, such as jinja2 or jinja2>=3.0. pip does not update an installed package when its version satisfies the line.
    • If the file does not have a line for the package, add a line. Without a line, pip keeps the old version.

    Use one of these lines:

    • jinja2>=3.1.6 installs the newest version that is 3.1.6 or later.
    • jinja2==3.1.6 installs exactly 3.1.6.
    Important

    Do not run only python -m pip install --upgrade jinja2 in a project that has a requirements.txt file. That command updates the package, but not the file. The next python -m pip install -r requirements.txt installs the old version again.

  3. If the project has a requirements.txt file, install the packages from the file:

    Terminal
    python -m pip install -r requirements.txt

    If the project does not have a requirements.txt file, install the fixed version directly:

    Terminal
    python -m pip install "jinja2>=3.1.6"

    To install exactly 3.1.6, use this command instead:

    Terminal
    python -m pip install "jinja2==3.1.6"

    If the command shows No module named pip, the virtual environment does not have pip. Use uv pip in place of python -m pip.

    If the command shows externally-managed-environment, see pip shows an externally-managed-environment error.

  4. Restart Python, the Jupyter kernel, or the app that uses the package. Then make sure that the project has the fixed version or a later version. The Version line of this command must show that version:

    Terminal
    python -m pip show jinja2
  5. If you keep the project in Git, commit the changed requirements.txt file. Other people who use the project then pull the commit and run this command:

    Terminal
    python -m pip install -r requirements.txt
  6. If the project is also on Connect, follow the steps in Update content on Connect.

  1. Open a terminal in the project folder.

  2. Look for the package in the dependencies list of pyproject.toml.

    If pyproject.toml lists the package, set the fixed version there. This command updates pyproject.toml and uv.lock, and installs the new version:

    Terminal
    uv add "jinja2>=3.1.6"

    jinja2>=3.1.6 installs the newest version that is 3.1.6 or later. To install exactly 3.1.6, use this command instead:

    Terminal
    uv add "jinja2==3.1.6"

    Both commands stop a later update from installing the old version again.

    If pyproject.toml does not list the package, another package needs it. Update the vulnerable package with the uv lock command, then install it with the uv sync command:

    Terminal
    uv lock --upgrade-package jinja2
    uv sync

    To install one exact version, use these commands instead:

    Terminal
    uv lock --upgrade-package jinja2==3.1.6
    uv sync
  3. Restart Python, the Jupyter kernel, or the app that uses the package. Then make sure that the project has the fixed version or a later version:

    Terminal
    uv pip show jinja2
  4. If the project also has a requirements.txt file, write the file again from uv.lock. If you made the file with uv export before, run the same command again. If not, use this command:

    Terminal
    uv export --format requirements-txt --no-hashes --no-dev --no-emit-project -o requirements.txt
  5. If you keep the project in Git, commit the changed pyproject.toml, uv.lock, and requirements.txt files. Other people who use the project then pull the commit and run this command:

    Terminal
    uv sync
  6. If the project is also on Connect, follow the steps in Update content on Connect.

Update content on Connect

Connect runs the package versions that you published. Connect does not update them. To fix content on Connect, update the source project and publish it again. For all the ways to publish, see Publishing with Connect.

To publish the content again, you must be the owner of the content or a collaborator on it. If you are a viewer of the content, send these steps to the owner of the content. The content list in Connect shows the owner of each content item.

  1. Find the source project of the content. This is the folder that you published from. For Git-backed content, it is the Git repository of the content.

  2. Update the package in the source project. Do the steps in Update the package in your project, in the tab for your tool.

  3. If the project has a requirements.txt file, the publishing tools use it. Make sure that the file shows the fixed version. An old requirements.txt file next to a new uv.lock file deploys the old version.

  4. Publish the content again to the same location on Connect. Use the method that you used before:

    • Publish in Positron or VS Code: publish again, and select the same content.

    • rsconnect-python: run the same rsconnect deploy command as before.

    • Git-backed content: run the same rsconnect write-manifest command as before, with the --overwrite option. Then commit manifest.json and the changed dependency files, and push the commit.

      Connect deploys the new commit at its next check. To deploy the commit immediately, see Updating content.

  5. In Connect, open the content settings and select the Source tab. Make sure that the Python Packages section shows the fixed version or a later version. The Python environment restore log of the active bundle also shows the installed packages.

If Connect still shows the old version after you publish, an administrator can manage the packages of this content on the Connect server. Send the content name, the package name, and the fixed version to your Connect administrator.

Find a fixed version

A fixed version is usually the first version with the fix, or a later version.

  1. In Package Manager, search for the package and open its page.

  2. Open the Other Versions tab. A version that a known vulnerability affects shows a vulnerability count badge. If you do not see your version, click Get more releases.

  3. Select a version that is newer than your version and that no known vulnerability affects. Do not select a version with a, b, rc, or dev in its number. These are pre-release versions.

If your version has a known vulnerability but no badge, Package Manager does not have the data for this vulnerability. The lack of a badge on other versions does not guarantee that no vulnerability affects them. Read the advisory.

An advisory is the public report of the problem. The OSV database is one source of advisories. Search osv.dev for the package name or the advisory ID. Then open the advisory for PyPI. It lists the affected versions and the version that has the fix.

Sometimes there is no clean fix:

  • No version has the fix. All versions have the problem.
  • The fix is only in a new major version. A new major version can change how the package works, and your code can stop working.
  • The fix is only in a pre-release version. A pre-release version is a test version before the official release.

In each of these cases, ask your security team or your administrator what to do.

Troubleshooting

The update keeps the old version or cannot find the fixed version

Each tool shows a different result:

  • pip shows ERROR: No matching distribution found.
  • uv add shows that the requirements are unsatisfiable.
  • uv lock --upgrade-package keeps the old version and shows no error.

Your repository URL can have a snapshot date that is before the fix. A repository URL with a date gives only the packages that were available on that date.

A date setting has the same result. uv has the exclude-newer setting, and pip 26.0 and later has the uploaded-prior-to setting.

For example, https://packagemanager.example.com/pypi/2024-12-01/simple is a URL with a date. Look for a URL with a date or for a date setting in these files that you own:

  • The --index-url line in requirements.txt
  • The [[tool.uv.index]] table or the exclude-newer setting in pyproject.toml
  • Your uv configuration file ~/.config/uv/uv.toml
  • Your pip configuration, which you set with pip config set, for example global.index-url or global.uploaded-prior-to

To see which file sets each pip value, run this command:

Terminal
python -m pip config debug

Then do one of these steps:

  • If the URL or the date setting is in a file that you own, change the date to a later date, or change the URL to latest. Then update the package again. See Obtaining a repository URL and Restricting packages by release date.
  • If the URL or the date setting is not in a file that you own, an administrator set it for the server, for example in /etc/uv/uv.toml or /etc/pip.conf. Ask that administrator for help. The date can be a rule of your organization.

The repository does not have the fixed version

Your Package Manager administrator can block a package version, or limit the packages in a repository. Ask your Package Manager administrator for help.

pip shows an externally-managed-environment error

If you install a package in the Python of your operating system without a virtual environment, pip shows error: externally-managed-environment. Your operating system manages the packages of that Python.

Make a virtual environment for the project in the project folder, and activate it. On macOS or Linux, run these commands:

Terminal
python3 -m venv .venv
source .venv/bin/activate

On Windows, see step 1 in the pip tab of Update the package in your project for the activate command.

Then install all the packages of the project in the virtual environment. If the project has a requirements.txt file, run this command:

Terminal
python -m pip install -r requirements.txt

Then do the steps in the pip tab of Update the package in your project again.

Warning

Do not use the --break-system-packages option. It can stop programs of the operating system from working.

Another package needs the old version

Another package of your project can need the old version. Each tool shows a different result:

  • python -m pip install -r requirements.txt and uv add stop with an error that names that package.

  • python -m pip install "jinja2>=3.1.6" installs the new version, but shows ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. The next lines name the package that needs the old version. That package can stop working.

  • uv lock --upgrade-package jinja2 keeps the old version with no error. To see the error, give the fixed version:

    Terminal
    uv lock --upgrade-package jinja2==3.1.6

Update the package that the error names too, then update the vulnerable package again. If the error continues, ask your administrator for help.

Publishing fails because Connect cannot find the fixed version

Connect can use its own repository URL. That URL can have a snapshot date that is before the fix. Send the package name, the fixed version, and the error message to your Connect administrator.

Back to top