feature/recipe_implementation #5
@ -11,6 +11,21 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Global safe read-only whitelist
|
||||||
|
var safeReadOnlyCommands = map[string]bool{
|
||||||
|
"ls": true,
|
||||||
|
"pwd": true,
|
||||||
|
"cat": true,
|
||||||
|
"tree": true,
|
||||||
|
"git status": true,
|
||||||
|
"git log": true,
|
||||||
|
"find": true,
|
||||||
|
"grep": true,
|
||||||
|
"cndump -s": true,
|
||||||
|
"tea repos list -o csv -lm 100": true,
|
||||||
|
"tea repos search -o csv": true,
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// stepRe still finds the headings (this one is solid)
|
// stepRe still finds the headings (this one is solid)
|
||||||
stepRe = regexp.MustCompile(`(?m)^### Step (\d+): (.+)$`)
|
stepRe = regexp.MustCompile(`(?m)^### Step (\d+): (.+)$`)
|
||||||
@ -32,6 +47,14 @@ func Load(path string, userParams map[string]any) (*Recipe, error) {
|
|||||||
return nil, fmt.Errorf("yaml parse: %w", err)
|
return nil, fmt.Errorf("yaml parse: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === SAFETY CHECK: reject dangerous allowed_shell_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("\033[31mERROR: Recipe contains unsafe shell command: %q\033[0m\n\nOnly the following read-only commands are allowed:\n ls, pwd, cat, tree, git status, git log, find, grep\n\nRemove or replace the dangerous command and try again.", cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Apply defaults + user --param overrides
|
// Apply defaults + user --param overrides
|
||||||
if r.Parameters == nil {
|
if r.Parameters == nil {
|
||||||
r.Parameters = make(map[string]Parameter)
|
r.Parameters = make(map[string]Parameter)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user