الملفات
codepill-sfu/app/src/XSS.js
2025-03-09 10:44:32 +01:00

110 أسطر
3.3 KiB
JavaScript

'use strict';
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', 'target'], // 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 {
return sanitizeData(dataObject);
} catch (error) {
log.error('Sanitization error:', error);
return dataObject; // Return original data in case of error
}
};
function needsDecoding(str) {
const urlEncodedPattern = /%[0-9A-Fa-f]{2}/g;
return urlEncodedPattern.test(str);
}
function safeDecodeURIComponent(str) {
try {
return decodeURIComponent(str);
} catch (e) {
log.error('Malformed URI component detected:', str);
return str; // Return original string if decoding fails
}
}
function sanitizeData(data) {
if (typeof data === 'string') {
// Decode HTML entities and URL encoded content
const decodedData = needsDecoding(data) ? he.decode(safeDecodeURIComponent(data)) : he.decode(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
}
// Sanitize object properties
function sanitizeObject(obj) {
return Object.keys(obj).reduce((acc, key) => {
acc[key] = sanitizeData(obj[key]);
return acc;
}, {});
}
module.exports = checkXSS;