CVE-2026-47781 Overview
CVE-2026-47781 is a code injection vulnerability [CWE-94] in PDM, a Python package and dependency manager. Versions up to and including 2.26.9 automatically load project-local plugins from a .pdm-plugins directory during initialization. An attacker-controlled file in an untrusted repository checkout can execute arbitrary Python code before any command is parsed. The vulnerability triggers even when running benign commands such as pdm --version. PDM version 2.27.0 contains the fix.
Critical Impact
Cloning or checking out a malicious repository and invoking any pdm command executes attacker code with the invoking user's privileges, with the strongest impact in CI/CD, automation, and privileged contexts.
Affected Products
- PDM (Python Development Master) versions up to and including 2.26.9
- CI/CD pipelines and automation workflows that invoke pdm against untrusted repositories
- Developer workstations checking out third-party PDM-managed projects
Discovery Timeline
- 2026-08-04 - CVE-2026-47781 published to NVD
- 2026-08-05 - Last updated in NVD database
Technical Details for CVE-2026-47781
Vulnerability Analysis
PDM implements a project-local plugin mechanism that loads plugins from a .pdm-plugins directory inside a project. The load_plugins() routine runs during Core.init(), which executes before command-line argument parsing. This ordering means every PDM invocation activates the plugin loader, regardless of the subcommand supplied. Any file placed under .pdm-plugins in a checked-out repository becomes attacker-controlled input to the interpreter.
The attack requires only that a user run pdm in a directory containing a malicious .pdm-plugins folder. Cloning an untrusted repository and executing a status command is sufficient. In continuous integration systems that automatically install project dependencies with PDM, exploitation is unattended and reaches build agents with production credentials.
Root Cause
The plugin loader passes .pdm-plugins to site.addsitedir(). Python's site module processes .pth files inside any added site directory. Lines in a .pth file that begin with import are executed immediately as Python code. Combining automatic plugin loading with .pth execution converts a repository file into an arbitrary code sink triggered by every pdm invocation.
Attack Vector
An attacker publishes or contributes a repository containing a .pdm-plugins/malicious.pth file whose first token is import. When a developer or CI job runs any pdm command in the checkout, Python evaluates the import line, executing attacker code with the privileges of the invoking user. The code runs before PDM parses arguments, so no specific subcommand or flag is required. Consult the GitHub Security Advisory GHSA-qq6c-99pv-prvf for the maintainer analysis.
Detection Methods for CVE-2026-47781
Indicators of Compromise
- Presence of a .pdm-plugins directory in repositories that do not legitimately ship PDM plugins.
- .pth files inside .pdm-plugins containing lines beginning with import that reference network, subprocess, or filesystem modules.
- Unexpected child processes of pdm such as shells, curl, wget, or Python one-liners in CI logs.
- Outbound network connections initiated by pdm invocations during dependency install or lock steps.
Detection Strategies
- Scan source repositories and CI workspaces for .pdm-plugins/*.pth files and treat any occurrence as suspicious until reviewed.
- Correlate PDM process starts with subsequent process, file, and network events on developer endpoints and build agents.
- Alert when pdm spawns interpreters or shells that were not invoked by the user's command line.
Monitoring Recommendations
- Ingest CI/CD runner telemetry into a centralized data lake and hunt for anomalous child processes of Python package managers.
- Baseline expected outbound destinations for build agents and flag deviations during PDM operations.
- Track PDM binary versions across developer fleets and fail builds when the installed version is below 2.27.0.
How to Mitigate CVE-2026-47781
Immediate Actions Required
- Upgrade PDM to version 2.27.0 or later on every developer workstation, container image, and CI runner.
- Audit existing repositories and cached workspaces for a .pdm-plugins directory and remove or quarantine unexpected entries.
- Restrict CI jobs that process untrusted pull requests from running pdm with production secrets until the upgrade is verified.
Patch Information
The PDM maintainers fixed the issue in release 2.27.0. Release notes and the corresponding commit are documented in the PDM 2.27.0 release notes. Pin the minimum PDM version in bootstrap scripts and container base images to prevent regression.
Workarounds
- Delete or refuse to check out .pdm-plugins directories in untrusted repositories before invoking pdm.
- Run pdm inside ephemeral, unprivileged containers with no access to secrets or persistent credentials until the upgrade is completed.
- Set PYTHONNOUSERSITE=1 and review any custom site configuration that could still process .pth files under the working directory.
# Upgrade PDM and verify the installed version meets the fixed release
pip install --upgrade 'pdm>=2.27.0'
pdm --version
# Fail-fast check for the vulnerable plugin directory before running pdm in CI
if [ -d ".pdm-plugins" ]; then
echo "Refusing to run pdm: untrusted .pdm-plugins directory present"
exit 1
fi
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

