mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-12 04:04:26 +00:00
- 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
60 lines
1.9 KiB
Go
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()
|
|
}
|