fix(security): харднинг загрузки файлов, контроль доступа и XSS
Подхвачено из закрытой параллельной сессии (план project_hardening_2026). Загрузки: magic.js получает safeExt/EXT_FOR_MIME — имя файла на диске берёт расширение из проверенного MIME, а не из client originalname (анти stored-XSS .html/.svg). avatar/flashcard/chat-загрузки дополнительно проверяют magic-байты: содержимое должно соответствовать MIME, иначе файл удаляется и 400. Доступ: fileController.getFolderAccess отдаёт список раздачи только владельцу или админу (была утечка имён/email учеников). testController.getOne гейтит видимость как list() — ученик не прочитает тексты заданий черновиков/вариантов по id. XSS: classes.html escJ() экранирует строку для JS-литерала в inline-onclick (имя ученика с кавычкой больше не ломает обработчик). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,18 @@ function getOne(req, res) {
|
||||
`).get(req.params.id);
|
||||
if (!t) return res.status(404).json({ error: 'Not found' });
|
||||
|
||||
// Доступ как в list(): ученик видит только помеченные доступными и не служебные
|
||||
// экзамен-варианты; учитель — только свои; админ — все. Иначе по id можно было бы
|
||||
// прочитать тексты заданий из черновиков/вариантов.
|
||||
const { role, id: uid } = req.user;
|
||||
const isStudent = role === 'student' || role === 'free_student';
|
||||
if (isStudent) {
|
||||
const isVariant = db.prepare('SELECT 1 FROM exam9_variant_tests WHERE test_id = ?').get(t.id);
|
||||
if (!t.available_to_students || isVariant) return res.status(404).json({ error: 'Not found' });
|
||||
} else if (role !== 'admin' && t.created_by !== uid) {
|
||||
return res.status(404).json({ error: 'Not found' });
|
||||
}
|
||||
|
||||
const questions = db.prepare(`
|
||||
SELECT q.id, q.text, q.type, q.difficulty, q.explanation,
|
||||
tp.name AS topic, s.name AS subject_name,
|
||||
|
||||
Reference in New Issue
Block a user