Skip to content

[BUG] Workspace symlinks bypass outside-workspace write approval #2344

Open

Description

@kanchinadd98

Summary

Write tools check isOutsideWorkspace with a lexical path check. A symlink inside the workspace can point to a directory outside the workspace. A write through that symlink is classified as inside the workspace. With alwaysAllowWrite on and alwaysAllowWriteOutsideWorkspace off, the write is auto-approved.

Found during review of #2343 (follow-up to #2249).

Affected code

  • isPathOutsideWorkspace in src/utils/pathUtils.ts uses path.resolve and a string prefix check. It does not resolve symlinks.
  • These tools use it to set isOutsideWorkspace in their approval payload: write_to_file, apply_diff, edit, edit_file, search_replace, apply_patch, and generate_image.
  • isRealPathOutsideWorkspace in the same file already resolves symlinks with realpath. It checks the deepest existing ancestor for paths that do not exist yet, and it fails closed. Only the openFile handler in webviewMessageHandler.ts uses it.

Steps to reproduce

  1. In the workspace, create a symlink to a directory outside it: ln -s ~/.config linked-config.
  2. Set autoApprovalEnabled: true, alwaysAllowWrite: true, and alwaysAllowWriteOutsideWorkspace: false.
  3. Ask the agent to edit linked-config/some-app/config.json.
  4. The edit is applied without an approval prompt.

Expected result

A write whose real target is outside the workspace sets isOutsideWorkspace: true. Auto-approval then asks, unless alwaysAllowWriteOutsideWorkspace is on or an allowedWriteFiles pattern matches.

Proposed approach

  • Move all write tools to isRealPathOutsideWorkspace together, so that all tools give the same result for the same path.
  • isRealPathOutsideWorkspace is async. The execute paths can await it. The streaming preview (handlePartial) can keep the lexical check, because the final approval decision uses the execute payload.
  • Add tests for each tool: a symlink to an outside directory asks, and a normal path inside the workspace is still auto-approved.

Version

Zoo Code 3.96.0 (main at d2a59a8).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions