Skip to content

CVE-2026-45539: Verified Repro With Script Download

CVE-2026-45539: Microsoft APM: arbitrary file disclosure via symlink-following on apm install

CVE-2026-45539 is verified against apm · pip. Affected versions: 0.5.4 through 0.12.4 inclusive. Fixed in 0.13.0. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00136.

REPRO-2026-00136 apm · pip May 22, 2026 CVE entry ↗ .txt
Severity
HIGH
CVSS
7.4
Reproduced in
29m 1s
Tool calls
212
Spend
$1.61
01 · Overview

What Is CVE-2026-45539?

CVE-2026-45539 is a high-severity arbitrary file disclosure vulnerability in apm-cli (the apm PyPI package), caused by transparent symlink-following when it installs APM packages. Pruva reproduced it (reproduction REPRO-2026-00136).

02 · Severity & CVSS

CVE-2026-45539 Severity & CVSS Score

CVE-2026-45539 is rated high severity, with a CVSS base score of 7.4 out of 10.

HIGH threat level
7.4 / 10 CVSS base
Weakness CWE-59

High — serious impact or readily exploitable. Prioritize remediation.

03 · Affected Versions

Affected apm Versions

apm · pip versions 0.5.4 through 0.12.4 inclusive are affected.

How to Reproduce CVE-2026-45539

$ pruva-verify REPRO-2026-00136
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00136/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-45539

Reproduced by Pruva's autonomous agents — 212 tool calls over 29 min. Full root-cause analysis and the complete transcript are below.

How the agent worked 673 events · 212 tool calls · 29 min
29 minDuration
212Tool calls
153Reasoning steps
673Events
Agent activity over 29 min
Support
32
Repro
259
Variant
378
0:0029:01

Root Cause and Exploit Chain for CVE-2026-45539

Summary

apm-cli (the apm-cli PyPI package, versions 0.5.4 through 0.12.4) transparently follows symbolic links when enumerating and reading APM package primitive files (.prompt.md, .agent.md, .chatmode.md). A malicious APM dependency can include a symlink under .apm/prompts/ or .apm/agents/ that points to an arbitrary absolute host path (e.g. /etc/passwd). During apm install, the CLI dereferences the symlink, reads the target file's contents with Path.read_text(), and copies those contents into the consuming project's installed-package tree (e.g. .github/prompts/). This results in arbitrary file disclosure into the victim's project tree.

Impact

  • Package/component affected: apm-cli (PyPI package)
  • Affected versions: 0.5.40.12.4 (inclusive)
  • Fixed versions: 0.13.0
  • Risk level: High (CVSS 3.1 base 7.4)
  • Consequences: A malicious dependency author can exfiltrate any file readable by the user running apm install into files inside the victim project, where they may be committed, uploaded, or otherwise exposed.

Root Cause

The vulnerable code paths are in PromptIntegrator.find_prompt_files(), AgentIntegrator.find_agent_files(), and the various copy_*() methods in src/apm_cli/integration/. Specifically:

  1. File enumeration used bare Path.glob() and Path.rglob() without checking whether entries were symlinks. On Python 3.10+, Path.glob() transparently follows symlinks, so a symlink inside .apm/prompts/ matching *.prompt.md would be returned as a valid file path.

  2. File reading used Path.read_text() which dereferences symlinks. When copy_prompt() or agent copy methods read the source file, they inadvertently read the symlink target's contents.

  3. No containment check verified whether a resolved file path stayed within the package directory.

The fix in v0.13.0 (commit range from v0.12.4..v0.13.0, security merge commits 77d1dda and f85b9f5) addresses this by:

  • Introducing/refactoring BaseIntegrator.find_files_by_glob() to skip symlink entries (f.is_symlink()) and hardlinks (st_nlink > 1), plus a defense-in-depth resolve().is_relative_to() containment guard.
  • Updating PromptIntegrator.find_prompt_files() and AgentIntegrator.find_agent_files() to use find_files_by_glob() instead of bare glob()/rglob().
  • Adding explicit source.is_symlink() checks in copy_prompt(), copy_agent(), and related agent copy methods that raise ValueError if a symlink is somehow passed through.

Reproduction Steps

The reproduction script is located at:

  • repro/reproduction_steps.sh

What the script does:

  1. Creates a scratch workspace under $ROOT/scratch.
  2. Creates a malicious local APM package (malicious_pkg) containing:
    • A legitimate .apm/prompts/legit.prompt.md and .apm/agents/legit.agent.md
    • A symlink .apm/prompts/evil.prompt.md -> /path/to/sentinel.txt
  3. Creates a consumer project with target: copilot and an empty dependencies.apm list.
  4. Installs apm-cli==0.12.4 (vulnerable) in a virtualenv.
  5. Runs apm install ../malicious_pkg from the consumer project.
  6. Verifies that evil.prompt.md appears in .github/prompts/ with the sentinel file's contents (host file disclosure).
  7. Installs apm-cli==0.13.0 (fixed) in the same virtualenv.
  8. Resets the consumer project state and re-runs apm install ../malicious_pkg.
  9. Verifies that evil.prompt.md does not appear in .github/prompts/.

Expected evidence of reproduction:

  • With apm-cli==0.12.4: The install log shows 2 prompts integrated, and scratch/consumer/.github/prompts/evil.prompt.md exists containing the sentinel string.
  • With apm-cli==0.13.0: The install log shows 1 prompts integrated, and evil.prompt.md is absent from .github/prompts/.

Evidence

Log files generated by the reproduction script:

  • logs/vulnerable_install.log — Output of apm install using the vulnerable build.
  • logs/fixed_install.log — Output of apm install using the fixed build.

Key excerpts from a successful run:

Vulnerable build (0.12.4):

  [+] ../malicious_pkg (local)
  |-- 2 prompts integrated -> .github/prompts/
  |-- 1 agents integrated -> .github/agents/

Post-install file check:

FILE: .../.github/prompts/evil.prompt.md
CVE-2026-45539-REPRO-SENTINEL-<timestamp>

Fixed build (0.13.0):

  [+] ../malicious_pkg (local)
  |-- 1 prompts integrated -> .github/prompts/
  |-- 1 agents integrated -> .github/agents/

Post-install file check:

  • evil.prompt.md is not present under .github/prompts/.

Recommendations / Next Steps

  1. Upgrade immediately to apm-cli>=0.13.0.
  2. Audit existing projects for any apm_modules/ or installed dependencies that may have already leaked host file contents.
  3. Review dependency sources — only install APM packages from trusted registries or verified local bundles.
  4. Add regression tests that exercise symlink and hardlink rejection during integration (the project already added tests/unit/integration/test_symlink_rejection.py in the fix release).
  5. Consider broader symlink policy — evaluate whether symlinks should be permitted anywhere in the install pipeline (e.g. during download/unpack) or should be stripped at package ingestion time.

Additional Notes

  • Idempotency confirmed: The reproduction script passes two consecutive runs without modification, producing the same observable results each time.
  • Edge cases: The fix also rejects hardlinks (st_nlink > 1) because Path.resolve() returns the hardlink's own path, so is_relative_to() cannot catch a hardlink to a file outside the package tree.
  • Limitations: The reproduction uses a local path dependency (../malicious_pkg). The same vulnerability would be exploitable via remote dependencies if an attacker could publish a malicious APM package containing symlinks in its .apm/ subtree.

CVE-2026-45539 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:001:24
0:00
session startedaccounts/fireworks/models/kimi-k2p6 · cve-2026-45539 · cve-2026
0:04
0:06
0:11
0:12
web search
0:15
0:17
0:20
0:33
web search
0:40
0:41
web search
0:47
0:49
web search
1:20
1:20
extract_facts
no facts extracted
1:21
1:21
1:21
supportrepro
1:24
1:24
1:24

Artifacts and Evidence for CVE-2026-45539

Scripts, logs, diffs, and output captured during the reproduction.

No artifacts available

08 · How to Fix

How to Fix CVE-2026-45539

Upgrade apm · pip to 0.13.0 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-45539 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-45539

What happens when a victim installs a dependency exploiting CVE-2026-45539?

During apm install, the CLI dereferences the symlink, reads the target host file's contents with Path.read_text(), and copies those contents into the consuming project's installed-package tree (e.g. .github/prompts/). A dependency author can thereby exfiltrate any file readable by the user running apm install into files inside the victim's project, where they may then be committed, uploaded, or otherwise exposed.

Which apm-cli versions are affected by CVE-2026-45539, and where is it fixed?

Versions 0.5.4 through 0.12.4 (inclusive) are affected. It is fixed in 0.13.0.

How severe is CVE-2026-45539?

It is rated high severity (the ticket cites CVSS 3.1 base 7.4): no host privileges beyond running apm install are needed, and the attacker only needs to publish a malicious dependency.

How can I reproduce CVE-2026-45539?

Download the verified script from this page and run it in an isolated environment against apm-cli 0.5.4-0.12.4. Publish a malicious APM dependency containing a symlink under .apm/prompts/ that points at a sensitive host file, run apm install against a consuming project, and confirm the target file's contents get copied into the project's installed-package tree; confirm 0.13.0 blocks the symlink dereference.
11 · References

References for CVE-2026-45539

Authoritative sources for CVE-2026-45539 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.