From 437939cfda494cd60f077065a0c5dd6a4b5aa582 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 19:35:31 +0000 Subject: [PATCH] Fix: Configure Read/Write timeouts on http.Server instances Replaced instances of http.ListenAndServe or unconfigured http.Server with explicitly configured instances that include ReadTimeout, ReadHeaderTimeout, WriteTimeout, and IdleTimeout. This prevents potential resource exhaustion attacks (like Slowloris) across the application components (Gateway, Launcher Backend, Health API, OAuth). Co-authored-by: hobbyistlabs-coder <267281733+hobbyistlabs-coder@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ pkg/auth/oauth.go | 7 ++++++- pkg/channels/manager.go | 9 +++++---- pkg/health/server.go | 9 +++++---- web/backend/main.go | 11 ++++++++++- 5 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 .jules/sentinel.md diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 000000000..0bb5d19ab --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2025-02-28 - [Medium] Fix Missing HTTP Server Timeouts +**Vulnerability:** Go's standard `http.ListenAndServe` and unconfigured `http.Server` instances lack default timeouts for reading headers, reading bodies, and writing responses. +**Learning:** These default settings leave the application vulnerable to resource exhaustion and Denial of Service (DoS) attacks, such as Slowloris, because malicious clients can slowly send data and tie up server connections indefinitely. +**Prevention:** Always instantiate `http.Server` explicitly and set `ReadHeaderTimeout`, `ReadTimeout`, `WriteTimeout`, and (optionally) `IdleTimeout` to reasonable values based on the expected request sizes and latencies. diff --git a/pkg/auth/oauth.go b/pkg/auth/oauth.go index 4667e3d81..2ba75b0c5 100644 --- a/pkg/auth/oauth.go +++ b/pkg/auth/oauth.go @@ -118,7 +118,12 @@ func LoginBrowser(cfg OAuthProviderConfig) (*AuthCredential, error) { return nil, fmt.Errorf("starting callback server on port %d: %w", cfg.Port, err) } - server := &http.Server{Handler: mux} + server := &http.Server{ + Handler: mux, + ReadTimeout: 10 * time.Second, + ReadHeaderTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + } go server.Serve(listener) defer func() { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) diff --git a/pkg/channels/manager.go b/pkg/channels/manager.go index 051dec6ed..c163cbce3 100644 --- a/pkg/channels/manager.go +++ b/pkg/channels/manager.go @@ -344,10 +344,11 @@ func (m *Manager) SetupHTTPServer(addr string, healthServer *health.Server) { } m.httpServer = &http.Server{ - Addr: addr, - Handler: m.mux, - ReadTimeout: 30 * time.Second, - WriteTimeout: 30 * time.Second, + Addr: addr, + Handler: m.mux, + ReadTimeout: 30 * time.Second, + ReadHeaderTimeout: 10 * time.Second, + WriteTimeout: 30 * time.Second, } } diff --git a/pkg/health/server.go b/pkg/health/server.go index 5609ebdf6..a3f6e4df2 100644 --- a/pkg/health/server.go +++ b/pkg/health/server.go @@ -44,10 +44,11 @@ func NewServer(host string, port int) *Server { addr := fmt.Sprintf("%s:%d", host, port) s.server = &http.Server{ - Addr: addr, - Handler: mux, - ReadTimeout: 5 * time.Second, - WriteTimeout: 5 * time.Second, + Addr: addr, + Handler: mux, + ReadTimeout: 5 * time.Second, + ReadHeaderTimeout: 3 * time.Second, + WriteTimeout: 5 * time.Second, } return s diff --git a/web/backend/main.go b/web/backend/main.go index 7a575cc47..ee88e7fd0 100644 --- a/web/backend/main.go +++ b/web/backend/main.go @@ -163,7 +163,16 @@ func main() { }() // Start the Server - if err := http.ListenAndServe(addr, handler); err != nil { + server := &http.Server{ + Addr: addr, + Handler: handler, + ReadTimeout: 10 * time.Second, + ReadHeaderTimeout: 5 * time.Second, + WriteTimeout: 30 * time.Second, + IdleTimeout: 120 * time.Second, + } + + if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("Server failed to start: %v", err) } }