fix drag-drop, UI glitches, & update validation
This commit is contained in:
13
CHANGELOG.md
13
CHANGELOG.md
@@ -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 Unicode‑enabled and added additional security measures. [(#19)](https://github.com/error311/FileRise/issues/19)
|
||||
- Updated filename, folder, and username regex acceptance patterns.
|
||||
|
||||
---
|
||||
|
||||
## Shift Key Multi‑Selection Changes 4/10/2025 v1.1.1
|
||||
|
||||
- **Implemented Range Selection:**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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;">×</span>
|
||||
<span id="closeChangePasswordModal" style="position:absolute; top:10px; right:10px; cursor:pointer; font-size:24px;">×</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;" />
|
||||
|
||||
41
js/auth.js
41
js/auth.js
@@ -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 () {
|
||||
|
||||
@@ -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;">×</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");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// dragDrop.js
|
||||
// fileDragDrop.js
|
||||
import { showToast } from './domUtils.js';
|
||||
import { loadFileList } from './fileListView.js';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// editor.js
|
||||
// fileEditor.js
|
||||
import { escapeHTML, showToast } from './domUtils.js';
|
||||
import { loadFileList } from './fileListView.js';
|
||||
import { t } from './i18n.js';
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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']));
|
||||
|
||||
@@ -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']));
|
||||
|
||||
@@ -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
|
||||
|
||||
17
upload.php
17
upload.php
@@ -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 ---
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user