Audit review (rounds 1-31): fix confirmed issues, remove dead code, harden tests

Security/correctness fixes:
- fail-open token validation preserved with lenient-audience; access token that
  fails verification and has no ID token to corroborate now fails closed
- token_manager blacklist-expiry: fix impossible tautology (x>24h && x<24h)
- constant-time OAuth state compare; clear state/nonce/code_verifier on
  callback token-exchange failure to prevent replay of half-failed flows
- StopAllTasks gated on last-instance close
- background-task panic recovery; remove task-registry counter leak
- circuit breaker half-open: release probe slot; CB + per-session outcome
  recorded once per refresh operation, not once per waiter
- Redis connection pool: handle nil receive when pool closed concurrently
- FileStore: atomic temp-write + rename
- errors_recovery health-check data race -> atomic

Test-isolation / hardening:
- reset helpers for Once-pinned global cache manager and transport pool
- coverage: utils 70->93% (redirect URL security contract); fuzz targets for
  bearer parsers; concurrent ServeHTTP race tests for the shared request path

Dead-code removal (verified orphaned, build+suite green): internal/providers
(30 files, 0 importers), internal/compat, internal/features, input_validation.

Verified: govulncheck, go mod tidy, yaegi-validate, cross-compile (darwin+
linux), golangci-lint (new-from-rev clean), 3x full race+shuffle green.
This commit is contained in:
2026-08-12 23:08:03 +01:00
parent f728f61777
commit 2ff14556d0
66 changed files with 1064 additions and 10046 deletions
+24 -5
View File
@@ -146,6 +146,22 @@ func (bt *BackgroundTask) Stop() {
})
}
// safeRunTask executes the task function, recovering from any panic so that a
// single panicking run does not permanently kill the background goroutine and its
// periodic loop. Under yaegi (where this plugin runs) unexpected panics are more
// likely, and a silently-dead background task (metadata refresh, autocleanup,
// memory monitor) would otherwise stop forever with no log or restart.
func (bt *BackgroundTask) safeRunTask() {
defer func() {
if r := recover(); r != nil {
if bt.logger != nil {
bt.logger.Errorf("Background task %s panicked (recovered): %v", bt.name, r)
}
}
}()
bt.taskFunc()
}
// run is the main loop for the background task.
// It executes the task function immediately, then periodically
// until the stop signal is received.
@@ -187,7 +203,7 @@ func (bt *BackgroundTask) run() {
}
return
default:
bt.taskFunc()
bt.safeRunTask()
}
for {
@@ -206,7 +222,7 @@ func (bt *BackgroundTask) run() {
}
return
default:
bt.taskFunc()
bt.safeRunTask()
}
case <-bt.stopChan:
if bt.logger != nil {
@@ -463,10 +479,13 @@ func (tr *TaskRegistry) RegisterTask(name string, task *BackgroundTask) error {
tr.mu.Lock()
defer tr.mu.Unlock()
// Task execution tracking is now handled in the run() method
tr.tasks[name] = task
tr.cb.OnTaskSuccess(name)
// Execution tracking (the circuit-breaker concurrent count) is handled by
// Start()/run(): Start() reserves a slot via OnTaskStart and run() releases
// it via OnTaskComplete. Do NOT also OnTaskSuccess here — that would
// double-count every registered task (+1 on register, +1 on Start, -1 on
// stop), leaking one slot per task and eventually blocking all background
// task creation at the concurrency limit.
if tr.logger != nil {
tr.logger.Debug("Registered background task: %s", name)