# WellNuo Security & Quality Audit Report **Дата:** 2026-01-22 **Версия:** 4.0 **Последнее обновление:** 2026-01-26 --- ## Резюме | Категория | Кол-во | Статус | |-----------|--------|--------| | ✅ Критичные (VULN-001 — VULN-008) | 6 | **ВЫПОЛНЕНО** — задеплоено 2026-01-26 | | 🟢 Быстрые рекомендации | 5 | Можно сделать за 2-3 часа | | 🟡 Средние рекомендации | ~15 | 1-3 дня на каждую | | 🔴 Большие задачи | 3 | 1-2 недели (после релиза) | | ⏸️ Заблокированные | 4 | Ждём legacy backend | --- # ✅ ВЫПОЛНЕНО — Критичные уязвимости > Задеплоено 2026-01-26. Code Review Score: 10/10. > PRD: `PRD-SECURITY.md` | # | Уязвимость | Что сделано | Файлы | |---|------------|-------------|-------| | 1 | **VULN-001**: Stripe Webhook | Проверка `STRIPE_WEBHOOK_SECRET` на старте, сервер не запустится без него | `webhook.js`, `index.js` | | 2 | **VULN-003**: JWT Secret | Проверка ≥32 символа на старте + обновлён secret до 64 символов | `index.js` | | 3 | **VULN-004**: OTP Rate Limit | 5 попыток/15мин на verify, 3/15мин на send | `auth.js` | | 4 | **VULN-005**: Input Validation | express-validator на всех POST/PATCH endpoints | `beneficiaries.js`, `stripe.js`, `invitations.js` | | 5 | **VULN-007**: Doppler | Инструкция создана | `backend/DOPPLER_SETUP.md` | | 6 | **VULN-008**: npm audit | Уязвимости в зависимостях исправлены | `package.json` | --- # ⏸️ ЗАБЛОКИРОВАНО — Ждём legacy backend > Эти задачи зависят от изменений на **eluxnetworks.net**. > Контакт: команда legacy backend. ### Что ждём: **Phase 1 (ETA: mid next week):** - `set_deployment` endpoint возвращает `deployment_id` в ответе **Phase 2 (post-MVP):** - Header-based auth (`Authorization: Bearer `) - `beneficiary_password` становится optional - `external_beneficiary_id` поле в таблице deployments ### Заблокированные задачи: | Задача | Проблема | Когда разблокируется | |--------|----------|---------------------| | **FE-003**: Legacy Credentials | Hardcoded `anandk` / `anandk_8` в `api.ts:1444` | Phase 2 | | **LEGACY-001**: Deployment ID | `set_deployment` не возвращает ID | Phase 1 | | **LEGACY-002**: Plaintext Passwords | Отправляем пароль в открытом виде | Phase 2 | | **LEGACY-003**: External ID Mapping | Нет `external_beneficiary_id` в БД | Phase 2 | --- # 🟢 БЫСТРЫЕ РЕКОМЕНДАЦИИ (2-3 часа всего) > Простые фиксы, можно сделать перед релизом. ### VULN-010: CORS Whitelist **Effort:** 30 минут **Файл:** `backend/src/index.js` **Проблема:** CORS разрешает запросы с любого домена. **Fix:** ```javascript const corsOptions = { origin: ['https://wellnuo.smartlaunchhub.com', 'exp://'], credentials: true }; app.use(cors(corsOptions)); ``` --- ### VULN-012: Request Size Limit **Effort:** 15 минут **Файл:** `backend/src/index.js` **Проблема:** Нет лимита на размер body — можно отправить огромный JSON и положить сервер. **Fix:** ```javascript app.use(express.json({ limit: '1mb' })); ``` --- ### FE-001: Stripe Key в Environment **Effort:** 15 минут **Файл:** `services/api.ts` или где используется Stripe **Проблема:** Stripe publishable key захардкожен. **Fix:** Вынести в `EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY`. --- ### FE-006: Убрать console.log в Production **Effort:** 30 минут **Файлы:** `babel.config.js`, `package.json` **Проблема:** Console.log попадают в production build. **Fix:** ```bash npm install babel-plugin-transform-remove-console --save-dev ``` ```javascript // babel.config.js module.exports = { presets: ['babel-preset-expo'], env: { production: { plugins: ['transform-remove-console'] } } }; ``` --- ### FE-007: JWT Expiration Check **Effort:** 1 час **Файл:** `services/api.ts` **Проблема:** Если токен истёк — пользователь получает непонятные ошибки вместо редиректа на логин. **Fix:** ```javascript function isTokenExpired(token) { try { const payload = JSON.parse(atob(token.split('.')[1])); return payload.exp * 1000 < Date.now(); } catch { return true; } } // Перед каждым запросом if (isTokenExpired(token)) { // Редирект на логин } ``` --- # 🟡 СРЕДНИЕ РЕКОМЕНДАЦИИ (1-3 дня каждая) > Полезно, но не критично для релиза. ### VULN-006: Логирование (Winston + Sentry) **Effort:** 1 день **Приоритет:** Высокий после релиза **Что делаем:** 1. Winston для structured logs 2. Sentry для алертов об ошибках 3. Audit Trail таблица для истории действий 4. Generic error messages в production (не показывать stack trace) **Почему важно:** Сейчас если что-то падает — мы не узнаем. Sentry пришлёт алерт. --- ### FE-004: SSL Pinning **Effort:** 1 день **Приоритет:** Средний **Что делаем:** 1. Удалить `app/(tabs)/bug.tsx` (если есть) 2. Добавить SSL pinning для API запросов 3. Проверка URL в WebView **Почему важно:** Защита от man-in-the-middle атак. --- ### VULN-009: Admin 2FA **Effort:** 2-3 дня **Приоритет:** Низкий (мало админов) **Что делаем:** TOTP или email code для admin операций. --- ### VULN-015: CSRF Protection **Effort:** 1 день **Приоритет:** Низкий (мобильное приложение) **Что делаем:** `csurf` middleware для web версии. --- ### FE-010: SecureStore вместо AsyncStorage **Effort:** 2-3 часа **Приоритет:** Средний **Что делаем:** Перенести tokens из AsyncStorage в SecureStore (зашифрованное хранилище). --- ### FE-014: Deep Links Validation **Effort:** 2-3 часа **Приоритет:** Средний **Что делаем:** Валидировать параметры deep links перед обработкой. --- ### FE-019: Client-Side Rate Limiting **Effort:** 1-2 часа **Приоритет:** Низкий **Что делаем:** Debounce на кнопки отправки форм. --- ### BUG-001 — BUG-005: Race Conditions & Memory Leaks **Effort:** 1 день **Приоритет:** Средний | Баг | Файл | Проблема | |-----|------|----------| | BUG-001 | `AuthContext.tsx:44` | Race condition в useEffect | | BUG-002 | `BeneficiaryContext.tsx:59` | Stale closure | | BUG-003 | `api.ts:316` | Silent auth failures | | BUG-004 | `beneficiaries/[id]/index.tsx:146` | Missing dependencies | | BUG-005 | `beneficiaries/[id]/index.tsx:107` | Interval not cleaned | --- ### Code Quality (SMELL-001 — SMELL-006) **Effort:** 2-3 дня **Приоритет:** Низкий - Duplicate navigation logic - Magic numbers → constants - Mixed auth token types - Excessive `any` types (40+) - Callback hell → async/await - Silent fallbacks → add logging --- # 🔴 БОЛЬШИЕ ЗАДАЧИ (1-2 недели, после релиза) ### VULN-017: Refresh Tokens **Effort:** 1 неделя (backend + frontend) **Приоритет:** После релиза **Сейчас:** JWT живёт 7 дней, нет refresh. **Рекомендуется:** - Access token: 15-60 минут - Refresh token: 7 дней - Автоматический refresh при истечении **Комментарий:** Работает и так, но refresh tokens — best practice. --- ### FE-017: Biometric Auth **Effort:** 3-5 дней **Приоритет:** После релиза **Что делаем:** Face ID / Touch ID для входа в приложение. **Комментарий:** Nice to have, не критично для MVP. --- ### RLS: Row Level Security **Effort:** 1-2 недели **Приоритет:** После релиза **Что это:** Защита данных на уровне базы данных. Даже если баг в коде — пользователь увидит только свои данные. **Подробный план:** См. ЧАСТЬ 4 в конце этого документа. **Комментарий:** Серьёзный рефакторинг. Делать когда будет время на полноценное тестирование. --- # 📋 App Store Compliance > Требования для публикации в App Store. | Задача | Статус | Комментарий | |--------|--------|-------------| | **PERMISSION-001**: Camera description | ⚠️ Проверить | Нужно человеческое описание в `app.json` | | **PERMISSION-002**: Microphone description | ⚠️ Проверить | Для голосового AI | | **DATA-001**: Account deletion | ❌ Нужно | Apple требует возможность удалить аккаунт | | **PRIVACY-001**: Privacy Policy URL | ⚠️ Проверить | Должен быть в `app.json` | | **PRIVACY-002**: Privacy Manifest (iOS 17+) | ❌ Нужно | `PrivacyInfo.xcprivacy` файл | | **CONTENT-001**: AI Chat Consent | ⚠️ Проверить | Согласие перед использованием AI | --- # 📊 Приоритеты ## Перед релизом (опционально, 2-3 часа): 1. VULN-010: CORS whitelist 2. VULN-012: Request size limit 3. FE-001: Stripe key в env 4. FE-006: Remove console.log 5. FE-007: JWT expiration check ## Сразу после релиза: 1. VULN-006: Winston + Sentry (мониторинг) 2. DATA-001: Account deletion (требование Apple) 3. BUG-001-005: Race conditions ## Когда будет время: 1. Refresh tokens 2. Biometric auth 3. RLS (Row Level Security) 4. SSL Pinning ## Ждём legacy backend: 1. FE-003: Legacy credentials (Phase 2) 2. LEGACY-001-003: Integration fixes (Phase 1/2) --- # ПРИЛОЖЕНИЕ: RLS (Row Level Security) — План реализации > Подробный план на случай если решим делать. ## Что это и зачем **Сейчас:** Проверки доступа в коде backend. Если баг — данные утекут. **С RLS:** Защита на уровне БД. Даже при баге, SQL injection, или прямом доступе — пользователь увидит только свои данные. ## Этапы | Этап | Время | Что делаем | |------|-------|------------| | 1. Подготовка | 2-3 часа | Создать роль `wellnuo_app`, включить RLS | | 2. Политики | 4-6 часов | Создать policies для каждой таблицы | | 3. Query Builder | 1-2 дня | Переделать под RLS | | 4. Routes | 2-3 дня | Обновить все endpoints | | 5. Тесты | 1 день | Unit + интеграционные | | 6. Деплой | 2-3 часа | Миграция + проверка | **Итого:** 1-2 недели ## Пример политики ```sql -- Пользователь видит только своих beneficiaries CREATE POLICY beneficiaries_select ON beneficiaries FOR SELECT USING ( id IN ( SELECT beneficiary_id FROM user_access WHERE accessor_id = current_setting('app.current_user_id', true) ) ); ``` ## Пример использования в коде ```javascript // До (без RLS) const { data } = await supabase.from('beneficiaries').select('*'); const accessible = data.filter(b => hasAccess(userId, b.id)); // ручная фильтрация // После (с RLS) const db = createDb(userId); // привязка к пользователю const { data } = await db.from('beneficiaries').select('*'); // RLS фильтрует автоматически ``` --- **Файл:** `AUDIT_REPORT.md` **Обновлён:** 2026-01-26