Источник: ARCHITECTURE-STRENGTHS.md (adversarial review), дата 2026-06-20
Приоритеты расставлены по тяжести риска: High > Medium > Low
| ID | Измерение | Приоритет | Effort | Слабость (кратко) |
|---|---|---|---|---|
| W-01 | Multi-tenancy / workspace isolation | High | M | Нет единого middleware-валидатора _wsId на уровне драйвера |
| W-02 | Envelope encryption | High | L | Нет механизма ротации ключей, single master key |
| W-03 | Dual-mode AI провайдер | High | S | Нет лимита на размер customBodyTemplate, JSON injection через template |
| W-04 | Observability stack | High | M | PII может попасть в Sentry, TTL конфликтует с compliance |
| W-05 | Event-driven / RabbitMQ | Medium | S | Ack при ошибке — потеря сбойных сообщений |
| W-06 | EAV кастомные поля | Medium | S | Нет composite индекса для фильтрации по значению |
| W-07 | RBAC система прав | Medium | M | Нет кэширования прав, каждый tool-call — round-trip в БД |
| W-08 | Deployment flexibility | Low | S | Production docker-compose не обнаружен; PM2-конфиг не co-located с сервисом |
| W-09 | Plugin архитектура Mongoose | Medium | S | Порядок подключения плагинов не валидируется runtime |
| W-10 | Tool-calling архитектура | Low | M | Fabrication guard не покрывает не-ObjectId идентификаторы |
_wsId на уровне драйвераИзмерение: Multi-tenancy / workspace isolation
Оценка в STRENGTHS: Сильно
Слабость: _wsId инжектится автоматически через modifyIncomeData в плагине WsId, но отсутствует middleware на уровне Mongoose-драйвера, который гарантировал бы наличие _wsId в каждом запросе к БД. При прямом вызове Model.find() без profile-контекста (миграционные скрипты, cron-задачи, прямые вызовы из воркеров) cross-tenant чтение становится возможным. Дополнительно, strict:false в botPermissions (employee.js) создаёт surface для неожиданных полей.
Риск: Security breach — cross-tenant утечка данных при обходе modifyIncomeData. Нарушение data isolation между workspace.
Приоритет: High
Effort: M (3-10 дней)
Рекомендуемое действие: Внедрить mongoose middleware (pre('find'), pre('aggregate') и т.д.) на уровне подключения, который проверяет наличие _wsId в filter/pipeline и бросает ошибку при отсутствии. Для скриптов миграции — явный escape-hatch через allowNoWsId: true.
Связанные задачи: Фаза 1 рефакторинга (mongoose миграция)
Измерение: Envelope encryption
Оценка в STRENGTHS: Хорошо
Слабость: Master key (SECURE_MASTER_KEY) — single point of compromise. Отсутствует механизм автоматической ротации ключей: при компрометации master key все workspace keys необходимо перезашифровать вручную. getUserEncryptionKey детерминированна (HMAC-SHA256), что означает один и тот же plaintext всегда даёт один и тот же ciphertext — отсутствие semantic security.
Риск: Security breach — компрометация master key раскрывает данные всех workspace. Compliance violation при невозможности продемонстрировать key rotation. Data loss при ручной ротации с ошибками.
Приоритет: High
Effort: L (10+ дней)
Рекомендуемое действие: Реализовать автоматический key rotation: dual-key режим (старый + новый), фоновая миграция зашифрованных данных, версионирование ключей в Workspace.encryptionKey. Рассмотреть KMS (AWS KMS / HashiCorp Vault) для хранения master key.
Связанные задачи: LAN-64 (bcrypt migration, SECURE_MASTER_KEY assertion)
customBodyTemplate, JSON injection через templateИзмерение: Dual-mode AI провайдер
Оценка в STRENGTHS: Хорошо
Слабость: В raw-режиме smarty_custom выполняет JSON.parse(rendered) на шаблоне, отрендеренном из пользовательского ввода. Хотя renderTemplate экранирует строки через JSON.stringify, сложные шаблоны с вложенными объектами могут генерировать невалидный JSON. Отсутствует лимит на размер customBodyTemplate — потенциальный DoS через exhaustion памяти.
Риск: Security — DoS через экспоненциально раздуваемый шаблон. Availability — краш воркера при невалидном JSON из template. Potential injection при обходе экранирования.
Приоритет: High
Effort: S (до 3 дней)
Рекомендуемое действие: Добавить валидацию размера customBodyTemplate (上限, например 64KB) при сохранении настроек бота. Обернуть JSON.parse в try-catch с конкретным сообщением об ошибке. Добавить fuzzing-тесты на template rendering.
Связанные задачи: LAN-81 (архитектура бэкенда)
Измерение: Observability stack
Оценка в STRENGTHS: Сильно
Слабость: Sentry beforeSend фильтрует test-окружение, но не scrubbing PII — чувствительные данные (има email, телефоны, содержимое сообщений) могут попасть в error reports. Audit log TTL 90 дней конфликтует с GDPR right to erasure (право на удаление) vs. audit retention requirements — нет механизма дифференцированного удаления. OTEL sampling 10% по умолчанию пропускает 90% трассировок, что затрудняет production debugging.
Риск: Compliance violation — утечка PII в third-party сервис (Sentry). GDPR violation при невозможности удалить персональные данные из audit log. Operational incident при недостатке трассировок для диагностики.
Приоритет: High
Effort: M (3-10 дней)
Рекомендуемое действие: Добавить PII-scrubbing в Sentry beforeSend (маскирование email, телефонов, имён). Реализовать selective purge для audit log: удалять PII-поля, сохраняя структуру записи. Увеличить sampling ratio или реализовать tail-based sampling для production.
Связанные задачи: LAN-65 (retention/PII), LAN-66 (audit/ACCESS_CONTROL)
Измерение: Event-driven / RabbitMQ
Оценка в STRENGTHS: Хорошо
Слабость: HandlersMap.handle() вызывает acker.ack() в блоке catch (строка 38), что означает сбойное сообщение ack'ается и удаляется из очереди без попадания в DLQ (Dead Letter Queue). Комментарий TODO: начать по-нормальному обрабатывать подтверждает известный технический долг.
Риск: Data loss — сбойные сообщения теряются без возможности восстановления. Operational incident — молча потерянные события приводят к рассинхронизации состояния между сервисами.
Приоритет: Medium
Effort: S (до 3 дней)
Рекомендуемое действие: Заменить acker.ack() в catch на acker.nack(true) (requeue) с ограничением числа retry (dead-letter после N попыток). Настроить DLQ exchange в RabbitMQ для перехвата невосстанавливаемых сообщений.
Связанные задачи: Фаза 2 рефакторинга (event-driven)
Измерение: EAV кастомные поля
Оценка в STRENGTHS: Хорошо
Слабость: EAV-запросы требуют join между тремя коллекциями (custom_field, custom_field_template, custom_field_relation). На custom_field_relation отсутствует composite индекс по (collectionName, _fieldId, value) — фильтрация по значению custom field выполняет full scan коллекции. Для объектов с большим количеством custom fields производительность деградирует линейно.
Риск: Degraded performance — full scan при фильтрации по custom fields на больших объёмах данных. Latency растёт нелинейно с числом custom fields на объекте.
Приоритет: Medium
Effort: S (до 3 дней)
Рекомендуемое действие: Создать compound index { collectionName: 1, _fieldId: 1, value: 1 } на custom_field_relation. Рассмотреть materialized view для часто запрашиваемых комбинаций полей.
Связанные задачи: Фаза 3 рефакторинга (mongoose)
Измерение: RBAC система прав
Оценка в STRENGTHS: Сильно
Слабость: Каждый tool-call в agent loop заново читает AccessRight из БД без кэширования. AccessRight.marks хранит массив ObjectId меток — при большом количестве меток (сотни) запрос marks: { $in: [...] } становится тяжёлым. commissioners — boolean, не поддерживает multiple commissioners per object.
Риск: Degraded performance — latency каждого tool-call увеличивается на round-trip к БД для проверки прав. При high-load сценариях (множество параллельных ботов) — contention на MongoDB.
Приоритет: Medium
Effort: M (3-10 дней)
Рекомендуемое действие: Внедрить in-memory кэш прав (TTL 30-60с) с инвалидацией при изменении AccessRight. Использовать Redis для распределённого кэша. Оптимизировать запросы с $in через paginated lookup при большом количестве меток.
Связанные задачи: LAN-64 (access control), Фаза 3 рефакторинга
Измерение: Deployment flexibility
Оценка в STRENGTHS: Частично
Слабость: ecosystem.config.cjs versioned в smarty-code/scripts/ (уровень моно-репо), но не в smarty-backend-stable/ — при работе с сервисом в изоляции конфиг не очевиден. docker-compose.local.yml покрывает только dev/тест-инфраструктуру (Mongo, Redis, RabbitMQ); production-compose не обнаружен. Local mode запускает 18+ воркеров в одном процессе — один unhandled rejection крашит весь стек. Нет graceful shutdown для отдельных воркеров.
Риск: Operational incident — production-развёртывание не полностью описано в репо. Краш одного воркера в local mode роняет все сервисы.
Приоритет: Low
Effort: S (до 3 дней)
Рекомендуемое действие: Добавить production docker-compose (или ссылку на ops-репо) в smarty-code/. Добавить per-worker error boundaries в local mode с автоматическим restart. Реализовать graceful shutdown (SIGTERM handler) для каждого воркера.
Связанные задачи: Фаза 4 рефакторинга (deployment)
Измерение: Plugin архитектура Mongoose
Оценка в STRENGTHS: Сильно
Слабость: 80+ плагинов без TypeScript — порядок подключения критичен (WsObject зависит от EmployeeObject, RightObject зависит от PublicObject + WsObject). Нет runtime-валидации совместимости: неправильный порядок manifestится как undefined без диагностического сообщения. Флаг firstPlugin передаётся, но не используется повсеместно.
Риск: Operational incident — mysterious undefined при неправильном порядке подключения плагинов. Сложность отладки: отсутствие stack trace, указывающего на проблемный плагин. Регресс при добавлении нового плагина.
Приоритет: Medium
Effort: S (до 3 дней)
Рекомендуемое действие: Добавить декларативные зависимости плагинов (requires: ['EmployeeObject']) и runtime-проверку при schema.plugin() — бросать ошибку если dependency не подключён. Логировать порядок подключения при инициализации.
Связанные задачи: LAN-81 (архитектура бэкенда)
Измерение: Tool-calling архитектура
Оценка в STRENGTHS: Сильно
Слабость: Fabrication guard (fabricationGuard.js) проверяет только ObjectId-подобные hex-строки (24-hex). Строковые ID (UUID, slug, external API identifiers) не детектируются — бот может подставить произвольную строку, и guard не заблокирует. Дополнительно, PLANNER_HARD_CAP = 30 — искусственный потолок; сложные workflows могут legitimately требовать больше шагов, и обрезка молча теряет данные без предупреждения.
Риск: Security — бот может ссылаться на выдуманные строковые идентификаторы, которые пройдут fabrication guard. Data integrity — молча обрезанный план теряет часть workflow без уведомления пользователя.
Приоритет: Low
Effort: M (3-10 дней)
Рекомендуемое действие: Расширить fabrication guard на поддержку UUID и slug-паттернов (regex + allowlist из контекста). Для PLANNER_HARD_CAP — добавить явное предупреждение пользователю при достижении лимита, а не молча обрезать.
Связанные задачи: LAN-81 (архитектура бэкенда)