diff --git a/upload.php b/upload.php index e630d26..1127f9c 100644 --- a/upload.php +++ b/upload.php @@ -8,7 +8,12 @@ if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) { exit; } +// Validate folder name input. Only allow letters, numbers, underscores, dashes, and spaces. $folder = isset($_POST['folder']) ? trim($_POST['folder']) : 'root'; +if ($folder !== 'root' && !preg_match('/^[A-Za-z0-9_\- ]+$/', $folder)) { + echo json_encode(["error" => "Invalid folder name"]); + exit; +} // Determine the target upload directory. $uploadDir = UPLOAD_DIR; @@ -23,15 +28,28 @@ if ($folder !== 'root') { } } +// Load metadata for uploaded files. $metadataFile = META_DIR . META_FILE; $metadata = file_exists($metadataFile) ? json_decode(file_get_contents($metadataFile), true) : []; $metadataChanged = false; +// Define a safe pattern for file names: letters, numbers, underscores, dashes, dots, and spaces. +$safeFileNamePattern = '/^[A-Za-z0-9_\-\. ]+$/'; + foreach ($_FILES["file"]["name"] as $index => $fileName) { - $targetPath = $uploadDir . basename($fileName); + // Use basename to strip any directory components. + $safeFileName = basename($fileName); + + // Validate that the sanitized file name contains only allowed characters. + if (!preg_match($safeFileNamePattern, $safeFileName)) { + echo json_encode(["error" => "Invalid file name: " . $fileName]); + exit; + } + + $targetPath = $uploadDir . $safeFileName; if (move_uploaded_file($_FILES["file"]["tmp_name"][$index], $targetPath)) { - // Use a metadata key that includes the folder if not in root. - $metaKey = ($folder !== 'root') ? $folder . "/" . $fileName : $fileName; + // Build the metadata key, including the folder if not in root. + $metaKey = ($folder !== 'root') ? $folder . "/" . $safeFileName : $safeFileName; if (!isset($metadata[$metaKey])) { $uploadedDate = date(DATE_TIME_FORMAT); $uploader = $_SESSION['username'] ?? "Unknown"; diff --git a/utils.js b/utils.js index 6eaf12e..60a0066 100644 --- a/utils.js +++ b/utils.js @@ -435,8 +435,19 @@ document.addEventListener("DOMContentLoaded", function () { .catch(error => console.error("Error loading file list:", error)); } + // Helper function to escape special HTML characters + function escapeHTML(str) { + return String(str) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + function renderFileTable(folder) { const fileListContainer = document.getElementById("fileList"); + // Use encodeURIComponent on folder for the URL part const folderPath = (folder === "root") ? "uploads/" : "uploads/" + encodeURIComponent(folder) + "/"; let tableHTML = ` @@ -465,17 +476,27 @@ document.addEventListener("DOMContentLoaded", function () { fileData.forEach(file => { // Determine if file is editable via your canEditFile() helper const isEditable = canEditFile(file.name); + // Escape user-supplied file name and other properties for safe HTML output. + const safeFileName = escapeHTML(file.name); + const safeModified = escapeHTML(file.modified); + const safeUploaded = escapeHTML(file.uploaded); + const safeSize = escapeHTML(file.size); + const safeUploader = escapeHTML(file.uploader || "Unknown"); + tableHTML += ` - - - - - - + + + + + + `; @@ -493,6 +514,8 @@ document.addEventListener("DOMContentLoaded", function () { }); }); + + // Show or hide action buttons based on whether files exist const deleteBtn = document.getElementById("deleteSelectedBtn"); const copyBtn = document.getElementById("copySelectedBtn"); @@ -508,7 +531,7 @@ document.addEventListener("DOMContentLoaded", function () { moveBtn.style.display = "none"; document.getElementById("copyMoveFolderSelect").style.display = "none"; } - + } function sortFiles(column, folder) { @@ -691,7 +714,7 @@ document.addEventListener("DOMContentLoaded", function () { const folderUsed = folder || currentFolder || "root"; const folderPath = (folderUsed === "root") ? "uploads/" : "uploads/" + encodeURIComponent(folderUsed) + "/"; const fileUrl = folderPath + encodeURIComponent(fileName) + "?t=" + new Date().getTime(); - + // First, use a HEAD request to check file size fetch(fileUrl, { method: "HEAD" }) .then(response => { @@ -704,8 +727,8 @@ document.addEventListener("DOMContentLoaded", function () { return fetch(fileUrl); }) .then(response => { - if (!response.ok) { - throw new Error("HTTP error! Status: " + response.status); + if (!response.ok) { + throw new Error("HTTP error! Status: " + response.status); } return response.text(); }) @@ -726,7 +749,7 @@ document.addEventListener("DOMContentLoaded", function () { }) .catch(error => console.error("Error loading file:", error)); }; - + window.saveFile = function (fileName, folder) { const editor = document.getElementById("fileEditor");
${file.name}${file.modified}${file.uploaded}${file.size}${file.uploader || "Unknown"}${safeFileName}${safeModified}${safeUploaded}${safeSize}${safeUploader}
Download - ${isEditable ? `` : ""} + ${isEditable + ? `` + : "" + }