Files
goclaw/internal/tools/sandbox_utils_test.go
Duy /zuey/andGitHub 532ff91d8e fix(security): harden upstream critical surfaces (#32)
* fix(security): harden upstream critical surfaces

Refs #30

* fix(security): close pre-landing review gaps

Refs #30

* fix(security): close official release blockers
2026-05-20 16:33:49 +07:00

146 lines
3.8 KiB
Go

package tools
import (
"context"
"testing"
)
func TestSandboxCwd(t *testing.T) {
tests := []struct {
name string
ctxWorkspace string // empty = no workspace in context
globalWorkspace string
containerBase string
want string
wantErr bool
}{
{
name: "no workspace in context — fallback to container base",
ctxWorkspace: "",
globalWorkspace: "/app/workspace",
containerBase: "/workspace",
want: "/workspace",
},
{
name: "workspace equals global mount",
ctxWorkspace: "/app/workspace",
globalWorkspace: "/app/workspace",
containerBase: "/workspace",
want: "/workspace",
},
{
name: "per-agent workspace",
ctxWorkspace: "/app/workspace/agent-a-workspace",
globalWorkspace: "/app/workspace",
containerBase: "/workspace",
want: "/workspace/agent-a-workspace",
},
{
name: "per-user workspace",
ctxWorkspace: "/app/workspace/agent-a/user-123",
globalWorkspace: "/app/workspace",
containerBase: "/workspace",
want: "/workspace/agent-a/user-123",
},
{
name: "team workspace",
ctxWorkspace: "/app/workspace/teams/team-uuid/chat-123",
globalWorkspace: "/app/workspace",
containerBase: "/workspace",
want: "/workspace/teams/team-uuid/chat-123",
},
{
name: "workspace outside global mount — error",
ctxWorkspace: "/other/path/agent-a",
globalWorkspace: "/app/workspace",
containerBase: "/workspace",
wantErr: true,
},
{
name: "custom container base",
ctxWorkspace: "/app/workspace/agent-a",
globalWorkspace: "/app/workspace",
containerBase: "/home/sandbox",
want: "/home/sandbox/agent-a",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
if tt.ctxWorkspace != "" {
ctx = WithToolWorkspace(ctx, tt.ctxWorkspace)
}
got, err := SandboxCwd(ctx, tt.globalWorkspace, tt.containerBase)
if tt.wantErr {
if err == nil {
t.Errorf("SandboxCwd() = %q, want error", got)
}
return
}
if err != nil {
t.Errorf("SandboxCwd() error = %v", err)
return
}
if got != tt.want {
t.Errorf("SandboxCwd() = %q, want %q", got, tt.want)
}
})
}
}
func TestResolveSandboxPath(t *testing.T) {
tests := []struct {
name string
path string
containerCwd string
want string
}{
{
name: "relative path joined with cwd",
path: "file.txt",
containerCwd: "/workspace/agent-a",
want: "/workspace/agent-a/file.txt",
},
{
name: "relative subdirectory path",
path: "subdir/file.txt",
containerCwd: "/workspace/agent-a",
want: "/workspace/agent-a/subdir/file.txt",
},
{
name: "absolute sibling workspace path is rejected to cwd",
path: "/workspace/agent-a/file.txt",
containerCwd: "/workspace/agent-b",
want: "/workspace/agent-b",
},
{
name: "absolute path inside cwd stays absolute",
path: "/workspace/agent-a/file.txt",
containerCwd: "/workspace/agent-a",
want: "/workspace/agent-a/file.txt",
},
{
name: "relative parent escape is rejected to cwd",
path: "../agent-b/file.txt",
containerCwd: "/workspace/agent-a",
want: "/workspace/agent-a",
},
{
name: "dot path",
path: ".",
containerCwd: "/workspace/agent-a",
want: "/workspace/agent-a",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ResolveSandboxPath(tt.path, tt.containerCwd)
if got != tt.want {
t.Errorf("ResolveSandboxPath(%q, %q) = %q, want %q", tt.path, tt.containerCwd, got, tt.want)
}
})
}
}