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'), '&lt');
70+		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
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'), '&lt');
101+		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
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'), '&lt');
127+		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
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'), '&lt');
161+		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
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'), '&lt');
179-										msg = msg.replace(new RegExp('>', 'g'), '&gt');
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'), '&lt');
190-										msg = msg.replace(new RegExp('>', 'g'), '&gt');
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'), '&lt');
231+		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
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'), '&lt');
274+		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
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'), '&lt');
312+		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
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'), '&lt');
350+		escapedValue = escapedValue.replace(new RegExp('>', 'g'), '&gt');
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