fix: бесконечный ретрай upload 400, скобки в именах файлов, тост batch-create

- useOfflineSync: tryUploadFile/trySendAttachmentMessage бросали ошибку без
  кода статуса — постоянная 400 не распознавалась regex \b400\b и вложение
  ретраилось бесконечно (спам POST /api/upload каждые ~30 сек).
- validateFilename: разрешены скобки () в именах (типично для фото с
  телефонов; сохраняемое имя всё равно генерируется), в текст 400 добавлено
  само имя файла для диагностики.
- BatchCreateDialog: тост ошибки показывает текст сервера (из BadRequestError
  или JSON в message 5xx) вместо общего «Ошибка создания задач».
This commit is contained in:
2026-08-03 15:25:11 +03:00
parent 1d5321ed0c
commit 9a387c5db0
3 changed files with 30 additions and 7 deletions

View File

@@ -69,7 +69,7 @@ export function getFileExt(originalname: string): string {
// Validates original filename:
// - extension must be in EXT_TO_MIME (whitelist)
// - name part blocks path traversal, shell metacharacters and control characters
// - Unicode letters (including Cyrillic), digits, spaces, dots, underscores and hyphens are allowed
// - Unicode letters (including Cyrillic), digits, spaces, dots, underscores, hyphens and parentheses are allowed
export function validateFilename(originalname: string): { valid: boolean; reason?: string } {
const basename = path.basename(originalname);
const ext = path.extname(basename).slice(1).toLowerCase();
@@ -81,9 +81,10 @@ export function validateFilename(originalname: string): { valid: boolean; reason
return { valid: false, reason: 'Пустое имя файла' };
}
// Block path traversal, shell metacharacters, and control characters
// Allow Unicode letters (including Cyrillic), digits, spaces, dots, underscores, hyphens
if (/[\/\\&|;$()<>\`\"'\x00-\x1f]/.test(namePart) || namePart.includes('..')) {
return { valid: false, reason: 'Имя файла содержит недопустимые символы' };
// Allow Unicode letters (including Cyrillic), digits, spaces, dots, underscores, hyphens, parentheses
// (скобки безопасны: сохраняемое имя файла генерируется, оригинальное используется только для отображения)
if (/[\/\\&|;$<>\`\"'\x00-\x1f]/.test(namePart) || namePart.includes('..')) {
return { valid: false, reason: `Имя файла содержит недопустимые символы: «${namePart.slice(0, 100)}»` };
}
return { valid: true };
}