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

86 lines
2.9 KiB
Go

package traefikoidc
import (
"crypto/sha256"
"fmt"
"testing"
"github.com/gorilla/sessions"
)
// ============================================================================
// R188: cross-chunk integrity binds chunked-token chunks to one another
// ---------------------------------------------------------------------------
// Each chunk is individually authenticated by its own securecookie MAC, so a
// forged chunk is impossible; but an authentic-but-mixed set (a stale chunk
// from an earlier session combined with the current session's chunks, where
// counts still match) previously assembled silently into a wrong token that
// only surfaced as a downstream JWT validation failure (→ forced re-auth).
// New writes now store a SHA-256 of the assembled chunk string on every
// chunk; processChunkedToken verifies it on read. Absent (old sessions) →
// skipped, preserving backward compatibility.
// Fail-on-old: a mixed set with matching count returns the reassembled
// token; the integrity check now rejects it with an error.
func TestR188_ChunkIntegrityDetectsMixedChunks(t *testing.T) {
cm := NewChunkManager(NewLogger(DefaultLogLevel))
config := TokenConfig{
Type: "access",
MinLength: 5,
MaxLength: 100 * 1024,
MaxChunks: 25,
MaxChunkSize: maxCookieSize,
}
// Reassembled chunks form the opaque token "aaabbb"; but chunk 0's
// stored integrity was computed over a DIFFERENT string (a stale chunk
// from another session), so the set is mixed despite counts matching.
chunks := map[int]*sessions.Session{
0: {Values: map[interface{}]interface{}{
"token_chunk": "aaa",
"token_total": 2,
"chunk_integrity": fmt.Sprintf("%x", sha256.Sum256([]byte("different-set"))),
}},
1: {Values: map[interface{}]interface{}{
"token_chunk": "bbb",
"token_total": 2,
}},
}
res := cm.GetToken("", false, chunks, config)
if res.Error == nil {
t.Fatalf("mixed chunk set must be rejected by chunk-integrity check; got token %q (old code returned it)", res.Token)
}
}
// A consistent set with a matching integrity value must still assemble.
func TestR188_ChunkIntegrityValidSetStillSucceeds(t *testing.T) {
cm := NewChunkManager(NewLogger(DefaultLogLevel))
config := TokenConfig{
Type: "access",
MinLength: 5,
MaxLength: 100 * 1024,
MaxChunks: 25,
MaxChunkSize: maxCookieSize,
}
chunks := map[int]*sessions.Session{
0: {Values: map[interface{}]interface{}{
"token_chunk": "aaa",
"token_total": 2,
"chunk_integrity": fmt.Sprintf("%x", sha256.Sum256([]byte("aaabbb"))),
}},
1: {Values: map[interface{}]interface{}{
"token_chunk": "bbb",
"token_total": 2,
}},
}
res := cm.GetToken("", false, chunks, config)
if res.Error != nil {
t.Fatalf("valid coherent chunk set must assemble; got error: %v", res.Error)
}
if res.Token != "aaabbb" {
t.Fatalf("expected reassembled token %q, got %q", "aaabbb", res.Token)
}
}