Remote templates: symlinks are silently dropped since 1.4.1, breaking shared code across template variants
@mhenc がすでに取り組んでいます。
2026年9月14日 から。
評価
この issue はまだ評価されていません。
説明
What happened
Since 1.4.1, agents-cli scaffold create --agent <remote git URL> silently drops every
symlink in the template source. A template that uses an intra-repo symlink to share a
library across several template variants scaffolds "successfully" into a project that is
missing that library and fails on import.
This is a regression: 1.4.0 copied a symlinked directory as real files.
The guard is copy_files.should_skip() in
google/agents/cli/scaffold/utils/template.py:
# Never follow symlinks from untrusted remote template sources
if path.is_symlink():
logging.warning(
f"Skipping symlink in template source (symlinks are not allowed): {path}"
)
return True
I understand the CWE-59 motivation (a template shipping id_rsa -> ~/.ssh/id_rsa). The
problem is that the check rejects all symlinks, including ones whose target never leaves
the cloned repository, and it does so without failing the command.
Reproduction
A repo laid out like this, pushed to GitHub, with the template at template/:
myrepo/
├── shared/ # single source of truth
│ └── config.py
└── template/
├── agents-cli-manifest.yaml
└── app/
├── agent.py # `from .shared.config import ...`
└── shared -> ../../shared # committed as a symlink (git mode 120000)
agents-cli scaffold create demo \
--agent https://github.com/<org>/myrepo/template@main --prototype
Expected: demo/app/shared/config.py exists (this is what 1.4.0 did).
Actual: demo/app/shared/ does not exist. The command prints
✅ Success! Your agent project is ready. and the project fails at import.
Version bisect
I ran each release's own copy_files over the same template directory:
| Version | app/shared/ in output |
|---|---|
| 1.0.0 / 1.2.0 / 1.3.0 / 1.4.0 | present (symlink dereferenced) |
| 1.4.1 / 1.5.0 | missing |
The Never follow symlinks from untrusted remote string first appears in 1.4.1.
Two things make this hard to notice
- It is a warning, not an error. Scaffolding reports success, and the failure only
shows up later as anImportErrorfar from its cause. local@does not exercise the same path.agents-cli scaffold create --agent local@<path>pre-copies the template with
shutil.copytree(local_path, template_source_path, ignore=...)
(scaffold/commands/create.py), andcopytreedefaults tosymlinks=False, i.e. it
dereferences. So a template author's CI that validates withlocal@— which the docs
present as the way to test a template locally — passes, while every real user of the
published URL gets a broken project. Our CI has been green this whole time.
Why this matters for multi-variant templates
We maintain an internal ADK template with several variants (a default ReAct agent, an A2UI
variant, a multi-agent GKE variant). All of them share one library — DI wiring for model
tiers, Secret Manager, artifact storage, logging/tracing — plus one ruff.toml and one
ty.toml.
The scaffolder only copies the directory --agent points at, so the only way to share
code across variants without duplicating it is an intra-repo symlink from each variant
into the shared directory. That is exactly what 1.4.1 broke. This is the same use case as
#62, which we're otherwise happy with.
The workaround is to vendor N physical copies of the shared library into the repo (one per
variant) and add a sync script plus a CI drift check. That works, but it means the "single
source of truth" only exists by convention, and every shared-code review carries N
mechanical duplicate diffs.
Suggested fixes, in order of preference
-
Allow a symlink whose resolved target stays inside the fetched repository.
Path.resolve()the link and require it to be under the clone root, skipping it
otherwise. That preserves the CWE-59 protection completely — an escaping link is still
refused — while letting a template share code within its own repo.One detail: the containment check needs to be against the cloned repo root, not the
template subdirectory. A shared library naturally sits at the repo root while the
template is a subdirectory, so the link legitimately points "up" out of the template
dir but never out of the repo. -
An opt-in in the template manifest, e.g.
settings.follow_symlinks: true, if you'd
rather the decision be explicit per template. -
At minimum, fail loudly. If symlinks stay banned, please make the command error out
(or print to stderr and list every dropped path in the summary) instead of logging a
warning and reporting success. Silently producing a broken project is the worst
outcome. It would also help to havelocal@skip symlinks the same way remote fetches
do, so a template author's local validation reflects what users will actually get.
Environment
- agents-cli 1.5.0 (and 1.4.1); last working: 1.4.0
- Linux, Python 3.13 / 3.14
- 主要言語
- Python
- スター
- 6k
- フォーク
- 670
- PR マージ指標
- 30日以内にマージされた PR はありません
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
google/agents-cli のほかの issue
-
documentation
難易度 2/5 1〜3時間 初心者へのやさしさ 92/100
google/agents-cli#86 ·
-
難易度 3/5 1〜2日 初心者へのやさしさ 68/100
google/agents-cli#85 · コメント 1 件 ·
-
難易度 3/5 1〜2日 初心者へのやさしさ 68/100
google/agents-cli#84 · コメント 1 件 ·
-
難易度 3/5 1〜2日 初心者へのやさしさ 72/100
google/agents-cli#83 · コメント 2 件 · リアクション 1 件 ·
-
google/agents-cli#79 · コメント 2 件 · 担当者 1 名 ·
google/agents-cli の issue をすべて見る
似ている issue
-
essnmx good first issue
難易度 1/5 1時間未満 初心者へのやさしさ 95/100
-
[Feature] 奇物选择添加优先级 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
syfoud/Simulated_Scepter#174 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
Giskard-AI/giskard-oss#2840 · コメント 1 件 ·
-
A claim comment carrying the issue number is silently declined while the workflow reports success オープンarea: repo bug perceived difficulty: 2
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
yeti-platform/yeti#1380 ·