package server import ( "net/http" "net/http/httptest" "strings" "testing" "time" ) func TestFailedSignInsAreThrottledPerAccount(t *testing.T) { h := newHarness(t) for i := 0; i < limitBudget["user"]; i++ { if _, status, _ := h.login("admin", "wrong-password-x", ""); status != http.StatusUnauthorized { t.Fatalf("attempt %d: %d", i, status) } } // Over budget, even the right password is refused, before any hashing. _, status, body := h.login("admin", adminPassword, "") if status != http.StatusTooManyRequests || body["code"] != "throttled" { t.Fatalf("after the budget: %d %v", status, body) } // Another account is not affected. h.makeUserNoLogin("bob", "bob-password-1") if _, status, _ := h.login("bob", "bob-password-1", ""); status != http.StatusOK { t.Fatalf("another account was throttled too: %d", status) } // The window passes. h.srv.limits.now = func() time.Time { return time.Now().Add(limitWindow) } if _, status, _ := h.login("admin", adminPassword, ""); status != http.StatusOK { t.Fatalf("still throttled after the window: %d", status) } } func TestUnknownUsersCountAgainstTheAddress(t *testing.T) { h := newHarness(t) for i := 0; i < limitBudget["ip"]; i++ { h.login("nobody-"+strings.Repeat("x", i%5+1), "whatever-password", "") } if _, status, _ := h.login("admin", adminPassword, ""); status != http.StatusTooManyRequests { t.Fatalf("an address over its budget could still sign in: %d", status) } } func TestClientIPTrustsOnlyConfiguredProxies(t *testing.T) { proxies, err := parseProxies([]string{"10.0.0.0/8", "192.168.1.5"}) if err != nil { t.Fatal(err) } s := &Server{proxies: proxies} req := func(remote, xff string) *http.Request { r := httptest.NewRequest("GET", "/", nil) r.RemoteAddr = remote if xff != "" { r.Header.Set("X-Forwarded-For", xff) } return r } for _, c := range []struct{ remote, xff, want string }{ {"203.0.113.9:1234", "1.2.3.4", "203.0.113.9"}, // untrusted peer: header ignored {"10.1.2.3:1234", "198.51.100.7", "198.51.100.7"}, // proxy: its client {"10.1.2.3:1234", "6.6.6.6, 198.51.100.7", "198.51.100.7"}, // a spoofed left entry is skipped {"10.1.2.3:1234", "198.51.100.7, 192.168.1.5", "198.51.100.7"}, {"10.1.2.3:1234", "", "10.1.2.3"}, } { if got := s.clientIP(req(c.remote, c.xff)); got != c.want { t.Errorf("clientIP(%s, %q) = %s, want %s", c.remote, c.xff, got, c.want) } } if _, err := parseProxies([]string{"not-an-address"}); err == nil { t.Error("a malformed proxy was accepted") } } func TestClampLabel(t *testing.T) { if got := clampLabel(" phone\x1b[31m\n"); got != "phone[31m" { t.Errorf("control characters kept: %q", got) } if got := clampLabel(strings.Repeat("รค", 200)); len([]rune(got)) != maxLabel { t.Errorf("label not clamped: %d runes", len([]rune(got))) } }