[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 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
|
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
|
||||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
* @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';
|
'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 Logger = require('./Logger');
|
||||||
const log = new Logger('Xss');
|
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) => {
|
const checkXSS = (dataObject) => {
|
||||||
try {
|
try {
|
||||||
if (Array.isArray(dataObject)) {
|
return sanitizeData(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;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('XSS error', { data: dataObject, error: error });
|
log.error('Sanitization error:', error);
|
||||||
return dataObject;
|
return dataObject; // Return original data in case of error
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function objectToJSONString(dataObject) {
|
// Recursively sanitize data based on its type
|
||||||
try {
|
function sanitizeData(data) {
|
||||||
return JSON.stringify(dataObject);
|
if (typeof data === 'string') {
|
||||||
} catch (error) {
|
// Decode HTML entities and URL encoded content
|
||||||
return false;
|
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) {
|
// Sanitize object properties
|
||||||
try {
|
function sanitizeObject(obj) {
|
||||||
return JSON.parse(jsonString);
|
return Object.keys(obj).reduce((acc, key) => {
|
||||||
} catch (error) {
|
acc[key] = sanitizeData(obj[key]);
|
||||||
return false;
|
return acc;
|
||||||
}
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = checkXSS;
|
module.exports = checkXSS;
|
||||||
|
|||||||
15
package.json
15
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mirotalksfu",
|
"name": "mirotalksfu",
|
||||||
"version": "1.5.59",
|
"version": "1.5.60",
|
||||||
"description": "WebRTC SFU browser-based video calls",
|
"description": "WebRTC SFU browser-based video calls",
|
||||||
"main": "Server.js",
|
"main": "Server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -27,7 +27,8 @@
|
|||||||
"nms-start": "docker-compose -f rtmpServers/node-media-server/docker-compose.yml up -d",
|
"nms-start": "docker-compose -f rtmpServers/node-media-server/docker-compose.yml up -d",
|
||||||
"nms-stop": "docker-compose -f rtmpServers/node-media-server/docker-compose.yml down",
|
"nms-stop": "docker-compose -f rtmpServers/node-media-server/docker-compose.yml down",
|
||||||
"nms-restart": "docker-compose -f rtmpServers/node-media-server/docker-compose.yml down && docker-compose -f rtmpServers/node-media-server/docker-compose.yml up -d",
|
"nms-restart": "docker-compose -f rtmpServers/node-media-server/docker-compose.yml down && docker-compose -f rtmpServers/node-media-server/docker-compose.yml up -d",
|
||||||
"nms-logs": "docker logs -f mirotalk-nms"
|
"nms-logs": "docker logs -f mirotalk-nms",
|
||||||
|
"unit-test": "npx mocha tests/checkXSS.js"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -64,11 +65,14 @@
|
|||||||
"compression": "1.7.4",
|
"compression": "1.7.4",
|
||||||
"cors": "2.8.5",
|
"cors": "2.8.5",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
|
"dompurify": "^3.1.6",
|
||||||
"express": "4.19.2",
|
"express": "4.19.2",
|
||||||
"express-openid-connect": "^2.17.1",
|
"express-openid-connect": "^2.17.1",
|
||||||
"fluent-ffmpeg": "^2.1.3",
|
"fluent-ffmpeg": "^2.1.3",
|
||||||
|
"he": "^1.2.0",
|
||||||
"httpolyglot": "0.1.2",
|
"httpolyglot": "0.1.2",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
|
"jsdom": "^24.1.1",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"mediasoup": "3.14.11",
|
"mediasoup": "3.14.11",
|
||||||
"mediasoup-client": "3.7.16",
|
"mediasoup-client": "3.7.16",
|
||||||
@@ -78,12 +82,13 @@
|
|||||||
"qs": "6.13.0",
|
"qs": "6.13.0",
|
||||||
"socket.io": "4.7.5",
|
"socket.io": "4.7.5",
|
||||||
"swagger-ui-express": "5.0.1",
|
"swagger-ui-express": "5.0.1",
|
||||||
"uuid": "10.0.0",
|
"uuid": "10.0.0"
|
||||||
"xss": "^1.0.15"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"mocha": "^10.7.3",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"nodemon": "^3.1.4",
|
"nodemon": "^3.1.4",
|
||||||
"prettier": "3.3.3"
|
"prettier": "3.3.3",
|
||||||
|
"should": "^13.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ if (location.href.substr(0, 5) !== 'https') location.href = 'https' + location.h
|
|||||||
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
|
* @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
|
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
|
||||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||||
* @version 1.5.59
|
* @version 1.5.60
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -4444,7 +4444,7 @@ function showAbout() {
|
|||||||
imageUrl: image.about,
|
imageUrl: image.about,
|
||||||
customClass: { image: 'img-about' },
|
customClass: { image: 'img-about' },
|
||||||
position: 'center',
|
position: 'center',
|
||||||
title: 'WebRTC SFU v1.5.59',
|
title: 'WebRTC SFU v1.5.60',
|
||||||
html: `
|
html: `
|
||||||
<br />
|
<br />
|
||||||
<div id="about">
|
<div id="about">
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
|
* @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
|
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
|
||||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||||
* @version 1.5.59
|
* @version 1.5.60
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
218
tests/checkXSS.js
Normal file
218
tests/checkXSS.js
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
// npx mocha test-checkXSS.js
|
||||||
|
|
||||||
|
require('should');
|
||||||
|
|
||||||
|
const checkXSS = require('../app/src/XSS');
|
||||||
|
|
||||||
|
describe('checkXSS', () => {
|
||||||
|
// 1. Basic Data Types Handling
|
||||||
|
|
||||||
|
it('should return numbers and booleans unchanged', () => {
|
||||||
|
checkXSS(42).should.equal(42);
|
||||||
|
checkXSS(true).should.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null and undefined unchanged', () => {
|
||||||
|
should.not.exist(checkXSS(null));
|
||||||
|
should.not.exist(checkXSS(undefined));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Simple String Handling
|
||||||
|
|
||||||
|
it('should sanitize strings with XSS injections', () => {
|
||||||
|
const maliciousString = '<script>alert("xss")</script>';
|
||||||
|
const sanitizedString = checkXSS(maliciousString);
|
||||||
|
sanitizedString.should.not.containEql('<script>');
|
||||||
|
sanitizedString.should.not.containEql('alert');
|
||||||
|
sanitizedString.should.not.containEql('</script>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize complex XSS injections', () => {
|
||||||
|
const complexString = '<svg><g/onload=alert(2)//<p>';
|
||||||
|
const sanitizedString = checkXSS(complexString);
|
||||||
|
sanitizedString.should.not.containEql('onload');
|
||||||
|
sanitizedString.should.equal('<svg><g></g></svg>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize HTML attributes', () => {
|
||||||
|
const maliciousHtml = '<a href="javascript:alert(\'xss\')">click me</a>';
|
||||||
|
const sanitizedHtml = checkXSS(maliciousHtml);
|
||||||
|
sanitizedHtml.should.not.containEql('javascript:');
|
||||||
|
sanitizedHtml.should.containEql('<a>click me</a>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize embedded scripts in HTML', () => {
|
||||||
|
const maliciousHtml = '<div><script>alert("xss")</script></div>';
|
||||||
|
const sanitizedHtml = checkXSS(maliciousHtml);
|
||||||
|
sanitizedHtml.should.not.containEql('<script>');
|
||||||
|
sanitizedHtml.should.containEql('<div></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle encoded XSS payloads', () => {
|
||||||
|
const encodedXss = '%3Cscript%3Ealert%28%27xss%27%29%3C%2Fscript%3E';
|
||||||
|
const sanitizedString = checkXSS(encodedXss);
|
||||||
|
sanitizedString.should.not.containEql('alert');
|
||||||
|
sanitizedString.should.equal('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle special characters used in XSS attacks', () => {
|
||||||
|
const specialCharsXss = "<div title=\"<img src='x' onerror='alert(1)'/>\">Test</div>";
|
||||||
|
const sanitizedSpecialChars = checkXSS(specialCharsXss);
|
||||||
|
sanitizedSpecialChars.should.not.containEql('onerror');
|
||||||
|
sanitizedSpecialChars.should.containEql('<div>Test</div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Handling Objects, Arrays, and Nested Structures
|
||||||
|
|
||||||
|
it('should sanitize objects with XSS injections', () => {
|
||||||
|
const maliciousObject = {
|
||||||
|
key1: '<script>alert("xss")</script>',
|
||||||
|
key2: 'normal string',
|
||||||
|
};
|
||||||
|
const sanitizedObject = checkXSS(maliciousObject);
|
||||||
|
sanitizedObject.key1.should.not.containEql('<script>');
|
||||||
|
sanitizedObject.key1.should.not.containEql('alert');
|
||||||
|
sanitizedObject.key1.should.not.containEql('</script>');
|
||||||
|
sanitizedObject.key2.should.equal('normal string');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize arrays with XSS injections', () => {
|
||||||
|
const maliciousArray = ['<script>alert("xss")</script>', 'normal string'];
|
||||||
|
const sanitizedArray = checkXSS(maliciousArray);
|
||||||
|
sanitizedArray[0].should.not.containEql('<script>');
|
||||||
|
sanitizedArray[0].should.not.containEql('alert');
|
||||||
|
sanitizedArray[0].should.not.containEql('</script>');
|
||||||
|
sanitizedArray[1].should.equal('normal string');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle nested objects and arrays with XSS injections', () => {
|
||||||
|
const nestedData = {
|
||||||
|
key1: [
|
||||||
|
'<script>alert("xss")</script>',
|
||||||
|
{
|
||||||
|
key2: '<img src="x" onerror="alert(\'xss\')">',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const sanitizedData = checkXSS(nestedData);
|
||||||
|
sanitizedData.key1[0].should.not.containEql('<script>');
|
||||||
|
sanitizedData.key1[0].should.not.containEql('alert');
|
||||||
|
sanitizedData.key1[0].should.not.containEql('</script>');
|
||||||
|
sanitizedData.key1[1].key2.should.not.containEql('onerror');
|
||||||
|
sanitizedData.key1[1].key2.should.equal('<img src="x">');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle XSS in nested HTML elements', () => {
|
||||||
|
const nestedXss = '<div><span onclick="alert(\'xss\')">Click me</span></div>';
|
||||||
|
const sanitizedNestedXss = checkXSS(nestedXss);
|
||||||
|
sanitizedNestedXss.should.not.containEql('onclick');
|
||||||
|
sanitizedNestedXss.should.containEql('<div><span>Click me</span></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle XSS through malicious attributes in different tags', () => {
|
||||||
|
const maliciousAttributes =
|
||||||
|
'<a href="#" onclick="alert(\'xss\')">Link</a><iframe src="javascript:alert(\'xss\')"></iframe>';
|
||||||
|
const sanitizedAttributes = checkXSS(maliciousAttributes);
|
||||||
|
sanitizedAttributes.should.not.containEql('onclick');
|
||||||
|
sanitizedAttributes.should.not.containEql('javascript:');
|
||||||
|
sanitizedAttributes.should.not.containEql('alert');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. Handling Specific Formats (JSON, Base64, etc.)
|
||||||
|
|
||||||
|
it('should handle XSS in JSON data', () => {
|
||||||
|
const maliciousJson = '{"key": "<img src=\'x\' onerror=\'alert(1)\'>"}';
|
||||||
|
const sanitizedJson = checkXSS(JSON.parse(maliciousJson));
|
||||||
|
sanitizedJson.key.should.not.containEql('onerror');
|
||||||
|
sanitizedJson.key.should.equal('<img src="x">');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize base64 encoded content', () => {
|
||||||
|
const maliciousBase64 = '<img src="data:image/svg+xml;base64,PHN2ZyBvbmxvYWQ9YWxlcnQoJ3hzcicpPg==">';
|
||||||
|
const sanitizedBase64 = checkXSS(maliciousBase64);
|
||||||
|
sanitizedBase64.should.not.containEql('onload');
|
||||||
|
sanitizedBase64.should.equal('<img>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize encoded HTML entities', () => {
|
||||||
|
const encodedHtmlEntities = '<script>alert('xss')</script>';
|
||||||
|
const sanitizedEntities = checkXSS(encodedHtmlEntities);
|
||||||
|
sanitizedEntities.should.not.containEql('<script>');
|
||||||
|
sanitizedEntities.should.equal('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize encoded and obfuscated payloads', () => {
|
||||||
|
const obfuscatedXss = '<img src="x" onerror="eval(String.fromCharCode(97,108,101,114,116) + \'(1)\')">';
|
||||||
|
const sanitizedObfuscatedXss = checkXSS(obfuscatedXss);
|
||||||
|
sanitizedObfuscatedXss.should.not.containEql('eval');
|
||||||
|
sanitizedObfuscatedXss.should.equal('<img src="x">');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5. Handling CSS and JavaScript URL XSS
|
||||||
|
|
||||||
|
it('should sanitize JavaScript and CSS injections', () => {
|
||||||
|
const jsInjection = '<div style="background-image: url(javascript:alert(\'xss\'))"></div>';
|
||||||
|
const sanitizedJsInjection = checkXSS(jsInjection);
|
||||||
|
sanitizedJsInjection.should.not.containEql('javascript:');
|
||||||
|
sanitizedJsInjection.should.containEql('<div></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle JavaScript URL XSS', () => {
|
||||||
|
const jsUrlXss = '<a href="javascript:alert(\'xss\')">Click me</a>';
|
||||||
|
const sanitizedJsUrl = checkXSS(jsUrlXss);
|
||||||
|
sanitizedJsUrl.should.not.containEql('javascript:');
|
||||||
|
sanitizedJsUrl.should.containEql('<a>Click me</a>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize `javascript:` URLs in CSS attributes', () => {
|
||||||
|
const maliciousCss = '<div style="background:url(javascript:alert(\'xss\'))"></div>';
|
||||||
|
const sanitizedCss = checkXSS(maliciousCss);
|
||||||
|
sanitizedCss.should.not.containEql('javascript:');
|
||||||
|
sanitizedCss.should.equal('<div></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 6. Handling SVG, MathML, and Data URIs
|
||||||
|
|
||||||
|
it('should handle XSS in SVG and MathML', () => {
|
||||||
|
const svgXss = '<svg><script>alert("xss")</script></svg>';
|
||||||
|
const sanitizedSvgXss = checkXSS(svgXss);
|
||||||
|
sanitizedSvgXss.should.not.containEql('<script>');
|
||||||
|
sanitizedSvgXss.should.equal('<svg></svg>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize data URIs in HTML attributes', () => {
|
||||||
|
const maliciousHtml = '<img src="data:image/svg+xml,<svg onload=alert(\'xss\')>">';
|
||||||
|
const sanitizedHtml = checkXSS(maliciousHtml);
|
||||||
|
sanitizedHtml.should.not.containEql('onload');
|
||||||
|
sanitizedHtml.should.equal('<img>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle data URL XSS', () => {
|
||||||
|
const dataUrlXss =
|
||||||
|
'<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNAAAAABJRU5ErkJggg==">';
|
||||||
|
const sanitizedDataUrl = checkXSS(dataUrlXss);
|
||||||
|
sanitizedDataUrl.should.not.containEql('data:');
|
||||||
|
sanitizedDataUrl.should.containEql('<img>');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 7. Handling Dynamic Content
|
||||||
|
|
||||||
|
it('should handle XSS in dynamic content', () => {
|
||||||
|
const dynamicXss =
|
||||||
|
'<div id="dynamicContent"></div><script>document.getElementById("dynamicContent").innerHTML = "<img src=\'x\' onerror=\'alert(1)\'>" </script>';
|
||||||
|
const sanitizedDynamicXss = checkXSS(dynamicXss);
|
||||||
|
sanitizedDynamicXss.should.not.containEql('onerror');
|
||||||
|
sanitizedDynamicXss.should.containEql('<div id="dynamicContent"></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 8. Handling Mixed Content
|
||||||
|
|
||||||
|
it('should sanitize mixed content', () => {
|
||||||
|
const mixedContent = '<div>Normal text <script>alert("xss")</script> more text</div>';
|
||||||
|
const sanitizedContent = checkXSS(mixedContent);
|
||||||
|
sanitizedContent.should.not.containEql('<script>');
|
||||||
|
sanitizedContent.should.containEql('<div>Normal text more text</div>');
|
||||||
|
});
|
||||||
|
});
|
||||||
المرجع في مشكلة جديدة
حظر مستخدم