Add explicit LC_* vars and PICOCLAW_* prefix blocklist
- Add explicit LC_* vars to default allowlist (locale vars) - Remove prefix matching for allowlist (use explicit only) - Add isBlocked() function for blocklist prefix matching - All PICOCLAW_* vars now blocked from LLM override - Update config and docs
This commit is contained in:
parent
09b42753bf
commit
00508b9636
3 changed files with 44 additions and 6 deletions
|
|
@ -58,13 +58,15 @@ The exec tool sanitizes the environment passed to child processes:
|
||||||
|
|
||||||
1. **Default allowlist** — Only these variables are inherited from the parent process:
|
1. **Default allowlist** — Only these variables are inherited from the parent process:
|
||||||
- `PATH`, `HOME`, `USER`, `LANG`, `SHELL`, `TERM`, `PWD`, `OLDPWD`, `HOSTNAME`, `LOGNAME`, `TZ`, `DISPLAY`, `TMPDIR`, `EDITOR`, `PAGER`
|
- `PATH`, `HOME`, `USER`, `LANG`, `SHELL`, `TERM`, `PWD`, `OLDPWD`, `HOSTNAME`, `LOGNAME`, `TZ`, `DISPLAY`, `TMPDIR`, `EDITOR`, `PAGER`
|
||||||
- Plus: `PICOCLAW_CONFIG`, `PICOCLAW_HOME`, `PICOCLAW_SERVICE_NAME`, `PICOCLAW_EXE`, `PICOCLAW_WORKSPACE`
|
|
||||||
- Plus: `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`
|
- Plus: `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`
|
||||||
- Plus any variable starting with `LC_`
|
- Plus locale: `LC_ALL`, `LC_CTYPE`, `LC_MESSAGES`, `LC_MONETARY`, `LC_NUMERIC`, `LC_TIME`, `LC_PAPER`, `LC_NAME`, `LC_ADDRESS`, `LC_TELEPHONE`, `LC_MEASUREMENT`, `LC_IDENTIFICATION`, `LC_COLLATE`
|
||||||
|
- Plus: `PICOCLAW_HOME`, `PICOCLAW_CONFIG`, `PICOCLAW_AGENT_WORKSPACE`, `PICOCLAW_EXE`, `PICOCLAW_SERVICE_NAME`, `PICOCLAW_EXEC_TIME`, `PICOCLAW_EXEC_TIMEOUT`
|
||||||
|
|
||||||
2. **Config env_set** — Variables from config are merged (can override inherited values)
|
2. **Config env_set** — Variables from config are merged (can override inherited values)
|
||||||
|
|
||||||
3. **LLM env injection** — The LLM can inject additional variables per-call via the `env` parameter:
|
3. **Config env_allowlist** — Additional explicit variable names to allow (extends default)
|
||||||
|
|
||||||
|
4. **LLM env injection** — The LLM can inject additional variables per-call via the `env` parameter:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -81,6 +83,7 @@ The exec tool sanitizes the environment passed to child processes:
|
||||||
**Blocked variables** — The LLM cannot override these sensitive variables:
|
**Blocked variables** — The LLM cannot override these sensitive variables:
|
||||||
- `PATH`, `HOME`, `USER`, `LOGNAME`, `SHELL`
|
- `PATH`, `HOME`, `USER`, `LOGNAME`, `SHELL`
|
||||||
- `LD_PRELOAD`, `LD_LIBRARY_PATH`, `LD_AUDIT`, `LD_DEBUG`
|
- `LD_PRELOAD`, `LD_LIBRARY_PATH`, `LD_AUDIT`, `LD_DEBUG`
|
||||||
|
- All `PICOCLAW_*` variables
|
||||||
|
|
||||||
### Default Blocked Command Patterns
|
### Default Blocked Command Patterns
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -646,7 +646,7 @@ type ExecConfig struct {
|
||||||
CustomAllowPatterns []string ` env:"PICOCLAW_TOOLS_EXEC_CUSTOM_ALLOW_PATTERNS" json:"custom_allow_patterns"`
|
CustomAllowPatterns []string ` env:"PICOCLAW_TOOLS_EXEC_CUSTOM_ALLOW_PATTERNS" json:"custom_allow_patterns"`
|
||||||
TimeoutSeconds int ` env:"PICOCLAW_TOOLS_EXEC_TIMEOUT_SECONDS" json:"timeout_seconds"` // 0 means use default (60s)
|
TimeoutSeconds int ` env:"PICOCLAW_TOOLS_EXEC_TIMEOUT_SECONDS" json:"timeout_seconds"` // 0 means use default (60s)
|
||||||
EnvSet map[string]string ` json:"env_set"` // env vars to set for all exec commands
|
EnvSet map[string]string ` json:"env_set"` // env vars to set for all exec commands
|
||||||
EnvAllowlist []string ` json:"env_allowlist"` // additional env vars to allow (extends default)
|
EnvAllowlist []string ` json:"env_allowlist"` // additional env vars to allow (extends default) - use explicit names, not wildcards
|
||||||
}
|
}
|
||||||
|
|
||||||
type SkillsToolsConfig struct {
|
type SkillsToolsConfig struct {
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,27 @@ var DefaultEnvAllowlist = map[string]bool{
|
||||||
"HTTP_PROXY": true,
|
"HTTP_PROXY": true,
|
||||||
"HTTPS_PROXY": true,
|
"HTTPS_PROXY": true,
|
||||||
"NO_PROXY": true,
|
"NO_PROXY": true,
|
||||||
|
|
||||||
|
// Locale
|
||||||
|
"LC_ALL": true,
|
||||||
|
"LC_CTYPE": true,
|
||||||
|
"LC_MESSAGES": true,
|
||||||
|
"LC_MONETARY": true,
|
||||||
|
"LC_NUMERIC": true,
|
||||||
|
"LC_TIME": true,
|
||||||
|
"LC_PAPER": true,
|
||||||
|
"LC_NAME": true,
|
||||||
|
"LC_ADDRESS": true,
|
||||||
|
"LC_TELEPHONE": true,
|
||||||
|
"LC_MEASUREMENT": true,
|
||||||
|
"LC_IDENTIFICATION": true,
|
||||||
|
"LC_COLLATE": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultEnvAllowPrefixes are env var prefixes that are always allowed.
|
// defaultEnvAllowPrefixes are env var prefixes that are always allowed.
|
||||||
|
// Currently empty - all allowed vars are explicit in DefaultEnvAllowlist.
|
||||||
var defaultEnvAllowPrefixes = []string{
|
var defaultEnvAllowPrefixes = []string{
|
||||||
"LC_",
|
// Currently empty - all allowed vars are explicit
|
||||||
}
|
}
|
||||||
|
|
||||||
// LLMBlocklist is the set of environment variable names that the LLM
|
// LLMBlocklist is the set of environment variable names that the LLM
|
||||||
|
|
@ -119,6 +135,25 @@ func WithAllowedEnv(envSet map[string]string, extraAllowlist []string) map[strin
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LLMBlocklistPrefixes are env var prefixes that the LLM cannot override.
|
||||||
|
var LLMBlocklistPrefixes = []string{
|
||||||
|
"PICOCLAW_",
|
||||||
|
}
|
||||||
|
|
||||||
|
// isBlocked returns true if the key is in the blocklist or matches a blocked prefix.
|
||||||
|
func isBlocked(key string) bool {
|
||||||
|
norm := envKey(key)
|
||||||
|
if LLMBlocklist[norm] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, prefix := range LLMBlocklistPrefixes {
|
||||||
|
if strings.HasPrefix(norm, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// MergeEnvVars merges multiple env sources into a final []string for exec.Cmd.Env.
|
// MergeEnvVars merges multiple env sources into a final []string for exec.Cmd.Env.
|
||||||
// baseEnv is the cached map from AllowedEnv.
|
// baseEnv is the cached map from AllowedEnv.
|
||||||
// envSet provides explicit key=value pairs (config, not filtered).
|
// envSet provides explicit key=value pairs (config, not filtered).
|
||||||
|
|
@ -141,7 +176,7 @@ func MergeEnvVars(baseEnv map[string]string, envSet, extraEnv map[string]string)
|
||||||
// Merge extraEnv (LLM-provided) - filtered by blocklist
|
// Merge extraEnv (LLM-provided) - filtered by blocklist
|
||||||
if extraEnv != nil {
|
if extraEnv != nil {
|
||||||
for k, v := range extraEnv {
|
for k, v := range extraEnv {
|
||||||
if LLMBlocklist[envKey(k)] {
|
if isBlocked(k) {
|
||||||
continue // Skip blocked vars
|
continue // Skip blocked vars
|
||||||
}
|
}
|
||||||
vars[envKey(k)] = v
|
vars[envKey(k)] = v
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue