package approval import ( "strings" "testing" ) func TestAnalyze(t *testing.T) { tests := []struct { name string script string wantCmds []string wantOpaque bool }{ {name: "simple", script: "go test ./...", wantCmds: []string{"go"}}, {name: "pipeline", script: "cat x | grep y | wc -l", wantCmds: []string{"cat", "grep", "wc"}}, { // The example from the plan: a naive prefix allowlist on "git" // would wave this straight through. name: "semicolon smuggles a second command", script: "git status; rm -rf ~", wantCmds: []string{"git", "rm"}, }, {name: "and-chained", script: "go build ./... && ./bin/app", wantCmds: []string{"go"}}, {name: "backticks are opaque", script: "echo `whoami`", wantOpaque: true}, {name: "dollar-paren is opaque", script: "echo $(cat /etc/passwd)", wantOpaque: true}, {name: "bash -c is opaque", script: `bash -c "rm -rf /"`, wantCmds: []string{"bash"}, wantOpaque: true}, {name: "python -c is opaque", script: `python3 -c "import os; os.system('x')"`, wantCmds: []string{"python3"}, wantOpaque: true}, {name: "variable in command position is opaque", script: "$CMD --flag", wantOpaque: true}, {name: "subshell", script: "(cd build && make)", wantCmds: []string{"cd", "make"}}, {name: "redirect is not by itself opaque", script: "echo hi > out.txt", wantCmds: []string{"echo"}}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := Analyze(tc.script) if got.ParseError != nil { t.Fatalf("parse: %v", got.ParseError) } if got.Opaque != tc.wantOpaque { t.Errorf("Opaque = %v, want %v (reason: %s)", got.Opaque, tc.wantOpaque, got.Reason) } for _, want := range tc.wantCmds { if !contains(got.Commands, want) { t.Errorf("commands = %v, want it to include %q", got.Commands, want) } } if tc.wantOpaque && got.Reason == "" { t.Error("an opaque command must explain why, for the prompt") } }) } } func TestAnalyzeInvalidShell(t *testing.T) { got := Analyze("if then fi done )") if got.ParseError == nil { t.Fatal("want a parse error") } if !got.Opaque { t.Error("unparseable input must be treated as opaque, never as safe") } } func TestDescribe(t *testing.T) { if got := Analyze("go test ./...").Describe(); got != "runs go" { t.Errorf("Describe() = %q", got) } if got := Analyze("git log $(cat f)").Describe(); !strings.Contains(got, "command substitution") { t.Errorf("Describe() = %q, want it to name the reason", got) } } func contains(xs []string, want string) bool { for _, x := range xs { if x == want { return true } } return false }