99d2826e03
Fix CVE-2021-4020: janus-gateway is vulnerable to Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com> Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
358 lines
14 KiB
Diff
358 lines
14 KiB
Diff
From ba166e9adebfe5343f826c6a9e02299d35414ffd Mon Sep 17 00:00:00 2001
|
|
From: Lorenzo Miniero <lminiero@gmail.com>
|
|
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 <fontaine.fabrice@gmail.com>
|
|
---
|
|
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("<li><a href='#' id='" + list[mp]["id"] + "'>" + list[mp]["name"] + " [" + list[mp]["date"] + "]" + "</a></li>");
|
|
+ $('#recslist').append("<li><a href='#' id='" + list[mp]["id"] + "'>" + escapeXmlTags(list[mp]["name"]) + " [" + list[mp]["date"] + "]" + "</a></li>");
|
|
}
|
|
$('#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("<li><a href='#' id='" + list[mp]["id"] + "'>" + list[mp]["description"] + " (" + list[mp]["type"] + ")" + "</a></li>");
|
|
+ $('#streamslist').append("<li><a href='#' id='" + list[mp]["id"] + "'>" + escapeXmlTags(list[mp]["description"]) + " (" + list[mp]["type"] + ")" + "</a></li>");
|
|
}
|
|
$('#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('<p style="color: purple;">[' + dateString + '] <i>' + msg + '</i>');
|
|
$('#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('<li id="rp' + username + '" class="list-group-item">' + participants[username] + '</li>');
|
|
@@ -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('<li id="rp' + p.username + '" class="list-group-item">' + participants[p.username] + '</li>');
|
|
@@ -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;
|