Exec 安全模型

wesgine 子进程执行安全:进程组管理、输出排空、环境净化与行长保护。


单一 Spawn 原语(INV-EXEC-PROC-01)

生产 Go 一律 proc.Command(ctx, ...):

import "internal/infra/proc"

cmd := proc.Command(ctx, "go", "build", "./...")

内置保障

能力说明
进程组Setpgid 建独立进程组
CancelSIGKILL 整个进程组
WaitDelay给 Wait() 封顶

禁止

// ❌ 裸 exec.Command
exec.Command("go", "build")
exec.CommandContext(ctx, "go", "build")
exec.Cmd{Path: "go"}

// ❌ 改写 Cancel / WaitDelay
cmd := proc.Command(ctx, ...)
cmd.Cancel = nil      // 恢复原缺陷
cmd.WaitDelay = 0     // 恢复原缺陷

豁免

3 个文件带义务:

文件义务
tool/host_shell.go自设 Cancel + WaitDelay
adapter/stt/tool.go设 SysProcAttr 建组(脱离 ctx)

进程组清理(INV-EXEC-11)

前台 exec 超时后必须 KillProcessGroup() 清理整个进程组。

ctx 结束
    ↓ KillProcessGroup()
    ↓ 整个进程组被杀
    ↓ 无逃逸的 grandchild

输出排空(INV-EXEC-DRAIN-01)

exec 输出读取必须无条件消费管道直到 EOF。

禁止 bufio.Scanner

// ❌ Scanner 在 1MB max-token 处停止 → 管道无人消费 → 死锁
scanner := bufio.NewScanner(stdout)
for scanner.Scan() { ... }

正确做法

// ✓ ReadSlice 循环 + cappedWriter

stdout 排空超时(INV-EXEC-12)

进程退出后 stdout 排空最多等 5s,超时强制 CloseOutput()。


环境净化

静态模式表(INV-SEC-04)

SanitizeEnv 两层清理:

  1. 静态模式表匹配(*_KEY、*_SECRET、*_TOKEN 等)
  2. SecretRef{source:env} 变量名登记(INV-SEC-03)

出网脱敏(INV-SEC-02)

唯一执行点 config.RedactSecrets。

SecretRef.MarshalJSON 吐的是含明文的 storage view——落盘要值。


行长保护(INV-LINE-01)

bufio.Scanner 超限时 Scan() 返回 false——与 EOF 同值。

正确做法

场景方案
内存输入bytes.Lines(零拷贝、无上限)
流式输入internal/infra/lineio(超限报告,不结束流)
网络 body显式 .Buffer(...) + 命名常量

截断处置

必须显式选择:

策略适用
致命LLM delta(tool_call 参数是 JSON 片段流)
跳过继续wesgine 事件(自包含,丢一条只赔一条)

相关文档