refactor(shell): 去掉 preCheckArg,参数由调用方 Quote 处理

只拦整个参数恰好等于元字符的检查既拦不住真正的注入,又会把 it's 这类合法文件名当非法字符拒掉

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
耗子
2026-09-23 02:17:40 +08:00
co-authored by Claude Fable 5.1
parent cc4b8d5acb
commit 3945857701
2 changed files with 12 additions and 52 deletions
+12 -49
View File
@@ -8,7 +8,6 @@ import (
"io"
"os"
"os/exec"
"slices"
"strings"
"syscall"
"time"
@@ -35,16 +34,12 @@ func newCmd(ctx context.Context, shell string) *exec.Cmd {
return cmd
}
// buildShell 校验参数并格式化命令
func buildShell(format string, args ...any) (string, error) {
if !preCheckArg(args) {
return "", errors.New("command contains illegal characters")
}
// buildShell 格式化命令,参数由调用方用 Quote 处理
func buildShell(format string, args ...any) string {
if len(args) == 0 {
return format, nil
return format
}
return fmt.Sprintf(format, args...), nil
return fmt.Sprintf(format, args...)
}
// runBuffered 执行命令并返回 stdout,失败时附带 stderr
@@ -67,20 +62,14 @@ func Exec(ctx context.Context, shell string) (string, error) {
// Execf 按格式串拼出 shell 命令后执行
func Execf(ctx context.Context, format string, args ...any) (string, error) {
shell, err := buildShell(format, args...)
if err != nil {
return "", err
}
shell := buildShell(format, args...)
return Exec(ctx, shell)
}
// ExecfWithEnv 安全执行 shell 命令,环境变量仅注入子进程
func ExecfWithEnv(ctx context.Context, env []string, format string, args ...any) (string, error) {
shell, err := buildShell(format, args...)
if err != nil {
return "", err
}
shell := buildShell(format, args...)
cmd := newCmd(ctx, shell)
ApplyEnv(cmd, env...)
@@ -90,10 +79,7 @@ func ExecfWithEnv(ctx context.Context, env []string, format string, args ...any)
// ExecfWithDir 在指定目录下执行 shell 命令
func ExecfWithDir(ctx context.Context, dir, format string, args ...any) (string, error) {
shell, err := buildShell(format, args...)
if err != nil {
return "", err
}
shell := buildShell(format, args...)
cmd := newCmd(ctx, shell)
cmd.Dir = dir
@@ -103,10 +89,7 @@ func ExecfWithDir(ctx context.Context, dir, format string, args ...any) (string,
// ExecfWithTimeout 执行 shell 命令并设置超时时间,ctx 取消或超时到期均终止进程
func ExecfWithTimeout(ctx context.Context, timeout time.Duration, format string, args ...any) (string, error) {
shell, err := buildShell(format, args...)
if err != nil {
return "", err
}
shell := buildShell(format, args...)
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
@@ -124,17 +107,14 @@ func ExecfWithTimeout(ctx context.Context, timeout time.Duration, format string,
// 异步即命令要活过调用方,入口断开取消链,否则 sleep 1 && systemctl restart acepanel 这类自杀式操作
// 会在 HTTP 响应写完的瞬间被杀且无人察觉
func ExecfAsync(ctx context.Context, format string, args ...any) error {
shell, err := buildShell(format, args...)
if err != nil {
return err
}
shell := buildShell(format, args...)
cmd := exec.CommandContext(context.WithoutCancel(ctx), "bash", "-c", shell)
ApplyEnv(cmd)
// 独立进程组,面板自身被信号终止时不牵连它
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
if err = cmd.Start(); err != nil {
if err := cmd.Start(); err != nil {
return err
}
@@ -158,10 +138,7 @@ func ExecWithOutput(ctx context.Context, shell string) error {
// ExecfWithOutput 按格式串拼出 shell 命令后执行并输出到终端
func ExecfWithOutput(ctx context.Context, format string, args ...any) error {
shell, err := buildShell(format, args...)
if err != nil {
return err
}
shell := buildShell(format, args...)
return ExecWithOutput(ctx, shell)
}
@@ -191,10 +168,7 @@ func ExecWithPipe(ctx context.Context, shell string) (io.ReadCloser, error) {
// ExecfWithPipe 按格式串拼出 shell 命令后执行并返回管道
func ExecfWithPipe(ctx context.Context, format string, args ...any) (io.ReadCloser, error) {
shell, err := buildShell(format, args...)
if err != nil {
return nil, err
}
shell := buildShell(format, args...)
return ExecWithPipe(ctx, shell)
}
@@ -229,14 +203,3 @@ func execWithLog(ctx context.Context, shell string, logFile string, flag int) er
return nil
}
func preCheckArg(args []any) bool {
illegals := []any{`&`, `|`, `;`, `$`, `'`, `"`, "`", `(`, `)`, "\n", "\r", `>`, `<`}
for arg := range slices.Values(args) {
if slices.Contains(illegals, arg) {
return false
}
}
return true
}
-3
View File
@@ -41,9 +41,6 @@ type Turn struct {
// NewPTYTurn 使用 PTY 执行命令,返回 Turn 用于流式读取输出
// 调用方需要负责调用 Close() 和 Wait()
func NewPTYTurn(ctx context.Context, ws *websocket.Conn, shell string, args ...any) (*Turn, error) {
if !preCheckArg(args) {
return nil, errors.New("command contains illegal characters")
}
if len(args) > 0 {
shell = fmt.Sprintf(shell, args...)
}