Skip to main content
Glama
executor.goβ€’4.51 kB
package main import ( "bytes" "context" "encoding/base64" "fmt" "os" "os/exec" "os/user" "strconv" "strings" "syscall" "time" "github.com/rs/zerolog" ) type ExecutionResult struct { Status string `json:"status"` ExitCode int `json:"exit_code"` Stdout string `json:"stdout"` Stderr string `json:"stderr"` Command string `json:"command"` ExecutionTime time.Duration `json:"execution_time"` SecurityInfo *SecurityInfo `json:"security_info,omitempty"` } type SecurityInfo struct { SecurityEnabled bool `json:"security_enabled"` WorkingDir string `json:"working_dir,omitempty"` RunAsUser string `json:"run_as_user,omitempty"` TimeoutApplied bool `json:"timeout_applied"` } type CommandExecutor struct { config SecurityConfig logger zerolog.Logger } func newCommandExecutor(cfg SecurityConfig, logger zerolog.Logger) *CommandExecutor { return &CommandExecutor{ config: cfg, logger: logger.With().Str("component", "executor").Logger(), } } func (e *CommandExecutor) execute( ctx context.Context, command string, useBase64 bool, ) (*ExecutionResult, error) { start := time.Now() e.logger.Info(). Str("command", command). Bool("base64", useBase64). Msg("Executing command") timeout := 30 * time.Second if e.config.MaxExecutionTime > 0 { timeout = e.config.MaxExecutionTime } cmdCtx, cancel := context.WithTimeout(ctx, timeout) defer cancel() result, err := e.executeSecureCommand(cmdCtx, command, useBase64) if err != nil { e.logger.Error(). Err(err). Str("command", command). Msg("Command execution failed") return nil, err } result.ExecutionTime = time.Since(start) result.SecurityInfo = &SecurityInfo{ SecurityEnabled: e.config.Enabled, TimeoutApplied: true, } if e.config.WorkingDirectory != "" { result.SecurityInfo.WorkingDir = e.config.WorkingDirectory } if e.config.RunAsUser != "" { result.SecurityInfo.RunAsUser = e.config.RunAsUser } e.logger.Info(). Str("command", command). Str("status", result.Status). Int("exit_code", result.ExitCode). Dur("execution_time", result.ExecutionTime). Msg("Command execution completed") return result, nil } func (e *CommandExecutor) executeSecureCommand( ctx context.Context, command string, useBase64 bool, ) (*ExecutionResult, error) { cmd := exec.CommandContext(ctx, "bash", "-c", command) if e.config.WorkingDirectory != "" { if err := os.MkdirAll(e.config.WorkingDirectory, 0755); err == nil { cmd.Dir = e.config.WorkingDirectory e.logger.Debug(). Str("working_dir", e.config.WorkingDirectory). Msg("Set working directory") } } if e.config.RunAsUser != "" { if u, err := user.Lookup(e.config.RunAsUser); err == nil { if uid, err := strconv.Atoi(u.Uid); err == nil { if gid, err := strconv.Atoi(u.Gid); err == nil { cmd.SysProcAttr = &syscall.SysProcAttr{ Credential: &syscall.Credential{ Uid: uint32(uid), Gid: uint32(gid), }, } e.logger.Debug(). Str("user", e.config.RunAsUser). Int("uid", uid). Int("gid", gid). Msg("Set process credentials") } } } } var stdoutBuf, stderrBuf bytes.Buffer cmd.Stdout = &stdoutBuf cmd.Stderr = &stderrBuf err := cmd.Run() if e.config.MaxOutputSize > 0 { if stdoutBuf.Len() > e.config.MaxOutputSize { e.logger.Warn(). Int("stdout_size", stdoutBuf.Len()). Int("max_size", e.config.MaxOutputSize). Msg("Stdout exceeds maximum size limit") return nil, fmt.Errorf("stdout exceeds maximum size limit") } if stderrBuf.Len() > e.config.MaxOutputSize { e.logger.Warn(). Int("stderr_size", stderrBuf.Len()). Int("max_size", e.config.MaxOutputSize). Msg("Stderr exceeds maximum size limit") return nil, fmt.Errorf("stderr exceeds maximum size limit") } } exitCode := 0 status := "success" if err != nil { status = "error" if exitError, ok := err.(*exec.ExitError); ok { exitCode = exitError.ExitCode() } else { exitCode = -1 } } var stdout, stderr string if useBase64 { stdout = base64.StdEncoding.EncodeToString(stdoutBuf.Bytes()) stderr = base64.StdEncoding.EncodeToString(stderrBuf.Bytes()) } else { stdout = strings.TrimRight(stdoutBuf.String(), "\n") stderr = strings.TrimRight(stderrBuf.String(), "\n") } return &ExecutionResult{ Status: status, ExitCode: exitCode, Stdout: stdout, Stderr: stderr, Command: command, }, nil }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sonirico/mcp-shell'

If you have feedback or need assistance with the MCP directory API, please join our Discord server