[mirotalksfu] - Improve security, add tests
هذا الالتزام موجود في:
@@ -43,7 +43,7 @@ dependencies: {
|
||||
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
|
||||
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
|
||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||
* @version 1.5.59
|
||||
* @version 1.5.60
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
128
app/src/XSS.js
128
app/src/XSS.js
@@ -1,66 +1,96 @@
|
||||
'use strict';
|
||||
|
||||
const xss = require('xss');
|
||||
const { JSDOM } = require('jsdom');
|
||||
const DOMPurify = require('dompurify');
|
||||
const he = require('he');
|
||||
|
||||
// Initialize DOMPurify with jsdom
|
||||
const window = new JSDOM('').window;
|
||||
const purify = DOMPurify(window);
|
||||
|
||||
const Logger = require('./Logger');
|
||||
const log = new Logger('Xss');
|
||||
|
||||
// Configure DOMPurify
|
||||
purify.setConfig({
|
||||
ALLOWED_TAGS: ['a', 'img', 'div', 'span', 'svg', 'g', 'p'], // Allow specific tags
|
||||
ALLOWED_ATTR: ['href', 'src', 'title', 'id', 'class'], // Allow specific attributes
|
||||
ALLOWED_URI_REGEXP: /^(?!data:|javascript:|vbscript:|file:|view-source:).*/, // Disallow dangerous URIs
|
||||
});
|
||||
|
||||
// Clean problematic attributes
|
||||
function cleanAttributes(node) {
|
||||
if (node.nodeType === window.Node.ELEMENT_NODE) {
|
||||
// Remove dangerous attributes
|
||||
const dangerousAttributes = ['onerror', 'onclick', 'onload', 'onmouseover', 'onfocus', 'onchange', 'oninput'];
|
||||
dangerousAttributes.forEach((attr) => {
|
||||
if (node.hasAttribute(attr)) {
|
||||
node.removeAttribute(attr);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle special cases for 'data:' URIs
|
||||
const src = node.getAttribute('src');
|
||||
if (src && src.startsWith('data:')) {
|
||||
node.removeAttribute('src');
|
||||
}
|
||||
|
||||
// Remove unsafe 'style' attributes
|
||||
if (node.hasAttribute('style')) {
|
||||
const style = node.getAttribute('style');
|
||||
if (style.includes('javascript:') || style.includes('data:')) {
|
||||
node.removeAttribute('style');
|
||||
}
|
||||
}
|
||||
|
||||
// Remove 'title' attribute if it contains dangerous content
|
||||
if (node.hasAttribute('title')) {
|
||||
const title = node.getAttribute('title');
|
||||
if (title.includes('javascript:') || title.includes('data:') || title.includes('onerror')) {
|
||||
node.removeAttribute('title');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hook to clean specific attributes that can cause XSS
|
||||
purify.addHook('beforeSanitizeAttributes', cleanAttributes);
|
||||
|
||||
// Main function to check and sanitize data
|
||||
const checkXSS = (dataObject) => {
|
||||
try {
|
||||
if (Array.isArray(dataObject)) {
|
||||
if (Object.keys(dataObject).length > 0 && typeof dataObject[0] === 'object') {
|
||||
dataObject.forEach((obj) => {
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
let objectJson = objectToJSONString(obj[key]);
|
||||
if (objectJson) {
|
||||
let jsonString = xss(objectJson);
|
||||
let jsonObject = JSONStringToObject(jsonString);
|
||||
if (jsonObject) {
|
||||
obj[key] = jsonObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
log.debug('XSS Array of Object sanitization done');
|
||||
return dataObject;
|
||||
}
|
||||
} else if (typeof dataObject === 'object') {
|
||||
let objectJson = objectToJSONString(dataObject);
|
||||
if (objectJson) {
|
||||
let jsonString = xss(objectJson);
|
||||
let jsonObject = JSONStringToObject(jsonString);
|
||||
if (jsonObject) {
|
||||
log.debug('XSS Object sanitization done');
|
||||
return jsonObject;
|
||||
}
|
||||
}
|
||||
} else if (typeof dataObject === 'string' || dataObject instanceof String) {
|
||||
log.debug('XSS String sanitization done');
|
||||
return xss(dataObject);
|
||||
}
|
||||
log.warn('XSS not sanitized', dataObject);
|
||||
return dataObject;
|
||||
return sanitizeData(dataObject);
|
||||
} catch (error) {
|
||||
log.error('XSS error', { data: dataObject, error: error });
|
||||
return dataObject;
|
||||
log.error('Sanitization error:', error);
|
||||
return dataObject; // Return original data in case of error
|
||||
}
|
||||
};
|
||||
|
||||
function objectToJSONString(dataObject) {
|
||||
try {
|
||||
return JSON.stringify(dataObject);
|
||||
} catch (error) {
|
||||
return false;
|
||||
// Recursively sanitize data based on its type
|
||||
function sanitizeData(data) {
|
||||
if (typeof data === 'string') {
|
||||
// Decode HTML entities and URL encoded content
|
||||
const decodedData = he.decode(decodeURIComponent(data));
|
||||
return purify.sanitize(decodedData);
|
||||
}
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
return data.map(sanitizeData);
|
||||
}
|
||||
|
||||
if (data && typeof data === 'object') {
|
||||
return sanitizeObject(data);
|
||||
}
|
||||
|
||||
return data; // For numbers, booleans, null, undefined
|
||||
}
|
||||
|
||||
function JSONStringToObject(jsonString) {
|
||||
try {
|
||||
return JSON.parse(jsonString);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
// Sanitize object properties
|
||||
function sanitizeObject(obj) {
|
||||
return Object.keys(obj).reduce((acc, key) => {
|
||||
acc[key] = sanitizeData(obj[key]);
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
module.exports = checkXSS;
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم