security: add maximum session limit to prevent memory exhaustion
Add configurable MaxSessions limit (default: 10000) to SessionStore. When the limit is reached, new session creation returns ErrTooManySessions and HTTP transport responds with 503 Service Unavailable. This prevents attackers from exhausting server memory by creating unlimited sessions through repeated initialize requests. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -245,6 +245,76 @@ func TestSessionStoreConcurrency(t *testing.T) {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestSessionStoreMaxSessions(t *testing.T) {
|
||||
maxSessions := 5
|
||||
store := NewSessionStoreWithLimit(30*time.Minute, maxSessions)
|
||||
defer store.Stop()
|
||||
|
||||
// Create sessions up to limit
|
||||
for i := 0; i < maxSessions; i++ {
|
||||
_, err := store.Create()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create session %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
if store.Count() != maxSessions {
|
||||
t.Errorf("Expected %d sessions, got %d", maxSessions, store.Count())
|
||||
}
|
||||
|
||||
// Try to create one more - should fail
|
||||
_, err := store.Create()
|
||||
if err != ErrTooManySessions {
|
||||
t.Errorf("Expected ErrTooManySessions, got %v", err)
|
||||
}
|
||||
|
||||
// Count should still be at max
|
||||
if store.Count() != maxSessions {
|
||||
t.Errorf("Expected %d sessions after failed create, got %d", maxSessions, store.Count())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionStoreMaxSessionsWithDeletion(t *testing.T) {
|
||||
maxSessions := 3
|
||||
store := NewSessionStoreWithLimit(30*time.Minute, maxSessions)
|
||||
defer store.Stop()
|
||||
|
||||
// Fill up the store
|
||||
sessions := make([]*Session, maxSessions)
|
||||
for i := 0; i < maxSessions; i++ {
|
||||
s, err := store.Create()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create session: %v", err)
|
||||
}
|
||||
sessions[i] = s
|
||||
}
|
||||
|
||||
// Should be full
|
||||
_, err := store.Create()
|
||||
if err != ErrTooManySessions {
|
||||
t.Error("Expected ErrTooManySessions when full")
|
||||
}
|
||||
|
||||
// Delete one session
|
||||
store.Delete(sessions[0].ID)
|
||||
|
||||
// Should be able to create again
|
||||
_, err = store.Create()
|
||||
if err != nil {
|
||||
t.Errorf("Should be able to create after deletion: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionStoreDefaultMaxSessions(t *testing.T) {
|
||||
store := NewSessionStore(30 * time.Minute)
|
||||
defer store.Stop()
|
||||
|
||||
// Just verify it uses the default (don't create 10000 sessions)
|
||||
if store.maxSessions != DefaultMaxSessions {
|
||||
t.Errorf("Expected default max sessions %d, got %d", DefaultMaxSessions, store.maxSessions)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateSessionID(t *testing.T) {
|
||||
ids := make(map[string]bool)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user