Skip to content

Commit 9db0726

Browse files
author
Avish Porwal
committed
Fixing unhandled NUL Bytes in API Requests
1 parent 9afbaac commit 9db0726

File tree

2 files changed

+29
-0
lines changed

2 files changed

+29
-0
lines changed

internal/api/handlers/v0/servers.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ func RegisterServersEndpoints(api huma.API, pathPrefix string, registry service.
5252
Description: "Get a paginated list of MCP servers from the registry",
5353
Tags: []string{"servers"},
5454
}, func(ctx context.Context, input *ListServersInput) (*Response[apiv0.ServerListResponse], error) {
55+
if containsNULByte(input.Cursor) {
56+
return nil, huma.Error400BadRequest("Invalid cursor: NUL byte not allowed")
57+
}
58+
5559
// Build filter from input parameters
5660
filter := &database.ServerFilter{}
5761

@@ -119,12 +123,18 @@ func RegisterServersEndpoints(api huma.API, pathPrefix string, registry service.
119123
if err != nil {
120124
return nil, huma.Error400BadRequest("Invalid server name encoding", err)
121125
}
126+
if containsNULByte(serverName) {
127+
return nil, huma.Error400BadRequest("Invalid server name: NUL byte not allowed")
128+
}
122129

123130
// URL-decode the version
124131
version, err := url.PathUnescape(input.Version)
125132
if err != nil {
126133
return nil, huma.Error400BadRequest("Invalid version encoding", err)
127134
}
135+
if containsNULByte(version) {
136+
return nil, huma.Error400BadRequest("Invalid version: NUL byte not allowed")
137+
}
128138

129139
var serverResponse *apiv0.ServerResponse
130140
// Handle "latest" as a special version
@@ -160,6 +170,9 @@ func RegisterServersEndpoints(api huma.API, pathPrefix string, registry service.
160170
if err != nil {
161171
return nil, huma.Error400BadRequest("Invalid server name encoding", err)
162172
}
173+
if containsNULByte(serverName) {
174+
return nil, huma.Error400BadRequest("Invalid server name: NUL byte not allowed")
175+
}
163176

164177
// Get all versions for this server
165178
servers, err := registry.GetAllVersionsByServerName(ctx, serverName)
@@ -186,3 +199,7 @@ func RegisterServersEndpoints(api huma.API, pathPrefix string, registry service.
186199
}, nil
187200
})
188201
}
202+
203+
func containsNULByte(s string) bool {
204+
return strings.IndexByte(s, 0) >= 0
205+
}

internal/api/handlers/v0/servers_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,8 @@ func TestServersEndpointEdgeCases(t *testing.T) {
461461
{"limit too high", "?limit=1000", http.StatusUnprocessableEntity, "validation failed"},
462462
{"negative limit", "?limit=-1", http.StatusUnprocessableEntity, "validation failed"},
463463
{"invalid updated_since format", "?updated_since=invalid", http.StatusBadRequest, "Invalid updated_since format"},
464+
{"cursor contains NUL", "?cursor=%00", http.StatusBadRequest, "Invalid cursor"},
465+
{"cursor contains non NUL", "?cursor=server", http.StatusOK, ""},
464466
{"future updated_since", "?updated_since=2030-01-01T00:00:00Z", http.StatusOK, ""},
465467
{"very old updated_since", "?updated_since=1990-01-01T00:00:00Z", http.StatusOK, ""},
466468
{"empty search parameter", "?search=", http.StatusOK, ""},
@@ -489,6 +491,16 @@ func TestServersEndpointEdgeCases(t *testing.T) {
489491
}
490492
})
491493

494+
t.Run("path parameter NUL byte rejected", func(t *testing.T) {
495+
req := httptest.NewRequest(http.MethodGet, "/v0/servers/%00/versions", nil)
496+
w := httptest.NewRecorder()
497+
498+
mux.ServeHTTP(w, req)
499+
500+
assert.Equal(t, http.StatusBadRequest, w.Code)
501+
assert.Contains(t, w.Body.String(), "Invalid server name")
502+
})
503+
492504
t.Run("response structure validation", func(t *testing.T) {
493505
req := httptest.NewRequest(http.MethodGet, "/v0/servers", nil)
494506
w := httptest.NewRecorder()

0 commit comments

Comments
 (0)