package main import ( "os" "path/filepath" "testing" ) func TestCheckAuthPrecedence_CookieBeforeBearer(t *testing.T) { // Anti-pattern: cookie checked first, bearer only as fallback. src := `package auth import "net/http" func extractClaims(r *http.Request) string { var token string cookie, err := r.Cookie("access_token") if err == nil && cookie.Value != "" { token = cookie.Value } if token == "" { auth := r.Header.Get("Authorization") _ = auth } return token } ` dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "bad.go"), []byte(src), 0o644); err != nil { t.Fatal(err) } vs := checkAuthPrecedence(dir) if len(vs) != 1 { t.Fatalf("expected 1 violation, got %d", len(vs)) } if vs[0].cookieLine == 0 { t.Error("violation should have a cookieLine set") } } func TestCheckAuthPrecedence_BearerBeforeCookie(t *testing.T) { // Correct pattern: bearer first, cookie fallback. src := `package auth import "net/http" import "strings" func extractClaims(r *http.Request) string { var token string authHeader := r.Header.Get("Authorization") if after, ok := strings.CutPrefix(authHeader, "Bearer "); ok { token = after } if token == "" { cookie, err := r.Cookie("access_token") if err == nil && cookie.Value != "" { token = cookie.Value } } return token } ` dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "good.go"), []byte(src), 0o644); err != nil { t.Fatal(err) } vs := checkAuthPrecedence(dir) if len(vs) != 0 { t.Fatalf("expected 0 violations, got %d: %+v", len(vs), vs) } } func TestCheckAuthPrecedence_CookieOnly(t *testing.T) { // Cookie-only auth (no bearer at all) — not a violation. src := `package auth import "net/http" func extractFromCookie(r *http.Request) string { cookie, err := r.Cookie("session") if err != nil { return "" } return cookie.Value } ` dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "cookie_only.go"), []byte(src), 0o644); err != nil { t.Fatal(err) } vs := checkAuthPrecedence(dir) if len(vs) != 0 { t.Fatalf("expected 0 violations, got %d", len(vs)) } } func TestCheckAuthPrecedence_BearerOnly(t *testing.T) { // Bearer-only auth (no cookie) — not a violation. src := `package auth import "net/http" import "strings" func extractBearer(r *http.Request) string { auth := r.Header.Get("Authorization") if after, ok := strings.CutPrefix(auth, "Bearer "); ok { return after } return "" } ` dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "bearer_only.go"), []byte(src), 0o644); err != nil { t.Fatal(err) } vs := checkAuthPrecedence(dir) if len(vs) != 0 { t.Fatalf("expected 0 violations, got %d", len(vs)) } } func TestCheckAuthPrecedence_SetCookieIgnored(t *testing.T) { // http.SetCookie should not be flagged as a cookie read. src := `package auth import "net/http" import "strings" func setCookieAndReadBearer(w http.ResponseWriter, r *http.Request) string { http.SetCookie(w, &http.Cookie{Name: "session", Value: "abc"}) auth := r.Header.Get("Authorization") if after, ok := strings.CutPrefix(auth, "Bearer "); ok { return after } return "" } ` dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "setcookie.go"), []byte(src), 0o644); err != nil { t.Fatal(err) } vs := checkAuthPrecedence(dir) if len(vs) != 0 { t.Fatalf("expected 0 violations, got %d: %+v", len(vs), vs) } } func TestCheckAuthPrecedence_TestFilesIgnored(t *testing.T) { // Test files should be ignored. src := `package auth import "net/http" func extractClaims(r *http.Request) string { var token string cookie, err := r.Cookie("access_token") if err == nil && cookie.Value != "" { token = cookie.Value } if token == "" { auth := r.Header.Get("Authorization") _ = auth } return token } ` dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "bad_test.go"), []byte(src), 0o644); err != nil { t.Fatal(err) } vs := checkAuthPrecedence(dir) if len(vs) != 0 { t.Fatalf("expected 0 violations for test files, got %d", len(vs)) } } func TestCheckAuthPrecedence_UnguardedBearerNotViolation(t *testing.T) { // Cookie first but bearer is NOT guarded by `if token == ""` — // both are read unconditionally, which is a different (acceptable) pattern // where the code might merge or compare both. src := `package auth import "net/http" import "strings" func extractClaims(r *http.Request) string { cookie, err := r.Cookie("access_token") cookieToken := "" if err == nil { cookieToken = cookie.Value } auth := r.Header.Get("Authorization") bearerToken := "" if after, ok := strings.CutPrefix(auth, "Bearer "); ok { bearerToken = after } if bearerToken != "" { return bearerToken } return cookieToken } ` dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "both.go"), []byte(src), 0o644); err != nil { t.Fatal(err) } vs := checkAuthPrecedence(dir) if len(vs) != 0 { t.Fatalf("expected 0 violations for unguarded bearer, got %d: %+v", len(vs), vs) } }