test(membership): regression for H6 owner spoof and H7 nonce-cache poison
TestAudit_OwnerSpoof: a body declaring a foreign owner endpoint or signing key is 403; a self-owned create is 201. TestAudit_NonceCachePoisonPreAuth: an unregistered identity's repeated nonce still fails 'not authorized' (never 'replayed'), proving it was not cached, while an authorized identity's replay is still rejected. Nonce cache unit tests: prune-after-TTL and cap-bounded memory.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
package membership
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestNonceCacheRememberPrune covers the replay/expiry behavior directly on the
|
||||
// cache: a fresh nonce is accepted (golden), an immediate repeat is rejected
|
||||
// (error), and after the TTL the same nonce is accepted again because its entry
|
||||
// was pruned (edge).
|
||||
func TestNonceCacheRememberPrune(t *testing.T) {
|
||||
nc := newNonceCache(50*time.Millisecond, 1000)
|
||||
base := time.Now()
|
||||
|
||||
if !nc.rememberOrReject("a", base) {
|
||||
t.Fatalf("first sighting should be accepted")
|
||||
}
|
||||
if nc.rememberOrReject("a", base) {
|
||||
t.Fatalf("an immediate replay should be rejected")
|
||||
}
|
||||
if !nc.rememberOrReject("a", base.Add(60*time.Millisecond)) {
|
||||
t.Fatalf("after the TTL the nonce should be accepted again (pruned)")
|
||||
}
|
||||
}
|
||||
|
||||
// TestNonceCacheCapBounded covers the memory bound (audit H7): with a long TTL so
|
||||
// nothing expires, inserting far more nonces than the cap must still keep the
|
||||
// cache at or under the cap (oldest evicted), and the order queue must not drift
|
||||
// from the map.
|
||||
func TestNonceCacheCapBounded(t *testing.T) {
|
||||
const capacity = 100
|
||||
nc := newNonceCache(time.Hour, capacity)
|
||||
base := time.Now()
|
||||
for i := 0; i < 500; i++ {
|
||||
nc.rememberOrReject("n"+strconv.Itoa(i), base)
|
||||
}
|
||||
|
||||
nc.mu.Lock()
|
||||
size := len(nc.seen)
|
||||
orderLen := len(nc.order)
|
||||
nc.mu.Unlock()
|
||||
|
||||
if size > capacity {
|
||||
t.Fatalf("cache exceeded its cap: %d > %d", size, capacity)
|
||||
}
|
||||
if orderLen != size {
|
||||
t.Fatalf("order queue drifted from the map: order=%d seen=%d", orderLen, size)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user