Files
traefikoidc/review_r137_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

74 lines
1.9 KiB
Go

package traefikoidc
import (
"fmt"
"sync"
"testing"
"time"
)
// TestClientCredentials_NoDataRaceWithDCRWrite verifies that request-path
// readers of the client credential fields (clientID/clientSecret/audience)
// snapshot them under metadataMu.RLock rather than reading them lock-free.
//
// DCR (performDynamicClientRegistration, main.go) rewrites these fields under
// metadataMu.Lock at runtime (when a first registration failed and the 2h
// metadata refresh retries), so a lock-free read is a data race (R137).
//
// Requires -race to discriminate: on the OLD code buildAuthURL read
// t.clientID t.audience directly (race -> flagged); on the NEW code it
// snapshots via clientCredentials (clean).
func TestClientCredentials_NoDataRaceWithDCRWrite(t *testing.T) {
// This test only discriminates under -race; without it there is nothing
// to observe, so keep it focused and fast for non-race runs.
ts := NewTestSuite(t)
ts.Setup()
oidc := ts.tOidc
stop := make(chan struct{})
var wg sync.WaitGroup
// Writer goroutine mutating the credential fields under metadataMu.Lock,
// mirroring what performDynamicClientRegistration does at runtime.
wg.Add(1)
go func() {
defer wg.Done()
i := 0
for {
select {
case <-stop:
return
default:
}
oidc.metadataMu.Lock()
oidc.clientID = fmt.Sprintf("rw-%d", i%3)
oidc.clientSecret = fmt.Sprintf("sec-%d", i%3)
oidc.audience = fmt.Sprintf("aud-%d", i%3)
oidc.metadataMu.Unlock()
i++
}
}()
// Reader goroutines exercising request-path reader functions that touch
// the credential fields.
for r := 0; r < 8; r++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case <-stop:
return
default:
}
// buildAuthURL reads t.clientID + t.audience (via snapshot now).
_ = oidc.buildAuthURL("https://app.example.com/callback", "state", "nonce", "")
}
}()
}
time.Sleep(300 * time.Millisecond)
close(stop)
wg.Wait()
}