From 5bf5c4e774f3dae8610a8ea9cba1204b1f3532ae Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Mon, 24 Aug 2026 10:33:29 +0300 Subject: [PATCH] docs: add old audit file --- audits/README.md | 1 + audits/audit-13-08-2026.md | 236 +++++++++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 audits/audit-13-08-2026.md diff --git a/audits/README.md b/audits/README.md index 6e0ff7c..a31ccb7 100644 --- a/audits/README.md +++ b/audits/README.md @@ -4,4 +4,5 @@ Текущий remediation status, следующий change и evidence завершённых работ находятся в [audit remediation registry](../development/audit-remediation.md). +- [Аудит 13.08.2026](audit-13-08-2026.md) - [Аудит 20.08.2026](audit-20-08-2026.md) diff --git a/audits/audit-13-08-2026.md b/audits/audit-13-08-2026.md new file mode 100644 index 0000000..8b942c7 --- /dev/null +++ b/audits/audit-13-08-2026.md @@ -0,0 +1,236 @@ +# Аудит качества WealthLedger + +Дата: 2026-08-13 +Обновление: 2026-08-14 — повторная проверка находок после доработок `align-frontend-foundation` и `align-frontend-ui-patterns`. Статус каждой находки помечен: `[done]` закрыто, `[partial]` частично, `[open]` не тронуто. + +Статус: снимок текущей рабочей копии; исправления и OpenSpec-артефакты не создавались. + +## Цель и охват + +Проведён тщательный аудит `frontend`, `backend`, `specs`, `docs`, корневых файлов координации и `.opencode`. Папка `references/` исключена. Оценивались качество кода, архитектура, безопасность, обработка ошибок, целостность данных, производительность, accessibility, качество сценариев тестирования, документация, OpenSpec и артефакты для AI-агентов. + +Текущая рабочая копия является источником фактов. История Git использовалась только для контекста жизненного цикла OpenSpec. Проценты и пороги test coverage намеренно не оценивались. + +## Методика и ограничения + +Аудит сопоставляет локальные правила из `AGENTS.md`, OpenSpec, feature docs, ADR и фактический код. Проверялись критичные цепочки: регистрация и сессии, transport API, подключение T-Bank, счета и портфель, а также путь «спецификация -> реализация -> документация -> инструкция агенту». + +Ограничения: + +- Не проводились нагрузочные тесты, анализ production-конфигурации, live-проверки T-Bank, TLS/proxy и multi-instance развёртывания. +- `references/` не анализировалась. +- В `specs` присутствуют staged additions/deletions артефактов двух изменений; это нестабильное состояние рабочей копии, а не подтверждённый стабильный релиз. +- Команда `opsx` отсутствует в среде, поэтому обязательная lifecycle-проверка `opsx-verify` не была выполнена. + +## Карта зрелости + +| Область | Оценка | Основание | +| --- | --- | --- | +| Код backend | Требует приоритетных исправлений | Сильные криптография и денежная арифметика, но есть риски ротации и отзыва сессий. | +| Код frontend | Удовлетворительно | Типизированный API и UI-состояния устойчивы; A-11/A-12 закрыты, но остаётся production fallback (A-03). | +| Тестовые сценарии | Требует усиления | Frontend покрыт (transport + UI resilience); backend concurrent E2E отсутствует. | +| Документация | Требует исправлений | Feature docs полезны, но часть устарела; architecture/development пока заглушки. | +| OpenSpec | Удовлетворительно с риском процесса | Основные спецификации качественные и валидны, но lifecycle и traceability между репозиториями недостаточны. | +| Артефакты для AI-агентов | Удовлетворительно с риском процесса | Границы репозиториев заданы хорошо, но OpenSpec context и multi-repo verification не формализованы. | + +## Приоритетный backlog + +| Приоритет | ID | Область | Владелец | Краткое действие | Статус | Зависимости | +| --- | --- | --- | --- | --- | --- | --- | +| High | A-01 | Безопасность сессий | backend | Сделать refresh rotation атомарной. | open | Prisma conditional update/transaction. | +| High | A-02 | Отзыв сессий | backend | Проверять активность `sid` либо документировать ограниченную модель отзыва access token. | open | Решение о DB lookup/cache в guard. | +| High | A-03 | Production transport | frontend | Убрать fallback на `localhost` для production; использовать явный origin или same-origin `/api`. | open | Контракт reverse proxy и env. | +| High | A-04 | Документация T-Bank | docs | Исправить устаревшее ограничение про счета и портфели. | open | Нет. | +| High | A-05 | OpenSpec lifecycle | specs | Атомарно завершить архивирование либо восстановить активные артефакты. | partial | Подтверждение намеренного lifecycle. | +| High | A-06 | Операционная документация | docs | Добавить архитектурную карту и workflow разработки/верификации. | partial | Подтверждение команд и границ от frontend/backend. | +| Medium | A-07 | Регистрация | backend | Преобразовать unique-constraint race в `EMAIL_ALREADY_REGISTERED`. | open | Prisma error type/code. | +| Medium | A-08 | API errors | backend | Исправить сериализацию validation errors. | open | Нет. | +| Medium | A-09 | Сессии и cache | backend | Ввести retention/index для sessions и bounded TTL/LRU cache для portfolio metadata. | open | Политика retention и лимиты. | +| Medium | A-10 | Большие портфели | backend | Ограничить параллелизм получения metadata ниже лимита очереди клиента. | open | Целевой latency limit. | +| Medium | A-11 | Auth transport | frontend | Синхронизировать восстановленный token с `accessTokenRef`. | done | Нет. | +| Medium | A-12 | FSD | frontend | Перевести внешние импорты слайсов на public API. | done | Нет. | +| Medium | A-13 | Тестовые сценарии | frontend/backend | Добавить конкурентные auth E2E и behavior-тесты T-Bank UI. | partial | A-01, A-07; React Query test wrapper. | +| Medium | A-14 | ADR и OpenSpec context | docs/specs/root | Актуализировать ADR, заполнить OpenSpec context и multi-repo evidence. | open | Согласование общей политики. | + +## Находки + +### High + +#### A-01. Ротация refresh token не атомарна — `[open]` + +- Доказательство: `backend/src/auth/auth.service.ts:75-96,148-166` выполняет read/verify/update в отдельных действиях. +- Обновление 2026-08-14: код не менялся; риск сохраняется. +- Риск: два параллельных запроса с одним cookie могут оба пройти проверку и получить access token; итоговый hash зависит от последней записи. +- Минимальное действие: обновлять с условием по `id`, `refreshTokenHash`, активности и сроку жизни; считать нулевое число обновлений отказом. +- Владелец: backend. Зависимости: Prisma conditional update/transaction. + +#### A-02. Отзыв сессии не прекращает действие уже выданного access JWT — `[open]` + +- Доказательство: `backend/src/auth/jwt.strategy.ts:19-23`, `backend/src/auth/jwt-auth.guard.ts:4-5`, `backend/src/auth/auth.service.ts:89-94,114-117` проверяют подпись, срок и тип token, но не состояние `sid`. +- Обновление 2026-08-14: backend не менялся; риск сохраняется. +- Минимальное действие: проверять активный неистёкший `sid` в guard/strategy либо явно принять и документировать bounded-revocation модель. +- Владелец: backend. Зависимости: решение о lookup/cache. + +#### A-03. Production fallback направляет bearer token на localhost пользователя — `[open]` + +- Доказательство: `frontend/src/shared/api/config.ts:1-2` использует `http://localhost:3000` при отсутствии build-time URL; `frontend/src/shared/api/client.ts:54-57` направляет туда bearer-authenticated requests. +- Обновление 2026-08-14: fallback `?? "http://localhost:3000"` перенесён в `frontend/src/shared/api/client.ts:6`; риск сохраняется. +- Минимальное действие: требовать явный production API origin либо использовать same-origin `/api`; завершать build/start при отсутствии допустимой настройки. +- Владелец: frontend. Зависимости: env и reverse-proxy contract. + +#### A-04. Документация T-Bank противоречит реализованным функциям — `[open]` + +- Доказательство: `docs/features/tbank-connection.md:38-40` откладывает счета и портфели, но `docs/features/brokerage-accounts.md:1-55` и `docs/features/portfolio-view.md:1-59` описывают их как поставленные. +- Обновление 2026-08-14: не исправлено. +- Минимальное действие: оставить в limitations только реально отложенные возможности. +- Владелец: docs. Зависимости: нет. + +#### A-05. Активные OpenSpec changes находятся в неполном переходном состоянии — `[partial]` + +- Доказательство: `git status` в `specs` показывает staged additions/deletions `design.md`, delta-spec и `tasks.md` для `add-brokerage-accounts` и `add-portfolio-view`; правила требуют полный набор в `specs/AGENTS.md:16-17`. +- Обновление 2026-08-14: `align-frontend-foundation` и `align-frontend-ui-patterns` архивированы в `archive/` и закоммичены в HEAD, но в индексе остаются 16 staged-записей `AD` (включая новые изменения); `git status` не чист, требуется `git reset` для завершения lifecycle. +- Минимальное действие: одним коммитом завершить archive/move либо восстановить активные артефакты. +- Владелец: specs. Зависимости: подтверждение lifecycle. + +#### A-06. Архитектурная и development-документация фактически отсутствуют — `[partial]` + +- Доказательство: `docs/architecture/README.md:1-3` и `docs/development/README.md:1-3` содержат только заглушки, хотя их назначение закреплено в `docs/AGENTS.md:15-17`. +- Обновление 2026-08-14: добавлена `docs/development/frontend-workflow.md` (Node 22, `npm run verify`, `openapi:sync`); `docs/architecture/README.md` остаётся заглушкой. +- Минимальное действие: добавить актуальную component/data-flow карту и руководство по setup, generation, verification и выпуску изменений. +- Владелец: docs. Зависимости: подтверждение команд и границ frontend/backend. + +### Medium + +#### A-07. Конкурентная регистрация возвращает 500 вместо контрактного 409 — `[open]` + +- Доказательство: `backend/src/auth/auth.service.ts:29-42` делает preflight `findUnique`, не обрабатывая unique-constraint вокруг `create`. +- Риск: один из параллельных запросов с одинаковым email завершится не `EMAIL_ALREADY_REGISTERED`. +- Минимальное действие: перехватывать Prisma unique-constraint и возвращать `ConflictException`. +- Владелец: backend. Зависимости: Prisma error type/code. + +#### A-08. Формат validation error не соответствует фактическому Nest payload — `[open]` + +- Доказательство: `backend/src/common/filters/http-exception.filter.ts:23-38,58-66` трактует `message: string[]` как `ValidationError[]`. +- Риск: публичный `VALIDATION_ERROR` содержит пустой `errors`, клиент не получает указания на неверные поля. +- Минимальное действие: задать `exceptionFactory` с `ValidationError[]` или корректно сериализовать строки Nest. +- Владелец: backend. Зависимости: нет. + +#### A-09. Сессии и portfolio cache не имеют границ роста — `[open]` + +- Доказательство: `backend/src/auth/auth.service.ts:137-165`, `backend/prisma/schema.prisma:28-39` не очищают sessions и не содержат expiry index; `backend/src/tbank/portfolio.service.ts:54-55,67-98,229-273` держит неограниченные process-local maps. +- Риск: рост SQLite-таблицы и памяти при длительной эксплуатации. +- Минимальное действие: retention/cleanup сессий и index `expiresAt`; bounded TTL/LRU с периодической очисткой cache. +- Владелец: backend. Зависимости: политика retention, лимиты cache. + +#### A-10. Большой портфель может терять metadata из-за burst concurrency — `[open]` + +- Доказательство: `backend/src/tbank/portfolio.service.ts:131-133,229-272` запускает все lookups одновременно, `backend/src/tbank/tbank.client.ts:208-209` отклоняет новые вызовы после 50 queued. +- Риск: часть позиций получает fallback FIGI/`Unknown instrument`, хотя данные доступны upstream. +- Минимальное действие: bounded concurrency ниже лимита очереди либо очередь без hard rejection. +- Владелец: backend. Зависимости: допустимая latency для крупных портфелей. + +#### A-11. Восстановленный access token не синхронизируется с transport ref — `[done]` + +- Доказательство: `frontend/src/features/auth/model/auth.tsx:66-75` обновляет state, но не `accessTokenRef.current`; `:100-110` настраивает shared client только из ref. +- Обновление 2026-08-14: исправлено. `auth.tsx` синхронизирует `accessTokenRef.current` при bootstrap/refresh/login/logout/clear (строки 54,74,87,96,108); transport — единственный владелец Bearer header, покрыт тестами в `client.test.ts`. +- Минимальное действие: синхронно обновлять ref при восстановлении token; затем удалить дублирующиеся headers. +- Владелец: frontend. Зависимости: нет. + +#### A-12. Нарушается FSD public API boundary — `[done]` + +- Доказательство: `frontend/src/app/routes/AppRoutes.tsx:2-9`, `src/pages/accounts/ui/AccountsPage.tsx:5-10`, `src/pages/settings/ui/SettingsPage.tsx:4-6`, `src/features/tbank-connection/ui/TbankConnectionSection.tsx:13-16` импортируют внутренности слайсов вопреки `frontend/docs/STRUCTURE.md:11`. +- Обновление 2026-08-14: исправлено. Импорты идут через public `index.ts`, добавлен `dependency-cruiser`; `npm run deps` проходит без нарушений (84 modules). +- Минимальное действие: экспортировать нужные элементы через `index.ts` и импортировать только public API. +- Владелец: frontend. Зависимости: нет. + +#### A-13. Критичные сценарии не защищены тестами — `[partial]` + +- Доказательство: `backend/test/app.e2e-spec.ts:36-85` не тестирует параллельные refresh/registration; `frontend/src/features/tbank-connection/ui/TbankConnectionSection.tsx:27-169` не имеет focused tests. +- Обновление 2026-08-14: frontend-часть закрыта — transport-тесты (`client.test.ts`), `RouteErrorBoundary.test.tsx`, `AccountPage.test.tsx`, `TbankConnectionSection.view.test.tsx`; `vitest run` 30/30. Backend-часть (concurrent auth E2E) не добавлена — backend не менялся. +- Минимальное действие: добавить concurrent E2E для auth и behavior tests для replace/delete/error/pending/invalidation UI. +- Владелец: backend и frontend. Зависимости: A-01, A-07; React Query test wrapper. + +#### A-14. Контекст и verification evidence не адаптированы к multi-repo SDD — `[open]` + +- Доказательство: `specs/openspec/config.yaml:1-20` оставлен примером; `.opencode/skills/openspec-verify-change/SKILL.md:70-115` не требует evidence matrix по `frontend`, `backend`, `docs`; архивные tasks, например `specs/openspec/changes/archive/2026-08-13-add-portfolio-view/tasks.md:13-15,23-32`, не содержат commit SHA и результаты проверок. +- Обновление 2026-08-14: не менялось; `config.yaml` всё ещё пример. +- Минимальное действие: задать OpenSpec context/rules и completion record: репозиторий, SHA, версия/ревизия контракта, дата и результат проверок. +- Владелец: specs и root. Зависимости: соглашение frontend/backend/docs. + +### Low + +#### A-15. ADR отражают план, а не реализованное состояние — `[open]` + +- Доказательство: `docs/adr/003-tbank-server-side-grpc-boundary.md:19-21`, `004-encrypted-per-user-tbank-token.md:13-15`, `006-auth-session-policy.md:13-15` описывают будущую реализацию; feature docs фиксируют поставленные функции. +- Риск: читатель не отличит accepted decision от delivery status. +- Минимальное действие: дополнить consequences статусом реализации и ссылкой на feature doc; обновить конфликтующий policy в `docs/adr/007-react-query-fetch-policy.md:9-15`. +- Владелец: docs. Зависимости: согласование frontend/backend policy. + +#### A-16. Локальные frontend guides не имеют явного статуса в общей документации — `[open]` + +- Доказательство: `frontend/AGENTS.md:19-24` требует читать пять внутренних документов, а корневой `AGENTS.md:17,23` описывает только durable docs. +- Риск: агенту неясно, какая документация авторитетна и как поддерживать локальные convention guides. +- Минимальное действие: отметить их как frontend-local conventions и связать с development index. +- Владелец: root. Зависимости: согласование frontend/docs. + +#### A-17. Небольшие UX/i18n несоответствия — `[open]` + +- Доказательство: `frontend/src/features/auth/ui/AuthForm.tsx:84-99` не содержит `autoComplete`; `src/entities/brokerage-account/ui/BrokerageAccountList.tsx:33-38`, `src/entities/portfolio/model/display.ts:20-25`, `src/features/tbank-connection/ui/TbankConnectionSection.tsx:99-104` жёстко используют `ru-RU` при наличии i18n. +- Риск: хуже работа password manager и некорректный формат дат при добавлении другой локали. +- Минимальное действие: добавить semantic autocomplete и использовать текущий язык i18n в formatters. +- Владелец: frontend. Зависимости: решение о дополнительных локалях для второй части. + +#### A-18. Непоследовательное оформление основной OpenSpec — `[open]` + +- Доказательство: `specs/openspec/specs/user-authentication/spec.md:1-5` начинается с `## Purpose`, в отличие от остальных спецификаций, например `specs/openspec/specs/tbank-connection/spec.md:1-7`. +- Риск: небольшая навигационная и tooling-неоднозначность. +- Минимальное действие: добавить `# user-authentication Specification`. +- Владелец: specs. Зависимости: нет. + +## Подтверждённые сильные стороны + +- T-Bank tokens шифруются AES-256-GCM с уникальным nonce и authentication tag: `backend/src/tbank/tbank.crypto.ts:16-43`. +- Tokens валидируются до сохранения и не возвращаются connection status API: `backend/src/tbank/tbank-connection.service.ts:34-61`, `backend/src/tbank/tbank-connection.controller.ts:35-55`. +- JWT algorithm закреплён, а для неизвестного пользователя используется dummy bcrypt hash: `backend/src/auth/jwt.strategy.ts:11-16`, `backend/src/auth/auth.service.ts:50-58`. +- Денежная арифметика использует `bigint`, не floating point: `backend/src/tbank/decimal.ts:1-44`. +- Frontend использует централизованный типизированный OpenAPI client, не хранит token в persistent storage, а маршруты и страницы имеют loading/error/empty patterns. +- Основные OpenSpec scenario-based, охватывают ошибки, cache и безопасность; feature docs содержат пользовательские потоки, API, ограничения и Mermaid diagrams. +- Корневые и локальные `AGENTS.md` ясно фиксируют независимые репозитории и SDD intent. + +## Результаты воспроизводимых проверок + +Проверки выполнены 2026-08-13 без запуска `test:cov` и без анализа процентов покрытия. Повторные проверки frontend — 2026-08-14. + +| Область | Команда | Результат | +| --- | --- | --- | +| frontend | `./node_modules/.bin/biome check .` | Успешно: 64 файла, замечаний нет. | +| frontend | `./node_modules/.bin/tsc -b --noEmit` | Успешно. | +| frontend | `./node_modules/.bin/vitest run` | Успешно: 8 files, 25 tests. | +| frontend | `./node_modules/.bin/tsc -b && ./node_modules/.bin/vite build` | Успешно; Vite предупреждает о JS chunk 582.99 kB (176.19 kB gzip) после minification. | +| frontend | `! rg -i todo src --glob '*.{ts,tsx}'` | Успешно: TODO не найдено. | +| backend | `rtk lint "{src,apps,libs,test}/**/*.ts"` | Успешно: ESLint без замечаний. | +| backend | `npm test` | Успешно: 8 suites, 22 tests. | +| backend | `rtk npm run build` | Успешно: Prisma generate и Nest build. | +| specs | `npx openspec validate --all --strict` | Успешно: 7 specs, 0 failures. | +| specs | `opsx validate --all --strict` | Не выполнено: `opsx: command not found`. | + +## Повторные проверки 2026-08-14 + +| Область | Команда | Результат | +| --- | --- | --- | +| frontend | `npm run typecheck` | Успешно. | +| frontend | `./node_modules/.bin/biome check .` | Успешно: 81 файл, замечаний нет. | +| frontend | `npm run openapi:check` | Успешно: generated artifact соответствует committed snapshot. | +| frontend | `npm run deps` | Успешно: `dependency-cruiser`, 84 modules, нарушений нет. | +| frontend | `npm run knip` | Успешно. | +| frontend | `vitest run` | Успешно: 30 tests (11 files). | +| specs | `npx openspec validate --all --strict` | Успешно: 8 specs (добавлен `frontend-ui-resilience`), 0 failures. | +| specs | `git status` | Грязный: 16 staged-записей `AD` в `openspec/changes/` — ожидает `git reset`. | + +## Рекомендуемый порядок исправлений + +1. Создать одну security-focused change для A-01, A-02, A-03 и A-07; добавить связанные concurrent tests из A-13 (backend-часть). Это единственный незакрытый High-блок. +2. Завершить lifecycle A-05: выполнить `git reset` в `specs`, чтобы убрать 16 staged-записей `AD` и вернуть воспроизводимый OpenSpec store. +3. Исправить A-04, A-06, A-14 и A-15 одним documentation/process change, включив multi-repo evidence matrix. +4. Выполнить A-08, A-09 и A-10 отдельными backend-изменениями. +5. Завершить Low-улучшения (A-16, A-17, A-18) после согласования локалей и статуса локальных руководств.