From cbaa114bb23d988886c47cd767a4230fc4590a13 Mon Sep 17 00:00:00 2001 From: Snider Date: Thu, 19 Feb 2026 14:20:39 +0000 Subject: [PATCH] fix(io/local): resolve symlinks on sandbox root to prevent false escape detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- pkg/io/local/client.go | 7 +++++++ pkg/io/local/client_test.go | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/io/local/client.go b/pkg/io/local/client.go index 452afad..e482c5d 100644 --- a/pkg/io/local/client.go +++ b/pkg/io/local/client.go @@ -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 } diff --git a/pkg/io/local/client_test.go b/pkg/io/local/client_test.go index 7471174..27ff139 100644 --- a/pkg/io/local/client_test.go +++ b/pkg/io/local/client_test.go @@ -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) { -- 2.45.3