How to Audit All Python Virtual Environments for Compromised Packages Without Executing Python

The article describes a practical workflow for discovering whether any Python virtual environments contain known malicious package versions. The author maintains a registry of all .venv directories across local disks and external volumes using find commands and shell hooks. A Bash script then iterates through the registry and runs uv pip freeze against each environment to list installed dependencies without invoking the Python interpreter. This approach avoids risks highlighted by recent supply-chain attacks on packages such as LiteLLM, where even python -V or pip freeze could trigger malicious .pth files. The method also supports locating outdated packages, identifying usage of deprecated libraries, and searching project code for specific functions. Configuration settings like PIP_REQUIRE_VIRTUALENV=true and the uv tool further prevent accidental global installations.

HabrTranslated from Russian

Read at source

The growing number of supply-chain attacks targeting popular packages has prompted developers to seek reliable ways to audit their Python installations. Recent incidents affected the npm package Axios, the vulnerability scanner Trivy, and the Python package LiteLLM. In response, one developer created a system to locate every virtual environment on a machine and inspect its installed packages without ever executing Python code.

Virtual environments isolate project dependencies by storing symbolic links to the Python interpreter and site-packages inside a dedicated directory, conventionally named .venv. Because Python itself does not track created environments, the author maintains a personal registry file at ~/.venv_registry that is updated both by periodic find commands and by a shell function that records every newly created environment.

The registry enables a simple Bash script that reads each path, verifies the directory still exists, and then calls uv pip freeze --python against the environment’s interpreter. Because uv is written in Rust, it parses installation metadata without running any Python code—an important safeguard after the LiteLLM compromise demonstrated that even innocuous commands such as python --version could execute attacker-controlled .pth files.

The same registry supports additional security and maintenance tasks. It can be used to identify outdated package versions across all projects, locate remaining usages of libraries the developer wishes to retire, and search project source trees for calls to specific functions without scanning the entire disk.

Global installations outside virtual environments are prevented by setting PIP_REQUIRE_VIRTUALENV=true and by relying on uv, which refuses to modify the system Python unless the --system flag is explicitly supplied. The resulting workflow therefore provides both rapid detection of known malicious packages and ongoing hygiene for a large collection of Python projects on macOS.

Meridian digestThe week's most consequential security stories, distilled into one briefing.See the digest

More in Supply Chain & Open Source

Passwordless Kubernetes Image Pulling with Artifact Keeper via ServiceAccount Tokens
Malicious Code Injected into LiteLLM Open Source AI Package via Supply Chain Attack
GhostAction Campaign Compromises 790 GitHub Repositories via Malicious Workflows