Harden published curriculum and student flows
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user