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:
@@ -4,6 +4,7 @@ const multer = require('multer');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||
const { safeExt } = require('../utils/magic');
|
||||
const ctrl = require('../controllers/avatarController');
|
||||
|
||||
/* ── multer: avatars only, 2 MB ────────────────────────────────────────── */
|
||||
@@ -13,7 +14,9 @@ const AVATAR_TYPES = new Set(['image/png', 'image/jpeg', 'image/webp']);
|
||||
const storage = multer.diskStorage({
|
||||
destination: AVATARS_DIR,
|
||||
filename: (_req, file, cb) => {
|
||||
const ext = path.extname(file.originalname).toLowerCase();
|
||||
// Расширение — из проверенного MIME (fileFilter уже сузил до image/*),
|
||||
// НЕ из client-controlled originalname (иначе .html/.svg → stored-XSS).
|
||||
const ext = safeExt(file.mimetype, '.png');
|
||||
const name = crypto.randomBytes(16).toString('hex') + ext;
|
||||
cb(null, name);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user