mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-27 21:33:41 +02:00
291 lines
8.0 KiB
Go
291 lines
8.0 KiB
Go
package execenv
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/pelletier/go-toml/v2"
|
|
)
|
|
|
|
func assertValidToml(t *testing.T, content string) {
|
|
t.Helper()
|
|
var parsed map[string]any
|
|
if err := toml.Unmarshal([]byte(content), &parsed); err != nil {
|
|
t.Fatalf("generated config.toml is invalid: %v\n---\n%s", err, content)
|
|
}
|
|
}
|
|
|
|
func parsedShellEnvPolicy(t *testing.T, content string) codexShellEnvironmentPolicy {
|
|
t.Helper()
|
|
var parsed struct {
|
|
Policy codexShellEnvironmentPolicy `toml:"shell_environment_policy"`
|
|
}
|
|
if err := toml.Unmarshal([]byte(content), &parsed); err != nil {
|
|
t.Fatalf("parse generated config.toml: %v\n---\n%s", err, content)
|
|
}
|
|
return parsed.Policy
|
|
}
|
|
|
|
func TestCodexShellEnvAllowlistUsesExactTaskAndSafeInheritedNames(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
inherited := []string{
|
|
"PATH=/usr/bin",
|
|
"SystemRoot=C:\\Windows",
|
|
"COMSPEC=C:\\Windows\\System32\\cmd.exe",
|
|
"PATHEXT=.COM;.EXE;.BAT;.CMD",
|
|
"USERPROFILE=C:\\Users\\test",
|
|
"APPDATA=C:\\Users\\test\\AppData\\Roaming",
|
|
"LOCALAPPDATA=C:\\Users\\test\\AppData\\Local",
|
|
"LANG=en_US.UTF-8",
|
|
"HTTPS_PROXY=http://proxy.example",
|
|
"SSL_CERT_FILE=/etc/ssl/cert.pem",
|
|
"SDKROOT=/opt/sdk",
|
|
"OPENAI_API_KEY=host-secret",
|
|
"GH_TOKEN=host-secret",
|
|
"MULTICA_LLM_API_KEY=daemon-secret",
|
|
"MULTICA_SERVER_URL=https://wrong.example",
|
|
}
|
|
explicit := map[string]string{
|
|
"MULTICA_TOKEN": "mat_task",
|
|
"MULTICA_SERVER_URL": "https://task.example",
|
|
"CUSTOM_FLAG": "enabled",
|
|
"ANTHROPIC_API_KEY": "agent-secret",
|
|
}
|
|
authorizedExplicit := []string{"ANTHROPIC_API_KEY"}
|
|
|
|
got := CodexShellEnvAllowlist(inherited, explicit, authorizedExplicit)
|
|
want := []string{
|
|
"ANTHROPIC_API_KEY",
|
|
"APPDATA",
|
|
"COMSPEC",
|
|
"CUSTOM_FLAG",
|
|
"HTTPS_PROXY",
|
|
"LANG",
|
|
"LOCALAPPDATA",
|
|
"MULTICA_SERVER_URL",
|
|
"MULTICA_TOKEN",
|
|
"PATH",
|
|
"PATHEXT",
|
|
"SDKROOT",
|
|
"SSL_CERT_FILE",
|
|
"SystemRoot",
|
|
"USERPROFILE",
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("CodexShellEnvAllowlist() = %#v, want %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestCodexShellEnvAllowlistOnlyAuthorizesExplicitCustomSecrets(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
inherited := []string{
|
|
"PATH=/usr/bin",
|
|
"CUSTOM_ACCESS_TOKEN=host-secret",
|
|
"HOST_SECRET=host-secret",
|
|
"MULTICA_TOKEN=daemon-secret",
|
|
}
|
|
explicit := map[string]string{
|
|
"CUSTOM_ACCESS_TOKEN": "agent-secret",
|
|
"x_secret": "agent-secret",
|
|
"Y_KEY": "agent-secret",
|
|
"UNAUTHORIZED_TOKEN": "daemon-secret",
|
|
"MULTICA_TOKEN": "mat_task",
|
|
}
|
|
authorizedExplicit := []string{
|
|
"custom_access_token", // Authorization matching is case-insensitive.
|
|
"X_SECRET",
|
|
"Y_KEY",
|
|
"HOST_SECRET", // Authorization alone cannot expose an inherited value.
|
|
}
|
|
|
|
got := CodexShellEnvAllowlist(inherited, explicit, authorizedExplicit)
|
|
want := []string{
|
|
"CUSTOM_ACCESS_TOKEN",
|
|
"MULTICA_TOKEN",
|
|
"PATH",
|
|
"x_secret",
|
|
"Y_KEY",
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("CodexShellEnvAllowlist() = %#v, want %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestEnsureCodexShellEnvPolicyConfigReplacesAllLegalPolicyForms(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
existing string
|
|
kept []string
|
|
removed []string
|
|
}{
|
|
{
|
|
name: "root dotted multiline arrays",
|
|
existing: `# keep root comment
|
|
model = "gpt-5.6"
|
|
shell_environment_policy.inherit = "none"
|
|
shell_environment_policy.include_only = [
|
|
"PATH", # old item comment
|
|
"HOME",
|
|
]
|
|
shell_environment_policy.exclude = [
|
|
"AWS_*",
|
|
]
|
|
|
|
[features]
|
|
foo = true
|
|
`,
|
|
kept: []string{"# keep root comment", `model = "gpt-5.6"`, "[features]", "foo = true"},
|
|
removed: []string{"old item comment", `"AWS_*"`},
|
|
},
|
|
{
|
|
name: "quoted table and comments",
|
|
existing: `model = "gpt-5.6"
|
|
|
|
["shell_environment_policy"] # legal quoted table
|
|
# policy-local comment remains harmless
|
|
inherit = "none"
|
|
include_only = [
|
|
"PATH",
|
|
"HOME",
|
|
]
|
|
set = { FOO = "bar", MULTICA_TOKEN = "stale" }
|
|
|
|
[features]
|
|
# keep feature comment
|
|
foo = true
|
|
`,
|
|
kept: []string{`model = "gpt-5.6"`, "# policy-local comment remains harmless", "# keep feature comment", "[features]", "foo = true"},
|
|
removed: []string{"legal quoted table", `FOO = "bar"`, `MULTICA_TOKEN = "stale"`},
|
|
},
|
|
{
|
|
name: "inline root and profile policies",
|
|
existing: `shell_environment_policy = { inherit = "none", include_only = ["PATH"] }
|
|
|
|
[profiles.work]
|
|
model = "gpt-5.6"
|
|
shell_environment_policy = { inherit = "core", exclude = ["AWS_*"] }
|
|
|
|
[features]
|
|
foo = true
|
|
|
|
[tools]
|
|
shell_environment_policy = "unrelated key with the same name"
|
|
`,
|
|
kept: []string{"[profiles.work]", `model = "gpt-5.6"`, "[features]", "foo = true", "[tools]", `shell_environment_policy = "unrelated key with the same name"`},
|
|
removed: []string{`inherit = "none"`, `inherit = "core"`, `"AWS_*"`},
|
|
},
|
|
{
|
|
name: "nested profile policy tables",
|
|
existing: `[profiles.work]
|
|
model = "gpt-5.6"
|
|
|
|
[profiles.work.shell_environment_policy]
|
|
include_only = [
|
|
"PATH",
|
|
]
|
|
|
|
[profiles.work.shell_environment_policy.set]
|
|
FOO = "bar"
|
|
|
|
[features]
|
|
foo = true
|
|
`,
|
|
kept: []string{"[profiles.work]", `model = "gpt-5.6"`, "[features]", "foo = true"},
|
|
removed: []string{"[profiles.work.shell_environment_policy]", "[profiles.work.shell_environment_policy.set]", `FOO = "bar"`},
|
|
},
|
|
}
|
|
|
|
includeOnly := []string{"CUSTOM_FLAG", "MULTICA_SERVER_URL", "MULTICA_TOKEN", "PATH", "SystemRoot"}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
configPath := filepath.Join(t.TempDir(), "config.toml")
|
|
if err := os.WriteFile(configPath, []byte(tt.existing), 0o644); err != nil {
|
|
t.Fatalf("write config.toml: %v", err)
|
|
}
|
|
|
|
if err := EnsureCodexShellEnvPolicyConfig(configPath, includeOnly, testLogger()); err != nil {
|
|
t.Fatalf("EnsureCodexShellEnvPolicyConfig: %v", err)
|
|
}
|
|
data, err := os.ReadFile(configPath)
|
|
if err != nil {
|
|
t.Fatalf("read config.toml: %v", err)
|
|
}
|
|
s := string(data)
|
|
assertValidToml(t, s)
|
|
for _, want := range tt.kept {
|
|
if !strings.Contains(s, want) {
|
|
t.Errorf("lost unrelated content %q:\n%s", want, s)
|
|
}
|
|
}
|
|
for _, unwanted := range tt.removed {
|
|
if strings.Contains(s, unwanted) {
|
|
t.Errorf("stale policy content %q remains:\n%s", unwanted, s)
|
|
}
|
|
}
|
|
if strings.Contains(s, `"MULTICA_*"`) {
|
|
t.Fatalf("managed policy must not use a MULTICA_* wildcard:\n%s", s)
|
|
}
|
|
if n := strings.Count(s, "[shell_environment_policy]"); n != 1 {
|
|
t.Fatalf("expected exactly one managed policy table, got %d:\n%s", n, s)
|
|
}
|
|
policy := parsedShellEnvPolicy(t, s)
|
|
if policy.Inherit != "all" || !policy.IgnoreDefaultExcludes {
|
|
t.Fatalf("unexpected managed policy: %#v", policy)
|
|
}
|
|
if !reflect.DeepEqual(policy.IncludeOnly, includeOnly) {
|
|
t.Fatalf("include_only = %#v, want %#v", policy.IncludeOnly, includeOnly)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEnsureCodexShellEnvPolicyConfigRejectsInvalidInputWithoutWriting(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
configPath := filepath.Join(t.TempDir(), "config.toml")
|
|
existing := []byte("model = [\n")
|
|
if err := os.WriteFile(configPath, existing, 0o644); err != nil {
|
|
t.Fatalf("write config.toml: %v", err)
|
|
}
|
|
if err := EnsureCodexShellEnvPolicyConfig(configPath, []string{"MULTICA_TOKEN", "PATH"}, testLogger()); err == nil {
|
|
t.Fatal("expected malformed input to fail")
|
|
}
|
|
got, err := os.ReadFile(configPath)
|
|
if err != nil {
|
|
t.Fatalf("read config.toml: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(got, existing) {
|
|
t.Fatalf("invalid input was modified: got %q, want %q", got, existing)
|
|
}
|
|
}
|
|
|
|
func TestEnsureCodexShellEnvPolicyConfigIsIdempotent(t *testing.T) {
|
|
t.Parallel()
|
|
configPath := filepath.Join(t.TempDir(), "config.toml")
|
|
includeOnly := []string{"MULTICA_SERVER_URL", "MULTICA_TOKEN", "PATH"}
|
|
|
|
for i := 0; i < 3; i++ {
|
|
if err := EnsureCodexShellEnvPolicyConfig(configPath, includeOnly, testLogger()); err != nil {
|
|
t.Fatalf("pass %d: %v", i, err)
|
|
}
|
|
}
|
|
|
|
data, err := os.ReadFile(configPath)
|
|
if err != nil {
|
|
t.Fatalf("read config.toml: %v", err)
|
|
}
|
|
s := string(data)
|
|
assertValidToml(t, s)
|
|
if n := strings.Count(s, multicaShellEnvBeginMarker); n != 1 {
|
|
t.Fatalf("expected exactly one managed block, got %d:\n%s", n, s)
|
|
}
|
|
}
|