Skip to content

A write follows a symlink at the file itself, so the workspace boundary is one component short #100

Description

@beardthelion

agent-computer/src/workspace.ts describes path confinement as three layers, and names the third, resolving symlinks, as the one people miss. On the write path it stops one component short.

resolvePath(requested, forWrite: true) resolves the directory a write will land in and checks that against the root, then returns the lexical target. If the last component is itself a symlink, writeFile follows it, and the bytes land wherever it points, as root inside the container.

Reproduced against a temporary workspace on main 2b2bc39:

workspace/notes.md -> /outside/victim.txt   (victim.txt contains "original")
write("notes.md", "OWNED")  ->  no refusal, victim.txt now contains "OWNED"

The read path is not affected: it resolves the file itself and refuses. Symlinked directories are refused in both directions, and there is an existing test for that case. It is only the final component of a write.

A dangling link is the same escape with an extra step. Writing through draft.txt -> /outside/not-yet.txt creates the file it names, so a check that asks where the link points learns nothing and lets it past.

Reachability

Nothing in the workspace API creates a symlink, which is why this reads as covered. The links come from elsewhere:

  • the shell added in Run OpenBot as one container, and give a Bot a shell #62. ln -s /etc/crontab /workspace/notes.md is one command, and a deployment that denies computer_run_command by policy while allowing computer_write_file is exactly the configuration where this matters.
  • an archive a Bot downloaded and unpacked.
  • a /workspace volume left by an earlier deployment.
  • in the shared-computer arrangement (no COMPUTER_SUPERVISOR_URL), every Bot shares one container and one workspace, so one Bot plants the link and another writes through it.

Test plan

  • Put a symlink at notes.md in a Bot's workspace pointing outside, ask the Bot to write to notes.md. Expect a refusal and the target file unchanged.
  • Same with a link pointing at a file that does not exist yet. Expect a refusal and no file created.
  • A link pointing back inside the workspace still reads and writes normally.

A PR follows.

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