fix drag-drop, UI glitches, & update validation

This commit is contained in:
Ryan
2025-04-11 03:21:09 -04:00
committed by GitHub
parent 4360f2830a
commit 337f529afd
36 changed files with 161 additions and 73 deletions

View File

@@ -1,5 +1,18 @@
# Changelog
## Changes 4/11/2025
- Fixed fileDragDrop issue from previous update.
- Fixed User Panel height changing unexpectedly on mouse over.
- Improved JS file comments for better documentation.
- Fixed userPermissions not updating after initial setting.
- Disabled folder and file sharing for readOnly users.
- Moved change password close button to the top right of the modal.
- Updated upload regex pattern to be Unicodeenabled and added additional security measures. [(#19)](https://github.com/error311/FileRise/issues/19)
- Updated filename, folder, and username regex acceptance patterns.
---
## Shift Key MultiSelection Changes 4/10/2025 v1.1.1
- **Implemented Range Selection:**

View File

@@ -49,7 +49,7 @@ if (!$newUsername || !$newPassword) {
}
// Validate username using preg_match (allow letters, numbers, underscores, dashes, and spaces).
if (!preg_match('/^[A-Za-z0-9_\- ]+$/', $newUsername)) {
if (!preg_match('/^[\p{L}\p{N}_\- ]+$/u', $newUsername)) {
echo json_encode(["error" => "Invalid username. Only letters, numbers, underscores, dashes, and spaces are allowed."]);
exit;
}

View File

@@ -44,7 +44,7 @@ $destinationFolder = trim($data['destination']);
$files = $data['files'];
// Validate folder names: allow letters, numbers, underscores, dashes, spaces, and forward slashes.
$folderPattern = '/^[A-Za-z0-9_\- \/]+$/';
$folderPattern = '/^[\p{L}\p{N}_\-\s\/\\\\]+$/u';
if ($sourceFolder !== 'root' && !preg_match($folderPattern, $sourceFolder)) {
echo json_encode(["error" => "Invalid source folder name."]);
exit;
@@ -104,7 +104,7 @@ $destMetadata = file_exists($destMetaFile) ? json_decode(file_get_contents($dest
$errors = [];
// Define a safe file name pattern: letters, numbers, underscores, dashes, dots, parentheses, and spaces.
$safeFileNamePattern = '/^[A-Za-z0-9_\-\.\(\) ]+$/';
$safeFileNamePattern = '/^[\p{L}\p{N}\p{M}%\-\.\(\) _]+$/u';
foreach ($files as $fileName) {
// Save the original name for metadata lookup.

View File

@@ -45,13 +45,13 @@ $folderName = trim($input['folderName']);
$parent = isset($input['parent']) ? trim($input['parent']) : "";
// Basic sanitation: allow only letters, numbers, underscores, dashes, and spaces in folderName
if (!preg_match('/^[A-Za-z0-9_\- ]+$/', $folderName)) {
if (!preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $folderName)) {
echo json_encode(['success' => false, 'error' => 'Invalid folder name.']);
exit;
}
// Optionally, sanitize the parent folder if needed.
if ($parent && !preg_match('/^[A-Za-z0-9_\- \/]+$/', $parent)) {
if ($parent && !preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $parent)) {
echo json_encode(['success' => false, 'error' => 'Invalid parent folder name.']);
exit;
}

View File

@@ -10,6 +10,16 @@ if (!$input) {
exit;
}
$username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username);
if ($username) {
$userPermissions = loadUserPermissions($username);
if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) {
echo json_encode(["error" => "Read-only users are not allowed to create shared folders."]);
exit();
}
}
$folder = isset($input['folder']) ? trim($input['folder']) : "";
$expirationMinutes = isset($input['expirationMinutes']) ? intval($input['expirationMinutes']) : 60;
$password = isset($input['password']) ? $input['password'] : "";
@@ -17,7 +27,7 @@ $allowUpload = isset($input['allowUpload']) ? intval($input['allowUpload']) : 0;
// Validate folder name using regex.
// Allow letters, numbers, underscores, hyphens, spaces and slashes.
if ($folder !== 'root' && !preg_match('/^[A-Za-z0-9_\- \/]+$/', $folder)) {
if ($folder !== 'root' && !preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $folder)) {
echo json_encode(["error" => "Invalid folder name."]);
exit;
}

View File

@@ -9,13 +9,23 @@ if (!$input) {
exit;
}
$username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username);
if ($username) {
$userPermissions = loadUserPermissions($username);
if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) {
echo json_encode(["error" => "Read-only users are not allowed to create share files."]);
exit();
}
}
$folder = isset($input['folder']) ? trim($input['folder']) : "";
$file = isset($input['file']) ? basename($input['file']) : "";
$expirationMinutes = isset($input['expirationMinutes']) ? intval($input['expirationMinutes']) : 60;
$password = isset($input['password']) ? $input['password'] : "";
// Validate folder using regex.
if ($folder !== 'root' && !preg_match('/^[A-Za-z0-9_\- \/]+$/', $folder)) {
if ($folder !== 'root' && !preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $folder)) {
echo json_encode(["error" => "Invalid folder name."]);
exit;
}

View File

@@ -69,7 +69,7 @@ if (!isset($data['files']) || !is_array($data['files'])) {
$folder = isset($data['folder']) ? trim($data['folder']) : 'root';
// Validate folder: allow letters, numbers, underscores, dashes, spaces, and forward slashes
if ($folder !== 'root' && !preg_match('/^[A-Za-z0-9_\- \/]+$/', $folder)) {
if ($folder !== 'root' && !preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $folder)) {
echo json_encode(["error" => "Invalid folder name."]);
exit;
}
@@ -96,7 +96,7 @@ $movedFiles = [];
$errors = [];
// Define a safe file name pattern: allow letters, numbers, underscores, dashes, dots, and spaces.
$safeFileNamePattern = '/^[A-Za-z0-9_\-\.\(\) ]+$/';
$safeFileNamePattern = '/^[\p{L}\p{N}\p{M}%\-\.\(\) _]+$/u';
foreach ($data['files'] as $fileName) {
$basename = basename(trim($fileName));

View File

@@ -50,7 +50,7 @@ if ($folderName === 'root') {
}
// Allow letters, numbers, underscores, dashes, spaces, and forward slashes.
if (!preg_match('/^[A-Za-z0-9_\- \/]+$/', $folderName)) {
if (!preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $folderName)) {
echo json_encode(['success' => false, 'error' => 'Invalid folder name.']);
exit;
}

View File

@@ -62,7 +62,7 @@ $deletedFiles = [];
$errors = [];
// Define a safe file name pattern.
$safeFileNamePattern = '/^[A-Za-z0-9_\-\.\(\) ]+$/';
$safeFileNamePattern = '/^[\p{L}\p{N}\p{M}%\-\.\(\) _]+$/u';
foreach ($filesToDelete as $trashName) {
$trashName = trim($trashName);

View File

@@ -14,7 +14,7 @@ $file = isset($_GET['file']) ? basename($_GET['file']) : '';
$folder = isset($_GET['folder']) ? trim($_GET['folder']) : 'root';
// Validate file name (allowing letters, numbers, underscores, dashes, dots, and parentheses)
if (!preg_match('/^[A-Za-z0-9_\-\.\(\) ]+$/', $file)) {
if (!preg_match('/^[\p{L}\p{N}\p{M}%\-\.\(\) _]+$/u', $file)) {
http_response_code(400);
echo json_encode(["error" => "Invalid file name."]);
exit;

View File

@@ -38,7 +38,7 @@ $files = $data['files'];
if ($folder !== "root") {
$parts = explode('/', $folder);
foreach ($parts as $part) {
if (empty($part) || $part === '.' || $part === '..' || !preg_match('/^[A-Za-z0-9_\-\.\(\) ]+$/', $part)) {
if (empty($part) || $part === '.' || $part === '..' || !preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $part)) {
http_response_code(400);
header('Content-Type: application/json');
echo json_encode(["error" => "Invalid folder name."]);
@@ -76,7 +76,7 @@ if (empty($files)) {
}
foreach ($files as $fileName) {
if (!preg_match('/^[A-Za-z0-9_\-\.\(\) ]+$/', $fileName)) {
if (!preg_match('/^[\p{L}\p{N}\p{M}%\-\.\(\) _]+$/u', $fileName)) {
http_response_code(400);
header('Content-Type: application/json');
echo json_encode(["error" => "Invalid file name: " . $fileName]);

View File

@@ -50,7 +50,7 @@ if (empty($files)) {
if ($folder !== "root") {
$parts = explode('/', $folder);
foreach ($parts as $part) {
if (empty($part) || $part === '.' || $part === '..' || !preg_match('/^[A-Za-z0-9_\-\.\(\) ]+$/', $part)) {
if (empty($part) || $part === '.' || $part === '..' || !preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $part)) {
http_response_code(400);
echo json_encode(["error" => "Invalid folder name."]);
exit;
@@ -92,7 +92,7 @@ $destMetadata = file_exists($destMetaFile) ? json_decode(file_get_contents($dest
$errors = [];
$allSuccess = true;
$extractedFiles = array(); // Array to collect names of extracted files
$safeFileNamePattern = '/^[A-Za-z0-9_\-\.\(\) ]+$/';
$safeFileNamePattern = '/^[\p{L}\p{N}\p{M}%\-\.\(\) _]+$/u';
// ---------- Process Each File ----------
foreach ($files as $zipFileName) {

View File

@@ -14,7 +14,7 @@ if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
$folder = isset($_GET['folder']) ? trim($_GET['folder']) : 'root';
// Allow only safe characters in the folder parameter (letters, numbers, underscores, dashes, spaces, and forward slashes).
if ($folder !== 'root' && !preg_match('/^[A-Za-z0-9_\- \/]+$/', $folder)) {
if ($folder !== 'root' && !preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $folder)) {
echo json_encode(["error" => "Invalid folder name."]);
exit;
}
@@ -53,7 +53,7 @@ $files = array_values(array_diff(scandir($directory), array('.', '..')));
$fileList = [];
// Define a safe file name pattern: letters, numbers, underscores, dashes, dots, parentheses, and spaces.
$safeFileNamePattern = '/^[A-Za-z0-9_\-\.\(\) ]+$/';
$safeFileNamePattern = '/^[\p{L}\p{N}\p{M}%\-\.\(\) _]+$/u';
foreach ($files as $file) {
// Skip hidden files (those that begin with a dot)

View File

@@ -20,7 +20,7 @@ function getSubfolders($dir, $relative = '') {
$folders = [];
$items = scandir($dir);
// Allow letters, numbers, underscores, dashes, and spaces in folder names.
$safeFolderNamePattern = '/^[A-Za-z0-9_\- ]+$/';
$safeFolderNamePattern = '/^[\p{L}\p{N}_\-\s\/\\\\]+$/u';
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
if (!preg_match($safeFolderNamePattern, $item)) {

View File

@@ -17,7 +17,7 @@ if (file_exists($usersFile)) {
$parts = explode(':', trim($line));
if (count($parts) >= 3) {
// Validate username format:
if (preg_match('/^[A-Za-z0-9_\- ]+$/', $parts[0])) {
if (preg_match('/^[\p{L}\p{N}_\- ]+$/u', $parts[0])) {
$users[] = [
"username" => $parts[0],
"role" => trim($parts[2])

View File

@@ -413,7 +413,7 @@
<!-- Change Password, Add User, Remove User, Rename File, and Custom Confirm Modals (unchanged) -->
<div id="changePasswordModal" class="modal" style="display:none;">
<div class="modal-content" style="max-width:400px; margin:auto;">
<span id="closeChangePasswordModal" style="cursor:pointer;">&times;</span>
<span id="closeChangePasswordModal" style="position:absolute; top:10px; right:10px; cursor:pointer; font-size:24px;">&times;</span>
<h3 data-i18n-key="change_password_title">Change Password</h3>
<input type="password" id="oldPassword" class="form-control" data-i18n-placeholder="old_password"
placeholder="Old Password" style="width:100%; margin: 5px 0;" />

View File

@@ -132,10 +132,11 @@ function updateAuthenticatedUI(data) {
if (data.username) {
localStorage.setItem("username", data.username);
}
/*
if (typeof data.folderOnly !== "undefined") {
localStorage.setItem("folderOnly", data.folderOnly ? "true" : "false");
}
*/
const headerButtons = document.querySelector(".header-buttons");
const firstButton = headerButtons.firstElementChild;
@@ -227,15 +228,29 @@ function checkAuthentication(showLoginToast = true) {
function submitLogin(data) {
setLastLoginData(data);
window.__lastLoginData = data;
sendRequest("auth.php", "POST", data, { "X-CSRF-Token": window.csrfToken })
.then(response => {
if (response.success || response.status === "ok") {
sessionStorage.setItem("welcomeMessage", "Welcome back, " + data.username + "!");
window.location.reload();
} else if (response.totp_required) {
openTOTPLoginModal();
} else if (response.error && response.error.includes("Too many failed login attempts")) {
showToast(response.error);
sendRequest("auth.php", "POST", data, { "X-CSRF-Token": window.csrfToken })
.then(response => {
if (response.success || response.status === "ok") {
sessionStorage.setItem("welcomeMessage", "Welcome back, " + data.username + "!");
// Fetch and update permissions, then reload.
sendRequest("getUserPermissions.php", "GET")
.then(permissionData => {
if (permissionData && typeof permissionData === "object") {
localStorage.setItem("folderOnly", permissionData.folderOnly ? "true" : "false");
localStorage.setItem("readOnly", permissionData.readOnly ? "true" : "false");
localStorage.setItem("disableUpload", permissionData.disableUpload ? "true" : "false");
}
})
.catch(() => {
// if fetching permissions fails.
})
.finally(() => {
window.location.reload();
});
} else if (response.totp_required) {
openTOTPLoginModal();
} else if (response.error && response.error.includes("Too many failed login attempts")) {
showToast(response.error);
const loginButton = document.getElementById("authForm").querySelector("button[type='submit']");
if (loginButton) {
loginButton.disabled = true;
@@ -293,7 +308,7 @@ function loadUserList() {
closeRemoveUserModal();
}
})
.catch(() => {});
.catch(() => { });
}
window.loadUserList = loadUserList;
@@ -320,7 +335,7 @@ function initAuth() {
method: "POST",
credentials: "include",
headers: { "X-CSRF-Token": window.csrfToken }
}).then(() => window.location.reload(true)).catch(() => {});
}).then(() => window.location.reload(true)).catch(() => { });
});
document.getElementById("addUserBtn").addEventListener("click", function () {
resetUserForm();
@@ -386,7 +401,7 @@ function initAuth() {
showToast("Error: " + (data.error || "Could not remove user"));
}
})
.catch(() => {});
.catch(() => { });
});
document.getElementById("cancelRemoveUserBtn").addEventListener("click", closeRemoveUserModal);
document.getElementById("changePasswordBtn").addEventListener("click", function () {

View File

@@ -162,9 +162,9 @@ export function openUserPanel() {
max-width: 600px;
width: 90%;
border-radius: 8px;
position: relative;
position: fixed;
overflow-y: auto;
max-height: 90vh;
max-height: 350px !important;
border: ${isDarkMode ? "1px solid #444" : "1px solid #ccc"};
transform: none;
transition: none;
@@ -187,7 +187,7 @@ export function openUserPanel() {
z-index: 3000;
`;
userPanelModal.innerHTML = `
<div class="modal-content" style="${modalContentStyles}">
<div class="modal-content user-panel-content" style="${modalContentStyles}">
<span id="closeUserPanel" style="position: absolute; top: 10px; right: 10px; cursor: pointer; font-size: 24px;">&times;</span>
<h3>User Panel (${username})</h3>
<button type="button" id="openChangePasswordModalBtn" class="btn btn-primary" style="margin-bottom: 15px;">Change Password</button>
@@ -800,12 +800,18 @@ function loadUserPermissionsList() {
if ((user.role && user.role === "1") || user.username.toLowerCase() === "admin") return;
// Use stored permissions if available; otherwise fall back to localStorage defaults.
const defaultPerm = {
folderOnly: localStorage.getItem("folderOnly") === "true",
readOnly: localStorage.getItem("readOnly") === "true",
disableUpload: localStorage.getItem("disableUpload") === "true"
};
const userPerm = (permissionsData && typeof permissionsData === "object" && permissionsData[user.username]) || defaultPerm;
const defaultPerm = {
folderOnly: false,
readOnly: false,
disableUpload: false,
};
// Normalize the username key to match server storage (e.g., lowercase)
const usernameKey = user.username.toLowerCase();
const userPerm = (permissionsData && typeof permissionsData === "object" && (usernameKey in permissionsData))
? permissionsData[usernameKey]
: defaultPerm;
// Create a row for the user.
const row = document.createElement("div");

View File

@@ -1,4 +1,4 @@
// dragDrop.js
// fileDragDrop.js
import { showToast } from './domUtils.js';
import { loadFileList } from './fileListView.js';

View File

@@ -1,4 +1,4 @@
// editor.js
// fileEditor.js
import { escapeHTML, showToast } from './domUtils.js';
import { loadFileList } from './fileListView.js';
import { t } from './i18n.js';

View File

@@ -299,7 +299,7 @@ export function renderFileTable(folder, container) {
});
});
updateFileActionButtons();
document.querySelectorAll("#fileListContent tbody tr").forEach(row => {
document.querySelectorAll("#fileList tbody tr").forEach(row => {
row.setAttribute("draggable", "true");
import('./fileDragDrop.js').then(module => {
row.addEventListener("dragstart", module.fileDragStartHandler);

View File

@@ -1,4 +1,4 @@
// contextMenu.js
// fileMenu.js
import { updateRowHighlight, showToast } from './domUtils.js';
import { handleDeleteSelected, handleCopySelected, handleMoveSelected, handleDownloadZipSelected, handleExtractZipSelected, renameFile } from './fileActions.js';
import { previewFile } from './filePreview.js';

View File

@@ -81,7 +81,7 @@ $username = trim($_SERVER['PHP_AUTH_USER']);
$password = trim($_SERVER['PHP_AUTH_PW']);
// Validate username format (optional)
if (!preg_match('/^[A-Za-z0-9_\- ]+$/', $username)) {
if (!preg_match('/^[\p{L}\p{N}_\- ]+$/u', $username)) {
header('WWW-Authenticate: Basic realm="FileRise Login"');
header('HTTP/1.0 401 Unauthorized');
echo 'Invalid username format';

View File

@@ -45,7 +45,7 @@ $sourceFolder = trim($data['source']) ?: 'root';
$destinationFolder = trim($data['destination']) ?: 'root';
// Allow only letters, numbers, underscores, dashes, spaces, and forward slashes in folder names.
$folderPattern = '/^[A-Za-z0-9_\- \/]+$/';
$folderPattern = '/^[\p{L}\p{N}_\-\s\/\\\\]+$/u';
if ($sourceFolder !== 'root' && !preg_match($folderPattern, $sourceFolder)) {
echo json_encode(["error" => "Invalid source folder name."]);
exit;
@@ -111,7 +111,7 @@ $srcMetadata = file_exists($srcMetaFile) ? json_decode(file_get_contents($srcMet
$destMetadata = file_exists($destMetaFile) ? json_decode(file_get_contents($destMetaFile), true) : [];
$errors = [];
$safeFileNamePattern = '/^[A-Za-z0-9_\-\.\(\) ]+$/';
$safeFileNamePattern = '/^[\p{L}\p{N}\p{M}%\-\.\(\) _]+$/u';
foreach ($data['files'] as $fileName) {
// Save the original name for metadata lookup.

View File

@@ -17,14 +17,12 @@ if (!isset($_POST['folder'])) {
exit;
}
$folder = $_POST['folder'];
// Validate the folder name (only alphanumerics, dashes allowed)
if (!preg_match('/^resumable_[A-Za-z0-9\-]+$/', $folder)) {
$folder = urldecode($_POST['folder']);
if (!preg_match('/^resumable_[\p{L}\p{N}_\-\s\/\\\\]+$/u', $folder)) {
echo json_encode(["error" => "Invalid folder name"]);
http_response_code(400);
exit;
}
$tempDir = rtrim(UPLOAD_DIR, '/\\') . DIRECTORY_SEPARATOR . $folder;
// If the folder doesn't exist, simply return success.

View File

@@ -30,7 +30,7 @@ if (!$usernameToRemove) {
}
// Optional: Validate the username format (allow letters, numbers, underscores, dashes, and spaces)
if (!preg_match('/^[A-Za-z0-9_\- ]+$/', $usernameToRemove)) {
if (!preg_match('/^[\p{L}\p{N}_\- ]+$/u', $usernameToRemove)) {
echo json_encode(["error" => "Invalid username format"]);
exit;
}

View File

@@ -40,7 +40,7 @@ if (!$data || !isset($data['folder']) || !isset($data['oldName']) || !isset($dat
$folder = trim($data['folder']) ?: 'root';
// For subfolders, allow letters, numbers, underscores, dashes, spaces, and forward slashes.
if ($folder !== 'root' && !preg_match('/^[A-Za-z0-9_\- \/]+$/', $folder)) {
if ($folder !== 'root' && !preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $folder)) {
echo json_encode(["error" => "Invalid folder name"]);
exit;
}
@@ -49,7 +49,7 @@ $oldName = basename(trim($data['oldName']));
$newName = basename(trim($data['newName']));
// Validate file names: allow letters, numbers, underscores, dashes, dots, parentheses, and spaces.
if (!preg_match('/^[A-Za-z0-9_\-\. \(\)]+$/', $oldName) || !preg_match('/^[A-Za-z0-9_\-\. \(\)]+$/', $newName)) {
if (!preg_match('/^[\p{L}\p{N}\p{M}%\-\.\(\) _]+$/u', $oldName) || !preg_match('/^[\p{L}\p{N}\p{M}%\-\.\(\) _]+$/u', $newName)) {
echo json_encode(["error" => "Invalid file name."]);
exit;
}

View File

@@ -48,7 +48,7 @@ $oldFolder = trim($input['oldFolder']);
$newFolder = trim($input['newFolder']);
// Validate folder names
if (!preg_match('/^[A-Za-z0-9_\- \/]+$/', $oldFolder) || !preg_match('/^[A-Za-z0-9_\- \/]+$/', $newFolder)) {
if (!preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $oldFolder) || !preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $newFolder)) {
echo json_encode(['success' => false, 'error' => 'Invalid folder name(s).']);
exit;
}

View File

@@ -53,7 +53,7 @@ if (!isset($data['files']) || !is_array($data['files'])) {
}
// Define a safe file name pattern.
$safeFileNamePattern = '/^[A-Za-z0-9_\-\.\(\) ]+$/';
$safeFileNamePattern = '/^[\p{L}\p{N}\p{M}%\-\.\(\) _]+$/u';
$restoredItems = [];
$errors = [];

View File

@@ -48,7 +48,7 @@ $folder = isset($data["folder"]) ? trim($data["folder"]) : "root";
// If a subfolder is provided, validate it.
// Allow letters, numbers, underscores, dashes, spaces, and forward slashes.
if ($folder !== "root" && !preg_match('/^[A-Za-z0-9_\- \/]+$/', $folder)) {
if ($folder !== "root" && !preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $folder)) {
echo json_encode(["error" => "Invalid folder name"]);
exit;
}

View File

@@ -20,6 +20,16 @@ if (!isset($headers['X-CSRF-Token']) || $headers['X-CSRF-Token'] !== $_SESSION['
exit;
}
$username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username);
if ($username) {
$userPermissions = loadUserPermissions($username);
if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) {
echo json_encode(["error" => "Read-only users are not allowed to file tags"]);
exit();
}
}
// Retrieve and sanitize input.
$data = json_decode(file_get_contents('php://input'), true);
$file = isset($data['file']) ? trim($data['file']) : '';
@@ -77,7 +87,7 @@ if ($file === "global") {
}
// Validate folder name.
if ($folder !== 'root' && !preg_match('/^[A-Za-z0-9_\- \/]+$/', $folder)) {
if ($folder !== 'root' && !preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $folder)) {
echo json_encode(["error" => "Invalid folder name."]);
exit;
}

View File

@@ -32,7 +32,7 @@ if (!$userId) {
}
// ——— Validate userId format ———
if (!preg_match('/^[A-Za-z0-9_\-]+$/', $userId)) {
if (!preg_match('/^[\p{L}\p{N}_\- ]+$/u', $userId)) {
http_response_code(400);
error_log("Invalid userId format: {$userId}");
exit(json_encode(['status'=>'error','message'=>'Invalid user identifier']));

View File

@@ -29,7 +29,7 @@ if (empty($_SESSION['username'])) {
// 4) Validate username format
$userId = $_SESSION['username'];
if (!preg_match('/^[A-Za-z0-9_\-]+$/', $userId)) {
if (!preg_match('/^[\p{L}\p{N}_\- ]+$/u', $userId)) {
http_response_code(400);
error_log("totp_saveCode: invalid username format: {$userId}");
exit(json_encode(['status'=>'error','message'=>'Invalid user identifier']));

View File

@@ -40,16 +40,39 @@ if (file_exists($permissionsFile)) {
$existingPermissions = [];
}
// Load user roles from the users file (similar to getUsers.php)
$usersFile = USERS_DIR . USERS_FILE;
$userRoles = [];
if (file_exists($usersFile)) {
$lines = file($usersFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
$parts = explode(':', trim($line));
if (count($parts) >= 3) {
// Validate username format:
if (preg_match('/^[\p{L}\p{N}_\- ]+$/u', $parts[0])) {
// Use a lowercase key for consistency.
$userRoles[strtolower($parts[0])] = trim($parts[2]);
}
}
}
}
// Loop through each permission update.
foreach ($permissions as $perm) {
// Ensure username is provided.
if (!isset($perm['username'])) continue;
$username = $perm['username'];
// Look up the user's role from the users file.
$role = isset($userRoles[strtolower($username)]) ? $userRoles[strtolower($username)] : null;
// Skip updating permissions for admin users.
if (strtolower($username) === "admin") continue;
if ($role === "1") {
continue;
}
// Update permissions: default any missing value to false.
$existingPermissions[$username] = [
$existingPermissions[strtolower($username)] = [
'folderOnly' => isset($perm['folderOnly']) ? (bool)$perm['folderOnly'] : false,
'readOnly' => isset($perm['readOnly']) ? (bool)$perm['readOnly'] : false,
'disableUpload' => isset($perm['disableUpload']) ? (bool)$perm['disableUpload'] : false

View File

@@ -65,14 +65,16 @@ if (isset($_POST['resumableChunkNumber'])) {
$resumableFilename = $_POST['resumableFilename'];
if (!preg_match('/^[A-Za-z0-9_\-\.\(\) ]+$/', $resumableFilename)) {
http_response_code(400); // Set an error HTTP status code
// First, strip directory components.
$resumableFilename = urldecode(basename($_POST['resumableFilename']));
if (!preg_match('/^[\p{L}\p{N}\p{M}%\-\.\(\) _]+$/u', $resumableFilename)) {
http_response_code(400);
echo json_encode(["error" => "Invalid file name: " . $resumableFilename]);
exit;
}
$folder = isset($_POST['folder']) ? trim($_POST['folder']) : 'root';
if ($folder !== 'root' && !preg_match('/^[A-Za-z0-9_\- \/]+$/', $folder)) {
if ($folder !== 'root' && !preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $folder)) {
echo json_encode(["error" => "Invalid folder name"]);
exit;
}
@@ -173,7 +175,7 @@ if (!preg_match('/^[A-Za-z0-9_\-\.\(\) ]+$/', $resumableFilename)) {
// ------------- Full Upload (Non-chunked) -------------
// Validate folder name input.
$folder = isset($_POST['folder']) ? trim($_POST['folder']) : 'root';
if ($folder !== 'root' && !preg_match('/^[A-Za-z0-9_\- \/]+$/', $folder)) {
if ($folder !== 'root' && !preg_match('/^[\p{L}\p{N}_\-\s\/\\\\]+$/u', $folder)) {
echo json_encode(["error" => "Invalid folder name"]);
exit;
}
@@ -195,10 +197,12 @@ if (!preg_match('/^[A-Za-z0-9_\-\.\(\) ]+$/', $resumableFilename)) {
$metadataCollection = []; // key: folder path, value: metadata array
$metadataChanged = []; // key: folder path, value: boolean
$safeFileNamePattern = '/^[A-Za-z0-9_\-\.\(\) ]+$/';
// Use a Unicode-enabled pattern to allow special characters.
$safeFileNamePattern = '/^[\p{L}\p{N}\p{M}%\-\.\(\) _]+$/u';
foreach ($_FILES["file"]["name"] as $index => $fileName) {
$safeFileName = basename($fileName);
// First, ensure we only work with the base filename to avoid traversal issues.
$safeFileName = trim(urldecode(basename($fileName)));
if (!preg_match($safeFileNamePattern, $safeFileName)) {
echo json_encode(["error" => "Invalid file name: " . $fileName]);
exit;
@@ -224,6 +228,7 @@ if (!preg_match('/^[A-Za-z0-9_\-\.\(\) ]+$/', $resumableFilename)) {
$uploadDir = rtrim(UPLOAD_DIR, '/\\') . DIRECTORY_SEPARATOR
. str_replace('/', DIRECTORY_SEPARATOR, $folderPath) . DIRECTORY_SEPARATOR;
}
// Reapply basename to the relativePath to get the final safe file name.
$safeFileName = basename($relativePath);
}
// --- End Minimal Folder/Subfolder Logic ---

View File

@@ -109,8 +109,6 @@ if (!move_uploaded_file($fileUpload['tmp_name'], $targetPath)) {
}
// --- Metadata Update for Shared Upload ---
// We want to update metadata similarly to your normal upload.
// Determine a key for metadata storage for the folder.
$metadataKey = ($folder === '' || $folder === 'root') ? "root" : $folder;
// Sanitize the metadata file name.
$metadataFileName = str_replace(['/', '\\', ' '], '-', $metadataKey) . '_metadata.json';