@@ -45,7 +45,7 @@ dependencies: {
4545 * @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
4646 * @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
4747 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com
48- * @version 1.8.39
48+ * @version 1.8.40
4949 *
5050 */
5151
@@ -1436,6 +1436,29 @@ io.sockets.on('connect', async (socket) => {
14361436 peer_uuid : peer_uuid ,
14371437 is_presenter : is_presenter ,
14381438 } ;
1439+
1440+ // Recover presenter status on reconnect: if a stale presenter entry
1441+ // exists for this user (same peer_name AND same peer_uuid) under a
1442+ // previous socket.id, migrate it to the current socket.id. peer_uuid
1443+ // is never broadcast to other peers, so it cannot be spoofed by
1444+ // someone who only learned the display name.
1445+ for ( const [ existingPeerID , existingPresenter ] of Object . entries ( presenters [ channel ] ) ) {
1446+ if (
1447+ existingPeerID !== socket . id &&
1448+ existingPresenter &&
1449+ existingPresenter . peer_name === peer_name &&
1450+ existingPresenter . peer_uuid === peer_uuid
1451+ ) {
1452+ delete presenters [ channel ] [ existingPeerID ] ;
1453+ presenters [ channel ] [ socket . id ] = presenter ;
1454+ log . debug ( '[' + socket . id + '] Presenter recovered on reconnect' , {
1455+ previous_peer_id : existingPeerID ,
1456+ peer_name : peer_name ,
1457+ } ) ;
1458+ break ;
1459+ }
1460+ }
1461+
14391462 // first we check if the username match the presenters username
14401463 if ( roomPresenters && roomPresenters . includes ( peer_name ) ) {
14411464 presenters [ channel ] [ socket . id ] = presenter ;
@@ -1572,15 +1595,16 @@ io.sockets.on('connect', async (socket) => {
15721595 }
15731596
15741597 //log.debug('[' + socket.id + '] Room action:', config);
1575- const { room_id, peer_id , peer_name, peer_uuid, password, action } = config ;
1598+ const { room_id, peer_name, peer_uuid, password, action } = config ;
15761599
15771600 if ( ! peers [ room_id ] ) {
15781601 log . warn ( 'Room action room not found' , { peer_id : socket . id , room_id } ) ;
15791602 return ;
15801603 }
15811604
1582- // Check if peer is presenter
1583- const isPresenter = isPeerPresenter ( room_id , peer_id , peer_name , peer_uuid ) ;
1605+ // Check if peer is presenter using the server-controlled socket.id
1606+ // (NOT the client-supplied peer_id) to prevent role spoofing.
1607+ const isPresenter = isPeerPresenter ( room_id , socket . id , peer_name , peer_uuid ) ;
15841608
15851609 let room_is_locked = false ;
15861610 //
@@ -1692,15 +1716,16 @@ io.sockets.on('connect', async (socket) => {
16921716
16931717 const { action, send_to_all, data } = config ;
16941718
1695- const { room_id, peer_id , peer_name, peer_uuid, to_peer_id } = data ;
1719+ const { room_id, peer_name, peer_uuid, to_peer_id } = data ;
16961720
16971721 log . debug ( 'cmd' , config ) ;
16981722
16991723 // Only the presenter can do this actions
17001724 const presenterActions = [ 'geoLocation' ] ;
17011725 if ( presenterActions . some ( ( v ) => action === v ) ) {
1702- // Check if peer is presenter
1703- const isPresenter = isPeerPresenter ( room_id , peer_id , peer_name , peer_uuid ) ;
1726+ // Authorize using the server-controlled socket.id, not the
1727+ // client-supplied peer_id, to prevent role spoofing.
1728+ const isPresenter = isPeerPresenter ( room_id , socket . id , peer_name , peer_uuid ) ;
17041729 // if not presenter do nothing
17051730 if ( ! isPresenter ) return ;
17061731 }
@@ -1798,8 +1823,9 @@ io.sockets.on('connect', async (socket) => {
17981823 // Only the presenter can do this actions
17991824 const presenterActions = [ 'muteAudio' , 'hideVideo' , 'ejectAll' ] ;
18001825 if ( presenterActions . some ( ( v ) => peer_action === v ) ) {
1801- // Check if peer is presenter
1802- const isPresenter = isPeerPresenter ( room_id , peer_id , peer_name , peer_uuid ) ;
1826+ // Authorize using the server-controlled socket.id, not the
1827+ // client-supplied peer_id, to prevent role spoofing.
1828+ const isPresenter = isPeerPresenter ( room_id , socket . id , peer_name , peer_uuid ) ;
18031829 // if not presenter do nothing
18041830 if ( ! isPresenter ) return ;
18051831 }
@@ -1845,10 +1871,12 @@ io.sockets.on('connect', async (socket) => {
18451871
18461872 if ( ! Validate . isValidData ( config ) ) return ;
18471873
1874+ // peer_id here is the TARGET to kick; the caller's identity is the
1875+ // server-controlled socket.id, not anything the client supplies.
18481876 const { room_id, peer_id, peer_uuid, peer_name } = config ;
18491877
1850- // Check if peer is presenter
1851- const isPresenter = await isPeerPresenter ( room_id , peer_id , peer_name , peer_uuid ) ;
1878+ // Authorize the caller using socket.id to prevent role spoofing.
1879+ const isPresenter = isPeerPresenter ( room_id , socket . id , peer_name , peer_uuid ) ;
18521880
18531881 // Only the presenter can kickOut others
18541882 if ( isPresenter ) {
@@ -2160,25 +2188,28 @@ async function getPeerGeoLocation(ip) {
21602188 */
21612189function isPeerPresenter ( room_id , peer_id , peer_name , peer_uuid ) {
21622190 try {
2163- if ( ! presenters [ room_id ] || ! presenters [ room_id ] [ peer_id ] ) {
2164- // Presenter not in the presenters config list, disconnected, or peer_id changed...
2165- for ( const [ existingPeerID , presenter ] of Object . entries ( presenters [ room_id ] || { } ) ) {
2166- if ( presenter . peer_name === peer_name ) {
2167- log . debug ( '[' + peer_id + '] Presenter found' , presenters [ room_id ] [ existingPeerID ] ) ;
2168- return true ;
2169- }
2170- }
2191+ // peer_id MUST be the server-controlled socket.id of the caller.
2192+ // Never trust a client-supplied peer_id, peer_name, or peer_uuid for
2193+ // authorization decisions: look up the stored presenter entry by
2194+ // socket.id and require both peer_name and peer_uuid to match what
2195+ // was recorded at join time.
2196+ const roomPresentersMap = presenters [ room_id ] ;
2197+ const stored = roomPresentersMap && roomPresentersMap [ peer_id ] ;
2198+
2199+ if ( ! stored ) {
2200+ log . debug ( '[' + peer_id + '] isPeerPresenter - no stored presenter entry for caller' , {
2201+ peer_name : peer_name ,
2202+ } ) ;
21712203 return false ;
21722204 }
21732205
21742206 const isPresenter =
2175- ( typeof presenters [ room_id ] === 'object' &&
2176- Object . keys ( presenters [ room_id ] [ peer_id ] ) . length > 1 &&
2177- presenters [ room_id ] [ peer_id ] [ 'peer_name' ] === peer_name &&
2178- presenters [ room_id ] [ peer_id ] [ 'peer_uuid' ] === peer_uuid ) ||
2179- ( roomPresenters && roomPresenters . includes ( peer_name ) ) ;
2207+ typeof stored === 'object' &&
2208+ Object . keys ( stored ) . length > 1 &&
2209+ stored . peer_name === peer_name &&
2210+ stored . peer_uuid === peer_uuid ;
21802211
2181- log . debug ( '[' + peer_id + '] isPeerPresenter' , presenters [ room_id ] [ peer_id ] ) ;
2212+ log . debug ( '[' + peer_id + '] isPeerPresenter' , { stored , isPresenter } ) ;
21822213
21832214 return isPresenter ;
21842215 } catch ( err ) {
0 commit comments