diff --git a/digest.go b/digest.go index 88ac4bd..276da03 100644 --- a/digest.go +++ b/digest.go @@ -83,7 +83,12 @@ func (da *DigestAuth) purgeLocked(count int) { } cache := digestCache(entries) sort.Sort(cache) - for _, client := range cache[:count] { + + purgeCount := count + if count > len(cache) { + purgeCount = len(cache) + } + for _, client := range cache[:purgeCount] { delete(da.clients, client.nonce) } } diff --git a/digest_test.go b/digest_test.go index e31086e..2342fe9 100644 --- a/digest_test.go +++ b/digest_test.go @@ -1,6 +1,7 @@ package auth import ( + "fmt" "context" "net/http" "net/url" @@ -79,6 +80,31 @@ func TestDigestAuthParams(t *testing.T) { } } +// TestDigestPurge tests that when we purge clients from the authenticator we do not purge +// more cache entries than the number of clients we have received. +// This is to avoid regressing and hitting a "slice bounds out of range" panic. +func TestDigestPurge(t *testing.T) { + t.Parallel() + // Creating dummy clients for the digest authenticator. + nClients := 10 + clients := make(map[string]*digestClient, nClients) + for i := 0; i < nClients; i++ { + clients[fmt.Sprintf("%d", i)] = &digestClient{} + } + + secrets := HtdigestFileProvider("test.htdigest") + da := &DigestAuth{ + Opaque: "U7H+ier3Ae8Skd/g", + Realm: "example.com", + Secrets: secrets, + clients: clients, + } + + // Purging more than the number of clients we have stored in the + // digest authenticator. + da.Purge(nClients * 2) +} + func TestNewContextNoDeadlock(t *testing.T) { t.Parallel() const (