Sprint 23 (adversarial): атаковали систему как недоброжелатель. Найдено 4 бага, все починены. Bug #001 (Medium): NULL-byte в Product.Name вызывал 500 без тела. Postgres TEXT не принимает \x00. Добавил NoControlChars() в ProductInputValidator + CounterpartyInputValidator. Bug #002 (Low): ProductInputValidator MaximumLength(200) конфликтовал со StringLength(500) в DTO и schema HasMaxLength(500). Сделал 500 везде. Counterparty: 200 → 255 (matches HasMaxLength). Bug #003 (CRITICAL): параллельные posting'и под Serializable выбрасывали PostgresException 40001 → middleware → 500 empty body. Добавил SerializationConflictMiddleware который мапит 40001 → 409 Conflict с {error, retryable: true}. Также SerializableRetry helper для явного retry внутри endpoint'ов с exp backoff. Применил retry-wrap к RetailSalesController.Post (PostCoreAsync extracted). Bug #004 (Low): цена 0.0000001 округлялась до 0 уже после прохождения required-price check (check был ДО RoundIfNeeded). FindMissing- RequiredPriceAsync теперь округляет перед сравнением — required цена реально > 0 после rounding. Bug reports: tests/e2e/reports/bugs/bug-00[1-4]-*.md (github-issue format). Multi-tenant attacks (cat 3): clean — все cross-org GET/PUT/DELETE дают 404, bulk-update affected=0, lists не утекают. Auth-edge (cat 2): clean — JWT tampering 401, garbage 401, CORS evil.com не получает allow-origin, fake refresh 400 invalid_grant. DOS (cat 7): clean — 50MB body 413, 200 headers 431, long URL 200. Hangfire safety (cat 8): clean — regular Admin → /hangfire 403, seed-demo использует tenant context, body org-id игнорируется. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2.1 KiB
Bug #001 — Product.Name с NULL-byte вызывает 500 (без ответа)
Severity: Medium
Component: POST /api/catalog/products (и любой другой endpoint, принимающий строку, которая попадает в TEXT/VARCHAR-колонку Postgres)
Found: Sprint 23 (adversarial bug-hunt), 2026-06-08
Воспроизведение
curl -X POST -H "Authorization: Bearer $TOK" -H "Content-Type: application/json" \
-d '{"name":"HelloWorld","unitOfMeasureId":"…","productGroupId":"…",
"vat":0,"vatEnabled":true,
"barcodes":[{"code":"NB-1","type":0,"isPrimary":true}],
"prices":[{"priceTypeId":"…","amount":500,"currencyId":"…"}]}' \
https://test.admin.food-market.kz/api/catalog/products
Ответ: HTTP 500 с пустым body.
Ожидание
400 с понятным сообщением: «В поле Name недопустимые символы (NUL)».
Причина
PostgreSQL TEXT/VARCHAR не допускает \x00 — Npgsql выбрасывает PostgresException 22021 (character not in repertoire). В контроллере ловится только DbUpdateException-в обёртке по IX_*_Article — все остальные uncaught → middleware возвращает 500.
Фикс
Добавить в ProductInputValidator (и CounterpartyInputValidator) проверку «нет NUL-байт в строковых полях». Для широкого охвата — отдельный MustNotContainControlChars() extension на StringValidator.
Альтернатива: глобальный JSON-middleware, который reject'ит strings c \x00. Менее точно (теряем контекст поля), но защищает все endpoint'ы автоматом.
Выбран первый вариант — точечно в FluentValidation.
Retest
После фикса: POST /api/catalog/products с \x00 в name → 400 с сообщением «Поле Name не должно содержать управляющих символов.».