CVE-2026-45539
Microsoft APM: Symlinks under `.apm/prompts/` and `.apm/agents/` are dereferenced during `apm install`, copying host-local file contents into the project tree
Microsoft APM is an open-source, community-driven dependency manager for AI agents. From 0.5.4 to 0.12.4, two primitive integrators in apm-cli enumerate package files with bare Path.glob() / Path.rglob() calls and read each match with Path.read_text(), transparently following symbolic links. A symlink committed inside a remote APM dependency under .apm/prompts/<x>.prompt.md or .apm/agents/<x>.agent.md is preserved verbatim into apm_modules/ on clone and then dereferenced during integration, with the resolved content written as a regular file into the project's deploy directories. The package content_hash, the pre-deploy SecurityGate scan, and apm audit do not flag this. The deploy roots are not added to the auto-generated .gitignore, so the resulting files are staged by git add by default. This vulnerability is fixed in 0.13.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Required
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.4
Microsoft APM: Symlinks under `.apm/prompts/` and `.apm/agents/` are dereferenced during `apm install`, copying host-local file contents into the project tree
Microsoft APM is an open-source, community-driven dependency manager for AI agents. From 0.5.4 to 0.12.4, two primitive integrators in apm-cli enumerate package files with bare Path.glob() / Path.rglob() calls and read each match with Path.read_text(), transparently following symbolic links. A symlink committed inside a remote APM dependency under .apm/prompts/<x>.prompt.md or .apm/agents/<x>.agent.md is preserved verbatim into apm_modules/ on clone and then dereferenced during integration, with the resolved content written as a regular file into the project's deploy directories. The package content_hash, the pre-deploy SecurityGate scan, and apm audit do not flag this. The deploy roots are not added to the auto-generated .gitignore, so the resulting files are staged by git add by default. This vulnerability is fixed in 0.13.0.
- Record
CVE-2026-45539- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N - Weakness
- CWE-59, CWE-200
- Updated
- 2026-11-02
EUVDEUVD-2026-30561
7.4
Microsoft APM is an open-source, community-driven dependency manager for AI agents. From 0.5.4 to 0.12.4, two primitive integrators in apm-cli enumerate package files with bare Path.glob() / Path.rglob() calls and read each match with Path.read_text(), transparently following symbolic links. A symlink committed inside a remote APM dependency under .apm/prompts/<x>.prompt.md or .apm/agents/<x>.agent.md is preserved verbatim into apm_modules/ on clone and then dereferenced during integration, with the resolved content written as a regular file into the project's deploy directories. The package content_hash, the pre-deploy SecurityGate scan, and apm audit do not flag this. The deploy roots are not added to the auto-generated .gitignore, so the resulting files are staged by git add by default. This vulnerability is fixed in 0.13.0.
- Record
EUVD-2026-30561- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-q5pp-gvjg-h7v4
7.4
Microsoft APM: Symlinks under `.apm/prompts/` and `.apm/agents/` are dereferenced during `apm install`, copying host-local file contents into the project tree
Summary
Two primitive integrators in apm-cli enumerate package files with bare Path.glob() / Path.rglob() calls and read each match with Path.read_text(), transparently following symbolic links.
A symlink committed inside a remote APM dependency under .apm/prompts/<x>.prompt.md or .apm/agents/<x>.agent.md is preserved verbatim into apm_modules/ on clone and then dereferenced during integration, with the resolved content written as a regular file into the project's deploy directories.
The package content_hash, the pre-deploy SecurityGate scan, and apm audit do not flag this. The deploy roots are not added to the auto-generated .gitignore, so the resulting files are staged by git add by default.
This was reproduced via the standard owner/repo#tag install flow against a real bare git repository. No --force or special flags were used.
Affected code
Sinks
-
src/apm_cli/integration/prompt_integrator.py
PromptIntegrator.find_prompt_files:package_path.glob("*.prompt.md")andapm_prompts.glob("*.prompt.md")
No symlink filter. -
src/apm_cli/integration/prompt_integrator.py
PromptIntegrator.copy_prompt:source.read_text("utf-8") -
src/apm_cli/integration/agent_integrator.py
AgentIntegrator.find_agent_files:package_path.glob("*.agent.md"),apm_agents.rglob("*.agent.md"),apm_agents.rglob("*.md"),apm_chatmodes.glob("*.chatmode.md")
No symlink filter. -
src/apm_cli/integration/agent_integrator.py
AgentIntegrator.copy_agent:source.read_text("utf-8") -
src/apm_cli/integration/agent_integrator.py
_write_codex_agent:source.read_text("utf-8"); resolved bytes are embedded intodeveloper_instructionsof the generated.codex/agents/<name>.toml -
src/apm_cli/integration/agent_integrator.py
_write_windsurf_agent_skill: same dereference pattern; resolved bytes land in.windsurf/skills/<name>/SKILL.md
Safe pattern already present in the codebase
src/apm_cli/integration/base_integrator.py
BaseIntegrator.find_files_by_glob()rejects:- symlinks via
f.is_symlink() - hardlinks via
f.stat().st_nlink > 1 - resolved paths escaping the package root
- symlinks via
This helper is already used by InstructionIntegrator.find_instruction_files.
Documented contract that the affected integrators violate
In src/apm_cli/install/phases/local_content.py, _copy_local_package documents the intent of preserving symlinks in apm_modules/:
This is security-relevant and not intended behavior because the codebase already documents that symlinks preserved in
apm_modules/are supposed to remain inert unless a consumer follows them safely. The affected integrators are exactly those consumer paths, and they dereference the symlink without sandboxing or symlink checks. That makes this an implementation gap, not expected design.
The affected integrators are the consumer tools that follow the link without sandboxing.
Reproducer
This proof of concept is localhost-only and uses a sentinel file, not a real secret.
It uses a real bare git repository and git config insteadOf so the install path is the same one APM uses for real GitHub clones (Repo.clone_from). No network access is required.
# 0. Clean slate
rm -rf /tmp/poc /tmp/poc_secret /tmp/poc_home
mkdir -p /tmp/poc/{remote_bare,victim_project,work_repo} /tmp/poc_home
# 1. Sentinel file outside the project and outside the package
echo 'APM-AUDIT-SENTINEL-X7Y2Q9-NOT-A-REAL-CREDENTIAL' > /tmp/poc_secret
# 2. Build a benign-looking APM package with two symlinks in it
cd /tmp/poc/work_repo
git init -q -b main .
git config user.email [email protected]
git config user.name 'PoC'
cat > apm.yml <<'YML'
name: helpful-agents
version: 1.0.0
description: Helpful AI agent collection
YML
mkdir -p .apm/agents .apm/prompts
cat > .apm/agents/helper.agent.md <<'AGENT'
---
name: helper
description: A helpful assistant
---
You are a helpful assistant.
AGENT
ln -s…
- Record
GHSA-q5pp-gvjg-h7v4- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N - Weakness
- CWE-59, CWE-200
- Updated
- 2026-11-02
NVD
7.4
Microsoft APM is an open-source, community-driven dependency manager for AI agents. From 0.5.4 to 0.12.4, two primitive integrators in apm-cli enumerate package files with bare Path.glob() / Path.rglob() calls and read each match with Path.read_text(), transparently following symbolic links. A symlink committed inside a remote APM dependency under .apm/prompts/<x>.prompt.md or .apm/agents/<x>.agent.md is preserved verbatim into apm_modules/ on clone and then dereferenced during integration, with the resolved content written as a regular file into the project's deploy directories. The package content_hash, the pre-deploy SecurityGate scan, and apm audit do not flag this. The deploy roots are not added to the auto-generated .gitignore, so the resulting files are staged by git add by default. This vulnerability is fixed in 0.13.0.
- Record
CVE-2026-45539- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N - Weakness
- CWE-59, CWE-200
- Updated
- 2026-11-02
OSV
7.4
Microsoft APM: Symlinks under `.apm/prompts/` and `.apm/agents/` are dereferenced during `apm install`, copying host-local file contents into the project tree
Microsoft APM is an open-source, community-driven dependency manager for AI agents. From 0.5.4 to 0.12.4, two primitive integrators in apm-cli enumerate package files with bare Path.glob() / Path.rglob() calls and read each match with Path.read_text(), transparently following symbolic links. A symlink committed inside a remote APM dependency under .apm/prompts/<x>.prompt.md or .apm/agents/<x>.agent.md is preserved verbatim into apm_modules/ on clone and then dereferenced during integration, with the resolved content written as a regular file into the project's deploy directories. The package content_hash, the pre-deploy SecurityGate scan, and apm audit do not flag this. The deploy roots are not added to the auto-generated .gitignore, so the resulting files are staged by git add by default. This vulnerability is fixed in 0.13.0.
- Record
CVE-2026-45539- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N - Weakness
- CWE-200, CWE-59
- Updated
- 2026-11-02
OSVGHSA-q5pp-gvjg-h7v4
7.4
Microsoft APM: Symlinks under `.apm/prompts/` and `.apm/agents/` are dereferenced during `apm install`, copying host-local file contents into the project tree
Summary
Two primitive integrators in apm-cli enumerate package files with bare Path.glob() / Path.rglob() calls and read each match with Path.read_text(), transparently following symbolic links.
A symlink committed inside a remote APM dependency under .apm/prompts/<x>.prompt.md or .apm/agents/<x>.agent.md is preserved verbatim into apm_modules/ on clone and then dereferenced during integration, with the resolved content written as a regular file into the project's deploy directories.
The package content_hash, the pre-deploy SecurityGate scan, and apm audit do not flag this. The deploy roots are not added to the auto-generated .gitignore, so the resulting files are staged by git add by default.
This was reproduced via the standard owner/repo#tag install flow against a real bare git repository. No --force or special flags were used.
Affected code
Sinks
-
src/apm_cli/integration/prompt_integrator.py
PromptIntegrator.find_prompt_files:package_path.glob("*.prompt.md")andapm_prompts.glob("*.prompt.md")
No symlink filter. -
src/apm_cli/integration/prompt_integrator.py
PromptIntegrator.copy_prompt:source.read_text("utf-8") -
src/apm_cli/integration/agent_integrator.py
AgentIntegrator.find_agent_files:package_path.glob("*.agent.md"),apm_agents.rglob("*.agent.md"),apm_agents.rglob("*.md"),apm_chatmodes.glob("*.chatmode.md")
No symlink filter. -
src/apm_cli/integration/agent_integrator.py
AgentIntegrator.copy_agent:source.read_text("utf-8") -
src/apm_cli/integration/agent_integrator.py
_write_codex_agent:source.read_text("utf-8"); resolved bytes are embedded intodeveloper_instructionsof the generated.codex/agents/<name>.toml -
src/apm_cli/integration/agent_integrator.py
_write_windsurf_agent_skill: same dereference pattern; resolved bytes land in.windsurf/skills/<name>/SKILL.md
Safe pattern already present in the codebase
src/apm_cli/integration/base_integrator.py
BaseIntegrator.find_files_by_glob()rejects:- symlinks via
f.is_symlink() - hardlinks via
f.stat().st_nlink > 1 - resolved paths escaping the package root
- symlinks via
This helper is already used by InstructionIntegrator.find_instruction_files.
Documented contract that the affected integrators violate
In src/apm_cli/install/phases/local_content.py, _copy_local_package documents the intent of preserving symlinks in apm_modules/:
This is security-relevant and not intended behavior because the codebase already documents that symlinks preserved in
apm_modules/are supposed to remain inert unless a consumer follows them safely. The affected integrators are exactly those consumer paths, and they dereference the symlink without sandboxing or symlink checks. That makes this an implementation gap, not expected design.
The affected integrators are the consumer tools that follow the link without sandboxing.
Reproducer
This proof of concept is localhost-only and uses a sentinel file, not a real secret.
It uses a real bare git repository and git config insteadOf so the install path is the same one APM uses for real GitHub clones (Repo.clone_from). No network access is required.
# 0. Clean slate
rm -rf /tmp/poc /tmp/poc_secret /tmp/poc_home
mkdir -p /tmp/poc/{remote_bare,victim_project,work_repo} /tmp/poc_home
# 1. Sentinel file outside the project and outside the package
echo 'APM-AUDIT-SENTINEL-X7Y2Q9-NOT-A-REAL-CREDENTIAL' > /tmp/poc_secret
# 2. Build a benign-looking APM package with two symlinks in it
cd /tmp/poc/work_repo
git init -q -b main .
git config user.email [email protected]
git config user.name 'PoC'
cat > apm.yml <<'YML'
name: helpful-agents
version: 1.0.0
description: Helpful AI agent collection
YML
mkdir -p .apm/agents .apm/prompts
cat > .apm/agents/helper.agent.md <<'AGENT'
---
name: helper
description: A helpful assistant
---
You are a helpful assistant.
AGENT
ln -s…
- Record
GHSA-q5pp-gvjg-h7v4- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N - Weakness
- CWE-200, CWE-59
- Updated
- 2026-11-02
OSVPYSEC-2026-2377
7.4
Microsoft APM: Symlinks under `.apm/prompts/` and `.apm/agents/` are dereferenced during `apm install`, copying host-local file contents into the project tree
Summary
Two primitive integrators in apm-cli enumerate package files with bare Path.glob() / Path.rglob() calls and read each match with Path.read_text(), transparently following symbolic links.
A symlink committed inside a remote APM dependency under .apm/prompts/<x>.prompt.md or .apm/agents/<x>.agent.md is preserved verbatim into apm_modules/ on clone and then dereferenced during integration, with the resolved content written as a regular file into the project's deploy directories.
The package content_hash, the pre-deploy SecurityGate scan, and apm audit do not flag this. The deploy roots are not added to the auto-generated .gitignore, so the resulting files are staged by git add by default.
This was reproduced via the standard owner/repo#tag install flow against a real bare git repository. No --force or special flags were used.
Affected code
Sinks
-
src/apm_cli/integration/prompt_integrator.py
PromptIntegrator.find_prompt_files:package_path.glob("*.prompt.md")andapm_prompts.glob("*.prompt.md")
No symlink filter. -
src/apm_cli/integration/prompt_integrator.py
PromptIntegrator.copy_prompt:source.read_text("utf-8") -
src/apm_cli/integration/agent_integrator.py
AgentIntegrator.find_agent_files:package_path.glob("*.agent.md"),apm_agents.rglob("*.agent.md"),apm_agents.rglob("*.md"),apm_chatmodes.glob("*.chatmode.md")
No symlink filter. -
src/apm_cli/integration/agent_integrator.py
AgentIntegrator.copy_agent:source.read_text("utf-8") -
src/apm_cli/integration/agent_integrator.py
_write_codex_agent:source.read_text("utf-8"); resolved bytes are embedded intodeveloper_instructionsof the generated.codex/agents/<name>.toml -
src/apm_cli/integration/agent_integrator.py
_write_windsurf_agent_skill: same dereference pattern; resolved bytes land in.windsurf/skills/<name>/SKILL.md
Safe pattern already present in the codebase
src/apm_cli/integration/base_integrator.py
BaseIntegrator.find_files_by_glob()rejects:- symlinks via
f.is_symlink() - hardlinks via
f.stat().st_nlink > 1 - resolved paths escaping the package root
- symlinks via
This helper is already used by InstructionIntegrator.find_instruction_files.
Documented contract that the affected integrators violate
In src/apm_cli/install/phases/local_content.py, _copy_local_package documents the intent of preserving symlinks in apm_modules/:
This is security-relevant and not intended behavior because the codebase already documents that symlinks preserved in
apm_modules/are supposed to remain inert unless a consumer follows them safely. The affected integrators are exactly those consumer paths, and they dereference the symlink without sandboxing or symlink checks. That makes this an implementation gap, not expected design.
The affected integrators are the consumer tools that follow the link without sandboxing.
Reproducer
This proof of concept is localhost-only and uses a sentinel file, not a real secret.
It uses a real bare git repository and git config insteadOf so the install path is the same one APM uses for real GitHub clones (Repo.clone_from). No network access is required.
# 0. Clean slate
rm -rf /tmp/poc /tmp/poc_secret /tmp/poc_home
mkdir -p /tmp/poc/{remote_bare,victim_project,work_repo} /tmp/poc_home
# 1. Sentinel file outside the project and outside the package
echo 'APM-AUDIT-SENTINEL-X7Y2Q9-NOT-A-REAL-CREDENTIAL' > /tmp/poc_secret
# 2. Build a benign-looking APM package with two symlinks in it
cd /tmp/poc/work_repo
git init -q -b main .
git config user.email [email protected]
git config user.name 'PoC'
cat > apm.yml <<'YML'
name: helpful-agents
version: 1.0.0
description: Helpful AI agent collection
YML
mkdir -p .apm/agents .apm/prompts
cat > .apm/agents/helper.agent.md <<'AGENT'
---
name: helper
description: A helpful assistant
---
You are a helpful assistant.
AGENT
ln -s…
- Record
PYSEC-2026-2377- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N - Updated
- 2026-11-02
Affected products
Affected packages
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-45539 is a high vulnerability (CVSS 7.4) in Microsoft Apm, published May 15, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.65%, the 51st percentile. Fixed in apm 0.13.0.
References
github.com/microsoft/apm/security/advisories/GHSA-q5pp-gvjg-h7v4
nvd.nist.gov/vuln/detail/CVE-2026-45539
github.com/microsoft/apm/commit/f85b9f54ad303159f9c448268eb7005c319fe02a
github.com/microsoft/apm/releases/tag/v0.13.0
github.com/advisories/GHSA-q5pp-gvjg-h7v4
github.com/CVEProject/cvelistV5/tree/main/cves/2026/45xxx/CVE-2026-45539.json
github.com/microsoft/apm
pypi.org/project/apm
Left out here: the rest of 3 source descriptions. The JSON record has them.