callPromotedTool: run tool scripts through sandbox instead of exec directly

This commit is contained in:
ollie 2026-07-29 00:35:07 +02:00
parent e3a9ff4bf3
commit 9ea56242c3
1 changed files with 13 additions and 17 deletions

View File

@ -273,7 +273,7 @@ func (e *Server) SetToolRegistry(r *Registry, sessionID string) {
}
// callPromotedTool executes a tool promoted via the registry by running the
// script file directly as an executable.
// script file directly inside the sandbox.
func (e *Server) callPromotedTool(ctx context.Context, tool string, args json.RawMessage) (json.RawMessage, error) {
// Resolve script path.
if strings.Contains(tool, "/") || strings.Contains(tool, "..") {
@ -281,7 +281,7 @@ func (e *Server) callPromotedTool(ctx context.Context, tool string, args json.Ra
}
path := filepath.Join(ToolsPath(), tool)
// Parse positional args.
// Parse positional args into a shell command string.
var positional []string
var argMap map[string]interface{}
if err := json.Unmarshal(args, &argMap); err == nil {
@ -294,29 +294,25 @@ func (e *Server) callPromotedTool(ctx context.Context, tool string, args json.Ra
}
}
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, 30*time.Second)
defer cancel()
// Execute the script directly — shebang handles the interpreter.
e.wdMu.RLock()
workDir := e.cwd
e.wdMu.RUnlock()
if workDir == "" {
workDir, _ = os.Getwd()
// Build a shell command that runs the tool script with its args.
// Quote each arg for safe shell passing.
quoted := make([]string, 0, len(positional)+1)
quoted = append(quoted, path)
for _, a := range positional {
quoted = append(quoted, fmt.Sprintf("%q", a))
}
cmd := exec.CommandContext(ctx, path, positional...)
cmd.Dir = workDir
code := strings.Join(quoted, " ")
output, err := cmd.CombinedOutput()
// Execute through the sandbox via executeWithStdin.
result, err := e.executeWithStdin(ctx, code, "bash", 30, "default", false, "")
if err != nil {
return json.Marshal(map[string]interface{}{
"isError": true,
"content": []map[string]string{{"type": "text", "text": string(output) + ": " + err.Error()}},
"content": []map[string]string{{"type": "text", "text": result + ": " + err.Error()}},
})
}
return json.Marshal(map[string]interface{}{
"content": []map[string]string{{"type": "text", "text": string(output)}},
"content": []map[string]string{{"type": "text", "text": result}},
})
}