Refactor input sanitization functions and improve file path handling across multiple files

- Introduced `safe_id_segment`, `safe_centro_id`, and `safe_aulario_config_path` functions to sanitize input and construct file paths securely.
- Updated `index.php`, `paneldiario.php`, `proyectos.php`, `aularios.php`, `centros.php`, `club_mkthumb.php`, `reset_password.php`, and `users.php` to utilize new sanitization functions.
- Enhanced error handling for file existence checks and directory traversal prevention.
- Ensured consistent use of safe path handling in user input across the application.
This commit is contained in:
Naiel
2026-02-19 14:45:51 +00:00
parent 905610717b
commit 192002880a
14 changed files with 645 additions and 199 deletions

View File

@@ -5,11 +5,31 @@ ob_end_flush();
ini_set('memory_limit', '1G');
header("Access-Control-Allow-Origin: *");
switch ($_GET["type"]) {
function safe_id_segment($value)
{
$value = basename((string)$value);
return preg_replace('/[^A-Za-z0-9_-]/', '', $value);
}
function safe_centro_id($value)
{
return preg_replace('/[^0-9]/', '', (string)$value);
}
function safe_filename($name)
{
$name = basename((string)$name);
$name = preg_replace('/[^A-Za-z0-9._-]/', '_', $name);
$name = ltrim($name, '.');
return $name;
}
$type = $_GET["type"] ?? "";
switch ($type) {
case "alumno_photo":
$centro = basename($_GET["centro"] ?? '');
$aulario = basename($_GET["aulario"] ?? '');
$alumno = basename($_GET["alumno"] ?? '');
$centro = safe_centro_id($_GET["centro"] ?? '');
$aulario = safe_id_segment($_GET["aulario"] ?? '');
$alumno = safe_id_segment($_GET["alumno"] ?? '');
// Additional validation to prevent empty names
if (empty($centro) || empty($aulario) || empty($alumno)) {
header("HTTP/1.1 403 Forbidden");
@@ -18,15 +38,23 @@ switch ($_GET["type"]) {
$relpath = "entreaulas/Centros/$centro/Aularios/$aulario/Alumnos/$alumno/photo.jpg";
break;
case "panel_actividades":
$centro = str_replace('..', '_', $_GET["centro"] ?? '');
$activity = str_replace('..', '_', $_GET["activity"] ?? '');
$centro = safe_centro_id($_GET["centro"] ?? '');
$activity = safe_id_segment($_GET["activity"] ?? '');
if (empty($centro) || empty($activity)) {
header("HTTP/1.1 400 Bad Request");
die("Invalid parameters");
}
$relpath = "entreaulas/Centros/$centro/Panel/Actividades/$activity/photo.jpg";
break;
case "comedor_image":
$centro = str_replace('..', '_', $_GET["centro"] ?? '');
$aulario = str_replace('..', '_', $_GET["aulario"] ?? '');
$centro = safe_centro_id($_GET["centro"] ?? '');
$aulario = safe_id_segment($_GET["aulario"] ?? '');
$date = preg_replace('/[^0-9-]/', '', $_GET["date"] ?? '');
$file = basename($_GET["file"] ?? '');
$file = safe_filename($_GET["file"] ?? '');
if (empty($centro) || empty($aulario) || empty($file)) {
header("HTTP/1.1 400 Bad Request");
die("Invalid parameters");
}
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
header("HTTP/1.1 400 Bad Request");
die("Invalid date");
@@ -36,9 +64,13 @@ switch ($_GET["type"]) {
$relpath = "entreaulas/Centros/$centro/Aularios/$aulario/Comedor/$ym/$day/$file";
break;
case "proyecto_file":
$centro = str_replace('..', '_', $_GET["centro"] ?? '');
$project = str_replace('..', '_', $_GET["project"] ?? '');
$file = basename($_GET["file"] ?? '');
$centro = safe_centro_id($_GET["centro"] ?? '');
$project = safe_id_segment($_GET["project"] ?? '');
$file = safe_filename($_GET["file"] ?? '');
if (empty($centro) || empty($project) || empty($file)) {
header("HTTP/1.1 400 Bad Request");
die("Invalid parameters");
}
// Ensure no directory traversal
if (strpos($file, '..') !== false || strpos($file, '/') !== false || strpos($file, '\\') !== false) {
header("HTTP/1.1 400 Bad Request");
@@ -73,6 +105,9 @@ switch ($_GET["type"]) {
$path = $project_dir . "/" . $file;
$uripath = str_replace("/DATA", "", $path);
break;
default:
header("HTTP/1.1 400 Bad Request");
die("Invalid type");
}
if (!isset($path)) {
$path = "/DATA/$relpath";
@@ -84,7 +119,8 @@ if (!isset($uripath)) {
// Validate that the resolved path is within /DATA directory
$real_path = realpath($path);
$real_base = realpath("/DATA");
if ($real_path === false || $real_base === false || strpos($real_path, $real_base) !== 0) {
$real_base_prefix = $real_base !== false ? rtrim($real_base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : null;
if ($real_path === false || $real_base === false || $real_base_prefix === null || strpos($real_path, $real_base_prefix) !== 0) {
header("HTTP/1.1 403 Forbidden");
die("Access denied");
}
@@ -96,7 +132,7 @@ if (!file_exists($real_path) || !is_file($real_path)) {
$mime = mime_content_type($real_path);
// Check if thumbnail is requested
if (file_exists($real_path . ".thumbnail") && $_GET["thumbnail"] == "1") {
if (file_exists($real_path . ".thumbnail") && (($_GET["thumbnail"] ?? "") === "1")) {
$real_path .= ".thumbnail";
$uripath .= ".thumbnail";
$mime = "image/jpeg";

View File

@@ -7,8 +7,37 @@ if (!in_array("entreaulas:docente", $_SESSION["auth_data"]["permissions"] ?? [])
die("Access denied");
}
$aulario_id = $_GET["aulario"] ?? "";
$centro_id = $_SESSION["auth_data"]["entreaulas"]["centro"] ?? "";
function safe_id_segment($value)
{
$value = basename((string)$value);
return preg_replace('/[^A-Za-z0-9_-]/', '', $value);
}
function safe_centro_id($value)
{
return preg_replace('/[^0-9]/', '', (string)$value);
}
function safe_alumno_name($value)
{
$value = basename((string)$value);
$value = trim($value);
$value = preg_replace('/[\x00-\x1F\x7F]/u', '', $value);
$value = str_replace(['/', '\\'], '', $value);
return $value;
}
function path_is_within($real_base, $real_path)
{
if ($real_base === false || $real_path === false) {
return false;
}
$base_prefix = rtrim($real_base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
return strpos($real_path, $base_prefix) === 0 || $real_path === rtrim($real_base, DIRECTORY_SEPARATOR);
}
$aulario_id = safe_id_segment($_GET["aulario"] ?? "");
$centro_id = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? "");
if (empty($aulario_id) || empty($centro_id)) {
require_once "_incl/pre-body.php";
@@ -22,9 +51,6 @@ if (empty($aulario_id) || empty($centro_id)) {
exit;
}
$aulario_id = basename($aulario_id);
$centro_id = basename($centro_id);
// Validate paths with realpath
$base_path = "/DATA/entreaulas/Centros";
$real_base = realpath($base_path);
@@ -53,7 +79,11 @@ switch ($_GET["form"] ?? '') {
}
// Sanitize filename
$nombre_safe = basename($nombre);
$nombre_safe = safe_alumno_name($nombre);
if ($nombre_safe === "") {
header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Nombre inválido"));
exit;
}
$alumno_path = "$alumnos_base_path/$nombre_safe";
// Validate path with realpath (after potential creation)
@@ -62,7 +92,7 @@ switch ($_GET["form"] ?? '') {
}
$real_alumnos_base = realpath($alumnos_base_path);
if ($real_alumnos_base === false || strpos($real_alumnos_base, $real_base) !== 0) {
if (!path_is_within($real_base, $real_alumnos_base)) {
header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Error: Ruta inválida"));
exit;
}
@@ -92,7 +122,7 @@ switch ($_GET["form"] ?? '') {
exit;
case 'edit':
$nombre_old = basename($_POST['nombre_old'] ?? '');
$nombre_old = safe_alumno_name($_POST['nombre_old'] ?? '');
$nombre_new = trim($_POST['nombre_new'] ?? '');
if (empty($nombre_old) || empty($nombre_new)) {
@@ -100,13 +130,27 @@ switch ($_GET["form"] ?? '') {
exit;
}
$nombre_new_safe = basename($nombre_new);
$nombre_new_safe = safe_alumno_name($nombre_new);
if ($nombre_new_safe === "") {
header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Nombre inválido"));
exit;
}
$alumno_old_path = "$alumnos_base_path/$nombre_old";
$alumno_new_path = "$alumnos_base_path/$nombre_new_safe";
if (!is_dir($alumnos_base_path)) {
header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Alumno no encontrado"));
exit;
}
$real_alumnos_base = realpath($alumnos_base_path);
if (!path_is_within($real_base, $real_alumnos_base)) {
header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Ruta inválida"));
exit;
}
// Validate paths with realpath
$real_old_path = realpath($alumno_old_path);
if ($real_old_path === false || strpos($real_old_path, $real_base) !== 0) {
if (!path_is_within($real_alumnos_base, $real_old_path)) {
header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Alumno no encontrado"));
exit;
}
@@ -144,7 +188,7 @@ switch ($_GET["form"] ?? '') {
exit;
case 'delete':
$nombre = basename($_POST['nombre'] ?? '');
$nombre = safe_alumno_name($_POST['nombre'] ?? '');
if (empty($nombre)) {
header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Nombre inválido"));
exit;
@@ -152,9 +196,19 @@ switch ($_GET["form"] ?? '') {
$alumno_path = "$alumnos_base_path/$nombre";
if (!is_dir($alumnos_base_path)) {
header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Alumno no encontrado"));
exit;
}
$real_alumnos_base = realpath($alumnos_base_path);
if (!path_is_within($real_base, $real_alumnos_base)) {
header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Ruta inválida"));
exit;
}
// Validate path with realpath
$real_alumno_path = realpath($alumno_path);
if ($real_alumno_path === false || strpos($real_alumno_path, $real_base) !== 0) {
if (!path_is_within($real_alumnos_base, $real_alumno_path)) {
header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Alumno no encontrado"));
exit;
}
@@ -198,7 +252,7 @@ switch ($_GET["action"] ?? '') {
exit;
case 'edit':
$nombre = basename($_GET['alumno'] ?? '');
$nombre = safe_alumno_name($_GET['alumno'] ?? '');
$alumno_path = "$alumnos_base_path/$nombre";
if (empty($nombre) || !file_exists($alumno_path)) {
@@ -246,7 +300,7 @@ switch ($_GET["action"] ?? '') {
exit;
case 'delete':
$nombre = basename($_GET['alumno'] ?? '');
$nombre = safe_alumno_name($_GET['alumno'] ?? '');
$alumno_path = "$alumnos_base_path/$nombre";
if (empty($nombre) || !file_exists($alumno_path)) {

View File

@@ -3,20 +3,56 @@ header("Content-Type: application/json; charset=utf-8");
require_once __DIR__ . "/../_incl/tools.security.php";
require_once __DIR__ . "/../_incl/auth_redir.php";
function safe_id_segment($value) {
$value = basename((string)$value);
return preg_replace('/[^A-Za-z0-9_-]/', '', $value);
}
function safe_centro_id($value) {
return preg_replace('/[^0-9]/', '', (string)$value);
}
function safe_aulario_config_path($centro_id, $aulario_id) {
$centro = safe_centro_id($centro_id);
$aulario = safe_id_segment($aulario_id);
if ($centro === '' || $aulario === '') {
return null;
}
return "/DATA/entreaulas/Centros/$centro/Aularios/$aulario.json";
}
function menu_types_path($centro_id, $aulario_id) {
$centro = safe_centro_id($centro_id);
$aulario = safe_id_segment($aulario_id);
if ($centro === '' || $aulario === '') {
return null;
}
return "/DATA/entreaulas/Centros/$centro/Aularios/$aulario/Comedor-MenuTypes.json";
}
function comedor_day_base_dir($centro_id, $aulario_id, $ym, $day) {
$centro = safe_centro_id($centro_id);
$aulario = safe_id_segment($aulario_id);
if ($centro === '' || $aulario === '') {
return null;
}
return "/DATA/entreaulas/Centros/$centro/Aularios/$aulario/Comedor/$ym/$day";
}
// Check permissions
if (!in_array("entreaulas:docente", $_SESSION["auth_data"]["permissions"] ?? [])) {
http_response_code(403);
die(json_encode(["error" => "Access denied", "code" => "FORBIDDEN"]));
}
$centro_id = $_SESSION["auth_data"]["entreaulas"]["centro"] ?? "";
$centro_id = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? "");
if ($centro_id === "") {
http_response_code(400);
die(json_encode(["error" => "Centro not found in session", "code" => "INVALID_SESSION"]));
}
$action = $_GET["action"] ?? ($_POST["action"] ?? "");
$aulario_id = Sf($_GET["aulario"] ?? $_POST["aulario"] ?? "");
$aulario_id = safe_id_segment(Sf($_GET["aulario"] ?? $_POST["aulario"] ?? ""));
// Validate aulario_id
if ($aulario_id === "") {
@@ -26,22 +62,23 @@ if ($aulario_id === "") {
// Verify that the user has access to this aulario
$userAulas = $_SESSION["auth_data"]["entreaulas"]["aulas"] ?? [];
if (!in_array($aulario_id, $userAulas)) {
$userAulas = array_values(array_filter(array_map('safe_id_segment', $userAulas)));
if (!in_array($aulario_id, $userAulas, true)) {
http_response_code(403);
die(json_encode(["error" => "Access denied to this aulario", "code" => "FORBIDDEN"]));
}
$aulario_path = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json";
$aulario = file_exists($aulario_path) ? json_decode(file_get_contents($aulario_path), true) : null;
$aulario_path = safe_aulario_config_path($centro_id, $aulario_id);
$aulario = ($aulario_path && file_exists($aulario_path)) ? json_decode(file_get_contents($aulario_path), true) : null;
// Handle shared comedor data
$source_aulario_id = $aulario_id;
$is_shared = false;
if ($aulario && !empty($aulario["shared_comedor_from"])) {
$shared_from = $aulario["shared_comedor_from"];
$shared_aulario_path = "/DATA/entreaulas/Centros/$centro_id/Aularios/$shared_from.json";
if (file_exists($shared_aulario_path)) {
$source_aulario_id = Sf($shared_from);
$shared_from = safe_id_segment($aulario["shared_comedor_from"]);
$shared_aulario_path = safe_aulario_config_path($centro_id, $shared_from);
if ($shared_aulario_path && file_exists($shared_aulario_path)) {
$source_aulario_id = $shared_from;
$is_shared = true;
}
}
@@ -51,13 +88,17 @@ $canEdit = in_array("sysadmin:access", $_SESSION["auth_data"]["permissions"] ??
// Helper functions
function get_menu_types($centro_id, $source_aulario_id) {
$menuTypesPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor-MenuTypes.json";
$menuTypesPath = menu_types_path($centro_id, $source_aulario_id);
$defaultMenuTypes = [
["id" => "basal", "label" => "Menú basal", "color" => "#0d6efd"],
["id" => "vegetariano", "label" => "Menú vegetariano", "color" => "#198754"],
["id" => "alergias", "label" => "Menú alergias", "color" => "#dc3545"],
];
if ($menuTypesPath === null) {
return $defaultMenuTypes;
}
if (!file_exists($menuTypesPath)) {
if (!is_dir(dirname($menuTypesPath))) {
mkdir(dirname($menuTypesPath), 0777, true);
@@ -146,7 +187,7 @@ function handle_get_menu() {
global $centro_id, $source_aulario_id;
$date = $_GET["date"] ?? date("Y-m-d");
$menuTypeId = $_GET["menu"] ?? "";
$menuTypeId = safe_id_segment($_GET["menu"] ?? "");
// Validate date
$dateObj = DateTime::createFromFormat("Y-m-d", $date);
@@ -172,7 +213,11 @@ function handle_get_menu() {
// Get menu data
$ym = $dateObj->format("Y-m");
$day = $dateObj->format("d");
$baseDir = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor/$ym/$day";
$baseDir = comedor_day_base_dir($centro_id, $source_aulario_id, $ym, $day);
if ($baseDir === null) {
http_response_code(400);
die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"]));
}
$dataPath = "$baseDir/_datos.json";
$menuData = [
@@ -212,7 +257,7 @@ function handle_save_menu() {
}
$date = $input["date"] ?? date("Y-m-d");
$menuTypeId = $input["menu_type"] ?? "";
$menuTypeId = safe_id_segment($input["menu_type"] ?? "");
$plates = $input["plates"] ?? [];
// Validate date
@@ -240,7 +285,11 @@ function handle_save_menu() {
// Get existing menu data
$ym = $dateObj->format("Y-m");
$day = $dateObj->format("d");
$baseDir = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor/$ym/$day";
$baseDir = comedor_day_base_dir($centro_id, $source_aulario_id, $ym, $day);
if ($baseDir === null) {
http_response_code(400);
die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"]));
}
$dataPath = "$baseDir/_datos.json";
$menuData = [
@@ -292,7 +341,7 @@ function handle_add_menu_type() {
$input = $_POST;
}
$newId = strtolower(trim($input["id"] ?? ""));
$newId = safe_id_segment(strtolower(trim($input["id"] ?? "")));
$newLabel = trim($input["label"] ?? "");
$newColor = trim($input["color"] ?? "#0d6efd");
@@ -301,7 +350,11 @@ function handle_add_menu_type() {
die(json_encode(["error" => "id and label are required", "code" => "MISSING_PARAM"]));
}
$menuTypesPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor-MenuTypes.json";
$menuTypesPath = menu_types_path($centro_id, $source_aulario_id);
if ($menuTypesPath === null) {
http_response_code(400);
die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"]));
}
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
// Check if already exists
@@ -330,14 +383,18 @@ function handle_delete_menu_type() {
$input = $_POST;
}
$deleteId = trim($input["id"] ?? "");
$deleteId = safe_id_segment(trim($input["id"] ?? ""));
if ($deleteId === "") {
http_response_code(400);
die(json_encode(["error" => "id is required", "code" => "MISSING_PARAM"]));
}
$menuTypesPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor-MenuTypes.json";
$menuTypesPath = menu_types_path($centro_id, $source_aulario_id);
if ($menuTypesPath === null) {
http_response_code(400);
die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"]));
}
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
$deleted = false;
@@ -371,7 +428,7 @@ function handle_rename_menu_type() {
$input = $_POST;
}
$renameId = trim($input["id"] ?? "");
$renameId = safe_id_segment(trim($input["id"] ?? ""));
$newLabel = trim($input["label"] ?? "");
$newColor = trim($input["color"] ?? "");
@@ -380,7 +437,11 @@ function handle_rename_menu_type() {
die(json_encode(["error" => "id and label are required", "code" => "MISSING_PARAM"]));
}
$menuTypesPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor-MenuTypes.json";
$menuTypesPath = menu_types_path($centro_id, $source_aulario_id);
if ($menuTypesPath === null) {
http_response_code(400);
die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"]));
}
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
$found = false;

View File

@@ -3,9 +3,39 @@ require_once "_incl/auth_redir.php";
require_once "_incl/pre-body.php";
require_once "_incl/tools.security.php";
$aulario_id = Sf($_GET["id"]);
$centro_id = $_SESSION["auth_data"]["entreaulas"]["centro"];
$aulario = json_decode(file_get_contents("/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json"), true);
function safe_id_segment($value)
{
$value = basename((string)$value);
return preg_replace('/[^A-Za-z0-9_-]/', '', $value);
}
function safe_centro_id($value)
{
return preg_replace('/[^0-9]/', '', (string)$value);
}
function safe_aulario_config_path($centro_id, $aulario_id)
{
$centro = safe_centro_id($centro_id);
$aulario = safe_id_segment($aulario_id);
if ($centro === '' || $aulario === '') {
return null;
}
return "/DATA/entreaulas/Centros/$centro/Aularios/$aulario.json";
}
$aulario_id = safe_id_segment(Sf($_GET["id"] ?? ""));
$centro_id = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? "");
$aulario_path = safe_aulario_config_path($centro_id, $aulario_id);
$aulario = ($aulario_path && file_exists($aulario_path)) ? json_decode(file_get_contents($aulario_path), true) : null;
if (!$aulario || !is_array($aulario)) {
?>
<div class="card pad">
<h1 class="card-title">Aulario no encontrado</h1>
<p>No se ha podido cargar la configuración del aulario.</p>
</div>
<?php require_once "_incl/post-body.php"; exit; }
?>
<div class="card pad">
<div>

View File

@@ -5,8 +5,29 @@ if (in_array("entreaulas:docente", $_SESSION["auth_data"]["permissions"] ?? [])
die("Access denied");
}
$aulario_id = Sf($_GET["aulario"] ?? "");
$centro_id = $_SESSION["auth_data"]["entreaulas"]["centro"] ?? "";
function safe_id_segment($value)
{
$value = basename((string)$value);
return preg_replace('/[^A-Za-z0-9_-]/', '', $value);
}
function safe_centro_id($value)
{
return preg_replace('/[^0-9]/', '', (string)$value);
}
function safe_aulario_config_path($centro_id, $aulario_id)
{
$centro = safe_centro_id($centro_id);
$aulario = safe_id_segment($aulario_id);
if ($centro === "" || $aulario === "") {
return null;
}
return "/DATA/entreaulas/Centros/$centro/Aularios/$aulario.json";
}
$aulario_id = safe_id_segment(Sf($_GET["aulario"] ?? ""));
$centro_id = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? "");
if ($aulario_id === "" || $centro_id === "") {
require_once "_incl/pre-body.php";
@@ -20,18 +41,18 @@ if ($aulario_id === "" || $centro_id === "") {
exit;
}
$aulario_path = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json";
$aulario = file_exists($aulario_path) ? json_decode(file_get_contents($aulario_path), true) : null;
$aulario_path = safe_aulario_config_path($centro_id, $aulario_id);
$aulario = ($aulario_path && file_exists($aulario_path)) ? json_decode(file_get_contents($aulario_path), true) : null;
// Check if this aulario shares comedor data from another aulario
$source_aulario_id = $aulario_id; // Default to current aulario
$is_shared = false;
if ($aulario && !empty($aulario["shared_comedor_from"])) {
$shared_from = $aulario["shared_comedor_from"];
$shared_aulario_path = "/DATA/entreaulas/Centros/$centro_id/Aularios/$shared_from.json";
if (file_exists($shared_aulario_path)) {
$source_aulario_id = Sf($shared_from);
$source_aulario_name = file_exists($shared_aulario_path) ? json_decode(file_get_contents($shared_aulario_path), true)["name"] ?? $shared_from : $shared_from;
$shared_from = safe_id_segment($aulario["shared_comedor_from"]);
$shared_aulario_path = safe_aulario_config_path($centro_id, $shared_from);
if ($shared_aulario_path && file_exists($shared_aulario_path)) {
$source_aulario_id = $shared_from;
$source_aulario_name = json_decode(file_get_contents($shared_aulario_path), true)["name"] ?? $shared_from;
$is_shared = true;
}
}
@@ -153,7 +174,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST" && $canEdit) {
$action = $_POST["action"] ?? "";
if ($action === "add_type") {
$newId = strtolower(trim($_POST["new_type_id"] ?? ""));
$newId = safe_id_segment(strtolower(trim($_POST["new_type_id"] ?? "")));
$newLabel = trim($_POST["new_type_label"] ?? "");
$newColor = trim($_POST["new_type_color"] ?? "#0d6efd");
if ($newId !== "" && $newLabel !== "") {
@@ -174,7 +195,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST" && $canEdit) {
}
if ($action === "delete_type") {
$deleteId = trim($_POST["delete_type_id"] ?? "");
$deleteId = safe_id_segment(trim($_POST["delete_type_id"] ?? ""));
if ($deleteId !== "") {
$deleted = false;
$newMenuTypes = [];
@@ -197,7 +218,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST" && $canEdit) {
}
if ($action === "rename_type") {
$renameId = trim($_POST["rename_type_id"] ?? "");
$renameId = safe_id_segment(trim($_POST["rename_type_id"] ?? ""));
$newLabel = trim($_POST["rename_type_label"] ?? "");
$newColor = trim($_POST["rename_type_color"] ?? "");
if ($renameId !== "" && $newLabel !== "") {
@@ -219,7 +240,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST" && $canEdit) {
}
if ($action === "save") {
$menuTypeId = $_POST["menu_type"] ?? $menuTypeId;
$menuTypeId = safe_id_segment($_POST["menu_type"] ?? $menuTypeId);
if (!isset($menuData["menus"][$menuTypeId])) {
$menuData["menus"][$menuTypeId] = blank_menu();
}
@@ -263,11 +284,15 @@ $nextDate = (clone $dateObj)->modify("+1 day")->format("Y-m-d");
$userAulas = $_SESSION["auth_data"]["entreaulas"]["aulas"] ?? [];
$aulaOptions = [];
foreach ($userAulas as $aulaId) {
$aulaPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulaId.json";
$aulaIdSafe = safe_id_segment($aulaId);
if ($aulaIdSafe === "") {
continue;
}
$aulaPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulaIdSafe.json";
$aulaData = file_exists($aulaPath) ? json_decode(file_get_contents($aulaPath), true) : null;
$aulaOptions[] = [
"id" => $aulaId,
"name" => $aulaData["name"] ?? $aulaId
"id" => $aulaIdSafe,
"name" => $aulaData["name"] ?? $aulaIdSafe
];
}
require_once "_incl/pre-body.php";

View File

@@ -8,9 +8,29 @@ if (!in_array("entreaulas:docente", $_SESSION["auth_data"]["permissions"] ?? [])
die("Acceso denegado");
}
$aulario_id = Sf($_GET["aulario"] ?? "");
$centro_id = Sf($_SESSION["auth_data"]["entreaulas"]["centro"] ?? "");
$alumno = Sf($_GET["alumno"] ?? "");
function safe_id_segment($value)
{
$value = basename((string)$value);
return preg_replace('/[^A-Za-z0-9_-]/', '', $value);
}
function safe_centro_id($value)
{
return preg_replace('/[^0-9]/', '', (string)$value);
}
function path_is_within($real_base, $real_path)
{
if ($real_base === false || $real_path === false) {
return false;
}
$base_prefix = rtrim($real_base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
return strpos($real_path, $base_prefix) === 0 || $real_path === rtrim($real_base, DIRECTORY_SEPARATOR);
}
$aulario_id = safe_id_segment(Sf($_GET["aulario"] ?? ""));
$centro_id = safe_centro_id(Sf($_SESSION["auth_data"]["entreaulas"]["centro"] ?? ""));
$alumno = safe_id_segment(Sf($_GET["alumno"] ?? ""));
if (empty($aulario_id) || empty($centro_id)) {
require_once "_incl/pre-body.php";
@@ -24,9 +44,6 @@ if (empty($aulario_id) || empty($centro_id)) {
exit;
}
$aulario_id = basename($aulario_id);
$centro_id = basename($centro_id);
// Validate paths with realpath
$base_path = "/DATA/entreaulas/Centros";
$real_base = realpath($base_path);
@@ -50,11 +67,7 @@ $alumnos = [];
// Resolve and validate alumnos path to ensure it stays within the allowed base directory
$alumnos_real_path = realpath($alumnos_base_path);
if ($alumnos_real_path !== false) {
// Ensure the resolved path is within the expected base path
$real_base_with_sep = rtrim($real_base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$alumnos_real_with_sep = rtrim($alumnos_real_path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
if (strpos($alumnos_real_with_sep, $real_base_with_sep) === 0 && is_dir($alumnos_real_path)) {
if (path_is_within($real_base, $alumnos_real_path) && is_dir($alumnos_real_path)) {
$alumnos = glob($alumnos_real_path . "/*", GLOB_ONLYDIR);
usort($alumnos, function($a, $b) {
return strcasecmp(basename($a), basename($b));
@@ -123,11 +136,24 @@ if (empty($alumno)) {
}
// If alumno is specified, validate and show their diary
$alumno = basename($alumno);
$alumno_path = "$alumnos_base_path/$alumno";
$real_alumnos_base = realpath($alumnos_base_path);
if (!path_is_within($real_base, $real_alumnos_base)) {
require_once "_incl/pre-body.php";
?>
<div class="card pad">
<h1>Diario del Alumno</h1>
<p>Ruta de alumnos inválida.</p>
</div>
<?php
require_once "_incl/post-body.php";
exit;
}
// Validate path with realpath
if (!is_dir($alumno_path)) {
$real_alumno_path = realpath($alumno_path);
if ($real_alumno_path === false || !path_is_within($real_alumnos_base, $real_alumno_path) || !is_dir($real_alumno_path)) {
require_once "_incl/pre-body.php";
?>
<div class="card pad">
@@ -138,6 +164,7 @@ if (!is_dir($alumno_path)) {
require_once "_incl/post-body.php";
exit;
}
$alumno_path = $real_alumno_path;
// Get diario types and data
$diario_types = [
@@ -223,11 +250,22 @@ require_once "_incl/pre-body.php";
<?php
// Show specific diary entry if requested
$type = Sf($_GET["type"] ?? "");
$type = safe_id_segment(Sf($_GET["type"] ?? ""));
$date = Sf($_GET["date"] ?? date("Y-m-d"));
if (!empty($type) && !empty($date)) {
$date = preg_replace('/[^0-9-]/', '', $date); // Sanitize date
$date = preg_replace('/[^0-9-]/', '', $date);
$is_valid_date = false;
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
$date_obj = DateTime::createFromFormat('Y-m-d', $date);
$is_valid_date = $date_obj && $date_obj->format('Y-m-d') === $date;
}
if (!$is_valid_date || !array_key_exists($type, $diario_types)) {
$type = "";
}
}
if (!empty($type) && !empty($date)) {
$type_file = "$alumno_path/Diario/$date/$type.json";
if (file_exists($type_file)):

View File

@@ -1,6 +1,28 @@
<?php
require_once "_incl/auth_redir.php";
require_once "_incl/pre-body.php";?>
require_once "_incl/pre-body.php";
function safe_id_segment($value)
{
$value = basename((string)$value);
return preg_replace('/[^A-Za-z0-9_-]/', '', $value);
}
function safe_centro_id($value)
{
return preg_replace('/[^0-9]/', '', (string)$value);
}
function safe_aulario_config_path($centro_id, $aulario_id)
{
$centro = safe_centro_id($centro_id);
$aulario = safe_id_segment($aulario_id);
if ($centro === '' || $aulario === '') {
return null;
}
return "/DATA/entreaulas/Centros/$centro/Aularios/$aulario.json";
}
?>
<div class="card pad">
<div>
<h1 class="card-title">¡Hola, <?php echo $_SESSION["auth_data"]["display_name"];?>!</h1>
@@ -11,13 +33,26 @@ require_once "_incl/pre-body.php";?>
</div>
<div id="grid">
<?php $user_data = $_SESSION["auth_data"];
$centro_id = $user_data["entreaulas"]["centro"];
$centro_id = safe_centro_id($user_data["entreaulas"]["centro"] ?? "");
foreach ($user_data["entreaulas"]["aulas"] as $aulario_id) {
$aulario = json_decode(file_get_contents("/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json"), true);
echo '<a href="/entreaulas/aulario.php?id=' . $aulario_id . '" class="btn btn-primary grid-item">
<img style="height: 125px;" src="' . $aulario["icon"] . '" alt="' . htmlspecialchars($aulario["name"]) . ' Icono">
$aulario_id = safe_id_segment($aulario_id);
if ($aulario_id === "") {
continue;
}
$aulario_path = safe_aulario_config_path($centro_id, $aulario_id);
if (!$aulario_path || !file_exists($aulario_path)) {
continue;
}
$aulario = json_decode(file_get_contents($aulario_path), true);
if (!is_array($aulario)) {
continue;
}
$aulario_name = $aulario["name"] ?? $aulario_id;
$aulario_icon = $aulario["icon"] ?? "/static/arasaac/aulario.png";
echo '<a href="/entreaulas/aulario.php?id=' . urlencode($aulario_id) . '" class="btn btn-primary grid-item">
<img style="height: 125px;" src="' . htmlspecialchars($aulario_icon, ENT_QUOTES) . '" alt="' . htmlspecialchars($aulario_name) . ' Icono">
<br>
' . htmlspecialchars($aulario["name"]) . '
' . htmlspecialchars($aulario_name) . '
</a>';
} ?>
</div>

View File

@@ -2,6 +2,25 @@
require_once "_incl/auth_redir.php";
require_once "_incl/tools.security.php";
ini_set("display_errors", "0");
function safe_id_segment($value) {
$value = basename((string)$value);
return preg_replace('/[^A-Za-z0-9_-]/', '', $value);
}
function safe_centro_id($value) {
return preg_replace('/[^0-9]/', '', (string)$value);
}
function safe_aulario_config_path($centro_id, $aulario_id) {
$centro = safe_centro_id($centro_id);
$aulario = safe_id_segment($aulario_id);
if ($centro === '' || $aulario === '') {
return null;
}
return "/DATA/entreaulas/Centros/$centro/Aularios/$aulario.json";
}
// Funciones auxiliares para el diario
function getDiarioPath($alumno, $centro_id, $aulario_id) {
// Validate path components to avoid directory traversal or illegal characters
@@ -85,11 +104,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_GET['api'])) {
}
}
switch ($_GET["form"]) {
$form_action = $_GET["form"] ?? "";
switch ($form_action) {
case "alumno_selected":
$alumno = $_GET["alumno"] ?? "";
$centro_id = $_SESSION["auth_data"]["entreaulas"]["centro"] ?? "";
$aulario_id = $_GET["aulario"] ?? '';
$alumno = safe_id_segment($_GET["alumno"] ?? "");
$centro_id = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? "");
$aulario_id = safe_id_segment($_GET["aulario"] ?? '');
$photo_url = $_GET["photo"] ?? '';
if ($alumno !== "" && $centro_id !== "" && $aulario_id !== "") {
$_SESSION["entreaulas_selected_alumno"] = $alumno;
@@ -207,8 +227,8 @@ ini_set("display_errors", "0");
<?php
// Verificar si hay un alumno seleccionado y cargar su progreso
$alumno_actual = $_SESSION["entreaulas_selected_alumno"] ?? '';
$centro_id = $_SESSION["auth_data"]["entreaulas"]["centro"] ?? '';
$aulario_id = $_GET["aulario"] ?? '';
$centro_id = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? '');
$aulario_id = safe_id_segment($_GET["aulario"] ?? '');
$diario_data = null;
$progress = [];
@@ -233,7 +253,8 @@ $paneles_completados = count(array_filter($progress));
$porcentaje = ($paneles_completados / $paneles_totales) * 100;
$todos_completados = ($paneles_completados === $paneles_totales);
switch ($_GET["action"]) {
$view_action = $_GET["action"] ?? "index";
switch ($view_action) {
default:
case "index":
if ($alumno_actual):
@@ -599,8 +620,8 @@ switch ($_GET["action"]) {
case "quien_soy":
// ¿Quién soy? - Identificación del alumno
$aulario_id = basename($_GET["aulario"] ?? '');
$centro_id = basename($_SESSION["auth_data"]["entreaulas"]["centro"] ?? '');
$aulario_id = safe_id_segment($_GET["aulario"] ?? '');
$centro_id = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? '');
// Validate parameters
if (empty($aulario_id) || empty($centro_id)) {
@@ -701,7 +722,14 @@ switch ($_GET["action"]) {
<?php
break;
case "actividades":
$actividades = glob("/DATA/entreaulas/Centros/" . $_SESSION["auth_data"]["entreaulas"]["centro"] . "/Panel/Actividades/*", GLOB_ONLYDIR);
$centro_actividades = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? '');
$actividades = [];
if ($centro_actividades !== '') {
$actividades_path = "/DATA/entreaulas/Centros/$centro_actividades/Panel/Actividades";
if (is_dir($actividades_path)) {
$actividades = glob($actividades_path . "/*", GLOB_ONLYDIR) ?: [];
}
}
?>
<script>
function seleccionarActividad(element, actividad, pictogramUrl) {
@@ -735,7 +763,7 @@ switch ($_GET["action"]) {
<div class="grid">
<?php foreach ($actividades as $actividad_path) {
$actividad_name = basename($actividad_path);
$pictogram_url = '/entreaulas/_filefetch.php?type=panel_actividades&activity=' . urlencode($actividad_name) . '&centro=' . urlencode($_SESSION["auth_data"]["entreaulas"]["centro"]);
$pictogram_url = '/entreaulas/_filefetch.php?type=panel_actividades&activity=' . urlencode($actividad_name) . '&centro=' . urlencode($centro_actividades);
?>
<a class="card grid-item" style="color: black;" onclick="seleccionarActividad(this, '<?php echo htmlspecialchars($actividad_name); ?>', '<?php echo htmlspecialchars($pictogram_url); ?>');">
<img src="<?php echo htmlspecialchars($pictogram_url); ?>" height="125" class="bg-white">
@@ -768,18 +796,18 @@ switch ($_GET["action"]) {
break;
case "menu":
// Menú del comedor (nuevo sistema, vista simplificada)
$aulario_id = Sf($_GET["aulario"] ?? '');
$centro_id = $_SESSION["auth_data"]["entreaulas"]["centro"] ?? "";
$aulario_id = safe_id_segment(Sf($_GET["aulario"] ?? ''));
$centro_id = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? "");
$source_aulario_id = $aulario_id;
$is_shared = false;
if ($aulario_id !== "" && $centro_id !== "") {
$aulario_path = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json";
$aulario = file_exists($aulario_path) ? json_decode(file_get_contents($aulario_path), true) : null;
$aulario_path = safe_aulario_config_path($centro_id, $aulario_id);
$aulario = ($aulario_path && file_exists($aulario_path)) ? json_decode(file_get_contents($aulario_path), true) : null;
if ($aulario && !empty($aulario["shared_comedor_from"])) {
$shared_from = Sf($aulario["shared_comedor_from"]);
$shared_aulario_path = "/DATA/entreaulas/Centros/$centro_id/Aularios/$shared_from.json";
if (file_exists($shared_aulario_path)) {
$shared_from = safe_id_segment(Sf($aulario["shared_comedor_from"]));
$shared_aulario_path = safe_aulario_config_path($centro_id, $shared_from);
if ($shared_aulario_path && file_exists($shared_aulario_path)) {
$source_aulario_id = $shared_from;
$is_shared = true;
}
@@ -790,13 +818,13 @@ switch ($_GET["action"]) {
$dateObj = DateTime::createFromFormat("Y-m-d", $dateParam) ?: new DateTime();
$date = $dateObj->format("Y-m-d");
$menuTypesPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor-MenuTypes.json";
$menuTypesPath = ($centro_id !== '' && $source_aulario_id !== '') ? "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor-MenuTypes.json" : "";
$defaultMenuTypes = [
["id" => "basal", "label" => "Menú basal", "color" => "#0d6efd"],
["id" => "vegetariano", "label" => "Menú vegetariano", "color" => "#198754"],
["id" => "alergias", "label" => "Menú alergias", "color" => "#dc3545"],
];
$menuTypes = json_decode(@file_get_contents($menuTypesPath), true);
$menuTypes = ($menuTypesPath !== '' && file_exists($menuTypesPath)) ? json_decode(@file_get_contents($menuTypesPath), true) : null;
if (!is_array($menuTypes) || count($menuTypes) === 0) {
$menuTypes = $defaultMenuTypes;
}
@@ -814,13 +842,13 @@ switch ($_GET["action"]) {
$ym = $dateObj->format("Y-m");
$day = $dateObj->format("d");
$dataPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor/$ym/$day/_datos.json";
$dataPath = ($centro_id !== '' && $source_aulario_id !== '') ? "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor/$ym/$day/_datos.json" : "";
$menuData = [
"date" => $date,
"menus" => []
];
if (file_exists($dataPath)) {
if ($dataPath !== '' && file_exists($dataPath)) {
$existing = json_decode(file_get_contents($dataPath), true);
if (is_array($existing)) {
$menuData = array_merge($menuData, $existing);

View File

@@ -57,6 +57,32 @@ function safe_filename($name)
return $name;
}
function safe_path_segment($value)
{
$value = basename((string)$value);
$value = preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
return $value;
}
function safe_join_file($base_dir, $filename)
{
$safe_name = safe_filename($filename);
if ($safe_name === '' || $safe_name === '.' || $safe_name === '..') {
return null;
}
return rtrim($base_dir, '/') . '/' . $safe_name;
}
function safe_aulario_config_path($centro_id, $aulario_id)
{
$safe_centro = safe_path_segment($centro_id);
$safe_aulario = safe_path_segment($aulario_id);
if ($safe_centro === '' || $safe_aulario === '') {
return null;
}
return "/DATA/entreaulas/Centros/$safe_centro/Aularios/$safe_aulario.json";
}
function sanitize_html($html)
{
$html = trim($html ?? "");
@@ -244,7 +270,12 @@ function save_project($proyectos_dir, $project_id, $data)
{
$project_dir = find_project_path($proyectos_dir, $project_id);
if (!$project_dir && isset($data["_project_dir"])) {
$project_dir = $data["_project_dir"];
$candidate_dir = (string)$data["_project_dir"];
$proyectos_base_real = realpath($proyectos_dir);
$candidate_real = realpath($candidate_dir);
if ($proyectos_base_real !== false && $candidate_real !== false && (strpos($candidate_real, $proyectos_base_real . DIRECTORY_SEPARATOR) === 0 || $candidate_real === $proyectos_base_real)) {
$project_dir = $candidate_dir;
}
}
if (!$project_dir) {
return false;
@@ -431,7 +462,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
if ($action === "share_project") {
$project_id = Sf($_POST["project_id"] ?? "");
$target_aulario = Sf($_POST["target_aulario"] ?? "");
$target_aulario = safe_path_segment(Sf($_POST["target_aulario"] ?? ""));
if ($project_id !== "" && $target_aulario !== "" && $target_aulario !== $aulario_id) {
// Only allow sharing local projects
@@ -439,8 +470,8 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
if (!$is_local_project) {
$error = "No se puede compartir un proyecto ajeno.";
} else {
$target_config_path = "/DATA/entreaulas/Centros/$centro_id/Aularios/$target_aulario.json";
if (!file_exists($target_config_path)) {
$target_config_path = safe_aulario_config_path($centro_id, $target_aulario);
if ($target_config_path === null || !file_exists($target_config_path)) {
$error = "Aulario de destino no encontrado.";
} else {
$target_config = json_decode(file_get_contents($target_config_path), true);
@@ -746,7 +777,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
}
if ($action === "approve_change" || $action === "reject_change") {
$change_id = Sf($_POST["change_id"] ?? "");
$change_id = safe_filename(Sf($_POST["change_id"] ?? ""));
$project_id = Sf($_POST["project_id"] ?? "");
if (!empty($change_id) && !empty($project_id)) {
@@ -758,9 +789,9 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
if (!empty($change_id) && !empty($project_id) && empty($error)) {
$pending_dir = "$project_dir/pending_changes";
$change_file = "$pending_dir/$change_id.json";
$change_file = safe_join_file($pending_dir, $change_id . ".json");
if (file_exists($change_file)) {
if ($change_file && file_exists($change_file)) {
$change_data = json_decode(file_get_contents($change_file), true);
if ($action === "approve_change") {
@@ -775,8 +806,8 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
$new_items[] = $item;
} else {
if (in_array($item["type"], ["file", "pdf_secure"], true) && isset($item["filename"])) {
$file_path = "$project_dir/" . $item["filename"];
if (file_exists($file_path)) {
$file_path = safe_join_file($project_dir, $item["filename"]);
if ($file_path && file_exists($file_path)) {
unlink($file_path);
if (file_exists($file_path . ".eadat")) {
unlink($file_path . ".eadat");
@@ -810,24 +841,25 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
}
if (in_array(($item["type"] ?? ""), ["file", "pdf_secure"], true) && !empty($change_data["pending_filename"])) {
$pending_file = "$pending_dir/" . Sf($change_data["pending_filename"]);
$target_file = "$project_dir/" . Sf($change_data["pending_filename"]);
if (file_exists($pending_file)) {
$pending_filename = safe_filename(Sf($change_data["pending_filename"]));
$pending_file = safe_join_file($pending_dir, $pending_filename);
$target_file = safe_join_file($project_dir, $pending_filename);
if ($pending_file && $target_file && file_exists($pending_file)) {
if (!is_dir($project_dir)) {
mkdir($project_dir, 0755, true);
}
rename($pending_file, $target_file);
if (!empty($item["filename"])) {
$old_path = "$project_dir/" . Sf($item["filename"]);
if (file_exists($old_path)) {
$old_path = safe_join_file($project_dir, Sf($item["filename"]));
if ($old_path && file_exists($old_path)) {
unlink($old_path);
if (file_exists($old_path . ".eadat")) {
unlink($old_path . ".eadat");
}
}
}
$item["filename"] = Sf($change_data["pending_filename"]);
$item["original_name"] = Sf($change_data["original_filename"] ?? $change_data["pending_filename"]);
$item["filename"] = $pending_filename;
$item["original_name"] = Sf($change_data["original_filename"] ?? $pending_filename);
$file_meta = [
"id" => $item_id,
@@ -841,8 +873,8 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
}
if (in_array(($item["type"] ?? ""), ["file", "pdf_secure"], true) && !empty($item["filename"])) {
$file_path = "$project_dir/" . $item["filename"];
if (file_exists($file_path . ".eadat")) {
$file_path = safe_join_file($project_dir, $item["filename"]);
if ($file_path && file_exists($file_path . ".eadat")) {
$file_meta = json_decode(file_get_contents($file_path . ".eadat"), true) ?: [];
$file_meta["name"] = $item["name"];
save_file_metadata($file_path, $file_meta);
@@ -875,16 +907,17 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
$item["room"] = $change_data["item_room"] ?? "";
} elseif (in_array($change_data["item_type"], ["file", "pdf_secure"], true) && !empty($change_data["pending_filename"])) {
// Move file from pending to project directory
$pending_file = "$pending_dir/" . Sf($change_data["pending_filename"]);
$target_file = "$project_dir/" . Sf($change_data["pending_filename"]);
$pending_filename = safe_filename(Sf($change_data["pending_filename"]));
$pending_file = safe_join_file($pending_dir, $pending_filename);
$target_file = safe_join_file($project_dir, $pending_filename);
if (file_exists($pending_file)) {
if ($pending_file && $target_file && file_exists($pending_file)) {
if (!is_dir($project_dir)) {
mkdir($project_dir, 0755, true);
}
rename($pending_file, $target_file);
$item["filename"] = Sf($change_data["pending_filename"]);
$item["original_name"] = Sf($change_data["original_filename"] ?? $change_data["pending_filename"]);
$item["filename"] = $pending_filename;
$item["original_name"] = Sf($change_data["original_filename"] ?? $pending_filename);
$file_meta = [
"id" => $item_id,
@@ -910,8 +943,8 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
} else {
// Reject - just delete pending file if exists
if (!empty($change_data["pending_filename"])) {
$pending_file = "$pending_dir/" . Sf($change_data["pending_filename"]);
if (file_exists($pending_file)) {
$pending_file = safe_join_file($pending_dir, Sf($change_data["pending_filename"]));
if ($pending_file && file_exists($pending_file)) {
unlink($pending_file);
}
}
@@ -919,10 +952,12 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
}
// Delete the change request file
if ($change_file && file_exists($change_file)) {
unlink($change_file);
}
}
}
}
if ($action === "delete_item") {
$project_id = Sf($_POST["project_id"] ?? "");
@@ -1009,7 +1044,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
} else {
// Delete file if it's a file type
if (in_array($item["type"], ["file", "pdf_secure"], true) && isset($item["filename"])) {
$file_path = $project_dir ? "$project_dir/" . $item["filename"] : null;
$file_path = $project_dir ? safe_join_file($project_dir, $item["filename"]) : null;
if ($file_path && file_exists($file_path)) {
unlink($file_path);
if (file_exists($file_path . ".eadat")) {
@@ -1217,8 +1252,8 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
}
if (isset($item["filename"])) {
$old_path = "$project_dir/" . $item["filename"];
if (file_exists($old_path)) {
$old_path = safe_join_file($project_dir, $item["filename"]);
if ($old_path && file_exists($old_path)) {
unlink($old_path);
if (file_exists($old_path . ".eadat")) {
unlink($old_path . ".eadat");
@@ -1239,8 +1274,8 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
save_file_metadata($target_path, $file_meta);
}
if (in_array($item["type"], ["file", "pdf_secure"], true) && $project_dir && isset($item["filename"])) {
$file_path = "$project_dir/" . $item["filename"];
if (file_exists($file_path . ".eadat")) {
$file_path = safe_join_file($project_dir, $item["filename"]);
if ($file_path && file_exists($file_path . ".eadat")) {
$file_meta = json_decode(file_get_contents($file_path . ".eadat"), true) ?: [];
$file_meta["name"] = $item_name;
save_file_metadata($file_path, $file_meta);
@@ -1422,7 +1457,7 @@ $view = $current_project ? "project" : "list";
<!-- Project Detail View -->
<?php
// Check if this is a linked project from another aulario
$source_aulario_for_project = $_GET["source"] ?? "";
$source_aulario_for_project = safe_path_segment($_GET["source"] ?? "");
$is_linked_project = false;
$linked_permission = "read_only";
@@ -1536,7 +1571,15 @@ $view = $current_project ? "project" : "list";
<title>share-variant</title>
<path d="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19A2.92,2.92 0 0,0 18,16.08Z" />
</svg>
<?= htmlspecialchars(json_decode(file_get_contents("/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_for_project.json"), true)["name"] ?? "") ?>
<?php
$source_aulario_path = safe_aulario_config_path($centro_id, $source_aulario_for_project);
$source_aulario_name = "";
if ($source_aulario_path && file_exists($source_aulario_path)) {
$source_aulario_data = json_decode(file_get_contents($source_aulario_path), true);
$source_aulario_name = $source_aulario_data["name"] ?? "";
}
?>
<?= htmlspecialchars($source_aulario_name) ?>
</span>
<?php endif; ?>
</h1>
@@ -1878,8 +1921,9 @@ $view = $current_project ? "project" : "list";
<?php foreach ($pending_changes as $change):
$requesting_aulario = $change["requested_by_aulario"] ?? "Desconocido";
// Get requesting aulario name
$req_aul_path = "/DATA/entreaulas/Centros/$centro_id/Aularios/$requesting_aulario.json";
$req_aul_data = file_exists($req_aul_path) ? json_decode(file_get_contents($req_aul_path), true) : null;
$requesting_aulario = safe_path_segment($requesting_aulario);
$req_aul_path = safe_aulario_config_path($centro_id, $requesting_aulario);
$req_aul_data = ($req_aul_path && file_exists($req_aul_path)) ? json_decode(file_get_contents($req_aul_path), true) : null;
$req_aul_name = $req_aul_data["name"] ?? $requesting_aulario;
$req_persona_name = $change["requested_by_persona_name"] ?? "Desconocido";
?>

View File

@@ -1,10 +1,24 @@
<?php
require_once "_incl/auth_redir.php";
require_once "_incl/tools.security.php";
switch ($_GET["form"]) {
function safe_path_segment($value)
{
$value = trim((string)$value);
$value = str_replace(["\0", "/", "\\"], "", $value);
$value = str_replace("..", "", $value);
$value = basename($value);
if ($value === "." || $value === "..") {
return "";
}
return $value;
}
$form_action = $_GET["form"] ?? "";
switch ($form_action) {
case "delete":
$aulario_id = Sf($_POST["aulario_id"] ?? "");
$centro_id = Sf($_POST["centro_id"] ?? "");
$aulario_id = safe_path_segment(Sf($_POST["aulario_id"] ?? ""));
$centro_id = safe_path_segment(Sf($_POST["centro_id"] ?? ""));
$aulario_file = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json";
if (!file_exists($aulario_file)) {
die("Aulario no encontrado.");
@@ -35,7 +49,7 @@ switch ($_GET["form"]) {
break;
case "create":
$user_data = $_SESSION["auth_data"];
$centro_id = Sf($_POST["centro"] ?? "");
$centro_id = safe_path_segment(Sf($_POST["centro"] ?? ""));
if (empty($centro_id) || !is_dir("/DATA/entreaulas/Centros/$centro_id")) {
die("Centro no válido.");
}
@@ -54,8 +68,8 @@ switch ($_GET["form"]) {
exit();
break;
case "save_edit":
$aulario_id = Sf($_POST["aulario_id"] ?? "");
$centro_id = Sf($_POST["centro_id"] ?? "");
$aulario_id = safe_path_segment(Sf($_POST["aulario_id"] ?? ""));
$centro_id = safe_path_segment(Sf($_POST["centro_id"] ?? ""));
$aulario_file = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json";
if (!file_exists($aulario_file)) {
die("Aulario no encontrado.");
@@ -65,7 +79,7 @@ switch ($_GET["form"]) {
$aulario_data["icon"] = Sf($_POST["icon"] ?? "/static/logo-entreaulas.png");
// Handle shared comedor configuration
$share_comedor_from = Sf($_POST["share_comedor_from"] ?? "");
$share_comedor_from = safe_path_segment(Sf($_POST["share_comedor_from"] ?? ""));
if (!empty($share_comedor_from) && $share_comedor_from !== "none") {
$aulario_data["shared_comedor_from"] = $share_comedor_from;
@@ -75,16 +89,21 @@ switch ($_GET["form"]) {
// Handle linked projects configuration
$linked_projects = [];
$linked_aularios = Sf($_POST["linked_aulario"] ?? []);
$linked_project_ids = Sf($_POST["linked_project_id"] ?? []);
$linked_permissions = Sf($_POST["linked_permission"] ?? []);
$linked_aularios = $_POST["linked_aulario"] ?? [];
$linked_project_ids = $_POST["linked_project_id"] ?? [];
$linked_permissions = $_POST["linked_permission"] ?? [];
for ($i = 0; $i < count($linked_aularios); $i++) {
if (!empty($linked_aularios[$i]) && !empty($linked_project_ids[$i])) {
$src_aul = safe_path_segment($linked_aularios[$i] ?? "");
$proj_id = safe_path_segment($linked_project_ids[$i] ?? "");
$perm = in_array(($linked_permissions[$i] ?? "read_only"), ["read_only", "request_edit", "full_edit"], true)
? ($linked_permissions[$i] ?? "read_only")
: "read_only";
if (!empty($src_aul) && !empty($proj_id)) {
$linked_projects[] = [
"source_aulario" => $linked_aularios[$i],
"project_id" => $linked_project_ids[$i],
"permission" => $linked_permissions[$i] ?? "read_only"
"source_aulario" => $src_aul,
"project_id" => $proj_id,
"permission" => $perm
];
}
}
@@ -102,7 +121,8 @@ switch ($_GET["form"]) {
}
require_once "_incl/pre-body.php";
switch ($_GET["action"]) {
$view_action = $_GET["action"] ?? "index";
switch ($view_action) {
case "new":
?>
<div class="card pad">
@@ -141,8 +161,8 @@ switch ($_GET["action"]) {
<?php
break;
case "edit":
$aulario_id = Sf($_GET["aulario"] ?? "");
$centro_id = Sf($_GET["centro"] ?? "");
$aulario_id = safe_path_segment(Sf($_GET["aulario"] ?? ""));
$centro_id = safe_path_segment(Sf($_GET["centro"] ?? ""));
$aulario_file = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json";
if (!file_exists($aulario_file)) {
die("Aulario no encontrado.");
@@ -385,8 +405,12 @@ switch ($_GET["action"]) {
<tbody>
<?php
$user_data = $_SESSION["auth_data"];
$centro_filter = Sf($_GET['centro'] ?? "*");
$aulas_filelist = glob("/DATA/entreaulas/Centros/$centro_filter/Aularios/*.json");
$centro_filter = safe_path_segment(Sf($_GET['centro'] ?? ""));
if ($centro_filter !== "") {
$aulas_filelist = glob("/DATA/entreaulas/Centros/$centro_filter/Aularios/*.json") ?: [];
} else {
$aulas_filelist = glob("/DATA/entreaulas/Centros/*/Aularios/*.json") ?: [];
}
foreach ($aulas_filelist as $aula_file) {
$aula_data = json_decode(file_get_contents($aula_file), true);
$centro_id = basename(dirname(dirname($aula_file)));

View File

@@ -1,9 +1,23 @@
<?php
require_once "_incl/auth_redir.php";
require_once "_incl/tools.security.php";
switch ($_GET["form"]) {
function safe_path_segment($value)
{
$value = trim((string)$value);
$value = str_replace(["\0", "/", "\\"], "", $value);
$value = str_replace("..", "", $value);
$value = basename($value);
if ($value === "." || $value === "..") {
return "";
}
return $value;
}
$form_action = $_GET["form"] ?? "";
switch ($form_action) {
case "create":
$centro_id = Sf($_POST["name"] ?? "");
$centro_id = safe_path_segment(Sf($_POST["name"] ?? ""));
if (empty($centro_id)) {
die("Nombre del centro no proporcionado.");
}
@@ -20,12 +34,12 @@ switch ($_GET["form"]) {
ini_set("display_errors", 1);
ini_set('upload_max_filesize', '256M');
ini_set('post_max_size', '256M');
$centro_id = Sf($_GET['centro'] ?? '');
$centro_id = safe_path_segment(Sf($_GET['centro'] ?? ''));
$centro_path = "/DATA/entreaulas/Centros/$centro_id";
if (!is_dir($centro_path)) {
die("Centro no válido.");
}
$activity_name = Sf($_POST["name"] ?? '');
$activity_name = safe_path_segment(Sf($_POST["name"] ?? ''));
if (empty($activity_name)) {
die("Nombre de la actividad no proporcionado.");
}
@@ -48,8 +62,8 @@ switch ($_GET["form"]) {
ini_set("display_errors", 1);
ini_set('upload_max_filesize', '256M');
ini_set('post_max_size', '256M');
$centro_id = Sf($_GET['centro'] ?? '');
$activity_name = Sf($_GET['activity'] ?? '');
$centro_id = safe_path_segment(Sf($_GET['centro'] ?? ''));
$activity_name = safe_path_segment(Sf($_GET['activity'] ?? ''));
$activity_path = "/DATA/entreaulas/Centros/$centro_id/Panel/Actividades/$activity_name";
if (!is_dir($activity_path)) {
die("Actividad no válida.");
@@ -59,8 +73,8 @@ switch ($_GET["form"]) {
$photo_path = "$activity_path/photo.jpg";
move_uploaded_file($activity_photo["tmp_name"], $photo_path);
}
if (Sf($_POST['nombre'] ?? '') != $activity_name) {
$new_activity_name = Sf($_POST['nombre'] ?? '');
if (safe_path_segment(Sf($_POST['nombre'] ?? '')) != $activity_name) {
$new_activity_name = safe_path_segment(Sf($_POST['nombre'] ?? ''));
$new_activity_path = "/DATA/entreaulas/Centros/$centro_id/Panel/Actividades/$new_activity_name";
if (is_dir($new_activity_path)) {
die("Ya existe una actividad con ese nombre.");
@@ -73,10 +87,11 @@ switch ($_GET["form"]) {
}
require_once "_incl/pre-body.php";
switch ($_GET["action"]) {
$view_action = $_GET["action"] ?? "index";
switch ($view_action) {
case "edit_activity":
$centro_id = Sf($_GET['centro'] ?? '');
$activity_name = Sf($_GET['activity'] ?? '');
$centro_id = safe_path_segment(Sf($_GET['centro'] ?? ''));
$activity_name = safe_path_segment(Sf($_GET['activity'] ?? ''));
$activity_path = "/DATA/entreaulas/Centros/$centro_id/Panel/Actividades/$activity_name";
if (!is_dir($activity_path)) {
die("Actividad no válida.");
@@ -112,7 +127,7 @@ switch ($_GET["action"]) {
<?php
break;
case "new_activity":
$centro_id = Sf($_GET['centro'] ?? '');
$centro_id = safe_path_segment(Sf($_GET['centro'] ?? ''));
$centro_path = "/DATA/entreaulas/Centros/$centro_id";
if (!is_dir($centro_path)) {
die("Centro no válido.");
@@ -160,7 +175,7 @@ switch ($_GET["action"]) {
<?php
break;
case "edit":
$centro_id = Sf($_GET['centro'] ?? '');
$centro_id = safe_path_segment(Sf($_GET['centro'] ?? ''));
$centro_path = "/DATA/entreaulas/Centros/$centro_id";
if (!is_dir($centro_path)) {
die("Centro no válido.");

View File

@@ -2,20 +2,38 @@
require_once "_incl/auth_redir.php";
require_once "_incl/pre-body.php";
require_once "../_incl/tools.photos.php";
switch ($_GET["action"]) {
$action = $_GET["action"] ?? "index";
switch ($action) {
case "generate_thumbs":
ini_set("max_execution_time", 300);
ini_set("memory_limit", "1024M");
// ini_set("display_errors", 1);
echo "<div class='card pad'><h1>Generando Miniaturas...</h1>";
// Iterate over all club photos and generate thumbnails if they don't exist
$club_cal_folders = array_filter(glob("/DATA/club/IMG/*"), 'is_dir');
$club_base = realpath("/DATA/club/IMG");
if ($club_base === false) {
echo "No se encontró el directorio base de imágenes.<br>";
echo "<h2>Proceso completado.</h2></div>";
break;
}
$club_cal_folders = array_filter(glob("/DATA/club/IMG/*") ?: [], 'is_dir');
foreach ($club_cal_folders as $cal_folder) {
$personas = array_filter(glob("$cal_folder/*"), 'is_dir');
$real_cal_folder = realpath($cal_folder);
if ($real_cal_folder === false || strpos($real_cal_folder, rtrim($club_base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) !== 0) {
continue;
}
$personas = array_filter(glob("$real_cal_folder/*") ?: [], 'is_dir');
foreach ($personas as $persona) {
$fotos = preg_grep('/^([^.])/', scandir($persona));
$real_persona = realpath($persona);
if ($real_persona === false || strpos($real_persona, rtrim($club_base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) !== 0) {
continue;
}
$fotos = preg_grep('/^([^.])/', scandir($real_persona));
foreach ($fotos as $foto) {
$foto_path = "$persona/$foto";
$foto_path = "$real_persona/$foto";
if (!is_file($foto_path)) {
continue;
}
$thumbnail_path = "$foto_path.thumbnail";
if (file_exists($thumbnail_path)) {
continue;

View File

@@ -1,9 +1,20 @@
<?php
require_once "_incl/auth_redir.php";
require_once "_incl/tools.security.php";
function safe_username($value)
{
$value = basename((string)$value);
$value = preg_replace('/[^a-zA-Z0-9._-]/', '', $value);
if (strpos($value, '..') !== false) {
return '';
}
return $value;
}
switch ($_GET['form'] ?? '') {
case 'save_password':
$username = Sf($_POST['username'] ?? '');
$username = safe_username(Sf($_POST['username'] ?? ''));
$new_password = $_POST['new_password'] ?? '';
$confirm_password = $_POST['confirm_password'] ?? '';
@@ -40,7 +51,7 @@ switch ($_GET['form'] ?? '') {
require_once "_incl/pre-body.php";
$username = $_GET['user'] ?? '';
$username = safe_username($_GET['user'] ?? '');
if (empty($username)) {
die("Usuario no especificado.");
}

View File

@@ -1,17 +1,34 @@
<?php
require_once "_incl/auth_redir.php";
require_once "_incl/tools.security.php";
function safe_username($value)
{
$value = basename((string)$value);
$value = preg_replace('/[^a-zA-Z0-9._-]/', '', $value);
if (strpos($value, '..') !== false) {
return '';
}
return $value;
}
function safe_centro_id($value)
{
return preg_replace('/[^0-9]/', '', (string)$value);
}
function safe_aulario_id($value)
{
$value = basename((string)$value);
return preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
}
switch ($_GET['form'] ?? '') {
case 'save_edit':
$username = Sf($_POST['username'] ?? '');
$username = safe_username(Sf($_POST['username'] ?? ''));
if (empty($username)) {
die("Nombre de usuario no proporcionado.");
}
// Validate username to prevent directory traversal
$username = basename($username);
if (preg_match('/[^a-zA-Z0-9._-]/', $username) || strpos($username, '..') !== false) {
die("Nombre de usuario inválido.");
}
$user_file = "/DATA/Usuarios/$username.json";
$userdata_old = [];
if (is_readable($user_file)) {
@@ -23,14 +40,25 @@ switch ($_GET['form'] ?? '') {
}
}
}
$permissions = $_POST['permissions'] ?? [];
if (!is_array($permissions)) {
$permissions = [];
}
$aulas = $_POST['aulas'] ?? [];
if (!is_array($aulas)) {
$aulas = [];
}
$aulas = array_values(array_filter(array_map('safe_aulario_id', $aulas)));
$userdata_new = [
'display_name' => $_POST['display_name'] ?? '',
'email' => $_POST['email'] ?? '',
'permissions' => $_POST['permissions'] ?? [],
'permissions' => $permissions,
'entreaulas' => [
'centro' => $_POST['centro'] ?? '',
'centro' => safe_centro_id($_POST['centro'] ?? ''),
'role' => $_POST['role'] ?? '',
'aulas' => $_POST['aulas'] ?? []
'aulas' => $aulas
]
];
// Merge old and new data to preserve any other fields, like password hashes or custom metadata.
@@ -119,10 +147,8 @@ switch ($_GET['action'] ?? '') {
break;
case 'edit':
require_once "_incl/pre-body.php";
$username = Sf($_GET['user'] ?? '');
// Validate username to prevent directory traversal
$username = basename($username);
if (preg_match('/[^a-zA-Z0-9._-]/', $username) || strpos($username, '..') !== false) {
$username = safe_username(Sf($_GET['user'] ?? ''));
if (empty($username)) {
die("Nombre de usuario inválido.");
}
$user_file = "/DATA/Usuarios/$username.json";
@@ -227,10 +253,11 @@ switch ($_GET['action'] ?? '') {
<div class="mb-3">
<label class="form-label">Aulas asignadas: <small>(Guarda primero para actualizar la lista)</small></label><br>
<?php
$aulas_filelist = glob("/DATA/entreaulas/Centros/" . ($userdata["entreaulas"]['centro'] ?? '') . "/Aularios/*.json");
$user_centro = safe_centro_id($userdata["entreaulas"]['centro'] ?? '');
$aulas_filelist = $user_centro !== '' ? (glob("/DATA/entreaulas/Centros/" . $user_centro . "/Aularios/*.json") ?: []) : [];
foreach ($aulas_filelist as $aula_file) {
$aula_data = json_decode(file_get_contents($aula_file), true);
$aula_id = basename($aula_file, ".json");
$aula_id = safe_aulario_id(basename($aula_file, ".json"));
$is_assigned = in_array($aula_id, $userdata["entreaulas"]['aulas'] ?? []);
echo '<div class="form-check form-check-inline">';
echo '<input class="form-check-input" type="checkbox" name="aulas[]" value="' . htmlspecialchars($aula_id) . '" id="aula-' . htmlspecialchars($aula_id) . '" ' . ($is_assigned ? 'checked' : '') . '>';