security(auth): anti-enumeration, returnTo-валидация, лимит попыток логина

Шаг 2.1 плана production-готовности:
- единый 401 при любом отказе логина + constant-time bcrypt
- forgot-password: идентичный ответ независимо от существования email
- sanitizeReturnTo (open redirect fix в /api/documents/generate-link)
- 10 неудачных попыток → блок 15 мин (in-memory, аудит)
- DATABASE_SSL_REJECT_UNAUTHORIZED env (дефолт true)
- доделка 0.7: статичные тексты в llm-providers/rag/finance-di2
- 8 новых тестов (104/104)
This commit is contained in:
2026-09-08 00:46:57 +03:00
parent 0828003141
commit 5750a3106f
15 changed files with 525 additions and 45 deletions

View File

@@ -10,6 +10,7 @@ import { SignatureService } from "./signature.service";
import { DocxImporterService } from "./docx-importer.service";
import * as storage from "../storage/documents.storage";
import { storage as appStorage } from "../storage/index";
import { sanitizeReturnTo } from "../utils/return-to";
export async function registerDocumentRoutes(app: Express) {
const templateService = new DocumentTemplateService();
@@ -326,7 +327,7 @@ export async function registerDocumentRoutes(app: Express) {
const templateId = Number(req.query.templateId);
const taskId = Number(req.query.taskId);
const format = (req.query.format as string) || 'both';
const returnTo = (req.query.returnTo as string) || '';
const returnTo = sanitizeReturnTo(req.query.returnTo);
if (!templateId || !taskId) {
return res.status(400).json({ error: "templateId и taskId обязательны" });