feat(safety): make safe shell commands user-configurable
- Replace hardcoded safeCommands map with sync.OnceValue loading from ~/.config/grokkit/safe_shell_commands.yaml - Provide fallback built-in safe list if config load fails - Add safe_shell_commands.yaml.example for user reference - Update safety check to use loaded map and prefix matching
This commit is contained in:
parent
7e4bdbc21c
commit
f9d99527e0
@ -4,44 +4,48 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global safe read-only whitelist
|
// Global safe read-only whitelist
|
||||||
var safeCommands = map[string]bool{
|
// LoadSafeCommands reads the user's safe shell commands config (with fallback)
|
||||||
//GNU Utilities
|
var safeCommands = sync.OnceValue(func() map[string]bool {
|
||||||
"ls": true,
|
cfgPath := filepath.Join(os.Getenv("HOME"), ".config", "grokkit", "safe_shell_commands.yaml")
|
||||||
"pwd": true,
|
|
||||||
"cat": true,
|
data, err := os.ReadFile(cfgPath)
|
||||||
"tree": true,
|
if err != nil {
|
||||||
"find": true,
|
// Fallback to a built-in safe list
|
||||||
"grep": true,
|
return map[string]bool{
|
||||||
"which": true,
|
"ls": true, "pwd": true, "cat": true, "tree": true,
|
||||||
//Git and Gitea
|
"find": true, "grep": true, "rg": true,
|
||||||
"git status": true,
|
"git status": true, "git log": true, "git diff": true, "git branch": true,
|
||||||
"git log": true,
|
"go test": true, "go vet": true, "go fmt": true, "go mod tidy": true,
|
||||||
"git diff": true,
|
"make test": true,
|
||||||
"git branch": true,
|
"pytest": true, "poetry run pytest": true, "ctest": true,
|
||||||
"tea repos list -o csv -lm 100": true,
|
"python -m pytest": true, "python": true, "poetry": true,
|
||||||
"tea repos search -o csv": true,
|
}
|
||||||
//Miscellaneous Utilities
|
}
|
||||||
"cndump -s": true,
|
|
||||||
// Safe test runners
|
var cfg struct {
|
||||||
"go test": true,
|
SafeCommands []string `yaml:"safe_commands"`
|
||||||
"make test": true,
|
}
|
||||||
"make lint": true,
|
err = yaml.Unmarshal(data, &cfg)
|
||||||
"pytest": true,
|
if err != nil {
|
||||||
"poetry run pytest": true,
|
return nil
|
||||||
"ctest": true,
|
}
|
||||||
"python -m pytest": true,
|
|
||||||
"go vet": true,
|
m := make(map[string]bool)
|
||||||
"go fmt": true,
|
for _, c := range cfg.SafeCommands {
|
||||||
"go mod tidy": true,
|
m[strings.ToLower(strings.TrimSpace(c))] = true
|
||||||
}
|
}
|
||||||
|
return m
|
||||||
|
})
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// stepRe still finds the headings (this one is solid)
|
// stepRe still finds the headings (this one is solid)
|
||||||
@ -64,13 +68,12 @@ 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 truly dangerous commands ===
|
// === SAFETY CHECK using user-configurable whitelist ===
|
||||||
|
safeMap := safeCommands()
|
||||||
for _, cmd := range r.AllowedShellCommands {
|
for _, cmd := range r.AllowedShellCommands {
|
||||||
trimmed := strings.TrimSpace(strings.ToLower(cmd))
|
trimmed := strings.ToLower(strings.TrimSpace(cmd))
|
||||||
|
|
||||||
// Allow exact matches or common prefixed commands
|
|
||||||
allowed := false
|
allowed := false
|
||||||
for safe := range safeCommands {
|
for safe := range safeMap {
|
||||||
if strings.HasPrefix(trimmed, safe) {
|
if strings.HasPrefix(trimmed, safe) {
|
||||||
allowed = true
|
allowed = true
|
||||||
break
|
break
|
||||||
|
|||||||
27
safe_shell_commands.yaml.example
Normal file
27
safe_shell_commands.yaml.example
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Grokkit safe shell commands whitelist
|
||||||
|
# Only commands listed here (or prefixed by these) are allowed in recipes.
|
||||||
|
# This is a safety boundary — never add rm, mv, cd, sudo, etc.
|
||||||
|
# This file should be placed in ~/.config/grokkit/safe_shell_commands.yaml
|
||||||
|
# customize it as you see fit.
|
||||||
|
|
||||||
|
safe_commands:
|
||||||
|
- ls
|
||||||
|
- pwd
|
||||||
|
- cat
|
||||||
|
- tree
|
||||||
|
- find
|
||||||
|
- grep
|
||||||
|
- rg # ripgrep
|
||||||
|
- git status
|
||||||
|
- git log
|
||||||
|
- git diff
|
||||||
|
- git branch
|
||||||
|
- go test
|
||||||
|
- go vet
|
||||||
|
- go fmt
|
||||||
|
- go mod tidy
|
||||||
|
- make test
|
||||||
|
- pytest
|
||||||
|
- poetry run pytest
|
||||||
|
- ctest
|
||||||
|
- python -m pytest
|
||||||
Loading…
Reference in New Issue
Block a user