From ae0dd4c8b3d5739d170af70ce941fd1c6ffe23f4 Mon Sep 17 00:00:00 2001 From: Zuev Date: Fri, 10 Jul 2026 22:20:50 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B1=D0=B0=D0=B3=20=D1=80=D0=B5=D0=BF=D0=BE?= =?UTF-8?q?=D1=80=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BUG_REPORT.md | 665 ++++++++++++++---- graphify-out/.graphify_learning.json | 69 +- ...лизируй_весь_проект_на_баги_и_ошибки_и_допол.md | 26 + graphify-out/reflections/LESSONS.md | 11 +- 4 files changed, 631 insertions(+), 140 deletions(-) create mode 100644 graphify-out/memory/query_20260710_181403_проанализируй_весь_проект_на_баги_и_ошибки_и_допол.md diff --git a/BUG_REPORT.md b/BUG_REPORT.md index 041768a..d2d13a3 100644 --- a/BUG_REPORT.md +++ b/BUG_REPORT.md @@ -1,219 +1,614 @@ # Отчёт по ошибкам и рискам проекта Magistr -Дата проверки: 2026-07-03 +Дата проверки: 2026-07-10 -Проверено вручную: backend Spring Boot, мультитенантность, авторизация, генерация расписания, основные frontend JS-файлы. Автотесты не запущены: в окружении нет `mvn` и `./mvnw`. +## Объём и способ проверки -## Нужно исправить в первую очередь +Проверены: -### 1. Высокая — JWT имеет небезопасные production-дефолты +- 166 Java-файлов backend, JPA-модели, репозитории, контроллеры, сервисы и конфигурация мультитенантности; +- обе Flyway-миграции без изменения существующих файлов; +- 23 JavaScript-файла, встроенный JavaScript страниц преподавателя/студента, HTML/CSS и SPA-маршрутизация; +- `compose.yaml`, Dockerfile, Gitea Actions; +- production-манифесты `../k8s/`, которые AGENTS.md относит к внешним зависимостям проекта; +- проектная документация и существующий граф связей `graphify-out/graph.json`. + +Выполненные проверки: + +- `mvn test` в Maven-контейнере: **30 тестов, 0 failures, 0 errors, BUILD SUCCESS**; +- `node --check` для всех внешних JS-файлов и извлечённых inline-скриптов: ошибок синтаксиса нет; +- `docker compose config --quiet`: Compose синтаксически корректен; +- `kubectl kustomize ../k8s`: Kustomize-манифесты собираются; +- сопоставление JPA-таблиц с Flyway DDL: отсутствующих таблиц для сущностей не найдено; +- `git diff --check`: проблем с пробелами и маркерами конфликтов нет. + +Ограничения проверки: не выполнялись E2E-тесты в браузере, нагрузочное тестирование, тестирование с реальным PostgreSQL и одновременной работой двух backend-pod. Конкурентные дефекты ниже подтверждены по коду и воспроизводимому сценарию, но не запускались против production-кластера. + +## Сводка + +| Приоритет | Количество | +|---|---:| +| Критический | 2 | +| Высокий | 10 | +| Средний | 19 | +| Низкий | 3 | +| **Всего** | **34** | + +## Критические проблемы + +### 1. Production-манифесты содержат известные JWT/DB-секреты и хранят пароли в ConfigMap **Файлы:** -- `backend/src/main/java/com/magistr/app/config/auth/JwtProperties.java:13-17` + - `backend/src/main/resources/application.properties:18-22` -- `backend/src/main/java/com/magistr/app/controller/AuthController.java:162-168` +- `backend/src/main/java/com/magistr/app/config/auth/JwtProperties.java:13-17` +- `../k8s/config.yaml:15-26` +- `../k8s/backend.yaml:1-23` +- `../k8s/otel-collector.yaml:1-25` -**Проблема:** если на проде не задан `JWT_SECRET`, приложение использует известный дефолтный секрет `dev-only-change-this-jwt-secret-32-bytes-minimum`. При этом `JWT_REFRESH_COOKIE_SECURE` по умолчанию `false`, и refresh-cookie может уходить без флага `Secure`. +**Проблема:** приложение имеет известный JWT-секрет по умолчанию и небезопасный дефолт `refresh-cookie-secure=false`. Production-манифест `config.yaml` передаёт другой, но также заранее известный placeholder-секрет, который удовлетворяет проверке длины. Пароли PostgreSQL записаны открытым текстом в `Secret.stringData`, tenant ConfigMap и ConfigMap коллектора OpenTelemetry. Kubernetes Secret в YAML не шифрует значение в исходном файле, а ConfigMap вообще не предназначен для секретов. -**Риск:** подделка access-токенов при известном секрете; утечка refresh-cookie при ошибочной HTTP/прокси-конфигурации. +**Риск:** подделка access JWT, компрометация всех tenant-БД и метрик, повторное использование опубликованных credentials. Если эти значения когда-либо применялись, их нужно считать скомпрометированными. **Как исправить:** -- На production-профиле падать при отсутствии `JWT_SECRET` и при секрете короче требуемой длины. -- Для production сделать `app.jwt.refresh-cookie-secure=true` обязательным. -- Разделить dev/prod профили: дефолтный секрет оставить только в `application-dev.properties`. + +- Немедленно ротировать JWT- и DB-секреты во всех окружениях. +- Удалить реальные/фиксированные значения из файлов и истории репозиториев. +- Использовать External Secrets, SOPS/Sealed Secrets или секреты CI/CD; tenant credentials и credentials OTel хранить в Secret, а не ConfigMap. +- На production-профиле завершать запуск при пустом, дефолтном или известном placeholder `JWT_SECRET` и при `JWT_REFRESH_COOKIE_SECURE != true`. --- -### 2. Высокая — race condition при ротации refresh-токена - -**Файл:** `backend/src/main/java/com/magistr/app/config/auth/RefreshTokenService.java:50-78` - -**Проблема:** `rotate()` читает refresh-токен через `findByTokenHash()`, проверяет активность, затем отзывает старый и создаёт новый. Нет блокировки строки или атомарного `UPDATE ... WHERE revoked_at IS NULL`. Два параллельных запроса с одним refresh-токеном могут оба пройти проверку и создать две активные сессии. - -**Риск:** повторное использование одного refresh-токена, раздвоение сессий, некорректная ротация `rotatedToTokenHash`. - -**Как исправить:** -- Добавить pessimistic lock в репозиторий (`@Lock(PESSIMISTIC_WRITE)` для поиска по `tokenHash`) внутри транзакции. -- Либо сделать атомарное обновление: `UPDATE auth_refresh_tokens SET revoked_at = now, rotated_to_token_hash = :newHash WHERE token_hash = :hash AND revoked_at IS NULL AND expires_at > now` и создавать новый токен только если обновлена 1 строка. -- Добавить тест на два параллельных refresh-запроса. - ---- - -### 3. Высокая — `AuthContext` может протечь между запросами при `403` - -**Файл:** `backend/src/main/java/com/magistr/app/config/auth/AuthorizationInterceptor.java:35-49` - -**Проблема:** пользователь кладётся в `AuthContext` на строке 45, а при нехватке роли метод возвращает `false` на строке 49. В таком сценарии `afterCompletion()` текущего interceptor'а может не выполниться, и `ThreadLocal` останется в потоке до следующей очистки. - -**Риск:** утечка контекста пользователя между запросами в servlet thread pool. Сейчас это в основном риск безопасности и будущих багов, но его лучше устранить сразу. - -**Как исправить:** перед каждым `return false` после `AuthContext.setCurrentUser(user)` вызывать `AuthContext.clear()`. Ещё лучше — не устанавливать `AuthContext`, пока не пройдена ролевая проверка, или обернуть обработку отказов в безопасный helper. - ---- - -### 4. Высокая — точечные изменения расписания не проверяются на конфликты - -**Файл:** `backend/src/main/java/com/magistr/app/controller/ScheduleOverrideController.java:71-127` - -**Проблема:** при `MOVE`/`REPLACE` валидируется только существование нового преподавателя, аудитории и слота. Не проверяется, свободны ли преподаватель/аудитория/группа в выбранную дату и пару. - -**Риск:** учебный отдел может создать override, который назначит преподавателя или аудиторию на две пары одновременно. Основные правила расписания конфликты проверяют, а override — нет. - -**Как исправить:** -- Перед сохранением override построить расписание на `lessonDate` и проверить занятость нового `timeSlot`, `teacher`, `classroom`, групп/подгрупп базового слота. -- Переиспользовать/вынести конфликтную логику из `ScheduleRuleAdminController` в сервис. -- Вернуть `409 Conflict` с описанием конфликтующего занятия. - ---- - -### 5. Средняя/высокая — расписание преподавателя не показывает пары, где он назначен через override +### 2. Flyway создаёт во всех новых tenant-БД предсказуемые учётные записи и демонстрационные данные **Файлы:** -- `backend/src/main/java/com/magistr/app/service/ScheduleQueryService.java:47-58` -- `backend/src/main/java/com/magistr/app/repository/ScheduleRuleRepository.java:36-54` -**Проблема:** если поиск выполняется только по `teacherId`, сервис строит расписание через `buildScheduleForTeacher(teacherId)`, то есть берёт только базовые правила, где преподаватель указан в слоте. Если override заменил преподавателя на этого teacherId, базовое правило в выборку не попадёт, и после `applyOverrides()` такая пара не появится. +- `backend/src/main/resources/db/migration/V1__init.sql:21-24,38-42,85-92,221-265` +- `docs/README.md:45-54` -**Риск:** преподаватель не увидит замену/переназначенную ему пару в своём расписании. +**Проблема:** V1 создаёт администратора с публично документированным коротким паролем, а также несколько тестовых пользователей с одинаковым известным паролем. Эта же миграция запускается для tenant-БД, добавленных в production, и одновременно добавляет демонстрационные кафедры, группы и другие данные. + +**Риск:** немедленный административный доступ к свежему tenant, если пароль не сменили вручную; загрязнение production-БД тестовыми сущностями. **Как исправить:** -- При поиске по `teacherId` дополнительно учитывать overrides с `newTeacher.id = teacherId` за период. -- Либо строить расписание по группам для затронутых override-слотов и после применения override фильтровать по преподавателю. -- Добавить тест: базовый преподаватель A, override `newTeacher=B`, поиск по `teacherId=B` должен вернуть пару. + +- Не менять V1. Добавить новую миграцию `V3__disable_demo_accounts.sql`, которая архивирует/удаляет демонстрационные аккаунты и данные там, где они не были явно сохранены. +- Создавать первого администратора отдельной bootstrap-процедурой с одноразовым случайным секретом. +- Разнести demo/dev seed и обязательную production-схему. +- Ротировать пароли уже созданных tenant-БД. + +## Высокий приоритет + +### 3. Race condition при ротации refresh-токена + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/config/auth/RefreshTokenService.java:50-91` +- `backend/src/main/java/com/magistr/app/repository/AuthRefreshTokenRepository.java:10` + +**Проблема:** `rotate()` читает токен, проверяет его активность, а затем отзывает старый и создаёт новый без блокировки строки или условного атомарного UPDATE. Два параллельных запроса с одним cookie могут оба пройти проверку и создать две новые сессии. + +**Риск:** повторное использование refresh-токена, раздвоение цепочки ротации и обход ожидаемой семантики single-use. + +**Как исправить:** использовать `PESSIMISTIC_WRITE` либо атомарный `UPDATE ... WHERE revoked_at IS NULL AND expires_at > now()` и создавать следующий токен только при обновлении одной строки. Добавить конкурентный интеграционный тест с PostgreSQL. --- -### 6. Средняя — обновление существующего тенанта может удалить рабочее подключение и сохранить нерабочее +### 4. `AuthContext` остаётся в servlet-потоке после отказа по роли + +**Файл:** `backend/src/main/java/com/magistr/app/config/auth/AuthorizationInterceptor.java:45-49,65-68` + +**Проблема:** пользователь записывается в ThreadLocal до проверки `@RequireRoles`. Если interceptor возвращает `false`, его собственный `afterCompletion()` не вызывается, поэтому контекст не очищается. + +**Риск:** следующий запрос на том же потоке может увидеть данные предыдущего пользователя, особенно если он проходит через публичный auth endpoint или останавливается в более раннем interceptor. + +**Как исправить:** очищать контекст в начале `preHandle()` и перед каждым `return false`; ещё лучше — устанавливать пользователя только после успешной проверки роли. Добавить MockMvc-тест последовательных запросов на одном executor-потоке. + +--- + +### 5. Точечные изменения расписания создаются без проверки конфликтов и факта существования пары **Файлы:** -- `backend/src/main/java/com/magistr/app/controller/DatabaseController.java:104-116` + +- `backend/src/main/java/com/magistr/app/controller/ScheduleOverrideController.java:71-126` +- `backend/src/main/java/com/magistr/app/service/ScheduleQueryService.java:67-141` + +**Проблема:** для `MOVE`/`REPLACE` проверяется только существование сущностей. Не проверяется занятость преподавателя, аудитории и групп в новой паре. Также можно сохранить override на дату, когда базовый слот вообще не генерируется, либо сохранить фактически пустой `REPLACE`. + +**Риск:** двойное назначение ресурсов; накопление неработающих изменений, которые API считает успешными. + +**Как исправить:** перед сохранением построить исходную пару на указанную дату, проверить action-specific поля и пересечения уже применённого расписания. При конфликте возвращать `409 Conflict`. + +--- + +### 6. Расписание преподавателя не показывает пары, назначенные ему через override + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/service/ScheduleQueryService.java:47-63` +- `backend/src/main/java/com/magistr/app/repository/ScheduleRuleRepository.java:36-58` +- `backend/src/main/java/com/magistr/app/repository/ScheduleOverrideRepository.java:14-26` + +**Проблема:** запрос только по `teacherId` сначала выбирает базовые правила этого преподавателя. Если override заменяет преподавателя A на B, правило A не попадает в исходную выборку B, поэтому последующее `applyOverrides()` уже не может добавить пару. + +**Риск:** преподаватель B не видит назначенную ему замену. + +**Как исправить:** учитывать overrides с `newTeacher.id = teacherId` за период и достраивать соответствующие базовые пары до финальной фильтрации. + +--- + +### 7. Валидатор правил расписания пропускает несколько нарушений инвариантов + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/controller/ScheduleRuleAdminController.java:220-228,440-481,524-565` +- `backend/src/main/java/com/magistr/app/service/ScheduleGeneratorService.java:252-293` +- `backend/src/main/resources/db/migration/V1__init.sql:707-726` + +**Проблемы:** + +- конфликт ищется только с уже сохранёнными правилами; два конфликтующих/дублирующих слота внутри одного нового правила не сравниваются между собой; +- пользователь слота проверяется на архивность, но не на роль `TEACHER`; +- `lessonFormat` проверяется только на непустоту, хотя DB допускает лишь «Очно»/«Онлайн»; +- нечётное число академических часов принимается, хотя генератор списывает по два часа и может выдать больше часов, чем задано. + +**Риск:** дубли в один момент времени, назначение студента/администратора преподавателем, HTTP 500 на DB CHECK и неверный расход часов. + +**Как исправить:** валидировать пары новых слотов между собой, роль пользователя, enum формата и кратность часов двум; продублировать ключевые ограничения новой Flyway-миграцией там, где это возможно. + +--- + +### 8. Ошибка в строке календарной сетки возвращает 400, но удаляет старую сетку и коммитит часть новой + +**Файл:** `backend/src/main/java/com/magistr/app/controller/AcademicCalendarController.java:125-146` + +**Проблема:** `saveGrid()` работает в транзакции, сначала удаляет всю старую сетку, затем валидирует и сохраняет строки по одной. `IllegalArgumentException` ловится внутри transactional-метода и не выходит за границу proxy, поэтому транзакция считается успешной. + +**Воспроизведение:** передать список, где первая строка корректна, а вторая содержит неверный день/дату. API вернёт 400, но старая сетка будет удалена, а первая новая строка сохранится. + +**Риск:** тихая потеря календарного учебного графика при ошибке пользователя. + +**Как исправить:** сначала провалидировать весь список и уникальность ключей, затем одним шагом заменять данные; либо не перехватывать исключение внутри транзакции/помечать транзакцию rollback-only. + +--- + +### 9. Обновление tenant может удалить рабочее подключение и успешно сохранить нерабочее + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/controller/DatabaseController.java:104-124,175-182` - `backend/src/main/java/com/magistr/app/config/tenant/TenantRoutingDataSource.java:135-148` -- `backend/src/main/java/com/magistr/app/config/tenant/TenantConfigWatcher.java:147-158` +- `backend/src/main/java/com/magistr/app/config/tenant/TenantConfigWatcher.java:133-160` -**Проблема:** при добавлении тенанта с уже существующим доменом старый DataSource сначала удаляется и закрывается (`removeTenant()`), затем добавляется новый. Hikari настроен с `setInitializationFailTimeout(-1)`, поэтому невалидная БД может быть добавлена без ошибки. `initDatabaseForTenant()` ловит исключения Flyway и не пробрасывает их наружу, поэтому API может вернуть успех и записать нерабочий конфиг. +**Проблема:** существующий DataSource сначала удаляется и закрывается. Новый Hikari pool создаётся с `initializationFailTimeout=-1`. Ошибка Flyway проглатывается, а результат записи ConfigMap игнорируется. API способен вернуть успех после неудачной миграции или неудачной персистенции. -**Риск:** админ одной ошибкой в JDBC URL/пароле может выключить рабочий tenant и разнести нерабочую конфигурацию через ConfigMap. +**Риск:** одна опечатка в URL/пароле отключает рабочий tenant; разные pod получают разную конфигурацию. -**Как исправить:** -- Сначала создать и проверить новый DataSource во временном объекте (`testConnection`, Flyway migrate) и только после успеха атомарно заменить старый. -- `initDatabaseForTenant()` должен возвращать результат или бросать исключение, чтобы `DatabaseController` не писал невалидный ConfigMap. -- При ошибке оставлять старое подключение активным. +**Как исправить:** создать временный pool, проверить соединение и миграции, затем атомарно заменить старый. Ошибки миграции и ConfigMap должны прерывать операцию; нужен compensating rollback. --- -### 7. Средняя — watcher не применяет изменения URL/логина/пароля существующего тенанта +### 10. Два backend-pod могут потерять изменения tenant-конфигурации, а probes не видят отказ БД -**Файл:** `backend/src/main/java/com/magistr/app/config/tenant/TenantConfigWatcher.java:102-125` +**Файлы:** -**Проблема:** `syncTenants()` добавляет только новые домены и удаляет исчезнувшие. Если в `tenants.json` изменились `url`, `username` или `password` для уже существующего `domain`, текущий DataSource не заменяется. +- `../k8s/backend.yaml:31,86-97` +- `backend/src/main/java/com/magistr/app/controller/DatabaseController.java:177-182` +- `backend/src/main/java/com/magistr/app/config/tenant/ConfigMapUpdater.java:62-92` -**Риск:** после обновления ConfigMap часть pod'ов продолжит ходить в старую БД/со старыми credentials до рестарта. +**Проблема:** deployment имеет две реплики. Каждая PATCH-операция перезаписывает весь `tenants.json` из локальной in-memory карты без resourceVersion/compare-and-swap. Параллельное добавление A на pod-1 и B на pod-2 приводит к last-write-wins и потере A или B. TCP readiness/liveness считает pod здоровым, даже если все tenant-БД недоступны и Flyway завершился ошибкой. -**Как исправить:** сравнивать весь `TenantConfig` для существующих доменов. При отличии — безопасно пересоздавать DataSource после успешной проверки нового подключения. +**Риск:** потерянные конфигурационные изменения и маршрутизация трафика на фактически неработающий pod. + +**Как исправить:** вынести операции в единый coordinator/CRD/БД либо применять optimistic locking к ConfigMap с повтором; добавить health endpoint, проверяющий миграции и доступность обязательных tenant. --- -### 8. Средняя — отключена проверка TLS-сертификата Kubernetes API +### 11. Дашборд может показать зелёное «конфликтов нет», когда проверка фактически не выполнена + +**Файл:** `frontend/admin/js/views/dashboard.js:148-168,274-287` + +**Проблемы:** + +- диапазон недели вычисляется двойным мутированием одного `Date`; в первые дни месяца конец диапазона попадает в начало предыдущего месяца и оказывается раньше начала; +- каждый запрос кафедры имеет `catch(() => [])`, поэтому сетевые/серверные ошибки превращаются в пустое расписание; +- после этого пустой результат отображается как «Конфликты расписания не обнаружены». + +**Проверенный пример:** для 2026-07-02 код строит диапазон примерно 2026-06-29 … 2026-06-05 вместо 2026-06-29 … 2026-07-05. + +**Риск:** ложное подтверждение безопасности расписания именно в панели контроля. + +**Как исправить:** вычислять воскресенье как `monday + 6 дней` на отдельном объекте, форматировать локальную дату без UTC и различать состояния «нет конфликтов»/«проверка частично или полностью не выполнена». + +--- + +### 12. Access JWT и удалённый JavaScript выполняются в одном origin + +**Файлы:** + +- `frontend/admin/js/api.js:5-7,155-163` +- `frontend/script.js:5-28` +- `frontend/admin/js/otel.js:1-8` + +**Проблема:** JWT хранится в `localStorage`. Страница входа динамически исполняет неприкреплённые по версии модули с `esm.sh`, а admin — удалённые модули без SRI. Такой код имеет доступ к DOM, введённому паролю и `localStorage`. + +**Риск:** компрометация CDN/пакета или XSS сразу даёт учётные данные и Bearer-токен. + +**Как исправить:** собирать и раздавать telemetry dependencies локально с lockfile; держать access token в памяти или перейти на защищённую cookie-сессию; добавить строгий CSP и убрать inline-скрипты. + +## Средний приоритет + +### 13. Watcher не применяет изменения существующего tenant и не повторяет неудачную синхронизацию + +**Файл:** `backend/src/main/java/com/magistr/app/config/tenant/TenantConfigWatcher.java:60-71,102-125` + +**Проблема:** сравниваются только множества доменов — смена URL, логина или пароля существующего домена игнорируется. Кроме того, новый hash записывается до JSON parsing/sync; если синхронизация падает, следующий poll считает файл уже обработанным и не повторяет операцию. + +**Риск:** pod продолжает использовать старые credentials либо навсегда остаётся в частично синхронизированном состоянии до следующего изменения файла/рестарта. + +**Как исправить:** сравнивать весь нормализованный `TenantConfig`, записывать hash только после полного успеха и сохранять retry/backoff-состояние. + +--- + +### 14. Полностью отключена проверка TLS Kubernetes API **Файл:** `backend/src/main/java/com/magistr/app/config/tenant/ConfigMapUpdater.java:74-76,104-119` -**Проблема:** `createInsecureClient()` доверяет любому сертификату (`checkServerTrusted` пустой). Комментарий объясняет это self-signed CA, но в Kubernetes правильный CA доступен в serviceaccount volume. +**Проблема:** trust manager принимает любой сертификат. Kubernetes CA уже доступен в serviceaccount volume. -**Риск:** MITM внутри сети кластера может подменить Kubernetes API и получить serviceaccount token/подменить ConfigMap. +**Риск:** MITM к API server, утечка serviceaccount token и подмена tenant ConfigMap. -**Как исправить:** использовать CA из `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt` и стандартную проверку hostname/cert chain. Не использовать trust-all клиент. +**Как исправить:** загружать `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt`, использовать стандартную проверку цепочки и hostname. --- -### 9. Средняя — кабинет кафедры может перезаписать дисциплину другой кафедры при импорте +### 15. Редактор временных слотов позволяет создать пересекающиеся интервалы и сломать базовые правила + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/controller/TimeSlotAdminController.java:152-224` +- `backend/src/main/java/com/magistr/app/service/ScheduleGeneratorService.java:418-435` + +**Проблемы:** + +- проверяется только уникальность номера пары, но не пересечение временных интервалов внутри сетки; +- update разрешает перенести существующий базовый слот в MANUAL scope, хотя правила обязаны ссылаться на DEFAULT; +- `durationMinutes` может не соответствовать разнице `startTime/endTime`. + +**Риск:** два занятия реально идут одновременно, но конфликтный анализ считает их разными слотами; существующие правила исчезают из ожидаемой базовой сетки. + +**Как исправить:** запретить пересечение интервалов, фиксировать scope используемого базового слота и вычислять duration на backend. + +--- + +### 16. Учебные годы и семестры допускают пересечения и выход семестра за границы года + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/controller/AcademicCalendarAdminController.java:46-129,188-237` +- `backend/src/main/java/com/magistr/app/service/AcademicDateService.java:32-34` +- `backend/src/main/java/com/magistr/app/repository/SemesterRepository.java:13-17` + +**Проблема:** проверяется только порядок двух дат. Можно пересечь учебные годы, пересечь осенний и весенний семестры, вынести семестр за границы года. При update также не проверяется дубликат title/type до DB constraint. + +**Риск:** `findFirst...` выбирает произвольный семестр для даты; номер недели и чётность становятся недетерминированными, а часть update-запросов заканчивается HTTP 500. + +**Как исправить:** централизовать календарную валидацию, запретить пересечения и добавить DB-level exclusion/range constraints новой миграцией. + +--- + +### 17. Изменение календаря или группы нарушает уже сохранённые назначения + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/controller/AcademicCalendarController.java:85-98,178-214` +- `backend/src/main/java/com/magistr/app/controller/GroupController.java:173-212,322-359` + +**Проблема:** после назначения графика группе можно изменить у графика учебный год, специальность, профиль, форму или количество курсов; можно также изменить эти поля у группы. Существующее `student_group_calendar_assignments` не перепроверяется и не удаляется. При уменьшении courseCount остаются строки сетки старших курсов. + +**Риск:** расписание строится по графику, который больше не соответствует группе/году, хотя API проверял соответствие при первоначальном назначении. + +**Как исправить:** запрещать изменение ключевых измерений у используемого графика либо транзакционно перевалидировать назначения, grid и subjects. + +--- + +### 18. Историческая модель кафедр преподавателя используется непоследовательно + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/controller/UserController.java:218-259,310-324` +- `backend/src/main/java/com/magistr/app/controller/WorkloadController.java:51-60,85-100` +- `backend/src/main/java/com/magistr/app/controller/TeacherSubjectController.java:102-115` + +**Проблемы:** + +- будущий перевод немедленно меняет `users.department_id`, и преподаватель до `validFrom` появляется одновременно в старой и новой кафедре; +- исторический список фильтрует `User::isActiveRecord`, поэтому архивный преподаватель исчезает даже для даты, когда он работал; +- workload прошлых периодов относится к текущему `users.department_id`; +- дополнительная активная кафедральная связь учитывается в списке преподавателей, но `TeacherSubjectController` разрешает связь только по legacy `users.department_id`. + +**Риск:** неверные исторические отчёты и неработоспособность дополнительного назначения преподавателя. + +**Как исправить:** считать кафедру через `teacher_department_assignments` на дату; legacy-поле обновлять только в дату вступления перевода в силу либо не использовать для бизнес-решений. + +--- + +### 19. Роль `DEPARTMENT` может запрашивать workload чужой кафедры + +**Файл:** `backend/src/main/java/com/magistr/app/controller/WorkloadController.java:43-135` + +**Проблема:** endpoints принимают произвольный `departmentId` или отсутствие фильтра и не сопоставляют его с `AuthContext.departmentId`. + +**Риск:** межкафедральное раскрытие отчётов и обход ограничений, которые уже применяются в `DepartmentWorkspaceController` и части `UserController`. + +**Как исправить:** для `DEPARTMENT` принудительно использовать кафедру из токена; явно документировать endpoints, где глобальный просмотр действительно разрешён. + +--- + +### 20. Глобальные workload/free-classrooms ломаются при количестве активных групп больше 50 + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/controller/WorkloadController.java:43-135` +- `backend/src/main/java/com/magistr/app/service/ScheduleQueryService.java:160-180` + +**Проблема:** агрегирующие endpoints переиспользуют общий поиск с ограничением 50 групп. `free-classrooms` вообще не принимает scope, поэтому в крупном tenant неизбежно получает `IllegalArgumentException` вместо списка свободных аудиторий. + +**Риск:** штатная функция перестаёт работать при росте университета. + +**Как исправить:** сделать специализированные агрегирующие запросы/батч-генерацию, а не использовать ограничение интерактивного широкого поиска. + +--- + +### 21. Импорт кафедры может переназначить дисциплину другой кафедры **Файл:** `backend/src/main/java/com/magistr/app/controller/DepartmentWorkspaceController.java:80-90` -**Проблема:** импорт ищет дисциплину только по глобальному имени (`findByName`) и затем без проверки меняет `departmentId` на кафедру текущего пользователя. Если дисциплина с таким названием уже принадлежит другой кафедре, она будет переназначена. +**Проблема:** дисциплина ищется по глобальному имени, после чего `departmentId` безусловно меняется на текущую кафедру. -**Риск:** потеря принадлежности дисциплины и связей расписания/календарей у другой кафедры. +**Риск:** потеря принадлежности и изменение расписаний/календарей другой кафедры. -**Как исправить:** -- Не менять `departmentId` существующей дисциплины другой кафедры. -- Сделать уникальность по `(department_id, lower(name))`, если одинаковые названия допустимы у разных кафедр. -- При конфликте возвращать понятную ошибку или создавать отдельную запись в рамках кафедры. +**Как исправить:** не менять чужую запись; решить бизнес-правило уникальности и при необходимости перейти на `(department_id, lower(name))` новой миграцией. --- -### 10. Средняя — кафедра может смотреть workload по чужим кафедрам - -**Файл:** `backend/src/main/java/com/magistr/app/controller/WorkloadController.java:43-130` - -**Проблема:** endpoints `/api/workload/*` доступны роли `DEPARTMENT` и принимают произвольный `departmentId`, но не сверяют его с `AuthContext.departmentId`. Также при `departmentId` отсутствующем запрос строится по всем группам/кафедрам. - -**Риск:** оператор кафедры может получить загруженность преподавателей/аудиторий/кафедр за другие подразделения, если это не было задумано бизнес-правилами. - -**Как исправить:** для роли `DEPARTMENT` принудительно использовать `AuthContext.getCurrentUser().departmentId()` и игнорировать/запрещать чужой `departmentId`. Аналогично проверить `ScheduleSearchController`, если расписание не должно быть глобально видимым. - ---- - -### 11. Средняя — семестры можно создать вне учебного года или с пересечениями +### 22. Права frontend и backend расходятся для учебного отдела **Файлы:** -- `backend/src/main/java/com/magistr/app/controller/AcademicCalendarAdminController.java:95-129,201-211` -- `backend/src/main/java/com/magistr/app/service/AcademicDateService.java:32-34` -- `backend/src/main/java/com/magistr/app/repository/SemesterRepository.java:13` -**Проблема:** при создании/обновлении семестра проверяется только `endDate >= startDate`. Нет проверки, что семестр лежит внутри своего учебного года и не пересекается с другим семестром. При этом расписание ищет `findFirstByStartDateLessThanEqualAndEndDateGreaterThanEqual()`, то есть при пересечении дата будет сопоставляться с произвольным первым семестром. +- `backend/src/main/java/com/magistr/app/controller/SpecialityController.java:23-24,38-58,196-213` +- `backend/src/main/java/com/magistr/app/controller/EducationFormController.java:17-18,33-59` +- `frontend/admin/js/views/academic-calendar.js:106-118,207-218` +- `frontend/admin/settings/js/main.js:47-52` -**Риск:** генерация расписания и чётность недель будут работать некорректно на пересекающихся датах. +**Проблемы:** -**Как исправить:** -- Валидировать границы семестра относительно `AcademicYear.startDate/endDate`. -- Запрещать пересечение семестров в рамках учебного года. -- Желательно добавить DB-level exclusion constraint/range constraint или хотя бы сервисную проверку и тест. +- `EDUCATION_OFFICE` видит вкладку календарных графиков, но её обязательные GET `/api/specialties` и profiles разрешены только `ADMIN`; инициализация попадает в 403; +- settings показывает учебному отделу CRUD форм обучения, но POST/DELETE backend разрешены только `ADMIN`. + +**Риск:** видимые штатные экраны частично или полностью не работают. + +**Как исправить:** согласовать role matrix в одном источнике; либо расширить read/write права, либо скрыть недоступные действия. --- -### 12. Низкая/средняя — access-токен хранится в `localStorage` +### 23. Генерация расписания имеет выраженный N+1 по дням, правилам и группам -**Файл:** `frontend/admin/js/api.js:5-7,155-163` +**Файлы:** -**Проблема:** access JWT хранится в `localStorage`, откуда его может украсть любой XSS в админке. Refresh-токен уже вынесен в HttpOnly cookie, но access-токен остаётся доступным JS. +- `backend/src/main/java/com/magistr/app/service/ScheduleGeneratorService.java:64-90,107-124,187-229,297-308` +- `backend/src/main/java/com/magistr/app/service/AcademicDateService.java:58-87` -**Риск:** при XSS злоумышленник получает Bearer token и может выполнять API-запросы до истечения TTL. +**Проблема:** семестр повторно ищется внутри каждого правила, а `isTheoryDay()` для каждой группы и даты выполняет отдельные запросы assignment + calendar day. В teacher mode это повторяется по всем группам правила. -**Как исправить:** -- По возможности держать access token в памяти и обновлять через HttpOnly refresh cookie. -- Усилить CSP (`script-src` без inline), запретить небезопасные вставки HTML. -- Провести отдельный XSS-аудит всех `innerHTML`. +**Риск:** диапазон 120 дней и десятки групп порождают тысячи/десятки тысяч SQL-запросов, таймауты и нагрузку на каждую tenant-БД. + +**Как исправить:** заранее батчем загружать semesters, assignments и calendar days за диапазон; передавать уже найденный semester в `processRuleForDate()`. Добавить метрики query count и нагрузочный тест. --- -### 13. Низкая — frontend местами вставляет текст ошибки через `innerHTML` без экранирования +### 24. Истёкшие и отозванные refresh-токены никогда не удаляются -**Файл:** `frontend/admin/settings/js/views/database.js:37-49` +**Файлы:** -**Проблема:** `e.message` вставляется в `innerHTML` без `escapeHtml()`. Сейчас большинство серверных ошибок фиксированные, но часть ошибок может включать текст из внешних систем/драйверов. +- `backend/src/main/java/com/magistr/app/config/auth/RefreshTokenService.java:34-107` +- `backend/src/main/java/com/magistr/app/repository/AuthRefreshTokenRepository.java:8-11` -**Риск:** потенциальный XSS в настройках БД при попадании HTML в сообщение ошибки. +**Проблема:** каждый login/refresh добавляет строку, ротация лишь ставит `revoked_at`. Cleanup job/repository delete отсутствует. -**Как исправить:** заменить на `textContent` или оборачивать `escapeHtml(e.message)`. +**Риск:** неограниченный рост `auth_refresh_tokens` и индексов. + +**Как исправить:** периодически удалять давно истёкшие/отозванные строки с разумным audit retention; покрыть job тестом. --- -### 14. Низкая — backend image скачивает OpenTelemetry javaagent по `latest` +### 25. DB constraint violations превращаются в 500 и иногда раскрывают текст драйвера -**Файл:** `backend/Dockerfile:7` +**Файлы:** -**Проблема:** сборка всегда скачивает `latest` javaagent с GitHub без pin версии/checksum. +- `backend/src/main/java/com/magistr/app/controller/GlobalExceptionHandler.java:23-55` +- `backend/src/main/java/com/magistr/app/controller/GroupController.java:166-169,216-219` +- `backend/src/main/java/com/magistr/app/controller/SubjectController.java:122-125` +- `backend/src/main/java/com/magistr/app/controller/DatabaseController.java:121-124,166-171` -**Риск:** невоспроизводимые сборки; внезапные несовместимости; supply-chain риск. +**Проблема:** нет общего обработчика `DataIntegrityViolationException`/constraint name. Часть контроллеров ловит общий `Exception` и возвращает клиенту `e.getMessage()`. -**Как исправить:** закрепить конкретную версию javaagent и проверять checksum. +**Риск:** неправильный HTTP status, английские/технические сообщения в UI и раскрытие деталей схемы/JDBC. -## Дополнительно проверить после исправлений +**Как исправить:** добавить единый перевод constraint → 409/400 с русским сообщением; не отдавать внутренний exception text. -1. Добавить Maven Wrapper (`mvnw`), чтобы проверки запускались одинаково в CI и локально. -2. Прогнать `mvn test` и добавить тесты на: - - параллельную ротацию refresh-токена; - - override с конфликтом преподавателя/аудитории; - - расписание преподавателя, назначенного через override; - - обновление существующего tenant config; - - запрет чужого `departmentId` для роли `DEPARTMENT`. -3. Не изменять существующие Flyway-миграции. Если нужны DB constraints/indexes — добавлять новую миграцию `V3__...sql`. +--- + +### 26. Чистый локальный запуск по документации не публикует приложение и имеет противоречивую конфигурацию БД + +**Файлы:** + +- `compose.yaml:1-43` +- `backend/src/main/resources/application.properties:3-6,18-22` +- `docs/README.md:33-43` +- `docs/INFRASTRUCTURE.md:3-37` + +**Проблемы:** + +- frontend/backend не имеют `ports`, а Caddy не входит в Compose, поэтому на чистой машине `localhost:80` после указанной команды недоступен; +- `POSTGRES_DB` может изменить создаваемую DB, но backend URL жёстко указывает `app_db`; +- JWT-переменные из `.env` не передаются контейнеру backend; +- для PostgreSQL не объявлен именованный volume, поэтому после down/recreate данные могут остаться в потерянном anonymous volume. + +**Как исправить:** добавить локальный reverse proxy/ports, передавать согласованный JDBC URL и JWT env, объявить named volume и обновить quick start. + +--- + +### 27. CI/CD разворачивает код без запуска тестов, а tag pipeline перезапускает `:main` + +**Файлы:** + +- `.gitea/workflows/docker-build.yaml:3-8,35-40,63-68,73-96` +- `backend/Dockerfile:1-6` +- `../k8s/backend.yaml:45-46` +- `../k8s/frontend.yaml:20-21` + +**Проблема:** workflow не имеет test job, а Dockerfile выполняет `mvn package -DskipTests`. На событии tag metadata публикует tag-образ, но deployment продолжает ссылаться на mutable `:main` и просто перезапускается. + +**Риск:** production получает непроверенный commit; release-tag может перезапустить старый main-образ вместо релиза. + +**Как исправить:** обязательные test/compile/static-check jobs до push/deploy; деплой image digest или конкретного release tag; concurrency lock и rollback при failed rollout. + +--- + +### 28. Нет ограничения попыток входа + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/controller/AuthController.java:57-86` +- `../k8s/ingress.yaml:1-45` + +**Проблема:** login не имеет rate limit, задержки, lockout или CAPTCHA; в Ingress также нет соответствующего middleware/аннотации. + +**Риск:** online brute force и password spraying, особенно опасные вместе с известными seed-аккаунтами. + +**Как исправить:** rate limit по tenant+IP+username, прогрессивная задержка и аудит неудачных входов без логирования пароля. + +--- + +### 29. Часовой пояс backend не зафиксирован, а часть frontend дат строится через UTC + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/model/LifecycleEntity.java:94-98` +- `backend/src/main/java/com/magistr/app/controller/UserController.java:85,164,235,271` +- `backend/src/main/java/com/magistr/app/service/ScheduleGeneratorService.java:236` +- `frontend/admin/js/views/dashboard.js:48,159-160` +- `frontend/admin/js/views/department-workspace.js:494-504` +- `compose.yaml` и `../k8s/backend.yaml` + +**Проблема:** бизнес-даты используют `LocalDate.now()`/`LocalDateTime.now()` с timezone JVM, но контейнеры не получают `TZ`/`user.timezone`. Frontend использует `toISOString()`, поэтому в Москве до 03:00 получает предыдущую календарную дату. + +**Риск:** архивирование, перевод, доступность ресурсов и «сегодняшнее расписание» сдвигаются на день/несколько часов в зависимости от окружения. + +**Как исправить:** внедрить `Clock` и явный `ZoneId` для бизнес-даты, хранить абсолютные timestamps как UTC/`Instant`, форматировать date-only локальными компонентами. + +--- + +### 30. Сборки и deployment используют mutable/непроверяемые артефакты + +**Файлы:** + +- `backend/Dockerfile:7` +- `../k8s/otel-collector.yaml:59` +- `../k8s/backend.yaml:45-46` +- `../k8s/frontend.yaml:20-21` +- `.gitea/workflows/docker-build.yaml:83-87` + +**Проблема:** javaagent и kubectl скачиваются как latest/stable без checksum, collector использует `latest`, приложения — mutable `main`. + +**Риск:** невоспроизводимые сборки, неожиданные несовместимости и supply-chain подмена. + +**Как исправить:** pin версии и image digest, проверять SHA256/signature, генерировать SBOM и сканировать образы. + +--- + +### 31. Группы допускают некорректную численность и ломают существующее деление на подгруппы + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/controller/GroupController.java:173-212,250-272` +- `backend/src/main/java/com/magistr/app/controller/SubgroupController.java:151-175` +- `backend/src/main/resources/db/migration/V1__init.sql:203-219` + +**Проблема:** `groupSize` проверяется только на null, `yearStartStudy` — только на ноль; DB не имеет CHECK для положительной численности. При update можно уменьшить группу ниже суммы активных подгрупп, хотя SubgroupController такой результат запрещает при редактировании подгрупп. + +**Риск:** отрицательная/нулевая численность, неверный расчёт вместимости и внутренне противоречивые подгруппы. + +**Как исправить:** единый валидатор create/update, проверка суммы подгрупп и новая миграция с CHECK constraints. + +## Низкий приоритет + +### 32. Лимит «120 дней» фактически разрешает 121 дату + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/service/ScheduleQueryService.java:148-157` +- `backend/src/main/java/com/magistr/app/service/ScheduleGeneratorService.java:135-144` + +**Проблема:** проверяется `DAYS.between(start, end) > 120`, но обе границы включены. Разница 120 означает 121 календарную дату. + +**Как исправить:** проверять включительную длину (`between + 1`) либо изменить текст/документацию. + +--- + +### 33. Остались XSS-точки в сообщениях ошибок и видимые поля пароля + +**Файлы:** + +- `frontend/admin/settings/js/views/database.js:39,49` +- `frontend/admin/js/main.js:260-262` +- `frontend/admin/settings/js/main.js:130-133` +- `frontend/admin/views/users.html:11-12` +- `frontend/admin/js/views/teacher-requests.js:47-51` + +**Проблема:** несколько `e.message` вставляются через `innerHTML` без экранирования. Поля пароля при создании пользователя/одобрении заявки имеют `type="text"`. + +**Риск:** потенциальный DOM XSS при попадании управляемого текста ошибки; пароль виден на экране и может сохраняться как обычный текст формы. + +**Как исправить:** использовать `textContent`/`escapeHtml`, а для паролей — `type="password"` и корректные `autocomplete`. + +--- + +### 34. Проектный языковой регламент нарушен в UI и логах + +**Файлы:** + +- `backend/src/main/java/com/magistr/app/config/tenant/TenantRoutingDataSource.java:65,78,95` +- `backend/src/main/java/com/magistr/app/config/tenant/TenantDataSourceConfig.java:60,123` +- `frontend/admin/settings/js/views/database.js:14-16,60-62` +- `frontend/admin/js/otel.js:47` + +**Проблема:** присутствуют английские production-логи и UI-метки `Online/Offline`; JDBC exception также может попасть в UI на английском. + +**Как исправить:** привести пользовательские сообщения и проектные логи к русскому языку; технические идентификаторы оставлять в structured fields. + +## Что покрыть тестами в первую очередь + +1. PostgreSQL/Testcontainers + Flyway: чистая БД, upgrade существующей БД, отсутствие активных demo-аккаунтов. +2. Два параллельных refresh-запроса с одним токеном. +3. Цепочка interceptor: 403, затем публичный/защищённый запрос на том же потоке. +4. Override с конфликтом и поиск преподавателя, назначенного через `newTeacher`. +5. Внутренние конфликты одного нового правила, роль преподавателя, нечётные часы и недопустимый формат. +6. `saveGrid` с корректной и ошибочной строкой: после 400 старая сетка должна остаться целой. +7. Изменение tenant URL/credentials, неудачная миграция, параллельные PATCH от двух pod. +8. Более 50 активных групп для workload/free-classrooms. +9. Future-dated перевод преподавателя и исторические отчёты. +10. E2E для `EDUCATION_OFFICE`: календарные графики и настройки форм обучения. +11. Frontend-тест дат в первые дни месяца и в 00:00–03:00 Europe/Moscow. + +## Рекомендуемый порядок исправления + +1. Ротация/удаление секретов и отключение seed-аккаунтов. +2. Data-loss и security races: calendar grid, refresh rotation, AuthContext. +3. Атомарность tenant-конфигурации и реальные health checks. +4. Конфликты overrides/rules и корректность расписания преподавателя. +5. Согласование ролей, календарных инвариантов и исторической модели кафедр. +6. Производительность генератора, Compose/CI/CD и frontend hardening. + +Существующие Flyway-миграции не изменять. Все исправления схемы оформлять новой миграцией, начиная с `V3__...sql`. diff --git a/graphify-out/.graphify_learning.json b/graphify-out/.graphify_learning.json index 6da639c..3dd8c8c 100644 --- a/graphify-out/.graphify_learning.json +++ b/graphify-out/.graphify_learning.json @@ -1,5 +1,70 @@ { - "generated_at": "2026-07-04T10:57:14.633512+00:00", - "nodes": {}, + "generated_at": "2026-07-10T17:12:35.296680+00:00", + "nodes": { + "backend_src_main_java_com_magistr_app_config_auth_authorizationinterceptor_authorizationinterceptor": { + "code_fingerprint": "9f11d3686fe75ecd9f0b8abaa42702ed900baa8cc253a4025e377fe527fc3b81", + "label": "AuthorizationInterceptor", + "last": "2026-07-04T10:57:57.276777+00:00", + "provenance": [ + { + "date": "2026-07-04T10:57:57.276777+00:00", + "outcome": "useful", + "q": "Почему RequireRoles связывает множество контроллеров, моделей и сервисов как cross-community bridge?" + } + ], + "score": 0.865333358, + "source_file": "backend/src/main/java/com/magistr/app/config/auth/AuthorizationInterceptor.java", + "status": "tentative", + "uses": 1 + }, + "backend_src_main_java_com_magistr_app_config_auth_requireroles_requireroles": { + "code_fingerprint": "9690db03571a132129eadcebf6c24c1217bfb6b1781ec52eb05819e53266ce7e", + "label": "RequireRoles", + "last": "2026-07-04T10:57:57.276777+00:00", + "provenance": [ + { + "date": "2026-07-04T10:57:57.276777+00:00", + "outcome": "useful", + "q": "Почему RequireRoles связывает множество контроллеров, моделей и сервисов как cross-community bridge?" + } + ], + "score": 0.865333358, + "source_file": "backend/src/main/java/com/magistr/app/config/auth/RequireRoles.java", + "status": "tentative", + "uses": 1 + }, + "backend_src_main_java_com_magistr_app_model_schedulerule_schedulerule": { + "code_fingerprint": "692d61c1bfd5b46f5865174dde8cf2d962d2ccd124b5e4320e2ba8535bb29605", + "label": "ScheduleRule", + "last": "2026-07-04T12:07:28.781712+00:00", + "provenance": [ + { + "date": "2026-07-04T12:07:28.781712+00:00", + "outcome": "useful", + "q": "Корректны ли inferred-связи вокруг ScheduleRule?" + } + ], + "score": 0.866299207, + "source_file": "backend/src/main/java/com/magistr/app/model/ScheduleRule.java", + "status": "tentative", + "uses": 1 + }, + "backend_src_test_java_com_magistr_app_model_scheduleruletest_scheduleruletest": { + "code_fingerprint": "73cb9594bd5dc29ba9d9bde11e860d2c182b205b36062df87a8a8783611ffe79", + "label": "ScheduleRuleTest", + "last": "2026-07-04T12:07:28.781712+00:00", + "provenance": [ + { + "date": "2026-07-04T12:07:28.781712+00:00", + "outcome": "useful", + "q": "Корректны ли inferred-связи вокруг ScheduleRule?" + } + ], + "score": 0.866299207, + "source_file": "backend/src/test/java/com/magistr/app/model/ScheduleRuleTest.java", + "status": "tentative", + "uses": 1 + } + }, "version": 1 } diff --git a/graphify-out/memory/query_20260710_181403_проанализируй_весь_проект_на_баги_и_ошибки_и_допол.md b/graphify-out/memory/query_20260710_181403_проанализируй_весь_проект_на_баги_и_ошибки_и_допол.md new file mode 100644 index 0000000..0120119 --- /dev/null +++ b/graphify-out/memory/query_20260710_181403_проанализируй_весь_проект_на_баги_и_ошибки_и_допол.md @@ -0,0 +1,26 @@ +--- +type: "query" +date: "2026-07-10T18:14:03.213067+00:00" +question: "проанализируй весь проект на баги и ошибки и дополни '/mnt/HDD/ProjectMagistr/magistr/BUG_REPORT.md'" +contributor: "graphify" +outcome: "useful" +source_nodes: ["GlobalExceptionHandler", "AuthorizationInterceptor", "TenantConfigWatcher", "ConfigMapUpdater", "ScheduleRuleAdminController"] +--- + +# Q: проанализируй весь проект на баги и ошибки и дополни '/mnt/HDD/ProjectMagistr/magistr/BUG_REPORT.md' + +## Answer + +Expanded from original query via vocab: [error, exception, conflict, validation, authorization, database, schedule, tenant, frontend, backend, repository, controller]. Выполнен статический аудит backend, frontend, Flyway, Compose, CI/CD и Kubernetes; подтверждены 34 проблемы, а BUG_REPORT.md дополнен приоритетами, рисками, сценариями воспроизведения и рекомендациями. Maven: 30 тестов без ошибок; JavaScript, Compose и Kustomize прошли синтаксические проверки. + +## Outcome + +- Signal: useful + +## Source Nodes + +- GlobalExceptionHandler +- AuthorizationInterceptor +- TenantConfigWatcher +- ConfigMapUpdater +- ScheduleRuleAdminController \ No newline at end of file diff --git a/graphify-out/reflections/LESSONS.md b/graphify-out/reflections/LESSONS.md index 51e1271..ec1f82c 100644 --- a/graphify-out/reflections/LESSONS.md +++ b/graphify-out/reflections/LESSONS.md @@ -1,11 +1,16 @@ # Lessons -_Auto-generated by `graphify reflect` from 0 session memories in graphify-out/memory/. Deterministic; no LLM. Use for orientation — verify before relying, and revisit dead ends if the code has changed since._ +_Auto-generated by `graphify reflect` from 2 session memories in graphify-out/memory/. Deterministic; no LLM. Use for orientation — verify before relying, and revisit dead ends if the code has changed since._ ## Summary -- 0 useful · 0 dead ends · 0 corrected · 0 unmarked +- 2 useful · 0 dead ends · 0 corrected · 0 unmarked ## Lessons -_No marked outcomes yet._ +**Tentative** — useful in fewer than 2 results; verify before relying. + +- `ScheduleRule` (1× useful) +- `ScheduleRuleTest` (1× useful) +- `AuthorizationInterceptor` (1× useful) +- `RequireRoles` (1× useful)