fix(io/local): resolve symlinks on sandbox root to prevent false escape detection
Some checks failed
Auto Merge / merge (pull_request) Has been cancelled
CI / qa (pull_request) Has been cancelled
Coverage / coverage (pull_request) Has been cancelled
PR Build / build (amd64, linux, ubuntu-latest) (pull_request) Has been cancelled
PR Build / draft-release (pull_request) Has been cancelled

On macOS, /var is a symlink to /private/var. When New() stores the
unresolved root but validatePath() resolves child paths via EvalSymlinks,
the mismatch causes filepath.Rel to produce ".." prefixes — triggering
false SECURITY sandbox escape warnings on every file operation.

Fix: resolve symlinks on the root path in New() so both sides compare
like-for-like. Updates TestNew to compare against resolved paths.

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-02-19 14:20:39 +00:00
parent f74c69c16e
commit cbaa114bb2
2 changed files with 10 additions and 1 deletions

View file

@ -21,6 +21,13 @@ func New(root string) (*Medium, error) {
if err != nil {
return nil, err
}
// Resolve symlinks so sandbox checks compare like-for-like.
// On macOS, /var is a symlink to /private/var — without this,
// EvalSymlinks on child paths resolves to /private/var/... while
// root stays /var/..., causing false sandbox escape detections.
if resolved, err := filepath.EvalSymlinks(abs); err == nil {
abs = resolved
}
return &Medium{root: abs}, nil
}

View file

@ -12,7 +12,9 @@ func TestNew(t *testing.T) {
root := t.TempDir()
m, err := New(root)
assert.NoError(t, err)
assert.Equal(t, root, m.root)
// New() resolves symlinks (macOS /var → /private/var), so compare resolved paths.
resolved, _ := filepath.EvalSymlinks(root)
assert.Equal(t, resolved, m.root)
}
func TestPath(t *testing.T) {