food-market/tests/e2e/reports/bugs/bug-002-validator-length-mismatch.md
nns 284ad095c1
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
fix(s23): adversarial bug-hunt — 4 bugs found, all fixed
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>
2026-06-08 01:35:50 +05:00

1.7 KiB
Raw Permalink Blame History

Bug #002 — ProductInput validator MaximumLength(200) vs StringLength(500)

Severity: Low (UX, consistency)
Component: Application/Catalog/CatalogDtos.cs (ProductInput) + Api/Infrastructure/Validation/Validators.cs
Found: Sprint 23, 2026-06-08

Воспроизведение

  • ProductInput.Name имеет [StringLength(500)] annotation.
  • БД-колонка products.Name имеет HasMaxLength(500).
  • ProductInputValidator (FluentValidation) кидает 400 при Name > 200 символов.

Пользователь читает аннотацию или DDL и ожидает 500 символов; на 201-чарном имени получает ошибку с упоминанием 200 — расхождение.

Воспроизведение

# 201 символов в name → 400 «The length of 'Name' must be 200 characters or fewer»
curl -X POST … -d '{"name":"Y...Y(500)","prices":[…]}'

Ожидание

Либо validator говорит 500 (совпадает с schema), либо schema режется до 200. Sprint 23 фикс: validator → 500 (расширяем — чем меньше data loss, тем лучше).

Фикс

- RuleFor(x => x.Name).NotEmpty().WithMessage("Название обязательно.")
-     .MaximumLength(200);
+ RuleFor(x => x.Name).NotEmpty().WithMessage("Название обязательно.")
+     .MaximumLength(500);

То же для CounterpartyInputValidator.Name (200 → 255 — Counterparty.Name имеет HasMaxLength(255)).

Retest

После фикса: POST с 201/499 символов → 201. POST с 501 символом → 400.