mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-17 04:37:48 +00:00
fix(security): close confirmed findings from the 2026-10-15 hunt (#166)
Seven findings are fixed. Each fix has a regression test. - Compare X-Forwarded-Host and the serving host case-insensitively, so a case variant cannot widen the session cookie Domain. - Reject a future iat on the opaque introspection path. - Serve the cached parsed JWKS inside the signature-refresh cooldown. - Release the half-open circuit slot when a probe panics. - Cap requests that wait for provider initialisation at 512. Extra requests receive 503 with Retry-After. - Cap cookie deletion headers at 32 cookie names per response. - Cap front-channel sid markers at 1000, so unauthenticated requests cannot evict backchannel or jti replay markers.
This commit is contained in:
@@ -93,8 +93,16 @@ func (cb *CircuitBreaker) ExecuteWithContext(ctx context.Context, fn func() erro
|
||||
return fmt.Errorf("circuit breaker is open")
|
||||
}
|
||||
|
||||
// Execute the function
|
||||
// A panicking fn must still settle the breaker, otherwise a half-open
|
||||
// probe slot leaks until the next open->half-open transition.
|
||||
panicked := true
|
||||
defer func() {
|
||||
if panicked {
|
||||
cb.recordFailure()
|
||||
}
|
||||
}()
|
||||
err := fn()
|
||||
panicked = false
|
||||
|
||||
if err != nil {
|
||||
cb.recordFailure()
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package recovery
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCircuitBreaker_PanickingProbeReleasesHalfOpenSlot(t *testing.T) {
|
||||
cb := NewCircuitBreaker(CircuitBreakerConfig{
|
||||
FailureThreshold: 1,
|
||||
SuccessThreshold: 1,
|
||||
Timeout: 20 * time.Millisecond,
|
||||
MaxRequests: 1,
|
||||
}, &mockLogger{})
|
||||
|
||||
_ = cb.ExecuteWithContext(context.Background(), func() error { return errors.New("fail") })
|
||||
time.Sleep(40 * time.Millisecond)
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
if recover() == nil {
|
||||
t.Error("panic must still propagate to the caller")
|
||||
}
|
||||
}()
|
||||
_ = cb.ExecuteWithContext(context.Background(), func() error { panic("boom") })
|
||||
}()
|
||||
|
||||
if cb.GetState() != CircuitBreakerOpen {
|
||||
t.Fatalf("state after panicking probe = %s, want open", cb.GetState())
|
||||
}
|
||||
time.Sleep(40 * time.Millisecond)
|
||||
if err := cb.ExecuteWithContext(context.Background(), func() error { return nil }); err != nil {
|
||||
t.Fatalf("probe after next timeout rejected: %v", err)
|
||||
}
|
||||
if cb.GetState() != CircuitBreakerClosed {
|
||||
t.Fatalf("state = %s, want closed", cb.GetState())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user