refactor(recipe): enhance shell command security with global safe list
- Introduce global safeCommands() map for command whitelisting. - Implement case-insensitive prefix checking for allowed commands. - Simplify argument handling by removing redundant int conversions. - Update error messages and comments for clarity on security policies. - Remove outdated comments and adjust prompt text for consistency.
This commit is contained in:
parent
4603a1ec7a
commit
383d28a91a
@ -258,7 +258,7 @@ func createUnifiedPatch(changes []FileChange, patchPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// executeReadOnlyShell — now handles numbers in args (like tree -L 3)
|
||||
// executeReadOnlyShell — safe, whitelisted, read-only shell execution with user confirmation
|
||||
func (r *Runner) executeReadOnlyShell(step Step, previousResults []string) {
|
||||
prompt := fmt.Sprintf(`You need additional context from the filesystem for this step.
|
||||
|
||||
@ -285,7 +285,7 @@ Return ONLY a JSON array of read-only commands. Example:
|
||||
}
|
||||
]
|
||||
|
||||
Only use safe read-only commands from the allowed list.`,
|
||||
Only use safe read-only commands.`,
|
||||
r.Recipe.Overview,
|
||||
strings.Join(previousResults, "\n\n---\n\n"),
|
||||
step.Objective,
|
||||
@ -312,7 +312,7 @@ Only use safe read-only commands from the allowed list.`,
|
||||
|
||||
type ShellCommand struct {
|
||||
Command string `json:"command"`
|
||||
Args []interface{} `json:"args"` // allows strings and numbers
|
||||
Args []interface{} `json:"args"`
|
||||
}
|
||||
|
||||
var cmds []ShellCommand
|
||||
@ -321,6 +321,9 @@ Only use safe read-only commands from the allowed list.`,
|
||||
return
|
||||
}
|
||||
|
||||
// Use the GLOBAL safe list for the security check
|
||||
safeMap := safeCommands()
|
||||
|
||||
for _, cmd := range cmds {
|
||||
// Build argument list, converting numbers to strings
|
||||
args := make([]string, len(cmd.Args))
|
||||
@ -330,8 +333,6 @@ Only use safe read-only commands from the allowed list.`,
|
||||
args[i] = v
|
||||
case float64:
|
||||
args[i] = strconv.FormatFloat(v, 'f', -1, 64)
|
||||
case int, int64:
|
||||
args[i] = fmt.Sprintf("%v", v)
|
||||
default:
|
||||
args[i] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
@ -354,16 +355,18 @@ Only use safe read-only commands from the allowed list.`,
|
||||
continue
|
||||
}
|
||||
|
||||
// Whitelist check
|
||||
// FINAL SECURITY CHECK — use the global safe list
|
||||
allowed := false
|
||||
for _, allowedCmd := range r.Recipe.AllowedShellCommands {
|
||||
if strings.HasPrefix(cmd.Command, allowedCmd) {
|
||||
trimmedCmd := strings.ToLower(strings.TrimSpace(cmd.Command))
|
||||
for safe := range safeMap {
|
||||
if strings.HasPrefix(trimmedCmd, strings.ToLower(safe)) {
|
||||
allowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !allowed {
|
||||
fmt.Printf(" ❌ Command not allowed: %s\n", cmd.Command)
|
||||
fmt.Printf(" ❌ Command not allowed by global safety policy: %s\n", cmd.Command)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user