1From ba166e9adebfe5343f826c6a9e02299d35414ffd Mon Sep 17 00:00:00 2001 2From: Lorenzo Miniero <lminiero@gmail.com> 3Date: Thu, 25 Nov 2021 17:20:53 +0100 4Subject: [PATCH] Fix potential Cross-site Scripting (XSS) exploits in demos 5 (#2817) 6 7[Retrieved (and backported) from: 8https://github.com/meetecho/janus-gateway/commit/ba166e9adebfe5343f826c6a9e02299d35414ffd] 9Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com> 10--- 11 html/audiobridgetest.js | 17 +++++++++++++---- 12 html/recordplaytest.js | 13 +++++++++++-- 13 html/screensharingtest.js | 11 ++++++++++- 14 html/streamingtest.js | 13 +++++++++++-- 15 html/textroomtest.js | 23 ++++++++++++++--------- 16 html/videocalltest.js | 15 ++++++++++++--- 17 html/videoroomtest.js | 13 +++++++++++-- 18 html/vp9svctest.js | 13 +++++++++++-- 19 8 files changed, 93 insertions(+), 25 deletions(-) 20 21diff --git a/html/audiobridgetest.js b/html/audiobridgetest.js 22index 18e1cc1839..f757789708 100644 23--- a/html/audiobridgetest.js 24+++ b/html/audiobridgetest.js 25@@ -178,7 +178,7 @@ $(document).ready(function() { 26 Janus.debug("Got a list of participants:", list); 27 for(var f in list) { 28 var id = list[f]["id"]; 29- var display = list[f]["display"]; 30+ var display = escapeXmlTags(list[f]["display"]); 31 var setup = list[f]["setup"]; 32 var muted = list[f]["muted"]; 33 var spatial = list[f]["spatial_position"]; 34@@ -222,7 +222,7 @@ $(document).ready(function() { 35 Janus.debug("Got a list of participants:", list); 36 for(var f in list) { 37 var id = list[f]["id"]; 38- var display = list[f]["display"]; 39+ var display = escapeXmlTags(list[f]["display"]); 40 var setup = list[f]["setup"]; 41 var muted = list[f]["muted"]; 42 var spatial = list[f]["spatial_position"]; 43@@ -267,7 +267,7 @@ $(document).ready(function() { 44 Janus.debug("Got a list of participants:", list); 45 for(var f in list) { 46 var id = list[f]["id"]; 47- var display = list[f]["display"]; 48+ var display = escapeXmlTags(list[f]["display"]); 49 var setup = list[f]["setup"]; 50 var muted = list[f]["muted"]; 51 var spatial = list[f]["spatial_position"]; 52@@ -429,7 +429,7 @@ function registerUsername() { 53 return; 54 } 55 var register = { request: "join", room: myroom, display: username }; 56- myusername = username; 57+ myusername = escapeXmlTags(username); 58 mixertest.send({ message: register}); 59 } 60 } 61@@ -448,3 +448,12 @@ function getQueryStringValue(name) { 62 results = regex.exec(location.search); 63 return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); 64 } 65+ 66+// Helper to escape XML tags 67+function escapeXmlTags(value) { 68+ if(value) { 69+ var escapedValue = value.replace(new RegExp('<', 'g'), '<'); 70+ escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); 71+ return escapedValue; 72+ } 73+} 74diff --git a/html/recordplaytest.js b/html/recordplaytest.js 75index 74ee7bed95..52b5ccbc4c 100644 76--- a/html/recordplaytest.js 77+++ b/html/recordplaytest.js 78@@ -423,11 +423,11 @@ function updateRecsList() { 79 Janus.debug("Got a list of available recordings:", list); 80 for(var mp in list) { 81 Janus.debug(" >> [" + list[mp]["id"] + "] " + list[mp]["name"] + " (" + list[mp]["date"] + ")"); 82- $('#recslist').append("<li><a href='#' id='" + list[mp]["id"] + "'>" + list[mp]["name"] + " [" + list[mp]["date"] + "]" + "</a></li>"); 83+ $('#recslist').append("<li><a href='#' id='" + list[mp]["id"] + "'>" + escapeXmlTags(list[mp]["name"]) + " [" + list[mp]["date"] + "]" + "</a></li>"); 84 } 85 $('#recslist a').unbind('click').click(function() { 86 selectedRecording = $(this).attr("id"); 87- selectedRecordingInfo = $(this).text(); 88+ selectedRecordingInfo = escapeXmlTags($(this).text()); 89 $('#recset').html($(this).html()).parent().removeClass('open'); 90 $('#play').removeAttr('disabled').click(startPlayout); 91 return false; 92@@ -545,3 +545,12 @@ function getQueryStringValue(name) { 93 results = regex.exec(location.search); 94 return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); 95 } 96+ 97+// Helper to escape XML tags 98+function escapeXmlTags(value) { 99+ if(value) { 100+ var escapedValue = value.replace(new RegExp('<', 'g'), '<'); 101+ escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); 102+ return escapedValue; 103+ } 104+} 105diff --git a/html/screensharingtest.js b/html/screensharingtest.js 106index 61eac70f43..c64d8dbd67 100644 107--- a/html/screensharingtest.js 108+++ b/html/screensharingtest.js 109@@ -161,7 +161,7 @@ $(document).ready(function() { 110 if(event === "joined") { 111 myid = msg["id"]; 112 $('#session').html(room); 113- $('#title').html(msg["description"]); 114+ $('#title').html(escapeXmlTags(msg["description"])); 115 Janus.log("Successfully joined room " + msg["room"] + " with ID " + myid); 116 if(role === "publisher") { 117 // This is our session, publish our stream 118@@ -514,3 +514,12 @@ function newRemoteFeed(id, display) { 119 } 120 }); 121 } 122+ 123+// Helper to escape XML tags 124+function escapeXmlTags(value) { 125+ if(value) { 126+ var escapedValue = value.replace(new RegExp('<', 'g'), '<'); 127+ escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); 128+ return escapedValue; 129+ } 130+} 131diff --git a/html/streamingtest.js b/html/streamingtest.js 132index 7dd2e1f681..3f9937f11c 100644 133--- a/html/streamingtest.js 134+++ b/html/streamingtest.js 135@@ -323,7 +323,7 @@ function updateStreamsList() { 136 Janus.debug(list); 137 for(var mp in list) { 138 Janus.debug(" >> [" + list[mp]["id"] + "] " + list[mp]["description"] + " (" + list[mp]["type"] + ")"); 139- $('#streamslist').append("<li><a href='#' id='" + list[mp]["id"] + "'>" + list[mp]["description"] + " (" + list[mp]["type"] + ")" + "</a></li>"); 140+ $('#streamslist').append("<li><a href='#' id='" + list[mp]["id"] + "'>" + escapeXmlTags(list[mp]["description"]) + " (" + list[mp]["type"] + ")" + "</a></li>"); 141 } 142 $('#streamslist a').unbind('click').click(function() { 143 selectedStream = $(this).attr("id"); 144@@ -345,7 +345,7 @@ function getStreamInfo() { 145 var body = { request: "info", id: parseInt(selectedStream) || selectedStream }; 146 streaming.send({ message: body, success: function(result) { 147 if(result && result.info && result.info.metadata) { 148- $('#metadata').html(result.info.metadata); 149+ $('#metadata').html(escapeXmlTags(result.info.metadata)); 150 $('#info').removeClass('hide').show(); 151 } 152 }}); 153@@ -394,6 +394,15 @@ function stopStream() { 154 simulcastStarted = false; 155 } 156 157+// Helper to escape XML tags 158+function escapeXmlTags(value) { 159+ if(value) { 160+ var escapedValue = value.replace(new RegExp('<', 'g'), '<'); 161+ escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); 162+ return escapedValue; 163+ } 164+} 165+ 166 // Helpers to create Simulcast-related UI, if enabled 167 function addSimulcastButtons() { 168 $('#curres').parent().append( 169diff --git a/html/textroomtest.js b/html/textroomtest.js 170index 082ae44905..3d0697e35a 100644 171--- a/html/textroomtest.js 172+++ b/html/textroomtest.js 173@@ -153,9 +153,7 @@ $(document).ready(function() { 174 var what = json["textroom"]; 175 if(what === "message") { 176 // Incoming message: public or private? 177- var msg = json["text"]; 178- msg = msg.replace(new RegExp('<', 'g'), '<'); 179- msg = msg.replace(new RegExp('>', 'g'), '>'); 180+ var msg = escapeXmlTags(json["text"]); 181 var from = json["from"]; 182 var dateString = getDateString(json["date"]); 183 var whisper = json["whisper"]; 184@@ -170,9 +168,7 @@ $(document).ready(function() { 185 } 186 } else if(what === "announcement") { 187 // Room announcement 188- var msg = json["text"]; 189- msg = msg.replace(new RegExp('<', 'g'), '<'); 190- msg = msg.replace(new RegExp('>', 'g'), '>'); 191+ var msg = escapeXmlTags(json["text"]); 192 var dateString = getDateString(json["date"]); 193 $('#chatroom').append('<p style="color: purple;">[' + dateString + '] <i>' + msg + '</i>'); 194 $('#chatroom').get(0).scrollTop = $('#chatroom').get(0).scrollHeight; 195@@ -180,7 +176,7 @@ $(document).ready(function() { 196 // Somebody joined 197 var username = json["username"]; 198 var display = json["display"]; 199- participants[username] = display ? display : username; 200+ participants[username] = escapeXmlTags(display ? display : username); 201 if(username !== myid && $('#rp' + username).length === 0) { 202 // Add to the participants list 203 $('#list').append('<li id="rp' + username + '" class="list-group-item">' + participants[username] + '</li>'); 204@@ -282,7 +278,7 @@ function registerUsername() { 205 username: myid, 206 display: username 207 }; 208- myusername = username; 209+ myusername = escapeXmlTags(username); 210 transactions[transaction] = function(response) { 211 if(response["textroom"] === "error") { 212 // Something went wrong 213@@ -312,7 +308,7 @@ function registerUsername() { 214 if(response.participants && response.participants.length > 0) { 215 for(var i in response.participants) { 216 var p = response.participants[i]; 217- participants[p.username] = p.display ? p.display : p.username; 218+ participants[p.username] = escapeXmlTags(p.display ? p.display : p.username); 219 if(p.username !== myid && $('#rp' + p.username).length === 0) { 220 // Add to the participants list 221 $('#list').append('<li id="rp' + p.username + '" class="list-group-item">' + participants[p.username] + '</li>'); 222@@ -418,3 +414,12 @@ function getQueryStringValue(name) { 223 results = regex.exec(location.search); 224 return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); 225 } 226+ 227+// Helper to escape XML tags 228+function escapeXmlTags(value) { 229+ if(value) { 230+ var escapedValue = value.replace(new RegExp('<', 'g'), '<'); 231+ escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); 232+ return escapedValue; 233+ } 234+} 235diff --git a/html/videocalltest.js b/html/videocalltest.js 236index d1c1ab8d07..18ccbc2c47 100644 237--- a/html/videocalltest.js 238+++ b/html/videocalltest.js 239@@ -148,7 +148,7 @@ $(document).ready(function() { 240 } else if(result["event"]) { 241 var event = result["event"]; 242 if(event === 'registered') { 243- myusername = result["username"]; 244+ myusername = escapeXmlTags(result["username"]); 245 Janus.log("Successfully registered as " + myusername + "!"); 246 $('#youok').removeClass('hide').show().html("Registered as '" + myusername + "'"); 247 // Get a list of available peers, just for fun 248@@ -163,7 +163,7 @@ $(document).ready(function() { 249 bootbox.alert("Waiting for the peer to answer..."); 250 } else if(event === 'incomingcall') { 251 Janus.log("Incoming call from " + result["username"] + "!"); 252- yourusername = result["username"]; 253+ yourusername = escapeXmlTags(result["username"]); 254 // Notify user 255 bootbox.hideAll(); 256 incoming = bootbox.dialog({ 257@@ -213,7 +213,7 @@ $(document).ready(function() { 258 }); 259 } else if(event === 'accepted') { 260 bootbox.hideAll(); 261- var peer = result["username"]; 262+ var peer = escapeXmlTags(result["username"]); 263 if(!peer) { 264 Janus.log("Call started!"); 265 } else { 266@@ -598,6 +598,15 @@ function getQueryStringValue(name) { 267 return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); 268 } 269 270+// Helper to escape XML tags 271+function escapeXmlTags(value) { 272+ if(value) { 273+ var escapedValue = value.replace(new RegExp('<', 'g'), '<'); 274+ escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); 275+ return escapedValue; 276+ } 277+} 278+ 279 // Helpers to create Simulcast-related UI, if enabled 280 function addSimulcastButtons(temporal) { 281 $('#curres').parent().append( 282diff --git a/html/videoroomtest.js b/html/videoroomtest.js 283index 6a566891d8..5a3ade9be9 100644 284--- a/html/videoroomtest.js 285+++ b/html/videoroomtest.js 286@@ -400,7 +400,7 @@ function registerUsername() { 287 ptype: "publisher", 288 display: username 289 }; 290- myusername = username; 291+ myusername = escapeXmlTags(username); 292 sfutest.send({ message: register }); 293 } 294 } 295@@ -530,7 +530,7 @@ function newRemoteFeed(id, display, audio, video) { 296 } 297 } 298 remoteFeed.rfid = msg["id"]; 299- remoteFeed.rfdisplay = msg["display"]; 300+ remoteFeed.rfdisplay = escapeXmlTags(msg["display"]); 301 if(!remoteFeed.spinner) { 302 var target = document.getElementById('videoremote'+remoteFeed.rfindex); 303 remoteFeed.spinner = new Spinner({top:100}).spin(target); 304@@ -685,6 +685,15 @@ function getQueryStringValue(name) { 305 return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); 306 } 307 308+// Helper to escape XML tags 309+function escapeXmlTags(value) { 310+ if(value) { 311+ var escapedValue = value.replace(new RegExp('<', 'g'), '<'); 312+ escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); 313+ return escapedValue; 314+ } 315+} 316+ 317 // Helpers to create Simulcast-related UI, if enabled 318 function addSimulcastButtons(feed, temporal) { 319 var index = feed; 320diff --git a/html/vp9svctest.js b/html/vp9svctest.js 321index eca0239c32..b22ccf3340 100644 322--- a/html/vp9svctest.js 323+++ b/html/vp9svctest.js 324@@ -387,7 +387,7 @@ function registerUsername() { 325 ptype: "publisher", 326 display: username 327 }; 328- myusername = username; 329+ myusername = escapeXmlTags(username); 330 sfutest.send({ message: register }); 331 } 332 } 333@@ -486,7 +486,7 @@ function newRemoteFeed(id, display, audio, video) { 334 } 335 } 336 remoteFeed.rfid = msg["id"]; 337- remoteFeed.rfdisplay = msg["display"]; 338+ remoteFeed.rfdisplay = escapeXmlTags(msg["display"]); 339 if(!remoteFeed.spinner) { 340 var target = document.getElementById('videoremote'+remoteFeed.rfindex); 341 remoteFeed.spinner = new Spinner({top:100}).spin(target); 342@@ -630,6 +630,15 @@ function newRemoteFeed(id, display, audio, video) { 343 }); 344 } 345 346+// Helper to escape XML tags 347+function escapeXmlTags(value) { 348+ if(value) { 349+ var escapedValue = value.replace(new RegExp('<', 'g'), '<'); 350+ escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); 351+ return escapedValue; 352+ } 353+} 354+ 355 // Helpers to create SVC-related UI for a new viewer 356 function addSvcButtons(feed) { 357 var index = feed; 358