Files
traefikoidc/review_r132_test.go
T
lukaszraczylo 6d96e8869b 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
2026-08-14 16:22:39 +01:00

60 lines
1.9 KiB
Go

package traefikoidc
// R132 regression: saveCombined must read token/main-session fields under
// sessionMutex.RLock so a concurrent Set* (which writes under the write
// side) cannot tear the combined payload. Previously only the extra-map
// walk took the Read side; the token field reads (getAccessTokenUnsafe &
// friends) were unlocked, a latent data race on the refresh-rotation
// path (a concurrently-rotated refresh token could be read half-consistently
// and re-encoded into the cookie, clobbering the fresh one). Run with -race.
import (
"sync"
"testing"
"net/http/httptest"
"github.com/gorilla/sessions"
)
func TestSaveCombined_NoDataRaceWithConcurrentSet(t *testing.T) {
sm := createTestSessionManager(t)
req := httptest.NewRequest("GET", "/", nil)
sd, err := sm.GetSession(req)
if err != nil {
t.Fatalf("GetSession: %v", err)
}
sd.useCombinedStorage = true
options := &sessions.Options{Path: "/", MaxAge: 86400, HttpOnly: true, Secure: true}
// Seed the refresh/access tokens so the concurrent save has something
// to read; SetAccessToken requires >=20-char tokens.
sd.SetAccessToken("aaaaaaaaaaaaaaaaaaaaaaaaaaaa") // must be >= 20 chars
sd.refreshSession.Values["token"] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"
// One reader goroutine calls saveCombined (payload build reads under
// RLock after R132) while many writers call SetAccessToken (writes
// under Lock). Before R132 the companion payload reads were unlocked,
// so -race flagged the torn read here.
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
rw := httptest.NewRecorder()
for j := 0; j < 500; j++ {
_ = sd.saveCombined(req, rw, options) // ignore cookie-write errors; testing the read race
}
}()
for i := 0; i < 8; i++ {
wg.Add(1)
go func(seed int) {
defer wg.Done()
for j := 0; j < 500; j++ {
sd.SetAccessToken("tok-%d-%d-abcdefghijklmnopqrstuvwxyz")
}
}(i)
}
wg.Wait()
}