feat(exec): 本地执行 Provider + 放权终端沙箱(白名单/工作目录/超时/输出截断/默认关)+ 文件沙箱;config 增 ExecSandbox(默认关)
This commit is contained in:
1 parent
88dc12466d
commit
430fc4666b
11 files changed
+1199
-20
No files matched your search
+21
-20
@@ -15,21 +15,22 @@ import (
|
||||
|
||||
// Config is the root configuration tree.
|
||||
type Config struct {
|
||||
Env string `env:"ENV" default:"development"`
|
||||
LogLevel string `env:"LOG_LEVEL" default:"debug"`
|
||||
Timezone string `env:"TZ" default:"Asia/Shanghai"`
|
||||
DataDir string `env:"DATA_DIR" default:"./data"`
|
||||
AllowedOrigins []string `env:"ALLOWED_ORIGINS" default:"http://localhost:5173,http://localhost:3000"`
|
||||
Env string `env:"ENV" default:"development"`
|
||||
LogLevel string `env:"LOG_LEVEL" default:"debug"`
|
||||
Timezone string `env:"TZ" default:"Asia/Shanghai"`
|
||||
DataDir string `env:"DATA_DIR" default:"./data"`
|
||||
AllowedOrigins []string `env:"ALLOWED_ORIGINS" default:"http://localhost:5173,http://localhost:3000"`
|
||||
|
||||
Server ServerConfig
|
||||
Auth AuthConfig
|
||||
DB DBConfig
|
||||
LLM LLMConfig
|
||||
Embedding EmbeddingConfig
|
||||
IoT IoTConfig
|
||||
Voice VoiceConfig
|
||||
Background BackgroundConfig
|
||||
WSHub WSHubConfig
|
||||
Server ServerConfig
|
||||
Auth AuthConfig
|
||||
DB DBConfig
|
||||
LLM LLMConfig
|
||||
Embedding EmbeddingConfig
|
||||
IoT IoTConfig
|
||||
Voice VoiceConfig
|
||||
Background BackgroundConfig
|
||||
WSHub WSHubConfig
|
||||
ExecSandbox ExecSandbox
|
||||
}
|
||||
|
||||
// ServerConfig holds the single gateway listener settings.
|
||||
@@ -76,16 +77,16 @@ type IoTConfig struct {
|
||||
|
||||
// VoiceConfig holds TTS/STT provider settings.
|
||||
type VoiceConfig struct {
|
||||
TTSProvider string `env:"TTS_PROVIDER" default:"edge-tts"`
|
||||
STTProvider string `env:"STT_PROVIDER" default:"dashscope"`
|
||||
DashscopeAPIKey string `env:"DASHSCOPE_API_KEY"`
|
||||
TTSProvider string `env:"TTS_PROVIDER" default:"edge-tts"`
|
||||
STTProvider string `env:"STT_PROVIDER" default:"dashscope"`
|
||||
DashscopeAPIKey string `env:"DASHSCOPE_API_KEY"`
|
||||
}
|
||||
|
||||
// BackgroundConfig holds the autonomous thinking settings.
|
||||
type BackgroundConfig struct {
|
||||
Enabled bool `env:"ENABLE_BACKGROUND_THINKING" default:"true"`
|
||||
IntervalSec int `env:"THINK_INTERVAL_SEC" default:"300"`
|
||||
SilenceTimeout int `env:"THINK_SILENCE_TIMEOUT_SEC" default:"120"`
|
||||
Enabled bool `env:"ENABLE_BACKGROUND_THINKING" default:"true"`
|
||||
IntervalSec int `env:"THINK_INTERVAL_SEC" default:"300"`
|
||||
SilenceTimeout int `env:"THINK_SILENCE_TIMEOUT_SEC" default:"120"`
|
||||
}
|
||||
|
||||
// WSHubConfig holds the WebSocket hub limits.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package config
|
||||
|
||||
// ExecSandbox holds the grant-based local execution sandbox settings
|
||||
// (DevDocs/design/04-运行时.md §8 and design/11 §5). The sandbox is closed by
|
||||
// default: the agent may run commands only when Enabled is true, and even then
|
||||
// only within the command allowlist and the root boundaries configured here.
|
||||
type ExecSandbox struct {
|
||||
// Enabled opens the sandbox. It is false by default — the agent cannot
|
||||
// execute anything unless the owner explicitly enables it. This is the
|
||||
// "default off, explicit grant" red line.
|
||||
Enabled bool `env:"EXEC_SANDBOX_ENABLED" default:"false"`
|
||||
|
||||
// CommandList is the allowlist of command names (argv[0]) the agent may run.
|
||||
// An empty list denies every command (fail closed).
|
||||
CommandList []string `env:"EXEC_SANDBOX_COMMANDS"`
|
||||
|
||||
// AllowedRoots are the directories commands and files are confined to.
|
||||
// Working directories and file paths must resolve inside one of them.
|
||||
AllowedRoots []string `env:"EXEC_SANDBOX_ROOTS"`
|
||||
|
||||
// AllowedEnv lists the environment variable names the agent may set on a
|
||||
// subprocess. Variable names outside this list are rejected.
|
||||
AllowedEnv []string `env:"EXEC_SANDBOX_ENV"`
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExecSandbox_DefaultDisabled(t *testing.T) {
|
||||
t.Setenv("JWT_SECRET", strings.Repeat("s", 40))
|
||||
t.Setenv("EXEC_SANDBOX_ENABLED", "false")
|
||||
t.Setenv("EXEC_SANDBOX_COMMANDS", "")
|
||||
t.Setenv("EXEC_SANDBOX_ROOTS", "")
|
||||
t.Setenv("EXEC_SANDBOX_ENV", "")
|
||||
|
||||
cfg, err := Load("")
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error: %v", err)
|
||||
}
|
||||
if cfg.ExecSandbox.Enabled {
|
||||
t.Errorf("ExecSandbox.Enabled = true, want default false (sandbox closed)")
|
||||
}
|
||||
if len(cfg.ExecSandbox.CommandList) != 0 {
|
||||
t.Errorf("CommandList = %v, want empty", cfg.ExecSandbox.CommandList)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecSandbox_EnvOverrides(t *testing.T) {
|
||||
t.Setenv("JWT_SECRET", strings.Repeat("s", 40))
|
||||
t.Setenv("EXEC_SANDBOX_ENABLED", "true")
|
||||
t.Setenv("EXEC_SANDBOX_COMMANDS", "echo,pwd")
|
||||
t.Setenv("EXEC_SANDBOX_ROOTS", "/tmp/sandbox,/home/me/workspace")
|
||||
t.Setenv("EXEC_SANDBOX_ENV", "PATH,HOME")
|
||||
|
||||
cfg, err := Load("")
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error: %v", err)
|
||||
}
|
||||
if !cfg.ExecSandbox.Enabled {
|
||||
t.Errorf("ExecSandbox.Enabled = false, want true")
|
||||
}
|
||||
if want := []string{"echo", "pwd"}; !reflect.DeepEqual(cfg.ExecSandbox.CommandList, want) {
|
||||
t.Errorf("CommandList = %v, want %v", cfg.ExecSandbox.CommandList, want)
|
||||
}
|
||||
if want := []string{"/tmp/sandbox", "/home/me/workspace"}; !reflect.DeepEqual(cfg.ExecSandbox.AllowedRoots, want) {
|
||||
t.Errorf("AllowedRoots = %v, want %v", cfg.ExecSandbox.AllowedRoots, want)
|
||||
}
|
||||
if want := []string{"PATH", "HOME"}; !reflect.DeepEqual(cfg.ExecSandbox.AllowedEnv, want) {
|
||||
t.Errorf("AllowedEnv = %v, want %v", cfg.ExecSandbox.AllowedEnv, want)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user