Audit review (rounds 1-31): fix confirmed issues, remove dead code, harden tests

Security/correctness fixes:
- fail-open token validation preserved with lenient-audience; access token that
  fails verification and has no ID token to corroborate now fails closed
- token_manager blacklist-expiry: fix impossible tautology (x>24h && x<24h)
- constant-time OAuth state compare; clear state/nonce/code_verifier on
  callback token-exchange failure to prevent replay of half-failed flows
- StopAllTasks gated on last-instance close
- background-task panic recovery; remove task-registry counter leak
- circuit breaker half-open: release probe slot; CB + per-session outcome
  recorded once per refresh operation, not once per waiter
- Redis connection pool: handle nil receive when pool closed concurrently
- FileStore: atomic temp-write + rename
- errors_recovery health-check data race -> atomic

Test-isolation / hardening:
- reset helpers for Once-pinned global cache manager and transport pool
- coverage: utils 70->93% (redirect URL security contract); fuzz targets for
  bearer parsers; concurrent ServeHTTP race tests for the shared request path

Dead-code removal (verified orphaned, build+suite green): internal/providers
(30 files, 0 importers), internal/compat, internal/features, input_validation.

Verified: govulncheck, go mod tidy, yaegi-validate, cross-compile (darwin+
linux), golangci-lint (new-from-rev clean), 3x full race+shuffle green.
This commit is contained in:
2026-08-12 23:08:03 +01:00
parent f728f61777
commit 2ff14556d0
66 changed files with 1064 additions and 10046 deletions
+10
View File
@@ -83,6 +83,11 @@ func (p *ConnectionPool) Get(ctx context.Context) (*RedisConn, error) {
select {
case conn = <-p.connections:
if conn == nil {
// Pool channel was closed concurrently (Close) - receiving from a
// closed channel yields the zero value.
return nil, ErrBackendClosed
}
// Reuse existing connection - validate if health check enabled
if p.config.EnableHealthCheck && !p.isConnectionHealthy(conn) {
// Connection is stale, close it and try again
@@ -118,6 +123,11 @@ func (p *ConnectionPool) Get(ctx context.Context) (*RedisConn, error) {
// Pool exhausted, wait for a connection with timeout
select {
case conn = <-p.connections:
if conn == nil {
// Pool channel was closed concurrently (Close) - receiving from a
// closed channel yields the zero value.
return nil, ErrBackendClosed
}
// Validate connection if health check enabled
if p.config.EnableHealthCheck && !p.isConnectionHealthy(conn) {
_ = conn.Close()
+41 -1
View File
@@ -367,7 +367,47 @@ func TestConnectionPool_Close(t *testing.T) {
require.NoError(t, err)
}
// TestConnectionPool_Timeouts tests various timeout scenarios
// TestConnectionPool_GetDuringClose_NoNilRegression ensures a Get racing with
// Close never yields a nil *RedisConn with a nil error (which previously
// happened when the receive from the closed p.connections channel returned
// the zero value), and never panics by calling Close on a nil receiver.
func TestConnectionPool_GetDuringClose_NoNilRegression(t *testing.T) {
mr := NewMiniredisServer(t)
config := &PoolConfig{
Address: mr.GetAddr(),
MaxConnections: 1,
ConnectTimeout: 1 * time.Second,
}
pool, err := NewConnectionPool(config)
require.NoError(t, err)
// Fill the pool so a concurrent Get blocks waiting for a connection.
held, err := pool.Get(context.Background())
require.NoError(t, err)
defer pool.Put(held)
type result struct {
conn *RedisConn
err error
}
resCh := make(chan result, 1)
go func() {
c, e := pool.Get(context.Background())
resCh <- result{c, e}
}()
// Give the goroutine time to reach the blocking receive on p.connections,
// then close the pool. Closing the channel unblocks the receive with the
// zero value; Get must surface ErrBackendClosed rather than (nil, nil).
time.Sleep(50 * time.Millisecond)
require.NoError(t, pool.Close())
res := <-resCh
require.Error(t, res.err, "Get after Close must return an error, not (nil, nil)")
assert.True(t, errors.Is(res.err, ErrBackendClosed), "want ErrBackendClosed, got %v", res.err)
}
func TestConnectionPool_Timeouts(t *testing.T) {
mr := NewMiniredisServer(t)