2
1
Эх сурвалжийг харах

package/janus-gateway: fix CVE-2021-4020

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>
(cherry picked from commit 99d2826e039858b749504cb53e713e8b53664b39)
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
Fabrice Fontaine 3 жил өмнө
parent
commit
2436f0d75c

+ 357 - 0
package/janus-gateway/0003-Fix-potential-Cross-site-Scripting-XSS-exploits-in-demos.patch

@@ -0,0 +1,357 @@
+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'), '&lt');
++		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
++		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'), '&lt');
++		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
++		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'), '&lt');
++		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
++		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'), '&lt');
++		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
++		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'), '&lt');
+-										msg = msg.replace(new RegExp('>', 'g'), '&gt');
++										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'), '&lt');
+-										msg = msg.replace(new RegExp('>', 'g'), '&gt');
++										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'), '&lt');
++		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
++		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'), '&lt');
++		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
++		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'), '&lt');
++		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
++		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'), '&lt');
++		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
++		return escapedValue;
++	}
++}
++
+ // Helpers to create SVC-related UI for a new viewer
+ function addSvcButtons(feed) {
+ 	var index = feed;

+ 3 - 0
package/janus-gateway/janus-gateway.mk

@@ -11,6 +11,9 @@ JANUS_GATEWAY_LICENSE_FILES = COPYING
 JANUS_GATEWAY_CPE_ID_VENDOR = meetecho
 JANUS_GATEWAY_CPE_ID_PRODUCT = janus
 
+# 0003-Fix-potential-Cross-site-Scripting-XSS-exploits-in-demos.patch
+JANUS_GATEWAY_IGNORE_CVES += CVE-2021-4020
+
 # ding-libs provides the ini_config library
 JANUS_GATEWAY_DEPENDENCIES = host-pkgconf jansson libnice \
 	libsrtp host-gengetopt libglib2 openssl libconfig \