PDM CVE-2026-47763
MEDIUMSeverity by source
Local vector and required user interaction reflect that victim must run PDM against an attacker-controlled checkout; PR:N because attacker needs no system privileges, only a shared repository; no confidentiality impact as only a write primitive is demonstrated.
Estimated by vuln.today — no official severity rating has been published for this CVE yet.
Lifecycle Timeline
2DescriptionCVE.org
Summary
PDM writes several project-local state or configuration files without symlink protection. If a malicious repository places those files as symlinks, local PDM operations can overwrite the symlink targets.
This creates an arbitrary file clobber primitive relative to the privileges of the invoking user.
Affected Behavior
- Project-local config writes can affect files outside the repository
- The most stable demonstrated sink is
pdm.toml - Related sinks include
.pdm-pythonand.python-version
Affected Code
src/pdm/project/config.py:303-350src/pdm/project/core.py:209-217src/pdm/cli/commands/use.py:187-189
Technical Details
Config.__init__() resolves the project-local pdm.toml path and _save_config() writes to the resolved target. If PROJECT_ROOT/pdm.toml is a symlink to another file, pdm config -l ... updates the target file instead of refusing the write.
The same general problem exists for other project-local persistence paths that are written directly with no lstat / O_NOFOLLOW protection.
For the pdm.toml PoC specifically, the target file must already contain parseable TOML. Otherwise the load step fails before the write path is reached. That parser constraint does not apply to the .pdm-python or .python-version sinks.
Impact
- Arbitrary file clobber as the invoking user
- Destructive modification of local files outside the repository root
- Useful primitive for privilege abuse when
pdmis run in elevated contexts
Reproduction
PoC:
# Replace this with a Python interpreter that can run `python -m pdm`.
PDM_PY=/path/to/python-with-pdm
tmpdir=$(mktemp -d)
target="$tmpdir/clobbered-target.toml"
cat > "$target" <<'EOF'
[seed]
value = 1
EOF
ln -s "$target" "$tmpdir/pdm.toml"
cat > "$tmpdir/pyproject.toml" <<'EOF'
[project]
name = "symlink-clobber-demo"
version = "0.0.1"
EOF
(
cd "$tmpdir" &&
"$PDM_PY" -m pdm config -l venv.in_project false
)
cat "$target"Expected result:
- A temporary project is created
pdm.tomlis a symlink to another TOML file- Running
pdm config -l venv.in_project falsemodifies the symlink target
Observed output from local validation:
--- target ---
[seed]
value = 1
[venv]
in_project = falseSeverity
Medium
CVSS v4.0
- Base score:
6.8(Medium) - Vector:
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N
Rationale:
AV:L: exploitation requires local execution ofpdmagainst an attacker-prepared checkoutAC:L: there is no complex constraint once the symlink sink existsAT:N: no extra prerequisite beyond the victim running the relevant command is requiredPR:N: the attacker does not need prior privileges on the victim systemUI:A: the victim must actively run a command that writes project-local state or configVC:N: the demonstrated issue is a write primitive, not a direct read primitiveVI:H: the attacker can cause unauthorized modification of files outside the repository rootVA:L: file clobber can disrupt local operation, but direct same-step availability impact is lower than a full RCESC:N/SI:N/SA:N: the base score is limited to the directly affected system
Root Cause
Project-local file sinks are treated as trusted regular files and are written without symlink checks or guarded atomic replacement.
Recommended Remediation
- Refuse to write project-local config/state files when the destination is a symlink
- Use
lstatandO_NOFOLLOWwhere available - Avoid resolving attacker-controlled project-local paths before writing
- Use atomic temp-file replacement only after confirming the destination is a regular file
Disclosure Notes
This issue is independent from the code-execution issues above. It is best tracked as a separate CVE candidate because the root cause and remediation are different.
AnalysisAI
Arbitrary file clobber in PDM (Python Development Master) below version 2.27.0 allows an attacker who controls a cloned project repository to overwrite files outside the repository root by planting symlinks at project-local config paths (pdm.toml, .pdm-python, .python-version) that PDM writes to without symlink protection. When a developer runs any PDM command triggering a project-local config write against the malicious checkout, the tool follows the symlink and overwrites the target with attacker-influenced content - up to arbitrary file corruption relative to the invoking user's privileges. A working proof-of-concept is included in the GitHub security advisory GHSA-ghq2-5c67-fprm; no active exploitation is confirmed in CISA KEV at time of analysis.
Technical ContextAI
PDM is a Python package and dependency manager distributed via pip (pkg:pip/pdm). The root cause is CWE-61 (UNIX Symbolic Link Following): project-local file sinks - specifically pdm.toml (handled in src/pdm/project/config.py:303-350 via Config.__init__() and _save_config()), .pdm-python (src/pdm/project/core.py:209-217), and .python-version (src/pdm/cli/commands/use.py:187-189) - are treated as trusted regular files. PDM resolves these paths and writes to the resolved destination without first calling lstat() to verify the path is not a symlink, and without using O_NOFOLLOW or atomic temp-file replacement guarded by a regular-file check. For the pdm.toml sink, an additional constraint exists: the symlink target must contain parseable TOML for the load step to succeed before the write path is reached. This constraint does not apply to .pdm-python or .python-version, making those sinks more reliably exploitable. The vulnerability is classified under CWE-61 and is architecturally equivalent to well-known TOCTOU symlink-following bugs in Unix tooling.
RemediationAI
Upgrade PDM to version 2.27.0 or later; this is the vendor-released patch that explicitly refuses to write project-local config and state files (pdm.toml, .pdm-python, .python-version) when the destination is a symlink. The fix is confirmed in the release notes at https://github.com/pdm-project/pdm/releases/tag/2.27.0 (issue #3788) and the security advisory GHSA-ghq2-5c67-fprm at https://github.com/pdm-project/pdm/security/advisories/GHSA-ghq2-5c67-fprm. For environments where immediate upgrade is not possible, the primary compensating control is to avoid running PDM commands (especially pdm config -l or pdm use) inside untrusted or externally-sourced repository checkouts. In CI/CD pipelines running PDM with elevated privileges, restrict which repositories are checked out and ensure workspace directories are freshly initialized rather than reused across builds, which eliminates the attacker's ability to pre-place symlinks. Note that these workarounds are operationally constraining and do not address the root cause - upgrading to 2.27.0 is strongly preferred.
Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t
BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser
pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi
The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python
BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica
OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph
pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.
In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse
Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/
pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne
Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301
Cross-user flow execution in Langflow (< 1.9.1) lets any authenticated API-key holder run another user's flow by passing
Same weakness CWE-61 – UNIX Symbolic Link (Symlink) Following
View allVendor StatusVendor
SUSE
Severity: Moderate| Product | Status |
|---|---|
| openSUSE Tumbleweed | Fixed |
Share
External POC / Exploit Code
Leaving vuln.today
GHSA-ghq2-5c67-fprm