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.
src/apm_cli/integration/prompt_integrator.py
PromptIntegrator.find_prompt_files: package_path.glob("*.prompt.md") and apm_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 into developer_instructions of 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
-...
0.13.0Exploitability
AV:NAC:LPR:NUI:RScope
S:CImpact
C:HI:NA:N7.4/CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N