@@ -4,6 +4,7 @@ import (
5 "encoding/json"
5 "encoding/json"
@@ -41,9 +42,74 @@ const (
43var bannedCommands = []string{
44var bannedCommands = []string{
44− "alias", "curl", "curlie", "wget", "axel", "aria2c",
45+ // Network/Download tools
45− "nc", "telnet", "lynx", "w3m", "links", "httpie", "xh",
46+ "alias",
46− "http-prompt", "chrome", "firefox", "safari",
47+ "aria2c",
64+ // System administration
91+ // System modification
104+ // Network configuration
49// getSafeReadOnlyCommands returns platform-appropriate safe commands
115// getSafeReadOnlyCommands returns platform-appropriate safe commands
@@ -244,7 +310,42 @@ Important:
244- Never update git config`, bannedCommandsStr, MaxOutputLength)
310- Never update git config`, bannedCommandsStr, MaxOutputLength)
313+func blockFuncs() []shell.BlockFunc {
314+ return []shell.BlockFunc{
315+ shell.CommandsBlocker(bannedCommands),
316+ shell.ArgumentsBlocker([][]string{
317+ // System package managers
320+ {"apt-get", "install"},
326+ {"zypper", "install"},
328+ // Language-specific package managers
329+ {"brew", "install"},
330+ {"cargo", "install"},
333+ {"npm", "install", "-g"},
334+ {"npm", "install", "--global"},
335+ {"pip", "install", "--user"},
336+ {"pip3", "install", "--user"},
337+ {"pnpm", "add", "-g"},
338+ {"pnpm", "add", "--global"},
339+ {"yarn", "global", "add"},
247func NewBashTool(permission permission.Service, workingDir string) BaseTool {
344func NewBashTool(permission permission.Service, workingDir string) BaseTool {
345+ // Set up command blocking on the persistent shell
346+ persistentShell := shell.GetPersistentShell(workingDir)
347+ persistentShell.SetBlockFuncs(blockFuncs())
248 return &bashTool{
349 return &bashTool{
249 permissions: permission,
350 permissions: permission,
250 workingDir: workingDir,
351 workingDir: workingDir,
@@ -289,13 +390,6 @@ func (b *bashTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error)
289 return NewTextErrorResponse("missing command"), nil
390 return NewTextErrorResponse("missing command"), nil
292− baseCmd := strings.Fields(params.Command)[0]
293− for _, banned := range bannedCommands {
294− if strings.EqualFold(baseCmd, banned) {
295− return NewTextErrorResponse(fmt.Sprintf("command '%s' is not allowed", baseCmd)), nil
299 isSafeReadOnly := false
393 isSafeReadOnly := false
300 cmdLower := strings.ToLower(params.Command)
394 cmdLower := strings.ToLower(params.Command)
@@ -349,7 +443,20 @@ func (b *bashTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error)
349 stdout = truncateOutput(stdout)
443 stdout = truncateOutput(stdout)
350 stderr = truncateOutput(stderr)
444 stderr = truncateOutput(stderr)
446+ slog.Info("Bash command executed",
447+ "command", params.Command,
450+ "exit_code", exitCode,
451+ "interrupted", interrupted,
352 errorMessage := stderr
455 errorMessage := stderr
456+ if errorMessage == "" && err != nil {
457+ errorMessage = err.Error()
353 if interrupted {
460 if interrupted {
354 if errorMessage != "" {
461 if errorMessage != "" {
355 errorMessage += "\n"
462 errorMessage += "\n"