Harden published curriculum and student flows

This commit is contained in:
Hamza-Ayed
2026-09-30 08:22:17 +03:00
parent e8163fe265
commit ce7b0fcf14
53 changed files with 4723 additions and 3317 deletions
+23 -9
View File
@@ -144,7 +144,6 @@ class CurriculumService
*/
public static function getCurriculumTree(): array
{
self::ensureStorage();
if (!file_exists(self::$manifestFile)) {
return [];
}
@@ -281,13 +280,19 @@ class CurriculumService
*/
public static function saveLessonMarkdown(string $relativePath, string $content): bool
{
if (!self::safeMarkdownPath($relativePath)) return false;
self::ensureStorage();
$fullPath = self::$storagePath . '/' . ltrim($relativePath, '/');
$root = realpath(self::$storagePath);
if ($root === false) return false;
$fullPath = $root . '/' . $relativePath;
$dir = dirname($fullPath);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
if (!mkdir($dir, 0750, true) && !is_dir($dir)) return false;
}
return file_put_contents($fullPath, $content) !== false;
$realDir = realpath($dir);
if ($realDir === false || !str_starts_with($realDir, $root . DIRECTORY_SEPARATOR)) return false;
if (is_link($fullPath)) return false;
return file_put_contents($fullPath, $content, LOCK_EX) !== false;
}
/**
@@ -295,12 +300,21 @@ class CurriculumService
*/
public static function getLessonMarkdown(string $relativePath): string
{
self::ensureStorage();
$fullPath = self::$storagePath . '/' . ltrim($relativePath, '/');
if (file_exists($fullPath)) {
return file_get_contents($fullPath);
$root = realpath(self::$storagePath);
if ($root === false || !self::safeMarkdownPath($relativePath)) return '';
$fullPath = realpath($root . '/' . $relativePath);
if ($fullPath === false || !str_starts_with($fullPath, $root . DIRECTORY_SEPARATOR) || !is_file($fullPath)) return '';
return file_get_contents($fullPath) ?: '';
}
public static function safeMarkdownPath(string $path): bool
{
if ($path === '' || str_starts_with($path, '/') || str_contains($path, '\\')
|| !str_ends_with(strtolower($path), '.md') || preg_match('/[\x00-\x1f]/', $path)) return false;
foreach (explode('/', $path) as $part) {
if ($part === '' || $part === '.' || $part === '..') return false;
}
return "# محتوى المنهاج\nالمحتوى المعتمد للمنهاج الرسمي.";
return true;
}
public static function saveLessonAiAssets(string $relativePath, array $assets): bool