diff --git a/workspace-server/internal/handlers/mcp.go b/workspace-server/internal/handlers/mcp.go index 3065ca4a..707c12f2 100644 --- a/workspace-server/internal/handlers/mcp.go +++ b/workspace-server/internal/handlers/mcp.go @@ -434,7 +434,8 @@ func (h *MCPHandler) dispatchRPC(ctx context.Context, workspaceID string, req mc } default: - base.Error = &mcpRPCError{Code: -32601, Message: "method not found: " + req.Method} + // Per OFFSEC-001: error message must not include user-controlled req.Method. + base.Error = &mcpRPCError{Code: -32601, Message: "method not found"} } return base diff --git a/workspace-server/internal/handlers/mcp_test.go b/workspace-server/internal/handlers/mcp_test.go index dbad430a..d306fa14 100644 --- a/workspace-server/internal/handlers/mcp_test.go +++ b/workspace-server/internal/handlers/mcp_test.go @@ -9,6 +9,7 @@ import ( "net/http" "net/http/httptest" "os" + "strings" "testing" "errors" @@ -204,6 +205,9 @@ func TestMCPHandler_NotificationsInitialized_Returns200(t *testing.T) { // Unknown method // ───────────────────────────────────────────────────────────────────────────── +// TestMCPHandler_UnknownMethod_Returns32601 verifies dispatchRPC returns +// -32601 for an unknown method. Per OFFSEC-001: the error message must be +// constant — req.Method is user-controlled and must NOT appear in the response. func TestMCPHandler_UnknownMethod_Returns32601(t *testing.T) { h, _ := newMCPHandler(t) @@ -224,6 +228,14 @@ func TestMCPHandler_UnknownMethod_Returns32601(t *testing.T) { if resp.Error.Code != -32601 { t.Errorf("expected code -32601, got %d", resp.Error.Code) } + // Message must be constant — no user-controlled method name leak. + if resp.Error.Message != "method not found" { + t.Errorf("error message should be constant 'method not found', got: %q", resp.Error.Message) + } + // Double-check the method name never appears in the message (defence-in-depth). + if strings.Contains(resp.Error.Message, "not/a/real/method") { + t.Error("error message must not echo the user-controlled method name") + } } // ─────────────────────────────────────────────────────────────────────────────