package browser import ( "net/http" "net/http/httptest" "testing" "time" ) func TestMutationRequiresOriginAndHeader(t *testing.T) { p, err := New([]string{"https://app.example.com"}) if err != nil { t.Fatal(err) } for _, tc := range []struct { origin, header, transport string valid bool }{ {"https://app.example.com", "1", "", true}, {"https://APP.example.com", "1", "", true}, {"https://evil.example.com", "1", "", false}, {"https://app.example.com.evil.test", "1", "", false}, {"https://app.example.com/path", "1", "", false}, {"https://app.example.com?query", "1", "", false}, {"https://app.example.com#fragment", "1", "", false}, {"https://app.example.com", "", "bearer", false}, {"", "1", "bearer", false}, {"null", "1", "cookie", false}, } { r := httptest.NewRequest(http.MethodPost, "https://app.example.com/api/session", nil) r.Header.Set("Origin", tc.origin) r.Header.Set(CSRFHeader, tc.header) r.Header.Set("X-Auth-Transport", tc.transport) if got := p.Validate(r); (got == nil) != tc.valid { t.Errorf("%+v: %v", tc, got) } } if p.Validate(httptest.NewRequest(http.MethodGet, "https://app.example.com/api/session", nil)) != nil { t.Fatal("read rejected") } for _, origin := range []string{"*", "http://app.example.com", "https://user@app.example.com", "https://app.example.com/"} { if _, err := New([]string{origin}); err == nil { t.Errorf("accepted configuration %q", origin) } } if _, err := New(nil); err == nil { t.Fatal("empty origin policy accepted") } } func TestSessionCookie(t *testing.T) { now := time.Date(2026, 9, 12, 0, 0, 0, 0, time.UTC) w := httptest.NewRecorder() if err := SetSession(w, "opaque-token", now, now.Add(time.Hour)); err != nil { t.Fatal(err) } c := w.Result().Cookies()[0] if c.Name != SessionCookie || !c.Secure || !c.HttpOnly || c.Path != "/" || c.Domain != "" || c.SameSite != http.SameSiteLaxMode || c.MaxAge != 3600 { t.Fatalf("bad cookie: %+v", c) } r := httptest.NewRequest(http.MethodGet, "https://app.example.com/api/session", nil) r.AddCookie(c) if value, err := Credential(r); err != nil || value != "opaque-token" { t.Fatal("cookie not read") } r.AddCookie(c) if _, err := Credential(r); err == nil { t.Fatal("ambiguous credential accepted") } if err := SetSession(httptest.NewRecorder(), "token", now, now); err == nil { t.Fatal("expired cookie accepted") } w = httptest.NewRecorder() ClearSession(w) c = w.Result().Cookies()[0] if c.MaxAge != -1 || c.Value != "" || c.Path != "/" || !c.Secure { t.Fatal("cookie not cleared safely") } }