diff --git a/internal/tools/exec_approval.go b/internal/tools/exec_approval.go index ead52043..35aaec38 100644 --- a/internal/tools/exec_approval.go +++ b/internal/tools/exec_approval.go @@ -61,8 +61,8 @@ var safeBins = map[string]bool{ // Read-only / info tools "cat": true, "echo": true, "ls": true, "pwd": true, "head": true, "tail": true, "wc": true, "sort": true, "uniq": true, "grep": true, - "find": true, "which": true, "whoami": true, "date": true, "env": true, - "printenv": true, "uname": true, "id": true, "hostname": true, + "find": true, "which": true, "whoami": true, "date": true, + "uname": true, "id": true, "hostname": true, "df": true, "du": true, "free": true, "uptime": true, "file": true, "stat": true, "dirname": true, "basename": true, "realpath": true, // Text processing diff --git a/internal/tools/scrub.go b/internal/tools/scrub.go index 2b670634..18c65c68 100644 --- a/internal/tools/scrub.go +++ b/internal/tools/scrub.go @@ -19,6 +19,15 @@ var credentialPatterns = []*regexp.Regexp{ regexp.MustCompile(`AKIA[A-Z0-9]{16}`), // Generic key=value patterns (case-insensitive) regexp.MustCompile(`(?i)(api[_-]?key|token|secret|password|bearer|authorization)\s*[:=]\s*["']?\S{8,}["']?`), + + // Connection strings (PostgreSQL, MySQL, MongoDB, Redis, AMQP) + regexp.MustCompile(`(?i)(postgres|postgresql|mysql|mongodb|redis|amqp)://[^\s"']+`), + // Generic KEY=/SECRET=/CREDENTIAL= env-var patterns (skip already-redacted [REDACTED] values) + regexp.MustCompile(`(?i)[A-Z_]*(KEY|SECRET|CREDENTIAL|PRIVATE)[A-Z_]*\s*=\s*[^\[\s]{8,}`), + // DSN/DATABASE_URL env vars (skip already-redacted values) + regexp.MustCompile(`(?i)(DSN|DATABASE_URL|REDIS_URL|MONGO_URI)\s*=\s*[^\[\s]{8,}`), + // Long hex strings (64+ chars) — likely encryption keys, hashes, or secrets + regexp.MustCompile(`[a-fA-F0-9]{64,}`), } const redactedPlaceholder = "[REDACTED]" diff --git a/internal/tools/shell.go b/internal/tools/shell.go index 5e7b0cf7..4eaf32bd 100644 --- a/internal/tools/shell.go +++ b/internal/tools/shell.go @@ -107,6 +107,16 @@ var defaultDenyPatterns = []*regexp.Regexp{ // ── Process manipulation ── regexp.MustCompile(`\bkill\s+-9\s`), regexp.MustCompile(`\b(killall|pkill)\b`), + + // ── Environment variable dumping ── + // Bare env/printenv/set/export dumps all vars including secrets (API keys, DSN, encryption keys). + // 'env VAR=val cmd' (env with assignment before command) is still allowed. + regexp.MustCompile(`^\s*env\s*$`), // bare 'env' + regexp.MustCompile(`^\s*env\s*\|`), // 'env | ...' (piped) + regexp.MustCompile(`^\s*env\s*>\s`), // 'env > file' + regexp.MustCompile(`\bprintenv\b`), // any printenv usage + regexp.MustCompile(`^\s*(set|export\s+-p|declare\s+-x)\s*($|\|)`), // shell var dumps + regexp.MustCompile(`\bcompgen\s+-e\b`), // bash env completion dump } // ExecTool executes shell commands, optionally inside a sandbox container.