From 7e4bdbc21cfdff7520eddfe4caa658ea5e565965 Mon Sep 17 00:00:00 2001 From: Greg Gauthier Date: Sat, 7 Mar 2026 17:59:59 +0000 Subject: [PATCH] refactor(recipe/loader): expand safe shell commands and refine validation logic - Renamed safeReadOnlyCommands to safeCommands for clarity. - Added support for additional safe commands including GNU utilities (find, grep, which), Git commands (diff, branch), and various test runners (go test, make test/lint, pytest, etc.). - Updated safety check to allow commands prefixed with any safe command for flexibility. - Improved error message for unsafe commands. --- internal/recipe/loader.go | 49 ++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/internal/recipe/loader.go b/internal/recipe/loader.go index 6bf3c2d..e803735 100644 --- a/internal/recipe/loader.go +++ b/internal/recipe/loader.go @@ -12,18 +12,35 @@ import ( ) // Global safe read-only whitelist -var safeReadOnlyCommands = map[string]bool{ - "ls": true, - "pwd": true, - "cat": true, - "tree": true, +var safeCommands = map[string]bool{ + //GNU Utilities + "ls": true, + "pwd": true, + "cat": true, + "tree": true, + "find": true, + "grep": true, + "which": true, + //Git and Gitea "git status": true, "git log": true, - "find": true, - "grep": true, - "cndump -s": true, + "git diff": true, + "git branch": true, "tea repos list -o csv -lm 100": true, "tea repos search -o csv": true, + //Miscellaneous Utilities + "cndump -s": true, + // Safe test runners + "go test": true, + "make test": true, + "make lint": true, + "pytest": true, + "poetry run pytest": true, + "ctest": true, + "python -m pytest": true, + "go vet": true, + "go fmt": true, + "go mod tidy": true, } var ( @@ -47,11 +64,21 @@ func Load(path string, userParams map[string]any) (*Recipe, error) { return nil, fmt.Errorf("yaml parse: %w", err) } - // === SAFETY CHECK: reject dangerous allowed_shell_commands === + // === SAFETY CHECK: reject truly dangerous commands === for _, cmd := range r.AllowedShellCommands { trimmed := strings.TrimSpace(strings.ToLower(cmd)) - if !safeReadOnlyCommands[trimmed] && !strings.HasPrefix(trimmed, "git status") && !strings.HasPrefix(trimmed, "git log") { - return nil, fmt.Errorf("\u001B[31mRecipe contains unsafe shell command: %q. Remove or replace the dangerous command in your recipe.\u001B[0m", cmd) + + // Allow exact matches or common prefixed commands + allowed := false + for safe := range safeCommands { + if strings.HasPrefix(trimmed, safe) { + allowed = true + break + } + } + if !allowed { + return nil, fmt.Errorf("\u001B[31mRecipe contains unsafe shell command: %q. "+ + "Remove or replace the dangerous command in your recipe.\u001B[0m", cmd) } }