From ba166e9adebfe5343f826c6a9e02299d35414ffd Mon Sep 17 00:00:00 2001 From: Lorenzo Miniero Date: Thu, 25 Nov 2021 17:20:53 +0100 Subject: [PATCH] Fix potential Cross-site Scripting (XSS) exploits in demos (#2817) [Retrieved (and backported) from: https://github.com/meetecho/janus-gateway/commit/ba166e9adebfe5343f826c6a9e02299d35414ffd] Signed-off-by: Fabrice Fontaine --- html/audiobridgetest.js | 17 +++++++++++++---- html/recordplaytest.js | 13 +++++++++++-- html/screensharingtest.js | 11 ++++++++++- html/streamingtest.js | 13 +++++++++++-- html/textroomtest.js | 23 ++++++++++++++--------- html/videocalltest.js | 15 ++++++++++++--- html/videoroomtest.js | 13 +++++++++++-- html/vp9svctest.js | 13 +++++++++++-- 8 files changed, 93 insertions(+), 25 deletions(-) diff --git a/html/audiobridgetest.js b/html/audiobridgetest.js index 18e1cc1839..f757789708 100644 --- a/html/audiobridgetest.js +++ b/html/audiobridgetest.js @@ -178,7 +178,7 @@ $(document).ready(function() { Janus.debug("Got a list of participants:", list); for(var f in list) { var id = list[f]["id"]; - var display = list[f]["display"]; + var display = escapeXmlTags(list[f]["display"]); var setup = list[f]["setup"]; var muted = list[f]["muted"]; var spatial = list[f]["spatial_position"]; @@ -222,7 +222,7 @@ $(document).ready(function() { Janus.debug("Got a list of participants:", list); for(var f in list) { var id = list[f]["id"]; - var display = list[f]["display"]; + var display = escapeXmlTags(list[f]["display"]); var setup = list[f]["setup"]; var muted = list[f]["muted"]; var spatial = list[f]["spatial_position"]; @@ -267,7 +267,7 @@ $(document).ready(function() { Janus.debug("Got a list of participants:", list); for(var f in list) { var id = list[f]["id"]; - var display = list[f]["display"]; + var display = escapeXmlTags(list[f]["display"]); var setup = list[f]["setup"]; var muted = list[f]["muted"]; var spatial = list[f]["spatial_position"]; @@ -429,7 +429,7 @@ function registerUsername() { return; } var register = { request: "join", room: myroom, display: username }; - myusername = username; + myusername = escapeXmlTags(username); mixertest.send({ message: register}); } } @@ -448,3 +448,12 @@ function getQueryStringValue(name) { results = regex.exec(location.search); return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); } + +// Helper to escape XML tags +function escapeXmlTags(value) { + if(value) { + var escapedValue = value.replace(new RegExp('<', 'g'), '<'); + escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); + return escapedValue; + } +} diff --git a/html/recordplaytest.js b/html/recordplaytest.js index 74ee7bed95..52b5ccbc4c 100644 --- a/html/recordplaytest.js +++ b/html/recordplaytest.js @@ -423,11 +423,11 @@ function updateRecsList() { Janus.debug("Got a list of available recordings:", list); for(var mp in list) { Janus.debug(" >> [" + list[mp]["id"] + "] " + list[mp]["name"] + " (" + list[mp]["date"] + ")"); - $('#recslist').append("
  • " + list[mp]["name"] + " [" + list[mp]["date"] + "]" + "
  • "); + $('#recslist').append("
  • " + escapeXmlTags(list[mp]["name"]) + " [" + list[mp]["date"] + "]" + "
  • "); } $('#recslist a').unbind('click').click(function() { selectedRecording = $(this).attr("id"); - selectedRecordingInfo = $(this).text(); + selectedRecordingInfo = escapeXmlTags($(this).text()); $('#recset').html($(this).html()).parent().removeClass('open'); $('#play').removeAttr('disabled').click(startPlayout); return false; @@ -545,3 +545,12 @@ function getQueryStringValue(name) { results = regex.exec(location.search); return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); } + +// Helper to escape XML tags +function escapeXmlTags(value) { + if(value) { + var escapedValue = value.replace(new RegExp('<', 'g'), '<'); + escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); + return escapedValue; + } +} diff --git a/html/screensharingtest.js b/html/screensharingtest.js index 61eac70f43..c64d8dbd67 100644 --- a/html/screensharingtest.js +++ b/html/screensharingtest.js @@ -161,7 +161,7 @@ $(document).ready(function() { if(event === "joined") { myid = msg["id"]; $('#session').html(room); - $('#title').html(msg["description"]); + $('#title').html(escapeXmlTags(msg["description"])); Janus.log("Successfully joined room " + msg["room"] + " with ID " + myid); if(role === "publisher") { // This is our session, publish our stream @@ -514,3 +514,12 @@ function newRemoteFeed(id, display) { } }); } + +// Helper to escape XML tags +function escapeXmlTags(value) { + if(value) { + var escapedValue = value.replace(new RegExp('<', 'g'), '<'); + escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); + return escapedValue; + } +} diff --git a/html/streamingtest.js b/html/streamingtest.js index 7dd2e1f681..3f9937f11c 100644 --- a/html/streamingtest.js +++ b/html/streamingtest.js @@ -323,7 +323,7 @@ function updateStreamsList() { Janus.debug(list); for(var mp in list) { Janus.debug(" >> [" + list[mp]["id"] + "] " + list[mp]["description"] + " (" + list[mp]["type"] + ")"); - $('#streamslist').append("
  • " + list[mp]["description"] + " (" + list[mp]["type"] + ")" + "
  • "); + $('#streamslist').append("
  • " + escapeXmlTags(list[mp]["description"]) + " (" + list[mp]["type"] + ")" + "
  • "); } $('#streamslist a').unbind('click').click(function() { selectedStream = $(this).attr("id"); @@ -345,7 +345,7 @@ function getStreamInfo() { var body = { request: "info", id: parseInt(selectedStream) || selectedStream }; streaming.send({ message: body, success: function(result) { if(result && result.info && result.info.metadata) { - $('#metadata').html(result.info.metadata); + $('#metadata').html(escapeXmlTags(result.info.metadata)); $('#info').removeClass('hide').show(); } }}); @@ -394,6 +394,15 @@ function stopStream() { simulcastStarted = false; } +// Helper to escape XML tags +function escapeXmlTags(value) { + if(value) { + var escapedValue = value.replace(new RegExp('<', 'g'), '<'); + escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); + return escapedValue; + } +} + // Helpers to create Simulcast-related UI, if enabled function addSimulcastButtons() { $('#curres').parent().append( diff --git a/html/textroomtest.js b/html/textroomtest.js index 082ae44905..3d0697e35a 100644 --- a/html/textroomtest.js +++ b/html/textroomtest.js @@ -153,9 +153,7 @@ $(document).ready(function() { var what = json["textroom"]; if(what === "message") { // Incoming message: public or private? - var msg = json["text"]; - msg = msg.replace(new RegExp('<', 'g'), '<'); - msg = msg.replace(new RegExp('>', 'g'), '>'); + var msg = escapeXmlTags(json["text"]); var from = json["from"]; var dateString = getDateString(json["date"]); var whisper = json["whisper"]; @@ -170,9 +168,7 @@ $(document).ready(function() { } } else if(what === "announcement") { // Room announcement - var msg = json["text"]; - msg = msg.replace(new RegExp('<', 'g'), '<'); - msg = msg.replace(new RegExp('>', 'g'), '>'); + var msg = escapeXmlTags(json["text"]); var dateString = getDateString(json["date"]); $('#chatroom').append('

    [' + dateString + '] ' + msg + ''); $('#chatroom').get(0).scrollTop = $('#chatroom').get(0).scrollHeight; @@ -180,7 +176,7 @@ $(document).ready(function() { // Somebody joined var username = json["username"]; var display = json["display"]; - participants[username] = display ? display : username; + participants[username] = escapeXmlTags(display ? display : username); if(username !== myid && $('#rp' + username).length === 0) { // Add to the participants list $('#list').append('

  • ' + participants[username] + '
  • '); @@ -282,7 +278,7 @@ function registerUsername() { username: myid, display: username }; - myusername = username; + myusername = escapeXmlTags(username); transactions[transaction] = function(response) { if(response["textroom"] === "error") { // Something went wrong @@ -312,7 +308,7 @@ function registerUsername() { if(response.participants && response.participants.length > 0) { for(var i in response.participants) { var p = response.participants[i]; - participants[p.username] = p.display ? p.display : p.username; + participants[p.username] = escapeXmlTags(p.display ? p.display : p.username); if(p.username !== myid && $('#rp' + p.username).length === 0) { // Add to the participants list $('#list').append('
  • ' + participants[p.username] + '
  • '); @@ -418,3 +414,12 @@ function getQueryStringValue(name) { results = regex.exec(location.search); return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); } + +// Helper to escape XML tags +function escapeXmlTags(value) { + if(value) { + var escapedValue = value.replace(new RegExp('<', 'g'), '<'); + escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); + return escapedValue; + } +} diff --git a/html/videocalltest.js b/html/videocalltest.js index d1c1ab8d07..18ccbc2c47 100644 --- a/html/videocalltest.js +++ b/html/videocalltest.js @@ -148,7 +148,7 @@ $(document).ready(function() { } else if(result["event"]) { var event = result["event"]; if(event === 'registered') { - myusername = result["username"]; + myusername = escapeXmlTags(result["username"]); Janus.log("Successfully registered as " + myusername + "!"); $('#youok').removeClass('hide').show().html("Registered as '" + myusername + "'"); // Get a list of available peers, just for fun @@ -163,7 +163,7 @@ $(document).ready(function() { bootbox.alert("Waiting for the peer to answer..."); } else if(event === 'incomingcall') { Janus.log("Incoming call from " + result["username"] + "!"); - yourusername = result["username"]; + yourusername = escapeXmlTags(result["username"]); // Notify user bootbox.hideAll(); incoming = bootbox.dialog({ @@ -213,7 +213,7 @@ $(document).ready(function() { }); } else if(event === 'accepted') { bootbox.hideAll(); - var peer = result["username"]; + var peer = escapeXmlTags(result["username"]); if(!peer) { Janus.log("Call started!"); } else { @@ -598,6 +598,15 @@ function getQueryStringValue(name) { return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); } +// Helper to escape XML tags +function escapeXmlTags(value) { + if(value) { + var escapedValue = value.replace(new RegExp('<', 'g'), '<'); + escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); + return escapedValue; + } +} + // Helpers to create Simulcast-related UI, if enabled function addSimulcastButtons(temporal) { $('#curres').parent().append( diff --git a/html/videoroomtest.js b/html/videoroomtest.js index 6a566891d8..5a3ade9be9 100644 --- a/html/videoroomtest.js +++ b/html/videoroomtest.js @@ -400,7 +400,7 @@ function registerUsername() { ptype: "publisher", display: username }; - myusername = username; + myusername = escapeXmlTags(username); sfutest.send({ message: register }); } } @@ -530,7 +530,7 @@ function newRemoteFeed(id, display, audio, video) { } } remoteFeed.rfid = msg["id"]; - remoteFeed.rfdisplay = msg["display"]; + remoteFeed.rfdisplay = escapeXmlTags(msg["display"]); if(!remoteFeed.spinner) { var target = document.getElementById('videoremote'+remoteFeed.rfindex); remoteFeed.spinner = new Spinner({top:100}).spin(target); @@ -685,6 +685,15 @@ function getQueryStringValue(name) { return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); } +// Helper to escape XML tags +function escapeXmlTags(value) { + if(value) { + var escapedValue = value.replace(new RegExp('<', 'g'), '<'); + escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); + return escapedValue; + } +} + // Helpers to create Simulcast-related UI, if enabled function addSimulcastButtons(feed, temporal) { var index = feed; diff --git a/html/vp9svctest.js b/html/vp9svctest.js index eca0239c32..b22ccf3340 100644 --- a/html/vp9svctest.js +++ b/html/vp9svctest.js @@ -387,7 +387,7 @@ function registerUsername() { ptype: "publisher", display: username }; - myusername = username; + myusername = escapeXmlTags(username); sfutest.send({ message: register }); } } @@ -486,7 +486,7 @@ function newRemoteFeed(id, display, audio, video) { } } remoteFeed.rfid = msg["id"]; - remoteFeed.rfdisplay = msg["display"]; + remoteFeed.rfdisplay = escapeXmlTags(msg["display"]); if(!remoteFeed.spinner) { var target = document.getElementById('videoremote'+remoteFeed.rfindex); remoteFeed.spinner = new Spinner({top:100}).spin(target); @@ -630,6 +630,15 @@ function newRemoteFeed(id, display, audio, video) { }); } +// Helper to escape XML tags +function escapeXmlTags(value) { + if(value) { + var escapedValue = value.replace(new RegExp('<', 'g'), '<'); + escapedValue = escapedValue.replace(new RegExp('>', 'g'), '>'); + return escapedValue; + } +} + // Helpers to create SVC-related UI for a new viewer function addSvcButtons(feed) { var index = feed;