Commit Graph
260 Commits
Author SHA1 Message Date
lukaszraczylo 925d62712e test: mark synthetic PKCE verifier with trufflehog:ignore
Quiet the Lob detector's false positive on the fake test-only code_verifier,
matching the repo's existing // trufflehog:ignore convention in token_test.go.
Local trufflehog filesystem scan is now 0 verified / 0 unverified.
2026-08-14 16:30:50 +01:00
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
lukaszraczylo cf0a7e2440 fix(jwk): use time.Since for elapsed-time computation (S1012) 2026-08-14 16:17:38 +01:00
lukaszraczylo 71d2cf592b build: pin toolchain to go1.26.6 (resolves 5 reachable stdlib CVEs)
- net/http, asn1, idna GO-2026 CVEs cleared; govulncheck now 0 findings
2026-08-14 16:17:32 +01:00
lukaszraczylo 113c3acc76 docs: complete config reference and config examples
- CONFIGURATION.md: add 11 previously-omitted optional parameters
- README: document 6 implemented-but-undocumented options
- fix example config (raw .env block made invalid YAML)
2026-08-14 16:17:28 +01:00
lukaszraczylo 4bd061abee fix(config,dcr): env fallbacks, DCR self-heal, per-source auth gate
- implement documented OIDC_* env fallbacks; hold metadataMu for clientAssertion write
- re-register client when client_secret expires; require secret for confidential
- add opt-in per-source external auth rate limiting (PerSourceLoginRateLimit)
2026-08-14 16:17:24 +01:00
lukaszraczylo 4d23d618f5 fix(cache): harden cache backends, resilience, recovery, and transports
- RESP -ERR no longer closes healthy conn / aborts batch; global cap replacement-aware
- global health-check pass serialized (latent data race); MemoryMonitor via ResourceManager
- token-exchange retry, open-circuit rejections counted as failures
- TLS-posture matching transport reuse (no silent InsecureSkipVerify handoff)
2026-08-14 16:16:51 +01:00
lukaszraczylo 8640451877 fix(auth): harden OIDC flow, token validation, session, and bearer handling
- replay entry covers exp + clock-skew window; callback GET-only (RFC 6749)
- callback rate-limit returns 429 + Retry-After; panic-recovery sets no-store
- cross-chunk integrity for chunked tokens; bounded chunked-token read scan
- HTTPError classification so 5xx/429 token-endpoint responses retry
- redaction of token/code/state in logs; per-source external auth gate
2026-08-14 16:16:26 +01:00
lukaszraczylo 3eb11b07e9 R92: validate all time claims on lenient-audience access tokens
On the lenient-audience path (validateStandardTokensRS), jwt.Verify
short-circuits at the aud check before reaching exp/iat/nbf validation,
leaving every time claim unvalidated. accessTokenUnexpired previously
re-patched only 'exp' (the R66 fix), so a token with a not-yet-reached
'nbf' but a valid exp was trusted for authorization before it was valid.

accessTokenUnexpired now re-applies the full main-path time validation:
verifyExpiration, verifyIssuedAt (iat required), and verifyNotBefore when
present — all clock-skew-aware. Regression TestR92LenientPathRejectsFutureNbf
(plus valid control) fails on old code (future-nbf token accepted), passes on
new.

R92 clean/declined: bearer auth (aud enforced, full claims — clean);
refresh rotation (correctly bound to session; no local reuse detection =
design); logout cookie completeness (all chunk names cleared, no revive;
two P3 non-security notes declined).
2026-08-13 19:42:26 +01:00
lukaszraczylo d0ec853499 R89: normalize excluded-url paths; apply defaults to pooled HTTP clients
Two verified defects from R89 scout round:

1. Excluded-URL dot-segment traversal widening (P1, auth bypass).
   shouldBypassAuth passes req.URL.Path raw (middleware.go:54); Go's server
   leaves '..' in URL.Path (url.ParseRequestURI does not normalize), so an
   exclusion for /public also matched /public/../admin, letting a real
   sibling (/admin) reach the backend unauthenticated. pathExcluded now
   path.Clean()s the request path ('.' -> '' to preserve root semantics)
   before boundary matching. Regression TestR89ExcludedPathDotTraversal
   fails on old (all 4 traversal cases wrongly excluded), passes on new.

2. Pooled HTTP client had no zero-value defaults (P2, unbounded stall).
   CreateHTTPClient applies defaults; CreatePooledHTTPClient and
   GetOrCreateTransport did not, so a partial config yielded Timeout=0 and
   ResponseHeaderTimeout=0 (a hanging upstream stalls forever). Extracted
   applyHTTPClientDefaults (single source of truth) and applied it in all
   three construction paths; pool cache key now reflects normalized values.
   Regression TestR89PooledClientAppliesDefaults fails on old, passes on new.

R89 clean/declined: session max-age IS absolute-and-enforced-every-request
(verified, review_r68 already locks it); AllowedUserDomains fail-closed (P2
'*' not a wildcard, P3 TrimSpace — declined as config-scoped).
2026-08-13 19:34:38 +01:00
lukaszraczylo 866e57380d R88: make StopAllTasks cover ResourceManager singleton tasks
TaskRegistry.CreateSingletonTask delegates to ResourceManager.RegisterBackgroundTask
and stores the task in the ResourceManager's registry, not TaskRegistry.tasks.
TaskRegistry.StopAllTasks previously iterated its own (always-empty) registry,
silently leaking every process-global singleton task's ticker goroutine past the
last Close (memory-monitor, replay-cache-cleanup) — tasks kept running real
periodic work with no instance to serve.

Add ResourceManager.StopAllTasks (stops all rm-owned tasks) and have
TaskRegistry.StopAllTasks stop both its own RegisterTask-direct tasks and the
ResourceManager-owned singletons.

Regression TestR88StopAllTasksStopsResourceManagerOwnedTasks (review_r88_test.go):
fails on old code (task still running after StopAllTasks), passes on new.
2026-08-13 19:25:03 +01:00
lukaszraczylo 9e39827f7d R87: validate backchannel logout token exp; live-refresh JWKS for logout kid-miss 2026-08-13 18:59:52 +01:00
lukaszraczylo 39b3c53d82 R86: atomic SetIfAbsent for replay cache; close double-accept and singleton-read race 2026-08-13 18:54:17 +01:00
lukaszraczylo 0dfa2979bb R83: DCR require client_secret for confidential auth, validate redirect_uri scheme; reject callback query/fragment 2026-08-13 18:46:53 +01:00
lukaszraczylo 3c41fa717a R82: req discovery endpoints, single-use code retry 2026-08-13 18:39:02 +01:00
lukaszraczylo 820d7969e3 R81: reject empty access token on callback+refresh 2026-08-13 18:34:11 +01:00
lukaszraczylo 893a7ea251 R80: req secret for introspection, pool conn on panic 2026-08-13 18:26:59 +01:00
lukaszraczylo 351aae866e R79: invalidation ttl tracks maxage, empty tpl hdr 2026-08-13 18:21:35 +01:00
lukaszraczylo 1c5e301730 R78: hybrid Clear flush, reauth reacquire, pool CAS 2026-08-13 18:17:04 +01:00
lukaszraczylo 0e57c0cad3 R77: bearer allowedUsers gate, session inUse CAS 2026-08-13 18:07:32 +01:00
lukaszraczylo 60980b5eb6 R76: dcr singleflight, L1 no-resurrect, TLS loopback 2026-08-13 17:59:14 +01:00
lukaszraczylo e7378d640d R75: azure unauth copy-paste, dcr guard, cookie domain 2026-08-13 17:44:34 +01:00
lukaszraczylo 5a54960cdd R74: recv handler panics, cache TTL race fix 2026-08-13 17:35:25 +01:00
lukaszraczylo ea37443ee0 R73: logout jti replay, expired ajax, no-value 2026-08-13 17:31:17 +01:00
lukaszraczylo 44ce6c0b97 R72: validate combined cookie chunk metadata 2026-08-13 17:24:35 +01:00
lukaszraczylo c408f7c295 R71: SSE/WS allowlist, session max-age, sec headers 2026-08-13 17:20:22 +01:00
lukaszraczylo 08de9b79aa R70: pooled-client cookie jar, refresh Save-failure cleanup 2026-08-13 17:15:42 +01:00
lukaszraczylo 6dbffcba40 R69: fix double pool-return on session-timeout path 2026-08-13 17:11:17 +01:00
lukaszraczylo d5fddebeaa R68: cache expiry TOCTOU, logout open-redirect, session max-age 2026-08-13 17:06:23 +01:00
lukaszraczylo 2941260a81 R67: apply allowedRolesAndGroups gate to SSE/WS bypass 2026-08-13 16:56:15 +01:00
lukaszraczylo fe5f517df5 R66: check expiry on lenient-audience token path 2026-08-13 16:51:32 +01:00
lukaszraczylo a475617ec6 R65: validate ES client-assertion key curve matches alg 2026-08-13 16:47:25 +01:00
lukaszraczylo fa12947a64 R64: cap metadata cache grace by configured limits 2026-08-13 16:44:07 +01:00
lukaszraczylo a593a9f9d7 R63: Shutdown waits for in-flight refresh goroutines 2026-08-13 16:40:44 +01:00
lukaszraczylo e6501951b0 R62: drain queued tasks on goroutine pool shutdown 2026-08-13 16:34:25 +01:00
lukaszraczylo 724b00ca10 R61: detect truncated chunked token via stored total 2026-08-13 16:10:20 +01:00
lukaszraczylo 1df9b98aeb R60: accept single-string groups/roles; fix mem over-evict 2026-08-13 16:06:43 +01:00
lukaszraczylo e61edaa5b3 R59: honor backend TTL on cache repopulation 2026-08-13 16:01:50 +01:00
lukaszraczylo 6ac451da1d R58: idempotent RefreshCoordinator Shutdown; no close-close 2026-08-13 15:58:20 +01:00
lukaszraczylo ed5f528c60 R57: FIFO eviction; keep fresh replay JTIs in cache 2026-08-13 15:55:20 +01:00
lukaszraczylo 49d1942de9 R56: honor RateLimit config as sustained req rate 2026-08-13 15:49:47 +01:00
lukaszraczylo ac2fdfe09d R55: blacklist session tokens on user logout 2026-08-13 15:34:17 +01:00
lukaszraczylo 4c5991f8e6 R54: refresh JWKS on unknown-kid to pick up key rotation 2026-08-13 15:30:58 +01:00
lukaszraczylo 08f474b551 R53: enforce 2048-bit minimum RSA key size on verify 2026-08-13 15:27:10 +01:00
lukaszraczylo da9c21d2a6 R51: guarantee openid scope in buildAuthURL when filter drops it 2026-08-13 15:23:13 +01:00
lukaszraczylo 7d6f09da87 Sanitize claim-derived identifier on auth-bypass headers (round 48) 2026-08-13 15:14:57 +01:00
lukaszraczylo 44af200408 Fix bearer Authorization template strip order + nonce constant-time compare (round 45) 2026-08-13 13:02:04 +01:00
lukaszraczylo d6a77d21a9 Harden redirect-count accessors against pooled-session data race (round 44) 2026-08-13 10:27:20 +01:00
lukaszraczylo e4b74cffd7 Apply configured conn limits in pooled transport (round 41)
http_client_pool.go SharedTransportPool.GetOrCreateTransport built the shared
http.Transport with hardcoded MaxConnsPerHost=5, MaxIdleConnsPerHost=2,
MaxIdleConns=10, IdleConnTimeout=30s while HTTPClientConfig's versions of
those fields drove configKey (cache identity) and were validated and tuned
per preset (Default: 20/10/50/30s; OIDCProvider: 50/25/100/90s). The
config was thus valid, keyed, and validated but silently ignored in the
transport used by main.go (CreatePooledHTTPClient) - dead config that left
OIDC-provider connection tuning without effect. The sibling factory path
(http_client_factory.go) already applied these values.

Now the pooled transport derives MaxConnsPerHost, MaxIdleConnsPerHost,
MaxIdleConns, and IdleConnTimeout from the config, consistent with the
factory path and with configKey.

Test: added TestSharedTransportPoolAppliesConfigLimits, which fails on the
old hardcoded code (fixed values) and passes now.

Verified: full source-order suite, full -race -count=1 -shuffle=on ./...
(all packages), golangci-lint run ./... (0 issues).
2026-08-13 08:43:08 +01:00
lukaszraczylo 0d5354bedc Fix dead CircuitBreaker.ResetTimeout config (round 39)
error_recovery.go: ResetTimeout was copied into the CircuitBreaker but never
consulted by the state machine (used only for a metric), so the documented
"how long to wait before fully closing the circuit" (reset_timeout, default
30s) had no effect: Open->HalfOpen used Timeout (60s), and a single
half-open success closed the circuit immediately, losing the intended
open -> half-open -> (reset cooldown) -> closed hysteresis and letting a
stray success immediately reopen (bounce).

Now the circuit remains half-open after entering it until resetTimeout has
elapsed; only then can a successful request fully close it. Half-open
requests are still allowed as probes. Default behavior is more
conservative (up to Timeout+ResetTimeout before full close).

Tests: updated TestCircuitBreakerHalfOpenTransition to the corrected
semantics; added TestCircuitBreakerResetTimeoutEnforced (fails on the old
dead-field code, where resetTimeout was ignored).

Verified: full source-order suite, full -race -count=1 -shuffle=on ./...
(all packages), golangci-lint run ./... (0 issues).
2026-08-13 05:50:33 +01:00