Skip to content
Merged
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
12 changes: 12 additions & 0 deletions changelog.d/fixed/vmafx-mcp-http-server-hardening.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
- `vmafx-mcp --transport=http` no longer exposes a Slowloris
(CWE-400) vector. The `&http.Server{...}` literal was missing
`ReadHeaderTimeout` / `ReadTimeout` / `WriteTimeout` /
`IdleTimeout`, so a malicious or stuck client could hold a
TCP connection open indefinitely while dripping header bytes
and exhaust the server's goroutine / file-descriptor budget.
Added the four timeouts mirroring the hardened pattern in
`cmd/vmafx-server/http_server.go` (10s read-header, 30s read,
120s write, 60s idle). Graceful shutdown was also unbounded
(`srv.Shutdown(context.Background())`) and could hang the
process forever if an in-flight request stalled; it now uses
`observability.GracefulShutdownTimeout` (30s) as an upper bound.
15 changes: 12 additions & 3 deletions cmd/vmafx-mcp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ import (
"os"
"os/signal"
"syscall"
"time"

"github.com/modelcontextprotocol/go-sdk/mcp"

"github.com/VMAFx/vmafx/pkg/observability"
)

const (
Expand Down Expand Up @@ -62,8 +65,12 @@ func main() {
logger.Info("vmafx-mcp starting on HTTP", "addr", addr)
handler := mcp.NewStreamableHTTPHandler(func(*http.Request) *mcp.Server { return srv }, nil)
httpSrv := &http.Server{
Addr: addr,
Handler: handler,
Addr: addr,
Handler: handler,
ReadHeaderTimeout: 10 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 120 * time.Second,
IdleTimeout: 60 * time.Second,
}
ln, err := net.Listen("tcp", addr)
if err != nil {
Expand All @@ -72,7 +79,9 @@ func main() {
}
go func() {
<-ctx.Done()
_ = httpSrv.Shutdown(context.Background()) //nolint:contextcheck
shutdownCtx, cancel := context.WithTimeout(context.Background(), observability.GracefulShutdownTimeout) //nolint:contextcheck
defer cancel()
_ = httpSrv.Shutdown(shutdownCtx)
}()
if err := httpSrv.Serve(ln); err != nil && err != http.ErrServerClosed {
fmt.Fprintf(os.Stderr, "http server error: %v\n", err)
Expand Down
Loading