Skip to content

File tool: shell env-var paths ($env:USERPROFILE\…, %USERPROFILE%, $HOME) are joined onto the workspace instead of expanded/rejected #389

Closed

Description

@kroteau

What happens
On Windows the model called file read with $env:USERPROFILE\.gitconfig. The approval prompt flagged it as a possible sandbox escape (0.93) and it was approved. The read then failed with:

cannot read $env:USERPROFILE\.gitconfig: CreateFile C:\Users\…\ipsupport-code\$env:USERPROFILE\.gitconfig: The filename, directory name, or volume label syntax is incorrect.

Cause
policy.Engine.Resolve (internal/policy/policy.go:469) only expands ~/ via expandTilde. Any other shell-style reference is seen as a relative path and joined onto e.base(). expandTilde also misses ~\ on Windows.

Why it matters

  • The model wastes turns guessing why the path failed.
  • The risk classifier reads the path as $HOME, so the user is asked to approve a path different from the one that actually gets opened.

Suggested fix
In Resolve, either:

  1. expand $env:NAME, %NAME%, $NAME/${NAME} and ~\ before the IsAbs check (the jail check still runs afterwards), or
  2. reject unexpanded variable references with a clear error ("file paths aren't shell-expanded; pass an absolute path").

Add a test next to the ~/note.json case in policy_test.go.

Version: HEAD dd44ba0, Windows 11, PowerShell 7.

Activity

  1. added a commit that references this issue on Oct 20, 2026
    b9dd4f1
  2. rromenskyi commented on Oct 20, 2026

    @rromenskyi
    Contributor

    Thanks @kroteau for the report. The cause and the two fix options you laid out were spot on.

    This shipped in v0.68.0 (#391), using your option 2. A path that starts with $env:NAME, ${NAME}, $NAME or %NAME% is now refused with "file paths are not shell-expanded — pass the full path". For directory variables such as USERPROFILE, HOME, APPDATA and TEMP, the error also gives the value, so the model can retry with the full path right away. ~\ now works on Windows too.

    We started with option 1 and expanded the reference. Review showed that expansion was a way to read secrets: reading $OPENAI_API_KEY resolved to <workspace>/sk-…, and the "no such file" error returned the key to the model without any prompt. That is why the fix refuses the path and names the value only for a short list of directory variables.

    The same release also has manual mode, which you asked for. shift+tab now cycles auto → plan → manual, and in manual every change asks first (#392). Thanks for that idea as well.

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