[mirotalksfu] - improve security

هذا الالتزام موجود في:
Miroslav Pejic
2023-04-22 21:29:09 +02:00
الأصل 714dab0cb4
التزام 45c9d4556d
3 ملفات معدلة مع 125 إضافات و58 حذوفات

عرض الملف

@@ -777,10 +777,7 @@ function startServer() {
log.debug('[Join] - Connected presenters grp by roomId', presenters); log.debug('[Join] - Connected presenters grp by roomId', presenters);
const isPresenter = const isPresenter = await isPeerPresenter(socket.room_id, peer_name, peer_uuid);
Object.keys(presenters[socket.room_id]).length > 1 &&
presenters[socket.room_id]['peer_name'] == peer_name &&
presenters[socket.room_id]['peer_uuid'] == peer_uuid;
roomList roomList
.get(socket.room_id) .get(socket.room_id)
@@ -1069,6 +1066,15 @@ function startServer() {
} }
}); });
async function isPeerPresenter(room_id, peer_name, peer_uuid) {
const isPresenter =
Object.keys(presenters[room_id]).length > 1 &&
presenters[room_id]['peer_name'] === peer_name &&
presenters[room_id]['peer_uuid'] === peer_uuid;
log.debug(peer_name, { isPresenter: isPresenter });
return isPresenter;
}
async function getPeerGeoLocation(ip) { async function getPeerGeoLocation(ip) {
const endpoint = config.IPLookup.getEndpoint(ip); const endpoint = config.IPLookup.getEndpoint(ip);
log.debug('Get peer geo', { ip: ip, endpoint: endpoint }); log.debug('Get peer geo', { ip: ip, endpoint: endpoint });

عرض الملف

@@ -553,16 +553,13 @@ th {
width: 100%; width: 100%;
} }
.msg-a { a {
color: #fff; color: #fff !important;
} }
.msg-a:hover { a:hover {
color: grey; color: grey !important;
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
} }
#chat-msg-a {
color: #fff;
}
/*-------------------------------------------------------------- /*--------------------------------------------------------------
# Chat room input area # Chat room input area

عرض الملف

@@ -2723,7 +2723,8 @@ class RoomClient {
isChatPasteTxt = false; isChatPasteTxt = false;
return this.userLog('info', 'No participants in the room', 'top-end'); return this.userLog('info', 'No participants in the room', 'top-end');
} }
let peer_msg = this.formatMsg(chatMessage.value.trim()); chatMessage.value = filterXSS(chatMessage.value.trim());
let peer_msg = this.formatMsg(chatMessage.value);
if (!peer_msg) { if (!peer_msg) {
return this.cleanMessage(); return this.cleanMessage();
} }
@@ -2794,7 +2795,8 @@ class RoomClient {
}, },
}).then((result) => { }).then((result) => {
if (result.value) { if (result.value) {
let peer_msg = this.formatMsg(result.value.trim()); result.value = filterXSS(result.value.trim());
let peer_msg = this.formatMsg(result.value);
if (!peer_msg) { if (!peer_msg) {
return this.cleanMessage(); return this.cleanMessage();
} }
@@ -2848,27 +2850,37 @@ class RoomClient {
} }
appendMessage(side, img, fromName, fromId, msg, toId, toName) { appendMessage(side, img, fromName, fromId, msg, toId, toName) {
let time = this.getTimeNow(); //
let msgBubble = toId == 'all' ? 'msg-bubble' : 'msg-bubble-private'; const getSide = filterXSS(side);
let replyMsg = fromId === this.peer_id ? `<hr/>Private message to ${toName}` : ''; const getImg = filterXSS(img);
let message = toId == 'all' ? msg : msg + replyMsg; const getFromName = filterXSS(fromName);
const getFromId = filterXSS(fromId);
const getMsg = filterXSS(msg);
const getToId = filterXSS(toId);
const getToName = filterXSS(toName);
const time = this.getTimeNow();
const msgBubble = getToId == 'all' ? 'msg-bubble' : 'msg-bubble-private';
const replyMsg = getFromId === this.peer_id ? `<hr/>Private message to ${getToName}` : '';
const message = getToId == 'all' ? getMsg : getMsg + replyMsg;
let msgHTML = ` let msgHTML = `
<div id="msg-${chatMessagesId}" class="msg ${side}-msg"> <div id="msg-${chatMessagesId}" class="msg ${getSide}-msg">
<img class="msg-img" src="${img}" /> <img class="msg-img" src="${getImg}" />
<div class=${msgBubble}> <div class=${msgBubble}>
<div class="msg-info"> <div class="msg-info">
<div class="msg-info-name">${fromName}</div> <div class="msg-info-name">${getFromName}</div>
<div class="msg-info-time">${time}</div> <div class="msg-info-time">${time}</div>
</div> </div>
<div id="${chatMessagesId}" class="msg-text">${message} <div id="${chatMessagesId}" class="msg-text">${message}
<hr/>`; <hr/>`;
// add btn direct reply to private message // add btn direct reply to private message
if (fromId != this.peer_id) { if (getFromId != this.peer_id) {
msgHTML += ` msgHTML += `
<button <button
class="fas fa-paper-plane" class="fas fa-paper-plane"
id="msg-private-reply-${chatMessagesId}" id="msg-private-reply-${chatMessagesId}"
onclick="rc.sendMessageTo('${fromId}','${fromName}')" onclick="rc.sendMessageTo('${getFromId}','${getFromName}')"
></button>`; ></button>`;
} }
msgHTML += ` msgHTML += `
@@ -2886,7 +2898,7 @@ class RoomClient {
</div> </div>
</div> </div>
`; `;
this.collectMessages(time, fromName, msg); this.collectMessages(time, getFromName, getMsg);
chatMsger.insertAdjacentHTML('beforeend', msgHTML); chatMsger.insertAdjacentHTML('beforeend', msgHTML);
chatMsger.scrollTop += 500; chatMsger.scrollTop += 500;
this.setTippy('msg-delete-' + chatMessagesId, 'Delete', 'top'); this.setTippy('msg-delete-' + chatMessagesId, 'Delete', 'top');
@@ -2930,31 +2942,35 @@ class RoomClient {
}); });
} }
formatMsg(message) { formatMsg(msg) {
const message = filterXSS(msg);
if (message.trim().length == 0) return; if (message.trim().length == 0) return;
if (this.isHtml(message)) return this.sanitizeHtml(message); if (this.isHtml(message)) return this.sanitizeHtml(message);
if (this.isValidHttpURL(message)) { if (this.isValidHttpURL(message)) {
if (isImageURL(message)) return '<img src="' + message + '" alt="img" width="180" height="auto"/>'; if (this.isImageURL(message)) return this.getImage(message);
if (this.isVideoTypeSupported(message)) return this.getIframe(message); //if (this.isVideoTypeSupported(message)) return this.getIframe(message);
return '<a href="' + message + '" target="_blank" class="msg-a">' + message + '</a>'; return this.getLink(message);
} }
if (isChatMarkdownOn) return marked.parse(message); if (isChatMarkdownOn) return marked.parse(message);
let pre = '<pre>' + message + '</pre>'; if (isChatPasteTxt && this.getLineBreaks(message) > 1) {
if (isChatPasteTxt) {
isChatPasteTxt = false; isChatPasteTxt = false;
return pre; return this.getPre(message);
}
if (this.getLineBreaks(message) > 1) {
return pre;
} }
if (this.getLineBreaks(message) > 1) return this.getPre(message);
console.log('FormatMsg', message);
return message; return message;
} }
sanitizeHtml(str) { sanitizeHtml(input) {
const tagsToReplace = { '&': '&amp;', '<': '&lt;', '>': '&gt;' }; const map = {
const replaceTag = (tag) => tagsToReplace[tag] || tag; '&': '&amp;',
const safe_tags_replace = (str) => str.replace(/[&<>]/g, replaceTag); '<': '&lt;',
return safe_tags_replace(str); '>': '&gt;',
'"': '&quot;',
"'": '&#039;',
'/': '&#x2F;',
};
return input.replace(/[&<>"'/]/g, (m) => map[m]);
} }
isHtml(str) { isHtml(str) {
@@ -2966,28 +2982,76 @@ class RoomClient {
return false; return false;
} }
isValidHttpURL(str) { isValidHttpURL(input) {
let url; const pattern = new RegExp(
try { '^(https?:\\/\\/)?' + // protocol
url = new URL(str); '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
} catch (_) { '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
return false; '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
} '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
return url.protocol === 'http:' || url.protocol === 'https:'; '(\\#[-a-z\\d_]*)?$',
'i',
); // fragment locator
return pattern.test(input);
} }
getIframe(url) { isImageURL(input) {
let is_youtube = this.getVideoType(url) == 'na' ? true : false; return input.match(/\.(jpeg|jpg|gif|png|tiff|bmp)$/) != null;
let video_audio_url = is_youtube ? this.getYoutubeEmbed(url) : url; }
return `
<iframe getImage(input) {
title="Chat-IFrame" const url = filterXSS(input);
src="${video_audio_url}" const div = document.createElement('div');
width="auto" const img = document.createElement('img');
frameborder="0" img.setAttribute('src', url);
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" img.setAttribute('width', '200px');
allowfullscreen img.setAttribute('height', 'auto');
></iframe>`; div.appendChild(img);
console.log('GetImg', div.firstChild.outerHTML);
return div.firstChild.outerHTML;
}
getLink(input) {
const url = filterXSS(input);
const a = document.createElement('a');
const div = document.createElement('div');
const linkText = document.createTextNode(url);
a.setAttribute('href', url);
a.setAttribute('target', '_blank');
a.appendChild(linkText);
div.appendChild(a);
console.log('GetLink', div.firstChild.outerHTML);
return div.firstChild.outerHTML;
}
getPre(input) {
const text = filterXSS(input);
const pre = document.createElement('pre');
const div = document.createElement('div');
pre.textContent = text;
div.appendChild(pre);
console.log('GetPre', div.firstChild.outerHTML);
return div.firstChild.outerHTML;
}
getIframe(input) {
const url = filterXSS(input);
const iframe = document.createElement('iframe');
const div = document.createElement('div');
const is_youtube = this.getVideoType(url) == 'na' ? true : false;
const video_audio_url = is_youtube ? this.getYoutubeEmbed(url) : url;
iframe.setAttribute('title', 'Chat-IFrame');
iframe.setAttribute('src', video_audio_url);
iframe.setAttribute('width', 'auto');
iframe.setAttribute('frameborder', '0');
iframe.setAttribute(
'allow',
'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture',
);
iframe.setAttribute('allowfullscreen', 'allowfullscreen');
div.appendChild(iframe);
console.log('GetIFrame', div.firstChild.outerHTML);
return div.firstChild.outerHTML;
} }
getLineBreaks(message) { getLineBreaks(message) {