package oidcsession import ( "bytes" "strings" "testing" "time" ) func TestLoginTransactionIsEncryptedBoundedAndPKCES256(t *testing.T) { now := time.Date(2026, 7, 13, 12, 0, 0, 0, time.UTC) cipher, _ := NewCipher(bytes.Repeat([]byte{9}, 32)) transaction, challenge, err := NewLoginTransaction("/workspace?tab=wallet", now) if err != nil { t.Fatal(err) } if transaction.State == transaction.Nonce || len(challenge) != 43 || challenge == transaction.PKCEVerifier { t.Fatal("state, nonce and PKCE values are not independent S256 material") } encoded, err := cipher.EncodeLoginTransaction(transaction) if err != nil { t.Fatal(err) } if strings.Contains(encoded, transaction.State) || strings.Contains(encoded, transaction.PKCEVerifier) { t.Fatal("login transaction cookie contains plaintext security material") } decoded, err := cipher.DecodeLoginTransaction(encoded, now.Add(9*time.Minute)) if err != nil || decoded.ReturnTo != transaction.ReturnTo { t.Fatalf("decode transaction=%#v err=%v", decoded, err) } if _, err := cipher.DecodeLoginTransaction(encoded, now.Add(10*time.Minute)); err == nil { t.Fatal("10-minute login transaction was accepted") } } func TestValidReturnToRejectsOpenRedirects(t *testing.T) { for _, value := range []string{"https://evil.example", "//evil.example", "/\\evil", "", "workspace"} { if ValidReturnTo(value) { t.Fatalf("unsafe returnTo accepted: %q", value) } } if !ValidReturnTo("/workspace/tasks?from=login#latest") { t.Fatal("safe relative returnTo was rejected") } }