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

No one assigned

    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