Skip to content

Commit

Permalink
[mirotalksfu] - improve path traversal, update dep
Browse files Browse the repository at this point in the history
  • Loading branch information
miroslavpejic85 committed Jan 31, 2025
1 parent 1727e64 commit ddca05a
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 17 deletions.
7 changes: 5 additions & 2 deletions app/src/Server.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ prod dependencies: {
@mattermost/client : https://www.npmjs.com/package/@mattermost/client
@sentry/node : https://www.npmjs.com/package/@sentry/node
axios : https://www.npmjs.com/package/axios
compression : https://www.npmjs.com/package/compression
colors : https://www.npmjs.com/package/colors
compression : https://www.npmjs.com/package/compression
cors : https://www.npmjs.com/package/cors
crypto-js : https://www.npmjs.com/package/crypto-js
discord.js : https://www.npmjs.com/package/discord.js
Expand All @@ -21,15 +21,18 @@ prod dependencies: {
express-openid-connect : https://www.npmjs.com/package/express-openid-connect
fluent-ffmpeg : https://www.npmjs.com/package/fluent-ffmpeg
he : https://www.npmjs.com/package/he
helmet : https://www.npmjs.com/package/helmet
httpolyglot : https://www.npmjs.com/package/httpolyglot
js-yaml : https://www.npmjs.com/package/js-yaml
jsdom : https://www.npmjs.com/package/jsdom
jsonwebtoken : https://www.npmjs.com/package/jsonwebtoken
mediasoup : https://www.npmjs.com/package/mediasoup
mediasoup-client : https://www.npmjs.com/package/mediasoup-client
ngrok : https://www.npmjs.com/package/ngrok
nodemailer : https://www.npmjs.com/package/nodemailer
openai : https://www.npmjs.com/package/openai
qs : https://www.npmjs.com/package/qs
sanitize-filename : https://www.npmjs.com/package/sanitize-filename
socket.io : https://www.npmjs.com/package/socket.io
swagger-ui-express : https://www.npmjs.com/package/swagger-ui-express
uuid : https://www.npmjs.com/package/uuid
Expand All @@ -55,7 +58,7 @@ dev dependencies: {
* @license For commercial or closed source, contact us at [email protected] or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - [email protected]
* @version 1.7.20
* @version 1.7.21
*
*/

Expand Down
34 changes: 30 additions & 4 deletions app/src/Validator.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,54 @@
'use strict';

const path = require('path');

const checkXSS = require('./XSS.js');

function isValidRoomName(input) {
if (typeof input !== 'string') {
if (!input || typeof input !== 'string') {
return false;
}
const room = checkXSS(input);
return !room ? false : !hasPathTraversal(room);
}

function isValidRecFileNameFormat(input) {
if (typeof input !== 'string') {
if (!input || typeof input !== 'string') {
return false;
}
if (!input.startsWith('Rec_') || !input.endsWith('.webm')) {
const validPattern = /^Rec_[a-zA-Z0-9_-]+\.webm$/;
if (!validPattern.test(input)) {
return false;
}
return !hasPathTraversal(input);
}

function hasPathTraversal(input) {
if (!input || typeof input !== 'string') {
return false;
}

let decodedInput = input;
try {
decodedInput = decodeURIComponent(input);
decodedInput = decodeURIComponent(decodedInput);
} catch (err) {}

const pathTraversalPattern = /(\.\.(\/|\\))+/;
return pathTraversalPattern.test(input);
const excessiveDotsPattern = /(\.{4,}\/+|\.{4,}\\+)/;
const complexTraversalPattern = /(\.{2,}(\/+|\\+))/;

if (complexTraversalPattern.test(decodedInput)) {
return true;
}

const normalizedPath = path.normalize(decodedInput);

if (pathTraversalPattern.test(normalizedPath) || excessiveDotsPattern.test(normalizedPath)) {
return true;
}

return false;
}

module.exports = {
Expand Down
32 changes: 29 additions & 3 deletions cloud/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,42 @@ app.listen(port, () => {

// Utils
function isValidRecFileNameFormat(input) {
if (typeof input !== 'string') {
if (!input || typeof input !== 'string') {
return false;
}
if (!input.startsWith('Rec_') || !input.endsWith('.webm')) {
const validPattern = /^Rec_[a-zA-Z0-9_-]+\.webm$/;
if (!validPattern.test(input)) {
return false;
}
return !hasPathTraversal(input);
}

function hasPathTraversal(input) {
if (!input || typeof input !== 'string') {
return false;
}

let decodedInput = input;
try {
decodedInput = decodeURIComponent(input);
decodedInput = decodeURIComponent(decodedInput);
} catch (err) {
// Ignore any errors during decoding
}

const pathTraversalPattern = /(\.\.(\/|\\))+/;
return pathTraversalPattern.test(input);
const excessiveDotsPattern = /(\.{4,}\/+|\.{4,}\\+)/;
const complexTraversalPattern = /(\.{2,}(\/+|\\+))/;

if (complexTraversalPattern.test(decodedInput)) {
return true;
}

const normalizedPath = path.normalize(decodedInput);

if (pathTraversalPattern.test(normalizedPath) || excessiveDotsPattern.test(normalizedPath)) {
return true;
}

return false;
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mirotalksfu",
"version": "1.7.20",
"version": "1.7.21",
"description": "WebRTC SFU browser-based video calls",
"main": "Server.js",
"scripts": {
Expand Down Expand Up @@ -79,7 +79,7 @@
"mediasoup-client": "3.8.1",
"ngrok": "^5.0.0-beta.2",
"nodemailer": "^6.10.0",
"openai": "^4.81.0",
"openai": "^4.82.0",
"qs": "6.14.0",
"sanitize-filename": "^1.6.3",
"socket.io": "4.8.1",
Expand Down
2 changes: 1 addition & 1 deletion public/js/Brand.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ let BRAND = {
},
about: {
imageUrl: '../images/mirotalk-logo.gif',
title: '<strong>WebRTC SFU v1.7.20</strong>',
title: '<strong>WebRTC SFU v1.7.21</strong>',
html: `
<button
id="support-button"
Expand Down
2 changes: 1 addition & 1 deletion public/js/Common.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ function joinRoom() {
}

function isValidRoomName(input) {
if (typeof input !== 'string') {
if (!input || typeof input !== 'string') {
return false;
}
const pathTraversalPattern = /(\.\.(\/|\\))+/;
Expand Down
4 changes: 2 additions & 2 deletions public/js/Room.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 [email protected] or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - [email protected]
* @version 1.7.20
* @version 1.7.21
*
*/

Expand Down Expand Up @@ -4905,7 +4905,7 @@ function showAbout() {
position: 'center',
imageUrl: BRAND.about?.imageUrl && BRAND.about.imageUrl.trim() !== '' ? BRAND.about.imageUrl : image.about,
customClass: { image: 'img-about' },
title: BRAND.about?.title && BRAND.about.title.trim() !== '' ? BRAND.about.title : 'WebRTC SFU v1.7.20',
title: BRAND.about?.title && BRAND.about.title.trim() !== '' ? BRAND.about.title : 'WebRTC SFU v1.7.21',
html: `
<br />
<div id="about">
Expand Down
2 changes: 1 addition & 1 deletion public/js/RoomClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* @license For commercial or closed source, contact us at [email protected] or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - [email protected]
* @version 1.7.20
* @version 1.7.21
*
*/

Expand Down
36 changes: 35 additions & 1 deletion tests/test-Validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,43 @@ describe('test-Validator', () => {
});

it('should return true for complex path traversal patterns', () => {
checkValidator.hasPathTraversal('....//').should.be.true();
checkValidator.hasPathTraversal('..//').should.be.true();
checkValidator.hasPathTraversal('..\\..\\').should.be.true();
checkValidator.hasPathTraversal('../../').should.be.true();
checkValidator.hasPathTraversal('.../../').should.be.true();
checkValidator.hasPathTraversal('....//').should.be.true();
checkValidator.hasPathTraversal('..//..//..//').should.be.true();
});

it('should return true for URL-encoded path traversal', () => {
checkValidator.hasPathTraversal('%2e%2e%2fRoom').should.be.true();
checkValidator.hasPathTraversal('%2e%2e%2f%2e%2e%2fRoom').should.be.true();
checkValidator.hasPathTraversal('%252e%252e%252f').should.be.true();
});

it('should return false for valid absolute paths', () => {
checkValidator.hasPathTraversal('/etc/passwd').should.be.false();
checkValidator.hasPathTraversal('C:\\Windows\\System32').should.be.false();
});

it('should return false for non-traversal relative paths', () => {
checkValidator.hasPathTraversal('Room/Room2').should.be.false();
checkValidator.hasPathTraversal('C:\\SomeDir\\OtherDir').should.be.false();
});

it('should return false for excessively long path inputs', () => {
const longPath = 'Room/'.repeat(1000);
checkValidator.hasPathTraversal(longPath).should.be.false();
});

it('should return false for paths with Windows reserved filenames', () => {
checkValidator.hasPathTraversal('C:\\CON\\myfile.txt').should.be.false();
checkValidator.hasPathTraversal('C:\\NUL\\myfile.txt').should.be.false();
});

it('should return false for valid Windows paths with backslashes', () => {
checkValidator.hasPathTraversal('C:\\Program Files\\MyApp').should.be.false();
checkValidator.hasPathTraversal('C:\\SomeDir\\OtherDir\\File.txt').should.be.false();
});
});
});

0 comments on commit ddca05a

Please sign in to comment.