Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## 2026-08-07 - Add ReadHeaderTimeout to Prevent Slowloris Attacks
**Vulnerability:** Found multiple `http.Server` instances without a configured `ReadHeaderTimeout` in `internal/mcp/oauth.go`, `internal/oauth/loopback.go`, `internal/provideroauth/openrouter.go`, and `internal/tools/bash_tool_test.go`, causing gosec G112 warnings and potential Slowloris (CWE-400) vulnerability.
**Learning:** Default `http.Server` configurations do not enforce timeouts for reading request headers. This gap can be exploited by an attacker sending headers slowly to keep connections open, exhausting server resources.
**Prevention:** Always explicitly configure `ReadHeaderTimeout` (e.g., `10 * time.Second`) when initializing an `http.Server` to ensure timely request header reading.
1 change: 1 addition & 0 deletions internal/mcp/oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ func Login(ctx context.Context, options LoginOptions) (StoredToken, error) {
}
resultChan := make(chan callbackResult, 1)
server := &http.Server{
ReadHeaderTimeout: 10 * time.Second,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/callback" {
http.NotFound(w, r)
Expand Down
5 changes: 4 additions & 1 deletion internal/oauth/loopback.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ func NewLoopbackListenerOnPort(state string, port int) (*LoopbackListener, error
state: state,
result: make(chan callbackResult, 1),
}
l.server = &http.Server{Handler: http.HandlerFunc(l.handle)}
l.server = &http.Server{
ReadHeaderTimeout: 10 * time.Second,
Handler: http.HandlerFunc(l.handle),
}
go func() { _ = l.server.Serve(ln) }()
return l, nil
}
Expand Down
36 changes: 19 additions & 17 deletions internal/provideroauth/openrouter.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,26 +79,28 @@ func OpenRouterLogin(ctx context.Context, opts OpenRouterOptions) (string, error

codeCh := make(chan string, 1)
errCh := make(chan error, 1)
server := &http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/callback" {
http.NotFound(w, r)
return
}
if code := strings.TrimSpace(r.URL.Query().Get("code")); code != "" {
_, _ = io.WriteString(w, "OpenRouter authorization complete. You may close this window.")
server := &http.Server{
ReadHeaderTimeout: 10 * time.Second,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/callback" {
http.NotFound(w, r)
return
}
if code := strings.TrimSpace(r.URL.Query().Get("code")); code != "" {
_, _ = io.WriteString(w, "OpenRouter authorization complete. You may close this window.")
select {
case codeCh <- code:
default:
}
return
}
w.WriteHeader(http.StatusBadRequest)
_, _ = io.WriteString(w, "Authorization failed. You may close this window.")
select {
case codeCh <- code:
case errCh <- errors.New("provideroauth: callback missing authorization code"):
default:
}
return
}
w.WriteHeader(http.StatusBadRequest)
_, _ = io.WriteString(w, "Authorization failed. You may close this window.")
select {
case errCh <- errors.New("provideroauth: callback missing authorization code"):
default:
}
})}
})}
go func() { _ = server.Serve(listener) }()
defer func() {
shutdownCtx, cancelShutdown := context.WithTimeout(context.Background(), time.Second)
Expand Down
1 change: 1 addition & 0 deletions internal/tools/bash_tool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func runBashToolHelper(command string) {
}
fmt.Println("listening", listener.Addr().String())
server := &http.Server{
ReadHeaderTimeout: 10 * time.Second,
Handler: http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
_, _ = response.Write([]byte("zero-server-ok"))
}),
Expand Down
Loading