Fix security vulnerability when using openai

- Security update required for those using text-to-speech with openai
- Improved overall sanitization of the filename
- Remove more characters with str_replace and 
- Use an array to define the characters to remove with str_replace.
- Add | and ` to the str_replace
- Added escapeshellarg for additional protection
This commit is contained in:
FusionPBX
2025-07-11 11:59:25 -06:00
committed by GitHub
parent 4e89803970
commit 234e7baaff

View File

@@ -17,7 +17,7 @@
The Initial Developer of the Original Code is
Mark J Crane <markjcrane@fusionpbx.com>
Portions created by the Initial Developer are Copyright (C) 2008-2024
Portions created by the Initial Developer are Copyright (C) 2008-2025
the Initial Developer. All Rights Reserved.
Contributor(s):
@@ -99,20 +99,25 @@
$recording_voice = $_POST["recording_voice"];
$recording_message = $_POST["recording_message"];
$recording_description = $_POST["recording_description"];
//sanitize recording filename and name
//sanitize: recording_filename
if (!empty($recording_filename)) {
$recording_filename_ext = strtolower(pathinfo($recording_filename, PATHINFO_EXTENSION));
if (!in_array($recording_filename_ext, ['wav','mp3','ogg'])) {
$recording_filename = pathinfo($recording_filename, PATHINFO_FILENAME);
$recording_filename = str_replace('.', '', $recording_filename);
}
$recording_filename = str_replace("\\", '', $recording_filename);
$recording_filename = str_replace('/', '', $recording_filename);
$recording_filename = str_replace('..', '', $recording_filename);
$replace = ['\\', '|', '/', '..', "`", "'"];
$recording_filename = str_replace($replace, '', $recording_filename);
$recording_filename = str_replace(' ', '-', $recording_filename);
$recording_filename = str_replace("'", '', $recording_filename);
}
$recording_name = str_replace("'", '', $recording_name);
//sanitize: recording_filename_original
if (!empty($recording_filename_original)) {
$replace = ['\\', '|', '/', '..', "`", "'"];
$recording_filename_original = str_replace($replace, '', $recording_filename_original);
$recording_filename_original = str_replace(' ', '-', $recording_filename_original);
}
}
//process the HTTP POST
@@ -179,22 +184,14 @@
//set the recording format
$recording_format = $recording_format ?? 'wav';
//if file name is not the same then rename the file
if (!empty($recording_filename) && !empty($recording_filename_original) && $recording_filename != $recording_filename_original) {
rename($_SESSION['switch']['recordings']['dir'].'/'.$_SESSION['domain_name'].'/'.$recording_filename_original, $_SESSION['switch']['recordings']['dir'].'/'.$_SESSION['domain_name'].'/'.$recording_filename);
}
//build the setting object and get the recording path
$recording_path = $settings->get('switch', 'recordings').'/'.$_SESSION['domain_name'];
//create the file name
if (empty($recording_filename)) {
$recording_filename = $recording_name.'.'.$recording_format;
$recording_filename = str_replace("\\", '', $recording_filename);
$recording_filename = str_replace('/', '', $recording_filename);
$recording_filename = str_replace('..', '', $recording_filename);
$recording_filename = str_replace(' ', '-', $recording_filename);
$recording_filename = str_replace("'", '', $recording_filename);
//if file name is not the same then rename the file
if (!empty($recording_filename) && !empty($recording_filename_original)
&& file_exists($recording_path.'/'.$recording_filename_original)
&& $recording_filename != $recording_filename_original) {
rename($recording_path.'/'.$recording_filename_original, $recording_path.'/'.$recording_filename);
}
//determine whether to create the recording
@@ -223,9 +220,11 @@
//fix invalid riff & data header lengths in generated wave file
if ($speech_engine == 'openai') {
$recording_filename_temp = str_replace('.'.$recording_format, '.tmp.'.$recording_format, $recording_filename);
exec('sox --ignore-length '.$recording_path.'/'.$recording_filename.' '.$recording_path.'/'.$recording_filename_temp);
if (file_exists($recording_path.'/'.$recording_filename)) {
exec('sox --ignore-length '.escapeshellarg($recording_path.'/'.$recording_filename).' '.escapeshellarg($recording_path.'/'.$recording_filename_temp));
}
if (file_exists($recording_path.'/'.$recording_filename_temp)) {
exec('rm -f '.$recording_path.'/'.$recording_filename.' && mv '.$recording_path.'/'.$recording_filename_temp.' '.$recording_path.'/'.$recording_filename);
exec('rm -f '.escapeshellarg($recording_path.'/'.$recording_filename).' && mv '.escapeshellarg($recording_path.'/'.$recording_filename_temp).' '.escapeshellarg($recording_path.'/'.$recording_filename));
}
unset($recording_filename_temp);
}
@@ -243,6 +242,10 @@
$array['recordings'][0]['recording_uuid'] = $recording_uuid;
$array['recordings'][0]['recording_filename'] = $recording_filename;
$array['recordings'][0]['recording_name'] = $recording_name;
if ($settings->get('recordings', 'storage_type', '') == 'base64'
&& file_exists($recording_path.'/'.$recording_filename)) {
$array['recordings'][0]['recording_base64'] = base64_encode(file_get_contents($recording_path.'/'.$recording_filename));
}
if ($speech_enabled || $transcribe_enabled) {
$array['recordings'][0]['recording_voice'] = $recording_voice;
$array['recordings'][0]['recording_message'] = $recording_message;
@@ -300,11 +303,11 @@
echo "<div class='action_bar' id='action_bar'>\n";
echo " <div class='heading'><b>".$text['title-edit']."</b></div>\n";
echo " <div class='actions'>\n";
echo button::create(['type'=>'button','label'=>$text['button-back'],'icon'=>$_SESSION['theme']['button_icon_back'],'id'=>'btn_back','style'=>'margin-right: 15px;','link'=>'recordings.php']);
echo button::create(['type'=>'button','label'=>$text['button-back'],'icon'=>$settings->get('theme', 'button_icon_back'),'id'=>'btn_back','style'=>'margin-right: 15px;','link'=>'recordings.php']);
if (permission_exists('recording_delete') && !empty($recording_uuid) && is_uuid($recording_uuid)) {
echo button::create(['type'=>'button','label'=>$text['button-delete'],'icon'=>$_SESSION['theme']['button_icon_delete'],'name'=>'btn_delete','style'=>'margin-right: 15px;','onclick'=>"modal_open('modal-delete','btn_delete');"]);
echo button::create(['type'=>'button','label'=>$text['button-delete'],'icon'=>$settings->get('theme', 'button_icon_delete'),'name'=>'btn_delete','style'=>'margin-right: 15px;','onclick'=>"modal_open('modal-delete','btn_delete');"]);
}
echo button::create(['type'=>'submit','label'=>$text['button-save'],'icon'=>$_SESSION['theme']['button_icon_save'],'id'=>'btn_save']);
echo button::create(['type'=>'submit','label'=>$text['button-save'],'icon'=>$settings->get('theme', 'button_icon_save'),'id'=>'btn_save']);
echo " </div>\n";
echo " <div style='clear: both;'></div>\n";
echo "</div>\n";
@@ -440,7 +443,7 @@
echo " ".$text['label-message']."\n";
echo "</td>\n";
echo "<td class='vtable' align='left'>\n";
echo " <textarea class='formfld' name='recording_message' style='width: 300px; height: 150px;'>".htmlspecialchars($recording_message, ENT_QUOTES, 'UTF-8')."</textarea>\n";
echo " <textarea class='formfld' name='recording_message' style='width: 300px; height: 150px;'>".escape_textarea($recording_message)."</textarea>\n";
echo "<br />\n";
echo $text['description-message']."\n";
echo "</td>\n";