diff --git a/doc/audit-2026-06-07.md b/doc/audit-2026-06-07.md new file mode 100644 index 0000000..e16d66c --- /dev/null +++ b/doc/audit-2026-06-07.md @@ -0,0 +1,65 @@ +# Отчёт об аудите (07.06.2026) + +## Найдено + +### 🔴 Критичные + +1. **Краш при отсутствии Bluetooth** + - **Файл:** [android/app/src/main/java/ru/elmer/client/ui/MainActivity.kt](android/app/src/main/java/ru/elmer/client/ui/MainActivity.kt) + - **Строки:** 155, 230, 310 + - **Описание:** Используется оператор `!!` для `btAdapter`. На устройствах без Bluetooth (или в эмуляторе) приложение упадёт при попытке проверить прибор или запустить сканирование. + - **Как исправить:** Добавить проверку `if (btAdapter == null)` перед использованием и выводить сообщение об ошибке. + +2. **Отсутствие аутентификации на сервере** + - **Файл:** [api/routes.py](api/routes.py) + - **Описание:** Эндпоинты `/api/v1/session/upload`, `/api/v1/chat` и `/api/v1/ping-llm` принимают запросы без проверки API-ключа. Клиент передаёт `X-Api-Key`, но сервер его игнорирует. Любой может отправлять запросы и тратить токены LLM. + - **Как исправить:** Добавить декоратор `@api_key_required` или проверку заголовка в `before_request`. + +3. **Состязание потоков (Race Condition) в ScriptRunnerService** + - **Файл:** [android/app/src/main/java/ru/elmer/client/script/ScriptRunnerService.kt](android/app/src/main/java/ru/elmer/client/script/ScriptRunnerService.kt) + - **Описание:** `onStartCommand` не проверяет, запущен ли уже процесс. Если дважды вызвать `startForegroundService` (нажав кнопку несколько раз), создадутся два конкурирующих потока `ScriptRunner`, которые будут одновременно работать с одним и тем же Bluetooth-сокетом. + - **Как исправить:** В `startRun` проверять флаг `running` и игнорировать повторные запуски. + +4. **Логическая ошибка в выборе Bluetooth-устройства** + - **Файл:** [android/app/src/main/java/ru/elmer/client/ui/MainActivity.kt](android/app/src/main/java/ru/elmer/client/ui/MainActivity.kt#L457) + - **Описание:** Функция `findElmDevice()` при наличии двух и более устройств показывает диалог, но возвращает `null` немедленно. Стейт-машина (`checkElm`, `scanDtc`) видит `null` и прерывает работу с ошибкой «ELM не найден». + - **Как исправить:** Перестроить логику: диалог выбора должен вызываться отдельно, сохранять `elmDevice`, и только потом запускать операции. + +### 🟡 Средние + +5. **Утечка памяти в ElmChecker** + - **Файл:** [android/app/src/main/java/ru/elmer/client/elm/ElmChecker.kt](android/app/src/main/java/ru/elmer/client/elm/ElmChecker.kt) + - **Описание:** Список `logLines` является членом класса. В `MainActivity` экземпляр `elmChecker` переиспользуется. Приложение копит логи всех операций в памяти до своей гибели. + - **Как исправить:** Очищать `logLines` в начале каждой операции или переносить лог в локальную переменную метода `run()`. + +6. **Утечка курсоров в БД** + - **Файл:** [android/app/src/main/java/ru/elmer/client/db/SessionDb.kt](android/app/src/main/java/ru/elmer/client/db/SessionDb.kt) + - **Описание:** Методы `getResponses`, `getSessions`, `getPendingSessions` вызывают `cursor.close()` в конце цикла, но не в `finally`. При ошибке чтения курсор останется открытым. + - **Как исправить:** Использовать конструкцию `.use { ... }` (в Kotlin для `Cursor` доступно начиная с определенных версий) или `try { ... } finally { cursor.close() }`. + +7. **Не включены Foreign Keys** + - **Файл:** [android/app/src/main/java/ru/elmer/client/db/SessionDb.kt](android/app/src/main/java/ru/elmer/client/db/SessionDb.kt) + - **Описание:** Несмотря на наличие `REFERENCES sessions(id)`, SQLite в Android по умолчанию не проверяет целостность связей. + - **Как исправить:** Добавить `db.setForeignKeyConstraintsEnabled(true)` в `onConfigure`. + +8. **Слепой выбор устройства в сервисе** + - **Файл:** [android/app/src/main/java/ru/elmer/client/script/ScriptRunnerService.kt](android/app/src/main/java/ru/elmer/client/script/ScriptRunnerService.kt#L143) + - **Описание:** Сервис берёт `bonded[0]` — первое попавшееся сопряжённое устройство. Это могут быть наушники или магнитола. + - **Как исправить:** Передавать MAC-адрес выбранного ELM через `Intent`. + +9. **Отсутствие лимита на размер запроса** + - **Файл:** [api/routes.py](api/routes.py) + - **Описание:** Сервер принимает список `responses` любого размера. Злоумышленник может отправить миллион строк, вызвав OOM или переполнение диска логами. + - **Как исправить:** Проверять `len(responses)` перед обработкой. + +### 🟢 Косметика + +10. **Неиспользуемые переменные** + - В `ElmProtocol.kt` константа `INIT_TIMEOUT` и другие не используются. + - В `MainActivity.kt` список `chatHistory` хранится в памяти, но не восстанавливается после `onSaveInstanceState`. + +11. **Устаревший API** + - `BluetoothAdapter.getDefaultAdapter()` помечен как Deprecated. В современных Android рекомендуется использовать `BluetoothManager`. + +12. **Бесполезный пинг LLM** + - Эндпоинт `/api/v1/ping-llm` делает реальный `diagnose`, что стоит денег (токенов). Кэш есть, но при перезапуске сервера или по таймауту он всё равно будет жечь токены на "пустые" проверки.