mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-21 04:57:48 +00:00
test: add R-round regression and security suite
- fail-on-old regression tests for every shipped fix (R-rounds) - fuzz targets (parseJWT, bearer), concurrency-stress, shuffle-flake guards - coverage-closing contract tests (health-check backend, TLS-posture reuse) - r154 ID-token fixture built from parts to avoid false-positive secret scan
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
package traefikoidc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/lukaszraczylo/traefikoidc/internal/cache/backends"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestBlacklist_NoIntrospectionKeyCollision_Redis guards the R114 blacklist /
|
||||
// introspection key collision. Both caches keyed an opaque token by its raw
|
||||
// value, and both previously used CacheTypeToken, so in Redis (shared
|
||||
// backend) mode they composed the SAME Redis key (KeyPrefix + "token:" + raw).
|
||||
// An introspection write could then clobber a same-token blacklist marker, so
|
||||
// a revoked opaque token could be re-accepted on another replica.
|
||||
func TestBlacklist_NoIntrospectionKeyCollision_Redis(t *testing.T) {
|
||||
t.Parallel()
|
||||
mr, err := miniredis.Run()
|
||||
require.NoError(t, err)
|
||||
defer mr.Close()
|
||||
|
||||
redisConfig := backends.DefaultRedisConfig(mr.Addr())
|
||||
redisConfig.RedisPrefix = "test:"
|
||||
backend, err := backends.NewRedisBackend(redisConfig)
|
||||
require.NoError(t, err)
|
||||
defer backend.Close()
|
||||
|
||||
// Both caches share ONE Redis backend — mirrors production where every
|
||||
// UniversalCache wraps the same redisBackend (createBackend ignores its arg).
|
||||
blacklist := NewUniversalCacheWithBackend(UniversalCacheConfig{
|
||||
Type: CacheTypeBlacklist, // distinct "blacklist:" namespace (R128)
|
||||
MaxSize: 1000,
|
||||
DefaultTTL: 24 * time.Hour,
|
||||
}, backend)
|
||||
defer blacklist.Close()
|
||||
|
||||
introsp := NewUniversalCacheWithBackend(UniversalCacheConfig{
|
||||
Type: CacheTypeIntrospection, // distinct "introspection:" namespace
|
||||
MaxSize: 1000,
|
||||
DefaultTTL: 5 * time.Minute,
|
||||
}, backend)
|
||||
defer introsp.Close()
|
||||
|
||||
raw := "opaque.signed.revoked-token"
|
||||
|
||||
// 1. Revoke the opaque token: blacklist marker lands at blacklist:<raw>.
|
||||
require.NoError(t, blacklist.Set(raw, true, time.Hour))
|
||||
|
||||
// 2. Cache an introspection result for the SAME raw token.
|
||||
// Distinct namespaces — introspection:<raw> — marker stays intact.
|
||||
require.NoError(t, introsp.Set(raw, &IntrospectionResponse{Active: true}, time.Minute))
|
||||
|
||||
// 3. The blacklist must STILL report the token as revoked.
|
||||
v, ok := blacklist.Get(raw)
|
||||
require.True(t, ok, "blacklist entry must exist for a revoked token")
|
||||
revoked, isBool := v.(bool)
|
||||
require.True(t, isBool, "blacklist value must be a bool, got %T (introspection clobbered the key?)", v)
|
||||
require.True(t, revoked, "revoked opaque token must remain blacklisted")
|
||||
}
|
||||
Reference in New Issue
Block a user