Skip to content

Security: symlink-follow arbitrary file read (CWE-59) + agent_directory path traversal (CWE-22) in agents create remote-template copy #51

Description

@aaronjmars

Security: two issues on the agents create remote-template path (fix branch attached)

Reported to the Google OSS VRP (g.co/vulnz), which validated it and asked for a fix as a PR. External PRs appear to be disabled on this mirror repo (CONTRIBUTING.md notes PRs aren't accepted), so I'm filing here with a ready-to-import fix branch.

Fix branch: main...aaronjmars:agents-cli:fix/security-template-symlink-and-path-traversal
File: src/google/agents/cli/scaffold/utils/template.py (+32 lines, no behavior change for legitimate templates)

In both cases the attacker is a malicious or compromised remote template repository, and the victim is a developer who runs agents create against it.

1. HIGH — symlink following → arbitrary local file read (CWE-59 / CWE-61 / CWE-200)

copy_files() copies template files with is_dir() + shutil.copy2, both of which dereference symlinks, and should_skip() has no symlink guard. Remote template files are copied after cookiecutter runs, so nothing mediates them.

A malicious template can ship a symlink pointing outside the template, e.g.:

creds -> ~/.ssh/id_rsa
env   -> ~/.config/gcloud/application_default_credentials.json

On agents create, the contents of those local files are copied into the generated project as ordinary files. The developer then typically git adds, commits, and pushes the new project — silently exfiltrating SSH keys / cloud credentials / .env secrets.

Fix: should_skip() skips symlinks entirely (never follows them) and logs a warning.

2. MEDIUM — path traversal via remote manifest agent_directory (CWE-22)

validate_agent_directory_name() early-returns for non-python languages. A remote manifest with language: go|java|typescript and agent_directory: "../../.." is therefore never validated, and traverses out of the intended output directory when joined into write paths (project_template / agent_directory).

Fix: before the language-specific checks, reject .. components, absolute paths, drive-qualified paths, and a leading ~ for all languages. Legitimate nested relative directories still validate — notably the built-in Java template's agent_directory: "src/main/java".

Testing

  • Traversal validationapp, agent, src/main/java, my_agent still pass; ../../../etc, ..\..\Windows, /etc/cron.d/x, ~/.ssh, C:\Windows, foo/../../bar are rejected across go/java/typescript; python identifier rules unchanged.
  • Symlink copy — before/after harness with a creds -> secret symlink: original copies the secret's contents into the generated project; patched skips the symlink and nothing leaks.

Notes

  • Happy to sign the Google CLA (cla.developers.google.com) if the fix is imported, and to adjust to whatever contribution form you prefer.
  • Detected by Aeon.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions