Author SHA1 Message Date
“Naeel” 614c00edea Document Opus validation findings 2026-08-31 17:40:25 +03:00
2 changed files with 140 additions and 0 deletions
+105
View File
@@ -259,6 +259,65 @@ thoughts 477, total 1627. Стоимость по тарифу Gemini 3.6 Flash
candidate output как выход: $0.00092, то есть около 9 копеек при курсе 95
рублей за доллар.
## 2026-08-31: Критическая валидация отчета Opus
Пользователь запросил документировать анализ Opus, выполнить критическую
сверку по коду и сформулировать вопросы до внесения изменений.
### Что подтверждено по коду
- `recipe_service/app.py`: есть дублирование вызовов `record(...)` по веткам
ответа; единый финализатор уменьшит риск расхождений полей метрик.
- `recipe_service/app.py`: `duration_ms` считается только в ветке `401`,
в остальных ветках передается `0`.
- `recipe_service/app.py`: используется `__import__('time').monotonic()`
вместо обычного `import time`.
- `recipe_service/app.py`: сравнение Bearer-токена выполнено через `==`,
без constant-time сравнения.
- `recipe_service/metrics.py`: `record()` вызывает `initialize()` на каждый
запрос, что приводит к повторной инициализации схемы БД.
- `gemini_proxy/app.py`: endpoint принимает `api_key_override` из формы.
- `android-app/app/build.gradle.kts`: `RECEIPT_API_TOKEN` попадает в
`BuildConfig`, значит секрет присутствует в клиентском APK.
- `android-app/app/src/main/java/ru/obdai/receipt/camera/CameraManager.kt`:
на каждом кадре выполняется цепочка YUV -> JPEG -> Bitmap.
- `android-app/app/src/main/java/ru/obdai/receipt/MainActivity.kt`:
текст результата выводится и в `Column`, и в `ResultOverlay`.
- `android-app/app/src/main/java/ru/obdai/receipt/crop/MedicationZoneDetector.kt`:
дата в формате `dd.mm.yyyy` сама по себе триггерит `PatientDataDetected`.
### Что уточнено по замечаниям Opus
- Замечание про `client_ip=127.0.0.1` корректно как риск достоверности
метрик за nginx. Для надежного учета реального IP требуется явная политика
доверия прокси и корректная обработка заголовков.
- Замечание про детализацию `502` корректно как вопрос баланса между
диагностикой и минимизацией утечки деталей провайдера.
- Замечание про отсутствие тестов для `recipe_service` подтверждено: в
текущем дереве есть `gemini_proxy/test_app.py`, но нет тестового файла
для `recipe_service`.
### Вопросы к Opus (блокеры перед правками)
1. По `gemini_proxy/app.py`: удаляем `api_key_override` полностью или
оставляем только в debug-режиме под отдельным флагом окружения?
2. По `recipe_service/app.py` ветке `502`: какой контракт ошибки нужен
клиенту - единое сообщение, коды причин, или ограниченный whitelist
провайдерских причин без сырого текста?
3. По учету IP за nginx: доверяем строго одному прокси (`x_for=1`) или
нужен более строгий trust chain по сети/подсети?
4. По Android токену: подтверждаем переход на краткоживущие серверные
токены (token exchange), или остается текущая схема MVP с явной
фиксацией риска?
5. По privacy-gate: дата в medication-зоне должна блокировать отправку,
или только снижать confidence и требовать подтверждение пользователя?
### Итог
Отчет Opus в значительной части подтвержден фактами по текущему коду.
Критичные изменения backend можно готовить после ответов на вопросы выше,
чтобы не сломать контракт клиента и не ослабить security-policy.
Вывод: лимит 500 токенов оказался недостаточным; следующий независимый тест
проводится с существенно большим лимитом и ещё более короткой JSON-схемой.
@@ -641,3 +700,49 @@ nginx-маршруты без redirect и health `/receipt/health`. Старые
IP `127.0.0.1`, MIME `image/png`, размер 531101 байт, длину prompt 88 и
непустой usage JSON. Тестовый image-файл удалён после запроса; приложение
читает изображение в память и не пишет его на диск.
## 2026-08-31: критическая валидация отчета Opus и обновление плана
Проведена проверка утверждений Opus по фактическому коду и конфигурации.
Подтверждено по backend:
- В `recipe_service/app.py` есть многократное дублирование блоков `record(...)` по веткам обработки.
- В `recipe_service/app.py` длительность `duration_ms` в большинстве веток фиксируется как `0`; фактический расчет есть только для `401`.
- В `recipe_service/app.py` используется `__import__('time').monotonic()` вместо нормального `import time`.
- В `recipe_service/app.py` сравнение bearer-токена выполнено через `==`, без constant-time сравнения.
- В `recipe_service/app.py` в `502` наружу пробрасывается provider detail, что может раскрывать внутренние детали upstream-ошибок.
- В `recipe_service/metrics.py` функция `record()` вызывает `initialize()` на каждый запрос.
- `recipe_service/nginx-recipe.conf` передает `X-Real-IP` и `X-Forwarded-For`, но в Flask используется `request.remote_addr`; без `ProxyFix` это адрес прокси.
Подтверждено по инфраструктуре:
- `recipe_service/recipe.service` слушает только `127.0.0.1:8770`.
- `gemini_proxy/gemini-proxy.service` слушает только `127.0.0.1:8768`.
Подтверждено по Android:
- `android-app/app/build.gradle.kts` задает `BuildConfig.RECEIPT_API_TOKEN`, токен попадает в клиентское приложение.
- `android-app/app/src/main/java/ru/obdai/receipt/network/ApiClient.kt` отправляет `Authorization: Bearer ...` из `BuildConfig`.
- `android-app/app/src/main/java/ru/obdai/receipt/camera/CameraManager.kt` выполняет YUV->JPEG->Bitmap на кадр в analyzer.
- `android-app/app/src/main/java/ru/obdai/receipt/MainActivity.kt` выводит результат и в `Column`, и в `ResultOverlay`.
- `android-app/app/src/main/java/ru/obdai/receipt/crop/MedicationZoneDetector.kt` блокирует при любой дате по regex, что дает ложные privacy-срабатывания.
- `android-app/app/src/main/AndroidManifest.xml` содержит `usesCleartextTraffic="false"`.
Подтверждено по тестам и зависимостям:
- Тесты присутствуют только в `gemini_proxy/test_app.py`.
- Для `recipe_service` тестов нет.
- Корневой `requirements.txt` и `recipe_service/requirements.txt` расходятся по version bounds.
Дополнительные риски, подтвержденные параллельным аудитом секретов:
- В локальных репозиториях обнаружены реальные токены/JWT и tracked secret-like файлы; часть секретов не отслеживается Git, но присутствует в рабочих каталогах.
- В `tf_provider` обнаружен tracked `secrets/llm.key`; в истории есть многочисленные совпадения по маркерам `sk-`.
- В `nifi_fork` история содержит совпадения по маркерам `sk-` и `AKIA`; часть совпадений относится к документации/тестам и требует ручной triage по значениям.
Статус:
- Отчет Opus по backend и Android в критических пунктах в основном подтвержден.
- Для части исторических совпадений по секретам нужна ручная классификация: реальный ключ, тестовый маркер или документационный пример.
- Код в этой итерации не менялся; выполнено документирование и валидация.
+35
View File
@@ -182,3 +182,38 @@ crop лекарственной строки
- начать с SQLite и перейти на отдельный PostgreSQL только при появлении требований к параллельной записи, админке или нескольким экземплярам сервиса.
Перед фиксацией порогов и коэффициентов scoring провести эксперимент на 50–200 обезличенных рецептах.
## 12. Критический план по результатам валидации Opus (2026-08-31)
### Подтвержденные проблемы и приоритет
1. `recipe_service/metrics.py`: убрать `initialize()` из `record()`; инициализация должна быть только на старте сервиса.
2. `recipe_service/app.py`: устранить дублирование `record(...)` через единый финализатор/обертку завершения запроса.
3. `recipe_service/app.py`: считать `duration_ms` во всех ветках, а не только при `401`.
4. `recipe_service/app.py`: заменить сравнение токена на constant-time (`hmac.compare_digest`).
5. `recipe_service/app.py`: минимизировать детализацию `502` для клиента, подробности оставлять в логах.
6. `recipe_service/app.py`: добавить учет заголовков прокси (`ProxyFix`) для корректного client IP.
7. Тесты: добавить `recipe_service/test_app.py` с кейсами `401/400/415/413/200`.
8. `requirements.txt`: выровнять root и `recipe_service/requirements.txt` по version bounds.
### Android-блок (после подтверждения модели безопасности)
1. Убрать долговременный статический секрет из APK или перейти на схему краткоживущих токенов.
2. Оптимизировать pipeline камеры: снизить частоту тяжелой конвертации или заменить способ получения bitmap.
3. Убрать двойной вывод результата (`Column` и `ResultOverlay`) и оставить один источник отображения.
4. Пересмотреть `containsPatientData` в `MedicationZoneDetector`: дата сама по себе не должна блокировать распознавание.
### Вопросы к Opus (требуют уточнения до правок)
1. `api_key_override` в `gemini_proxy/app.py`: удалять полностью или оставить только для локального debug-режима за флагом окружения?
2. По `502`: какой клиентский формат ошибки нужен — единое сообщение или кодизированные причины без деталей провайдера?
3. Для `ProxyFix`: доверяем ровно одному прокси (`x_for=1`) или нужен более строгий trust chain?
4. Для Android: подтверждаем перенос от статического API-токена к короткоживущему серверному token exchange?
5. По privacy-гейту: дата рецепта в medication-зоне допустима или должна только понижать confidence без блокировки?
### План выполнения после подтверждения
1. Backend фаза A: `metrics.py` + `app.py` (инициализация, duration, record-finalizer, compare_digest, ProxyFix).
2. Backend фаза B: тесты `recipe_service/test_app.py` + проверка pytest.
3. Backend фаза C: harmonize requirements.
4. Android фаза D: безопасность токена + оптимизация камеры + privacy-gate.