diff --git a/backend/internal/deployment/ssh.go b/backend/internal/deployment/ssh.go index c602eb7..69bc560 100644 --- a/backend/internal/deployment/ssh.go +++ b/backend/internal/deployment/ssh.go @@ -420,8 +420,8 @@ func (c *sshClientAdapter) Close() error { } func validateCommand(command []string) error { - for _, token := range command { - if strings.TrimSpace(token) == "" || strings.ContainsAny(token, "\x00\r\n") { + for index, token := range command { + if strings.TrimSpace(token) == "" || strings.ContainsRune(token, '\x00') || (strings.ContainsAny(token, "\r\n") && !(len(command) == 3 && index == 2 && command[0] == "sh" && command[1] == "-c")) { return errors.New("远程命令参数无效") } } diff --git a/backend/internal/deployment/ssh_test.go b/backend/internal/deployment/ssh_test.go index 8151b9d..194197c 100644 --- a/backend/internal/deployment/ssh_test.go +++ b/backend/internal/deployment/ssh_test.go @@ -59,6 +59,13 @@ func TestValidateCommandRejectsShellControlLines(t *testing.T) { } } +func TestValidateCommandAllowsShellScriptArgument(t *testing.T) { + command := []string{"sh", "-c", "set -eu\nprintf '%s\\n' 'ok'"} + if err := validateCommand(command); err != nil { + t.Fatalf("shell script argument should be accepted: %v", err) + } +} + func TestJoinShellArgsQuotesArguments(t *testing.T) { command := []string{"nginx", "-c", "/etc/nginx/my config.conf", "reload'check"} joined := joinShellArgs(command)