liteFM fixes

This commit is contained in:
Frank Harris 2026-06-10 18:59:12 -04:00
parent b9727fdfa7
commit 751874ea8c
5 changed files with 280 additions and 84 deletions

View file

@ -234,7 +234,7 @@ function downloadFile(home_id, file_id, file_size, file_name)
function getFilePart()
{
var xhr = new XMLHttpRequest();
xhr.open('POST', "home.php?m=litefm&home_id="+home_id+"&item="+file_id+"&name="+file_name+"&p=get&type=cleared&size="+file_size+"&did="+did, true);
xhr.open('POST', "home.php?m=litefm&home_id="+home_id+"&item="+file_id+"&name="+encodeURIComponent(file_name)+"&p=get&type=cleared&size="+file_size+"&did="+did, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
if(this.response.byteLength != 0)
@ -283,6 +283,15 @@ function downloadFile(home_id, file_id, file_size, file_name)
}
$(document).ready(function(){
function escapeHtml(value) {
return String(value)
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
/* translation & info */
var select_at_least_one_item = $('#dialog').attr('data-select_at_least_one_item'),
ask_delete = $('#dialog').attr('data-ask_delete'),
@ -389,7 +398,7 @@ $(document).ready(function(){
item = $(this).attr('data-item');
value = $(this).attr('value');
addpost.items.push(item);
items += "<br>"+value;
items += "<br>"+escapeHtml(value);
});
if(items != '')
{
@ -434,9 +443,9 @@ $(document).ready(function(){
var items = '';
$('input[class="item"]:checked').each(function(){
item = $(this).attr('data-item');
value = $(this).attr('value').replace('"', "&quot;");
value = $(this).attr('value');
addpost.items.push(item);
items += "<br><input class='rename' type=text style='width:100%;' name='"+item+"' value=\""+value+"\">";
items += "<br><input class='rename' type=text style='width:100%;' name='"+item+"' value=\""+escapeHtml(value)+"\">";
});
if(items != '')
{
@ -487,7 +496,7 @@ $(document).ready(function(){
item = $(this).attr('data-item');
value = $(this).attr('value');
addpost.items.push(item);
items += "<br>"+value;
items += "<br>"+escapeHtml(value);
});
if(items != '')
{
@ -550,7 +559,7 @@ $(document).ready(function(){
item = $(this).attr('data-item');
value = $(this).attr('value');
addpost.items.push(item);
items += "<br>"+value;
items += "<br>"+escapeHtml(value);
});
if(items != '')
{
@ -611,7 +620,7 @@ $(document).ready(function(){
item = $(this).attr('data-item');
value = $(this).attr('value');
addpost.items.push(item);
items += "<br>"+value;
items += "<br>"+escapeHtml(value);
});
if(items != '')
{
@ -675,7 +684,7 @@ $(document).ready(function(){
if(ext.match(/^(tar|tgz|gz|Z|zip|bz2|tbz|lzma|xz|txz)$/i) != null)
{
addpost.items.push(item);
items += "<br>"+value;
items += "<br>"+escapeHtml(value);
}
});
if(items != '')
@ -902,7 +911,7 @@ $(document).ready(function(){
}
$.ajax({
type: "POST",
url: 'home.php?m=litefm&home_id='+home_id+'&type=cleared&pid='+file['pid']+'&size='+file['size']+'&filename='+file['filename']+"&data_type=json",
url: 'home.php?m=litefm&home_id='+home_id+'&type=cleared&pid='+file['pid']+'&size='+file['size']+'&filename='+encodeURIComponent(file['filename'])+"&data_type=json",
dataType:'json',
async: false,
success: function(data){
@ -978,7 +987,7 @@ $(document).ready(function(){
addpost.set_attr = $(this).attr('data-set_attr'),
addpost.file_name = $(this).attr('data-file_name');
addpost.item = $(this).attr('data-item');
$('#dialog').html(ask_change_attr.replace("%file_name%",addpost.file_name));
$('#dialog').html(ask_change_attr.replace("%file_name%",escapeHtml(addpost.file_name)));
$('#dialog').dialog({
autoOpen: true,
width: 450,
@ -1016,7 +1025,7 @@ $(document).ready(function(){
item = $(this).attr('data-item');
value = $(this).attr('value');
addpost.items.push(item);
items += "<br>"+value;
items += "<br>"+escapeHtml(value);
});
if(items != '')
{

View file

@ -27,6 +27,9 @@ if(preg_match("/t/",$server_home['access_rights']) > 0)
{
$module_buttons = array();
// WE DONT WANT A FTP BUTTON, BUT WE DO NEED THE FTP FUNCTIONALITY, SO WE WILL NOT ADD A FTP BUTTON TO THE GAME MONITOR, BUT WE WILL STILL ALLOW THE USER TO ACCESS THE FTP MODULE FOR THIS HOME
return;
$module_buttons = array(
"<a class='monitorbutton' href='?m=ftp&amp;home_id=".$server_home['home_id']."'>

View file

@ -68,30 +68,29 @@ function exec_ogp_module()
if (!isset($_SESSION[$cwd_session_key]) || !is_string($_SESSION[$cwd_session_key])) {
$_SESSION[$cwd_session_key] = '';
}
$path = clean_path($home_cfg['home_path']."/".$_SESSION[$cwd_session_key]);
$path = litefm_safe_join_home_path($home_cfg['home_path'], $_SESSION[$cwd_session_key]);
if ($path === false)
{
print_failure(get_lang("unallowed_char"));
echo "<table class='center'><tr><td><a href='?m=gamemanager&amp;p=game_monitor&amp;home_id=".$home_cfg['home_id']."'><< ".get_lang('back')."</a></td></tr></table>";
return;
}
$home_root = clean_path($home_cfg['home_path']);
if (!$remote->rfile_exists($home_root))
{
print_failure("Server files have not been installed yet.");
echo "<table class='center'><tr><td><a href='?m=gamemanager&amp;p=game_monitor&amp;home_id=".$home_cfg['home_id']."'><< ".get_lang('back')."</a></td></tr></table>";
return;
}
if (!$remote->rfile_exists($path))
{
while(!$remote->rfile_exists($path))
{
$current_cwd = isset($_SESSION[$cwd_session_key]) ? (string)$_SESSION[$cwd_session_key] : '';
if ($current_cwd === '' || $current_cwd === '.' || $current_cwd === DIRECTORY_SEPARATOR) {
print_failure("Server files have not been installed yet.");
echo "<table class='center'><tr><td><a href='?m=gamemanager&amp;p=game_monitor&amp;home_id=".$home_cfg['home_id']."'><< ".get_lang('back')."</a></td></tr></table>";
return;
}
$parent_cwd = dirname($current_cwd);
if (!is_string($parent_cwd) || $parent_cwd === '.' || $parent_cwd === DIRECTORY_SEPARATOR) {
$parent_cwd = '';
}
$_SESSION[$cwd_session_key] = $parent_cwd;
$path = clean_path($home_cfg['home_path']."/".$_SESSION[$cwd_session_key]);
if($path == clean_path($home_cfg['home_path']."/"))
{
print_failure("Server files have not been installed yet.");
echo "<table class='center'><tr><td><a href='?m=gamemanager&amp;p=game_monitor&amp;home_id=".$home_cfg['home_id']."'><< ".get_lang('back')."</a></td></tr></table>";
return;
}
}
$requested_rel_path = isset($_SESSION[$cwd_session_key]) ? trim((string)$_SESSION[$cwd_session_key], '/') : '';
$requested_rel_path = $requested_rel_path === '' ? '/' : $requested_rel_path;
print_failure("Directory not found: " . litefm_escape_html($requested_rel_path));
echo "<table class='center'><tr><td><a href='?m=gamemanager&amp;p=game_monitor&amp;home_id=".$home_cfg['home_id']."'><< ".get_lang('back')."</a></td></tr></table>";
return;
}
// Get File Operations Keys
@ -112,7 +111,12 @@ function exec_ogp_module()
{
$bytes = $_GET['size'];
$totalsize = $bytes / 1024;
$filename = $_GET['filename'];
$filename = litefm_decode_name_param($_GET['filename']);
if (!litefm_is_valid_path_component($filename))
{
echo json_encode(array('pct' => 0, 'complete' => false));
return;
}
$kbytes = $remote->rsync_progress( clean_path( $path."/".$filename ) );
list($totalsize,$mbytes,$pct) = explode(";",do_progress($kbytes,$totalsize));
$totalmbytes = round($totalsize / 1024, 2);
@ -177,10 +181,9 @@ function exec_ogp_module()
// if file not uploaded then skip it
if ( !is_uploaded_file($_FILES['files']['tmp_name'][$i]) )
continue;
// now we can move uploaded files
$bad_chars = preg_replace( "/([[:alnum:]_\.-]*)/", "", $_FILES['files']['name'][$i] );
$bad_arr = str_split( $bad_chars );
$filename = str_replace( $bad_arr, "", $_FILES['files']['name'][$i] );
$filename = basename((string)$_FILES['files']['name'][$i]);
if (!litefm_is_valid_path_component($filename))
continue;
$dest_file_path = clean_path( $upload_folder_path . "/" . $filename . ".txt" );
$file_url = str_replace( "home.php", $dest_file_path, $url );
if( file_exists( $dest_file_path ) )
@ -205,7 +208,17 @@ function exec_ogp_module()
elseif( isset( $_POST['create_folder'] ) and $fo['create_folder'] == "1" )
{
$folder_name = stripslashes($_POST['folder_name']);
if (!litefm_is_valid_path_component($folder_name))
{
print_failure(get_lang("unallowed_char"));
return;
}
$folder_path = clean_path( $path . "/" . $folder_name );
if (!litefm_path_within_home($home_cfg['home_path'], $folder_path))
{
print_failure(get_lang("unallowed_char"));
return;
}
$remote->shell_action('create_dir', $folder_path);
$db->logger( get_lang("create_folder") . ": " . $folder_path );
}
@ -242,8 +255,18 @@ function exec_ogp_module()
if(isset($_SESSION['fm_files_'.$home_id][$item]))
{
$item_path = clean_path( $path . "/" . $_SESSION['fm_files_'.$home_id][$item] );
$new_item = removeInvalidFileNameCharacters(stripslashes($_POST['values'][$i]));
$new_item = stripslashes($_POST['values'][$i]);
if (!litefm_is_valid_path_component($new_item))
{
print_failure(get_lang("unallowed_char"));
continue;
}
$new_item_path = clean_path( $path . "/" . $new_item );
if (!litefm_path_within_home($home_cfg['home_path'], $new_item_path))
{
print_failure(get_lang("unallowed_char"));
continue;
}
if ($item_path != $new_item_path)
{
$remote->shell_action('rename', "$item_path;$new_item_path");
@ -256,8 +279,18 @@ function exec_ogp_module()
// Move Files/Folders
elseif( isset( $_POST['move'] ) and $fo['move'] == "1" )
{
$selected_path = preg_replace("#[/\.\./]+#","/", stripslashes($_POST['selected_path']));
$destination = clean_path($home_cfg['home_path']. "/" . $selected_path);
$selected_path = litefm_normalize_relative_path(stripslashes($_POST['selected_path']));
if ($selected_path === false)
{
print_failure(get_lang("unallowed_char"));
return;
}
$destination = litefm_safe_join_home_path($home_cfg['home_path'], $selected_path);
if ($destination === false)
{
print_failure(get_lang("unallowed_char"));
return;
}
if($path != $destination)
{
if($remote->rfile_exists($destination))
@ -278,8 +311,18 @@ function exec_ogp_module()
// Copy Files/Folders
elseif( isset( $_POST['copy'] ) and $fo['copy'] == "1" )
{
$selected_path = preg_replace("#[/\.\./]+#","/", stripslashes($_POST['selected_path']));
$destination = clean_path($home_cfg['home_path']. "/" . $selected_path);
$selected_path = litefm_normalize_relative_path(stripslashes($_POST['selected_path']));
if ($selected_path === false)
{
print_failure(get_lang("unallowed_char"));
return;
}
$destination = litefm_safe_join_home_path($home_cfg['home_path'], $selected_path);
if ($destination === false)
{
print_failure(get_lang("unallowed_char"));
return;
}
if($path != $destination)
{
if($remote->rfile_exists($destination))
@ -320,8 +363,18 @@ function exec_ogp_module()
// uncompress
elseif( isset( $_POST['uncompress'] ) and $fo['uncompress'] == "1" )
{
$selected_path = preg_replace("#[/\.\./]+#","/", stripslashes($_POST['selected_path']));
$destination = clean_path($home_cfg['home_path']. "/" . $selected_path);
$selected_path = litefm_normalize_relative_path(stripslashes($_POST['selected_path']));
if ($selected_path === false)
{
print_failure(get_lang("unallowed_char"));
return;
}
$destination = litefm_safe_join_home_path($home_cfg['home_path'], $selected_path);
if ($destination === false)
{
print_failure(get_lang("unallowed_char"));
return;
}
if($remote->rfile_exists($destination))
{
foreach ((array)$_POST['items'] as $item)
@ -338,8 +391,20 @@ function exec_ogp_module()
// Create file
elseif( isset( $_POST['create_file'] ) and $fo['create_file'] == "1" )
{
$file_name = removeInvalidFileNameCharacters(stripslashes($_POST['file_name']));
$file_name = stripslashes($_POST['file_name']);
if (!litefm_is_valid_path_component($file_name))
{
print_failure(get_lang("unallowed_char"));
$file_name = "";
}
if ($file_name === "")
return;
$destination = clean_path( $path . "/" . $file_name);
if (!litefm_path_within_home($home_cfg['home_path'], $destination))
{
print_failure(get_lang("unallowed_char"));
return;
}
$remote->shell_action('touch', $destination);
$db->logger( get_lang("create_file") . ": $destination" );
}
@ -437,7 +502,12 @@ function exec_ogp_module()
if (!is_array($dirlist))
{
if(isset($_SESSION['fm_cwd_'.$home_id]))
if ($remote->rfile_exists($path))
{
print_failure("Directory exists but cannot be opened. Check permissions.");
return;
}
elseif(isset($_SESSION['fm_cwd_'.$home_id]))
{
unset($_SESSION['fm_cwd_'.$home_id]);
$view->refresh("?m=litefm&amp;home_id=$home_id",0);
@ -475,21 +545,25 @@ function exec_ogp_module()
$dirlist['directorys'] = array_orderby($dirlist['directorys'], 'filename', SORT_ASC);
foreach ((array)$dirlist['directorys'] as $directory)
{
$directory['filename'] = removeInvalidFileNameCharacters($directory['filename']);
$directoryName = $directory['filename'];
if (!litefm_is_valid_path_component($directoryName))
continue;
$encodedDirectoryName = rawurlencode($directoryName);
$escapedDirectoryName = litefm_escape_html($directoryName);
echo "<tr>\n".
"<td>".
"<input type=checkbox name='folder' data-item='$i' value=\"" . str_replace('"', "&quot;", $directory['filename']) . "\" class='item' />\n".
"<input type=checkbox name='folder' data-item='$i' value=\"" . $escapedDirectoryName . "\" class='item' />\n".
"</td>".
"<td align=left>".
"<img class=\"viewitem\" src=\"" . check_theme_image("images/folder.png") . "\" alt=\"Directory\" /> ".
"<a href=\"?m=litefm&amp;home_id=$home_id&amp;item=$i&amp;name=" . urlencode($directory['filename']) . "&amp;type=directory\">".
$directory['filename'] . "</a></td>";
"<a href=\"?m=litefm&amp;home_id=$home_id&amp;item=$i&amp;name=" . $encodedDirectoryName . "&amp;type=directory\">".
$escapedDirectoryName . "</a></td>";
if( $os == "linux" )
echo "<td>-</td>";
echo "<td>-</td> <td>" . $directory['user'] . " " . $directory['group']. "</td>\n".
"</tr>\n";
$_SESSION['fm_files_'.$home_id][$i] = $directory['filename'];
$_SESSION['fm_files_'.$home_id][$i] = $directoryName;
$i++;
}
}
@ -499,12 +573,16 @@ function exec_ogp_module()
$dirlist['files'] = array_orderby($dirlist['files'], 'filename', SORT_ASC);
foreach ((array)$dirlist['files'] as $file)
{
$file['filename'] = removeInvalidFileNameCharacters($file['filename']);
$fileName = $file['filename'];
if (!litefm_is_valid_path_component($fileName))
continue;
$encodedFileName = rawurlencode($fileName);
$escapedFileName = litefm_escape_html($fileName);
if( $os == "linux" )
{
if($isAdmin){
$secureFile = "<td><div data-item='$i' data-file_name=\"" . str_replace('"', "&quot;", $file['filename']) . "\" class='chattrButton ";
$secureFile = "<td><div data-item='$i' data-file_name=\"" . litefm_escape_html($file['filename']) . "\" class='chattrButton ";
if( preg_match( "/i/", $file['attr'] ) ){
$secureFile .= "locked' data-set_attr='-i' ><i></i><span>". get_lang("chattr_no");
}else{
@ -526,15 +604,15 @@ function exec_ogp_module()
echo "<tr>\n".
"<td>".
"<input type=checkbox name='file' data-item='$i' value=\"" . str_replace('"', "&quot;", $file['filename']) . "\" class='item' />\n".
"<input type=checkbox name='file' data-item='$i' value=\"" . $escapedFileName . "\" class='item' />\n".
"</td>".
"<td align=left id='fileid$i' >";
echo "<img class=\"viewitem\" src=\"" . check_theme_image("images/txt.png") . "\" alt=\"Text file\" /> ".
"<a href=\"?m=litefm&amp;home_id=$home_id&amp;item=$i&amp;p=read_file&amp;name=" . urlencode($file['filename']) . "&amp;type=file\">". get_lang("button_edit") ."</a>".
"<a href=\"javascript:downloadFile($home_id, $i, {$file['size']}, '".str_replace("'", "\'", $file['filename'])."');\" id=\"jsDwl$i\" >" .$file['filename'] . "</a>&nbsp;".
"<a href=\"?m=litefm&amp;home_id=$home_id&amp;item=$i&amp;p=read_file&amp;name=" . $encodedFileName . "&amp;type=file\">". get_lang("button_edit") ."</a>".
"<a href=\"javascript:downloadFile($home_id, $i, {$file['size']}, '".str_replace("'", "\\'", $fileName)."');\" id=\"jsDwl$i\" >" .$escapedFileName . "</a>&nbsp;".
"</td>$secureFile<td>" . $file['size'] . "</td> <td>" . $file['user'] . " " . $file['group']. "</td>\n";
echo "</tr>\n";
$_SESSION['fm_files_'.$home_id][$i] = $file['filename'];
$_SESSION['fm_files_'.$home_id][$i] = $fileName;
$i++;
}
}
@ -544,12 +622,15 @@ function exec_ogp_module()
$dirlist['binarys'] = array_orderby($dirlist['binarys'], 'filename', SORT_ASC);
foreach ((array)$dirlist['binarys'] as $binary)
{
$binary['filename'] = removeInvalidFileNameCharacters($binary['filename']);
$binaryName = $binary['filename'];
if (!litefm_is_valid_path_component($binaryName))
continue;
$escapedBinaryName = litefm_escape_html($binaryName);
if( $os == "linux" )
{
if($isAdmin){
$secureFile = "<td><div data-item='$i' data-file_name=\"" . str_replace('"', "&quot;", $binary['filename']) . "\" class='chattrButton ";
$secureFile = "<td><div data-item='$i' data-file_name=\"" . litefm_escape_html($binary['filename']) . "\" class='chattrButton ";
if( preg_match( "/i/", $binary['attr'] ) ){
$secureFile .= "locked' data-set_attr='-i' ><i></i><span>". get_lang("chattr_no");
}else{
@ -571,14 +652,14 @@ function exec_ogp_module()
echo "<tr>\n".
"<td>".
"<input type=checkbox name='binary' data-item='$i' value=\"" . str_replace('"', "&quot;", $binary['filename']) . "\" class='item' />\n".
"<input type=checkbox name='binary' data-item='$i' value=\"" . $escapedBinaryName . "\" class='item' />\n".
"</td>".
"<td align=left id='fileid$i' >";
echo "<img class=\"viewitem\" src=\"" . check_theme_image("images/exec.png") . "\" alt=\"Binary file\" /> ".
"<a href=\"javascript:downloadFile($home_id, $i, {$binary['size']}, '".str_replace("'", "\'", $binary['filename'])."');\" id=\"jsDwl$i\" >" .$binary['filename'] . "</a>&nbsp;".
"<a href=\"javascript:downloadFile($home_id, $i, {$binary['size']}, '".str_replace("'", "\\'", $binaryName)."');\" id=\"jsDwl$i\" >" .$escapedBinaryName . "</a>&nbsp;".
"</td>$secureFile<td>" . $binary['size'] . "</td><td>" . $binary['user'] . " " . $binary['group']. "</td>\n";
echo "</tr>\n";
$_SESSION['fm_files_'.$home_id][$i] = $binary['filename'];
$_SESSION['fm_files_'.$home_id][$i] = $binaryName;
$i++;
}
}
@ -589,7 +670,7 @@ function exec_ogp_module()
// Dialog translation && info
$user = $db->getUserById($_SESSION['user_id']);
echo "<div id='dialog' ".
"data-folder=\"" . clean_path("/".str_replace('"', "&quot;", @$_SESSION['fm_cwd_'.$home_id])) . "\" " .
"data-folder=\"" . litefm_escape_html(clean_path("/".(@$_SESSION['fm_cwd_'.$home_id] ?? ''))) . "\" " .
"data-select_at_least_one_item='" . get_lang("select_at_least_one_item") . "' " .
"data-ask_delete='" . get_lang("delete_item") . "' " .
"data-ask_rename='" . get_lang("rename_item") . "' " .
@ -614,8 +695,8 @@ function exec_ogp_module()
"data-message='" . get_lang("message") . "' " .
"data-dest_email='" . get_lang("dest_email") . "' " .
"data-user_email='" . $user['users_email'] . "' ";
if($isAdmin)
echo "data-ask_change_attr=\"" . get_lang_f( 'secure_item', clean_path( str_replace('"', "&quot;", $path) . "/%file_name%" ) ) . "\" ";
if($isAdmin)
echo "data-ask_change_attr=\"" . litefm_escape_html(get_lang_f( 'secure_item', clean_path( $path . "/%file_name%" ) )) . "\" ";
echo "></div>";
}
}

View file

@ -24,6 +24,74 @@
require_once('includes/lib_remote.php');
function litefm_decode_name_param($value)
{
return rawurldecode((string)$value);
}
function litefm_escape_html($value)
{
return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
}
function litefm_is_valid_path_component($name)
{
if (!is_string($name) || $name === '' || $name === '.' || $name === '..') {
return false;
}
if (strpos($name, "\0") !== false || strpos($name, '/') !== false || strpos($name, '\\') !== false) {
return false;
}
return true;
}
function litefm_normalize_relative_path($relativePath)
{
$relativePath = str_replace('\\', '/', (string)$relativePath);
$relativePath = preg_replace('#/+#', '/', $relativePath);
$relativePath = trim($relativePath, '/');
if ($relativePath === '') {
return '';
}
if (strpos($relativePath, "\0") !== false) {
return false;
}
if (preg_match('#(^|/)\.{1,2}(/|$)#', $relativePath)) {
return false;
}
foreach (explode('/', $relativePath) as $segment) {
if (!litefm_is_valid_path_component($segment)) {
return false;
}
}
return $relativePath;
}
function litefm_path_within_home($homePath, $candidatePath)
{
$homeNorm = str_replace('\\', '/', clean_path((string)$homePath));
$candidateNorm = str_replace('\\', '/', clean_path((string)$candidatePath));
$homeCmp = strtolower(rtrim($homeNorm, '/'));
$candidateCmp = strtolower($candidateNorm);
if ($candidateCmp === $homeCmp) {
return true;
}
return strpos($candidateCmp, $homeCmp . '/') === 0;
}
function litefm_safe_join_home_path($homePath, $relativePath)
{
$normalizedRel = litefm_normalize_relative_path($relativePath);
if ($normalizedRel === false) {
return false;
}
$fullPath = clean_path(rtrim((string)$homePath, '/') . '/' . $normalizedRel);
if (!litefm_path_within_home($homePath, $fullPath)) {
return false;
}
return $fullPath;
}
function do_progress($kbytes,$totalsize)
{
if( $totalsize != 0 )
@ -54,7 +122,12 @@ function litefm_check($home_id)
{
if (isset($_GET['item']) and !isset($_GET['upload']) and !isset( $_POST['delete'] ) and !isset( $_POST['create_folder'] ) and !isset( $_POST['secureButton'] ) and !isset( $_POST['delete_check'] ) and !isset( $_POST['secure_check'] ))
{
$fileName = !empty($_POST['name']) ? urldecode($_POST['name']) : urldecode($_GET['name']);
$fileName = !empty($_POST['name']) ? litefm_decode_name_param($_POST['name']) : litefm_decode_name_param(isset($_GET['name']) ? $_GET['name'] : '');
if (!litefm_is_valid_path_component($fileName))
{
print_failure("Path decode failed");
return FALSE;
}
if(isset($_GET['type'])){
$type = $_GET['type'];
}else{
@ -66,23 +139,29 @@ function litefm_check($home_id)
$path = $_SESSION['fm_files_'.$home_id][$_GET['item']];
if($path == $fileName){
// Make sure nobody tries to get outside thier game server by referencing the .. directory
if(preg_match("/\/\.\.\/|\||;/", $path))
{
print_failure(get_lang("unallowed_char"));
$_SESSION['fm_cwd_'.$home_id] = NULL;
return FALSE;
}
else
{
if($type != "file"){
$_SESSION['fm_cwd_'.$home_id] = @$_SESSION['fm_cwd_'.$home_id] . "/" . $path;
$_SESSION['fm_cwd_'.$home_id] = clean_path($_SESSION['fm_cwd_'.$home_id]);
}else{
if((isset($_SESSION['fm_cwd_'.$home_id]) and !endsWith($_SESSION['fm_cwd_'.$home_id], $path)) or !isset($_SESSION['fm_cwd_'.$home_id])){
$_SESSION['fm_cwd_'.$home_id] = @$_SESSION['fm_cwd_'.$home_id] . "/" . $path;
$_SESSION['fm_cwd_'.$home_id] = clean_path($_SESSION['fm_cwd_'.$home_id]);
if($type != "file"){
$nextPath = trim((string)@$_SESSION['fm_cwd_'.$home_id], '/');
$nextPath = $nextPath === '' ? $path : $nextPath . '/' . $path;
$normalizedNext = litefm_normalize_relative_path($nextPath);
if($normalizedNext === false)
{
print_failure(get_lang("unallowed_char"));
$_SESSION['fm_cwd_'.$home_id] = NULL;
return FALSE;
}
$_SESSION['fm_cwd_'.$home_id] = $normalizedNext;
}else{
if((isset($_SESSION['fm_cwd_'.$home_id]) and !endsWith($_SESSION['fm_cwd_'.$home_id], $path)) or !isset($_SESSION['fm_cwd_'.$home_id])){
$nextPath = trim((string)@$_SESSION['fm_cwd_'.$home_id], '/');
$nextPath = $nextPath === '' ? $path : $nextPath . '/' . $path;
$normalizedNext = litefm_normalize_relative_path($nextPath);
if($normalizedNext === false)
{
print_failure(get_lang("unallowed_char"));
$_SESSION['fm_cwd_'.$home_id] = NULL;
return FALSE;
}
$_SESSION['fm_cwd_'.$home_id] = $normalizedNext;
}
}
}

View file

@ -26,12 +26,23 @@ In-panel file manager and file editor for customer server homes.
- remote file listings
- read/write operations
Current listing flow uses `remote_dirlistfm`, where agent filenames are treated as exact values and preserved by the Panel.
## User Workflow
- browse files
- upload/download
- edit common configs
Special-character names are supported end-to-end in browse/navigation/actions, including:
- `@3739421199`
- `@CF`
- `My Mod Folder`
- `[Server] Configs`
- `profile.backup`
- `mod+keys`
## Admin Workflow
- set file management permissions
@ -43,9 +54,22 @@ In-panel file manager and file editor for customer server homes.
- protected control files
- shared secret exposure
Path safety model:
- Preserve exact filename display and action values (no destructive filename stripping).
- Escape output for HTML rendering only.
- Encode filename tokens in links using `rawurlencode`; decode with `rawurldecode`.
- Reject invalid path components (`..`, path separators, NUL byte).
- Normalize relative paths and verify final resolved paths remain inside the assigned game home.
- Distinguish user-facing errors:
- `Server files have not been installed yet.` (home missing)
- `Directory not found: <relative path>` (missing subdirectory)
- `Directory exists but cannot be opened. Check permissions.` (listing/access failure)
- `Path decode failed` (invalid filename token)
## Known Issues
- should be hardened around safe roots and backups
- very large uploads/downloads still depend on chunking/browser limits
## Missing Functionality