Skip to content

Commit 8600d77

Browse files
authored
Merge commit from fork
fix(voicemail): keep custom-greeting upload paths inside the recordings dir
2 parents 0eb5485 + efd937b commit 8600d77

1 file changed

Lines changed: 34 additions & 5 deletions

File tree

  • root/usr/lib/node/nethcti-server/plugins/voicemail

root/usr/lib/node/nethcti-server/plugins/voicemail/voicemail.js

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,25 @@ var EVT_NEW_VOICE_MESSAGE = 'newVoiceMessage';
8989
*/
9090
var AUDIO_RECORDED_PATH = '/var/spool/asterisk/tmp';
9191

92+
/**
93+
* Resolves a name to an absolute path that is guaranteed to stay inside
94+
* AUDIO_RECORDED_PATH. Throws if the resulting path would be the directory
95+
* itself or escape it, so every filesystem operation is forced under the
96+
* recordings directory regardless of the name components.
97+
*
98+
* @method resolveRecordedPath
99+
* @param {string} name The filename to place inside AUDIO_RECORDED_PATH
100+
* @return {string} The absolute path inside AUDIO_RECORDED_PATH
101+
* @private
102+
*/
103+
function resolveRecordedPath(name) {
104+
var resolved = path.resolve(AUDIO_RECORDED_PATH, String(name));
105+
if (resolved.indexOf(AUDIO_RECORDED_PATH + path.sep) !== 0) {
106+
throw new Error('refusing to use a path outside the recordings directory');
107+
}
108+
return resolved;
109+
}
110+
92111
/**
93112
* The mpg123 binary for MP3 conversion.
94113
*
@@ -836,8 +855,8 @@ function setCustomVmAudioMsg(vm, type, audio, cb) {
836855
throw new Error('uploaded audio is not a valid RIFF WAV file (missing RIFF header)');
837856
}
838857
var tmpFilename = 'vm_' + vm + '_' + type + '_' + Date.now() + '_' + process.pid + '.wav';
839-
var tmpPath = path.join(AUDIO_RECORDED_PATH, tmpFilename);
840-
var tmpOutPath = path.join(AUDIO_RECORDED_PATH, 'vm_' + vm + '_' + type + '_' + Date.now() + '_' + process.pid + '_out.wav');
858+
var tmpPath = resolveRecordedPath(tmpFilename);
859+
var tmpOutPath = resolveRecordedPath('vm_' + vm + '_' + type + '_' + Date.now() + '_' + process.pid + '_out.wav');
841860
async.waterfall([
842861
function(callback) {
843862
var tmpExt = (normalized.detectedType === 'mp3' ? '.mp3' : '.wav');
@@ -916,9 +935,19 @@ function setCustomVmAudioMsgFromFile(vm, type, tempFilename, cb) {
916935
throw new Error('wrong parameters: ' + JSON.stringify(arguments));
917936
}
918937

919-
var sourcePath = path.join(AUDIO_RECORDED_PATH, tempFilename);
938+
// tempFilename must be a bare filename inside AUDIO_RECORDED_PATH: reject any
939+
// name with path components and the "", ".", ".." special names so that
940+
// readFile/stat/unlink always operate inside the recordings directory.
941+
var safeTempFilename = path.basename(tempFilename);
942+
if (safeTempFilename !== tempFilename ||
943+
safeTempFilename === '' || safeTempFilename === '.' || safeTempFilename === '..') {
944+
945+
throw new Error('invalid tempFilename');
946+
}
947+
948+
var sourcePath = resolveRecordedPath(safeTempFilename);
920949
var convertedTmpPath = null;
921-
var ext = path.extname(tempFilename);
950+
var ext = path.extname(safeTempFilename);
922951
var extLower = ext.toLowerCase();
923952

924953
// sequentially executes operations:
@@ -952,7 +981,7 @@ function setCustomVmAudioMsgFromFile(vm, type, tempFilename, cb) {
952981
return;
953982
}
954983
}
955-
var tmpOutPath = path.join(AUDIO_RECORDED_PATH, 'vm_' + vm + '_' + type + '_' + Date.now() + '_' + process.pid + '_out.wav');
984+
var tmpOutPath = resolveRecordedPath('vm_' + vm + '_' + type + '_' + Date.now() + '_' + process.pid + '_out.wav');
956985
convertedTmpPath = tmpOutPath;
957986
fs.stat(sourcePath, function(err) {
958987
if (err) {

0 commit comments

Comments
 (0)