package git import ( "context" "errors" "os" "path/filepath" "strings" "testing" ) func TestExtractHostFromPrompt(t *testing.T) { tests := []struct { prompt string wantHost string }{ {"Username for 'https://github.com': ", "github.com"}, {"Password for 'https://x-access-token@github.com': ", "github.com"}, {"Password for 'https://github.com.evil/repo.git': ", "github.com.evil"}, {"Password for 'https://x-tangled-token@knot.example.com:443': ", "knot.example.com"}, {"Password for 'https://knot.example.com': ", "knot.example.com"}, {"random string without url", ""}, } for _, tt := range tests { got := ExtractHostFromPrompt(tt.prompt) if got != tt.wantHost { t.Errorf("ExtractHostFromPrompt(%q) = %q, want %q", tt.prompt, got, tt.wantHost) } } } func TestHandleAskpassExactHostMatching(t *testing.T) { u, err := HandleAskpass("Username for 'https://github.com': ", "github.com", "mytoken") if err != nil || u != "x-access-token" { t.Fatalf("source username failed: %v, %q", err, u) } p, err := HandleAskpass("Password for 'https://x-access-token@github.com': ", "github.com", "mytoken") if err != nil || p != "mytoken" { t.Fatalf("source password failed: %v, %q", err, p) } _, err = HandleAskpass("Password for 'https://github.com.evil': ", "github.com", "mytoken") if err == nil { t.Fatal("expected error for github.com.evil subdomain mismatch") } _, err = HandleAskpass("Password for 'https://attacker.com': ", "github.com", "mytoken") if err == nil { t.Fatal("expected error for attacker.com mismatch") } } func TestCreateAskpassScriptExcludesSecrets(t *testing.T) { dir := t.TempDir() scriptPath, err := CreateAskpassScript(dir) if err != nil { t.Fatal(err) } content, err := os.ReadFile(scriptPath) if err != nil { t.Fatal(err) } secretToken := "supersecret12345" if strings.Contains(string(content), secretToken) { t.Fatal("askpass script contains secret token!") } if strings.Contains(string(content), "Bearer") { t.Fatal("askpass script contains Bearer string!") } if !strings.Contains(string(content), "askpass") { t.Fatal("askpass script does not invoke askpass command") } } func TestRedactSecrets(t *testing.T) { secret1 := "ghp_tokensecret98765" secret2 := "capability-secret-token" logMsg := "clone failed with ghp_tokensecret98765 and serving failed with capability-secret-token" redacted := RedactSecrets(logMsg, secret1, secret2) if strings.Contains(redacted, secret1) || strings.Contains(redacted, secret2) { t.Fatalf("secret was not redacted: %s", redacted) } if !strings.Contains(redacted, "[REDACTED]") { t.Fatalf("missing [REDACTED] in %s", redacted) } } func TestDiskLimitEnforcement(t *testing.T) { dir := t.TempDir() watchDir := filepath.Join(dir, "scratch") _ = os.MkdirAll(watchDir, 0700) runner := RealCommandRunner{ MaxDiskBytes: 100, WatchDir: watchDir, } testFile := filepath.Join(watchDir, "bigfile.txt") _, err := runner.Run(context.Background(), dir, nil, "sh", "-c", "head -c 10000 /dev/zero > '"+testFile+"' && sleep 1") if err == nil { t.Fatal("expected disk limit error, got nil") } if !strings.Contains(err.Error(), "scratch disk limit exceeded") { t.Fatalf("expected ErrDiskLimitExceeded, got: %v", err) } } func TestClassifyGitErrorKeepsRepoNotFoundRetryable(t *testing.T) { // github answers a repo the token cannot see with both phrases at once output := "remote: Repository not found.\nfatal: Authentication failed for 'https://github.com/alice/nope.git/'" err := ClassifyGitError(output, errors.New("exit status 128")) if !errors.Is(err, ErrRepoNotFound) { t.Fatalf("error = %v, want ErrRepoNotFound", err) } if errors.Is(err, ErrAuthRequired) { t.Fatal("a missing repository masqueraded as authorization required; the batch token would be scrubbed") } err = ClassifyGitError("fatal: Authentication failed for 'https://github.com/alice/repo.git/'", nil) if !errors.Is(err, ErrAuthRequired) || errors.Is(err, ErrRepoNotFound) { t.Fatalf("error = %v, want ErrAuthRequired only", err) } }