Vectrum
Sign In
80 lines · 2.6 KB · Go
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
}