Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Remote templates: symlinks are silently dropped since 1.4.1, breaking shared code across template variants

已关闭
#89 0 条评论 0 个 reaction 已指派 1 人 在 GitHub 查看

@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
  1. It is a warning, not an error. Scaffolding reports success, and the failure only
    shows up later as an ImportError far from its cause.
  2. 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), and copytree defaults to symlinks=False, i.e. it
    dereferences. So a template author's CI that validates with local@ — 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
  1. 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.

  2. An opt-in in the template manifest, e.g. settings.follow_symlinks: true, if you'd
    rather the decision be explicit per template.

  3. 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 have local@ 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

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

google/agents-cli 的其他 Issue

查看 google/agents-cli 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。