Surfaced during the DRY/sibling review of #624. Two follow-ups to the preset-loader transport hardening that PR #624 left for a future change.
A. Per-call client construction
`middlewares/preset.go::loadFromURL` constructs `&http.Client{Transport: TransportFactory()}` on every call. `TransportFactory()` returns a fresh `*http.Transport` per call (see `webhook_security.go::NewSafeTransport`), so each preset fetch gets its own connection pool with no idle-conn reuse. For the cache-miss path this is fine (one-shot fetch), but bursts wouldn't share connections.
Fix: cache the `*http.Client` on the `PresetLoader` struct in `NewPresetLoader`. Document that `SetTransportFactoryForTest` must run before `NewPresetLoader` to influence the cached client.
B. `slack.go` doesn't share TransportFactory
`middlewares/slack.go:94` constructs `&http.Client{Timeout: 5 * time.Second}` with no custom transport when the config doesn't supply one. So Slack notifications inherit the same risk PR #624 fixed for presets — implicit `http.DefaultTransport` posture, no convergence with the webhook stack's TLS-aware transport.
Fix: change the slack.go fallback to `&http.Client{Transport: TransportFactory(), Timeout: 5 * time.Second}`. The Slack middleware is deprecated in favor of the generic webhook system, but operators are still using it; defense-in-depth applies.
Severity
Low — defense-in-depth and minor performance. No CVE.
Acceptance
- `PresetLoader` holds a cached `*http.Client` (or a function that returns one).
- `slack.go` fallback client uses `TransportFactory()`.
- Regression test for slack: assert the fallback client's transport is the one returned by `TransportFactory()`.
Related
- #615 — preset loader TLS posture
- #624 — the fix that left these as follow-ups
Surfaced during the DRY/sibling review of #624. Two follow-ups to the preset-loader transport hardening that PR #624 left for a future change.
A. Per-call client construction
`middlewares/preset.go::loadFromURL` constructs `&http.Client{Transport: TransportFactory()}` on every call. `TransportFactory()` returns a fresh `*http.Transport` per call (see `webhook_security.go::NewSafeTransport`), so each preset fetch gets its own connection pool with no idle-conn reuse. For the cache-miss path this is fine (one-shot fetch), but bursts wouldn't share connections.
Fix: cache the `*http.Client` on the `PresetLoader` struct in `NewPresetLoader`. Document that `SetTransportFactoryForTest` must run before `NewPresetLoader` to influence the cached client.
B. `slack.go` doesn't share TransportFactory
`middlewares/slack.go:94` constructs `&http.Client{Timeout: 5 * time.Second}` with no custom transport when the config doesn't supply one. So Slack notifications inherit the same risk PR #624 fixed for presets — implicit `http.DefaultTransport` posture, no convergence with the webhook stack's TLS-aware transport.
Fix: change the slack.go fallback to `&http.Client{Transport: TransportFactory(), Timeout: 5 * time.Second}`. The Slack middleware is deprecated in favor of the generic webhook system, but operators are still using it; defense-in-depth applies.
Severity
Low — defense-in-depth and minor performance. No CVE.
Acceptance
Related