Cockatrice/tests/rate_limiter_test.cpp
Lukas Brübach b2f63255f0 [Security] Add per-address rate limiting for auth endpoints
Introduce a thread-safe RateLimiter that tracks attempts per key
(IP address) within a sliding time window, and wire it into the
authentication endpoints:

- Login: failed login attempts from an address are counted; once the
  configured maximum is exceeded within the window, further logins
  from that address are rejected with RespTooManyRequests. A
  successful login clears the failed attempts for that address.
- Registration: implement the previously stubbed
  tooManyRegistrationAttempts, limiting how many accounts can be
  created per address per window.
- Forgot-password: throttle both the email-request and the
  email-challenge paths per address.

New [security] settings with defaults:
  max_login_attempts_per_ip=5 / login_attempt_window_seconds=900
  max_registrations_per_ip=2 / registration_window_seconds=3600
  max_forgot_password_requests_per_ip=3 / forgot_password_window_seconds=3600

Adds unit tests for the RateLimiter (window limit, over-limit
blocking, clearing, per-key independence).

Took 3 minutes
2026-08-04 10:38:20 +02:00

50 lines
1.2 KiB
C++

#include "ratelimiter.h"
#include "gtest/gtest.h"
namespace
{
TEST(RateLimiterTest, AllowsAttemptsWithinLimit)
{
RateLimiter limiter;
ASSERT_FALSE(limiter.recordAttempt("ip", 3, 60));
ASSERT_FALSE(limiter.recordAttempt("ip", 3, 60));
ASSERT_FALSE(limiter.recordAttempt("ip", 3, 60));
ASSERT_FALSE(limiter.isBlocked("ip", 3, 60));
}
TEST(RateLimiterTest, BlocksAttemptsOverLimit)
{
RateLimiter limiter;
ASSERT_FALSE(limiter.recordAttempt("ip", 2, 60));
ASSERT_FALSE(limiter.recordAttempt("ip", 2, 60));
ASSERT_TRUE(limiter.recordAttempt("ip", 2, 60));
ASSERT_TRUE(limiter.isBlocked("ip", 2, 60));
}
TEST(RateLimiterTest, ClearAttempts)
{
RateLimiter limiter;
ASSERT_TRUE(limiter.recordAttempt("ip", 0, 60));
ASSERT_TRUE(limiter.isBlocked("ip", 0, 60));
limiter.clearAttempts("ip");
ASSERT_FALSE(limiter.isBlocked("ip", 0, 60));
}
TEST(RateLimiterTest, KeysAreIndependent)
{
RateLimiter limiter;
ASSERT_TRUE(limiter.recordAttempt("a", 0, 60));
ASSERT_FALSE(limiter.isBlocked("b", 0, 60));
ASSERT_TRUE(limiter.isBlocked("a", 0, 60));
}
} // namespace
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}