- setWiFi() now throws detailed errors instead of returning false - Shows specific error messages: "WiFi credentials rejected", timeout etc. - Added logging throughout BLE WiFi configuration flow - Fixed WiFi network deduplication (keeps strongest signal) - Ignore "Operation cancelled" error (normal cleanup behavior) - BatchSetupProgress shows actual error in hint field 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
13 KiB
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_deploymentendpoint возвращаетdeployment_idв ответе
Phase 2 (post-MVP):
- Header-based auth (
Authorization: Bearer <token>) beneficiary_passwordстановится optionalexternal_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:
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:
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:
npm install babel-plugin-transform-remove-console --save-dev
// 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:
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 день Приоритет: Высокий после релиза
Что делаем:
- Winston для structured logs
- Sentry для алертов об ошибках
- Audit Trail таблица для истории действий
- Generic error messages в production (не показывать stack trace)
Почему важно: Сейчас если что-то падает — мы не узнаем. Sentry пришлёт алерт.
FE-004: SSL Pinning
Effort: 1 день Приоритет: Средний
Что делаем:
- Удалить
app/(tabs)/bug.tsx(если есть) - Добавить SSL pinning для API запросов
- Проверка 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
anytypes (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 часа):
- VULN-010: CORS whitelist
- VULN-012: Request size limit
- FE-001: Stripe key в env
- FE-006: Remove console.log
- FE-007: JWT expiration check
Сразу после релиза:
- VULN-006: Winston + Sentry (мониторинг)
- DATA-001: Account deletion (требование Apple)
- BUG-001-005: Race conditions
Когда будет время:
- Refresh tokens
- Biometric auth
- RLS (Row Level Security)
- SSL Pinning
Ждём legacy backend:
- FE-003: Legacy credentials (Phase 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 недели
Пример политики
-- Пользователь видит только своих 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)
)
);
Пример использования в коде
// До (без 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