fix: block find / from bypassing workspace sandbox (fixes #2688)
This commit is contained in:
parent
f62de5c0d4
commit
ae484149ae
2 changed files with 57 additions and 1 deletions
|
|
@ -95,10 +95,12 @@ var (
|
|||
regexp.MustCompile(`\bssh\b.*@`),
|
||||
regexp.MustCompile(`\beval\b`),
|
||||
regexp.MustCompile(`\bsource\s+.*\.sh\b`),
|
||||
regexp.MustCompile(`\bfind\s+/\b`), // find / - traverse entire filesystem
|
||||
regexp.MustCompile(`\bls\s+/\b`), // ls / - list root directory
|
||||
}
|
||||
|
||||
// absolutePathPattern matches absolute file paths in commands (Unix and Windows).
|
||||
absolutePathPattern = regexp.MustCompile(`[A-Za-z]:\\[^\\\"']+|/[^\s\"']+`)
|
||||
absolutePathPattern = regexp.MustCompile(`[A-Za-z]:\\[^\\\"']+|/(?:[^\s\"']*)?`)
|
||||
|
||||
// safePaths are kernel pseudo-devices that are always safe to reference in
|
||||
// commands, regardless of workspace restriction. They contain no user data
|
||||
|
|
@ -111,6 +113,7 @@ var (
|
|||
"/dev/stdin": true,
|
||||
"/dev/stdout": true,
|
||||
"/dev/stderr": true,
|
||||
"/": true, // root is a path boundary, not a regular file
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1613,3 +1613,56 @@ func TestEncodeKeyTokenWithPtyKeyMode(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShellTool_FindRootBlocked(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
tool, err := NewExecTool(tmpDir, true)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to configure exec tool: %s", err)
|
||||
}
|
||||
|
||||
blocked := []string{
|
||||
"find / -name 'private*' -type f 2>/dev/null",
|
||||
"find /etc -name 'passwd'",
|
||||
"find / -type f -name '*.key'",
|
||||
"ls /",
|
||||
"ls /etc",
|
||||
}
|
||||
|
||||
for _, cmd := range blocked {
|
||||
result := tool.Execute(context.Background(), map[string]any{
|
||||
"action": "run",
|
||||
"command": cmd,
|
||||
})
|
||||
if !result.IsError {
|
||||
t.Errorf("expected command to be blocked: %s", cmd)
|
||||
}
|
||||
if !strings.Contains(result.ForLLM, "blocked") {
|
||||
t.Errorf("expected 'blocked' message for: %s\ngot: %s", cmd, result.ForLLM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestShellTool_FindInWorkspaceAllowed(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
tool, err := NewExecTool(tmpDir, true)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to configure exec tool: %s", err)
|
||||
}
|
||||
|
||||
allowed := []string{
|
||||
"find . -name '*.go'",
|
||||
"find -name '*.txt'",
|
||||
"echo hello",
|
||||
}
|
||||
|
||||
for _, cmd := range allowed {
|
||||
result := tool.Execute(context.Background(), map[string]any{
|
||||
"action": "run",
|
||||
"command": cmd,
|
||||
})
|
||||
if result.IsError && strings.Contains(result.ForLLM, "blocked") {
|
||||
t.Errorf("command should not be blocked: %s\n error: %s", cmd, result.ForLLM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue