fix: IAM баги после ревью
- ui/routes/auth.js: POST /login-token сделан async (был await без async)
- ui/routes/auth.js: IAM enrichment только при auth.isOidc (не в mock-режиме)
- src/auth.js: fetchIamUser, switchProfile возвращаются из initAuth()
- src/routes/oidc.js: auth.fetchIamUser вместо require('../auth') (устранение циклической зависимости)
- ui/routes/auth.js: auth.fetchIamUser вместо require
- tests/api-crud.sh: удалён хардкоженный prod-токен, токен только через аргумент
This commit is contained in:
@@ -40,3 +40,6 @@ test-results/
|
|||||||
.env.production
|
.env.production
|
||||||
DEPLOY-TESTING.md
|
DEPLOY-TESTING.md
|
||||||
tests/comprehensive.js
|
tests/comprehensive.js
|
||||||
|
|
||||||
|
# Промпты для внешних агентов
|
||||||
|
prompt-*.txt
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# AI‑анализ проекта ipwhitelist‑app — сравнение
|
||||||
|
|
||||||
|
> Дата: 2026-06-04
|
||||||
|
> Простая модель (GPT) vs DeepSeek V4 Pro
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Анализ простой модели (prompt‑review.txt)
|
||||||
|
|
||||||
|
### Общее заключение
|
||||||
|
Нет. Нельзя деплоить в production в текущем состоянии.
|
||||||
|
|
||||||
|
### Найденные проблемы
|
||||||
|
|
||||||
|
| # | Серьёзность | Описание |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | **Critical** | `ui/routes/auth.js` — `await` без `async` в `POST /login‑token` → SyntaxError |
|
||||||
|
| 2 | **Critical** | `server.js` — `/export` открыт публично без авторизации |
|
||||||
|
| 3 | **Major** | Источники профилей не унифицированы (JWT vs IAM), хардкод `WZ01112` для admin |
|
||||||
|
| 4 | **Major** | `jwt.decode()` без верификации в UI |
|
||||||
|
| 5 | **Minor** | `'unsafe-inline'` в CSP |
|
||||||
|
| 6 | **Minor** | Разнобой таймаутов HTTP (5s vs 10s) |
|
||||||
|
| 7 | **Minor** | Логирование IAM слабое |
|
||||||
|
|
||||||
|
### Оценки
|
||||||
|
|
||||||
|
| Критерий | Балл |
|
||||||
|
|---|---|
|
||||||
|
| Понятность DevOps | 8/10 |
|
||||||
|
| Чистота кода | 6/10 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Оценка DeepSeek V4 Pro
|
||||||
|
|
||||||
|
### Что модель нашла верно
|
||||||
|
- `await` без `async` — реальный критический баг, приложение упадёт при запуске.
|
||||||
|
- `/export` без авторизации — реальная дыра безопасности.
|
||||||
|
- Дублирование IAM/JWT‑логики — действительно размазано по `oidc.js`, `auth.js`, `ui/routes/auth.js`.
|
||||||
|
- `'unsafe-inline'` в CSP — надо убирать (nonce/hash).
|
||||||
|
- Разнобой таймаутов — мелочь, но стоит унифицировать.
|
||||||
|
|
||||||
|
### Что модель пропустила
|
||||||
|
|
||||||
|
| # | Серьёзность | Описание |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | **Critical** | Хардкоженный IAM‑токен `tazetdinovn@gmail.com` (prod) лежит в `tests/api-crud.sh` в открытом виде и уже в гите. |
|
||||||
|
| 2 | **Major** | `DEV_MODE=true` на итало — но код пытается вызывать `fetchIamUser` даже в mock‑режиме. IAM недоступен локально → каждый логин будет падать с таймаутом или ошибкой. |
|
||||||
|
| 3 | **Major** | Порядок middleware: `session` → `oidc` → `ui` — в OIDC‑роутере `src/routes/oidc.js` свой `req.session.user`, а `ui/index.js` делает свой `jwt.decode()` — возможен конфликт/перезапись. |
|
||||||
|
| 4 | **Major** | Нет механизма миграций БД — только `schema.sql` с CREATE, нет ALTER/версионирования. |
|
||||||
|
| 5 | **Minor** | `csrfToken` в `views/index.ejs` передаётся как пустая строка (`csrfToken: ''`) — CSRF фактически отключён для UI‑слоя. |
|
||||||
|
| 6 | **Minor** | `package.json` → `"version": "0.5.17"`, а `README.md` → `v0.5.14` — расхождение версий. |
|
||||||
|
| 7 | **Minor** | `require('../auth')` внутри `src/routes/oidc.js` создаёт циклическую зависимость: `auth.js` → `routes/oidc.js` → `auth.js`. Работает только из‑за кеша Node.js, но хрупко. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Итоговое сравнение
|
||||||
|
|
||||||
|
| Параметр | Простая модель | DeepSeek |
|
||||||
|
|---|---|---|
|
||||||
|
| Критические баги найдены | 2 из 3 | +1 (токен в гите) |
|
||||||
|
| Архитектурные проблемы | Поверхностно | Глубже (middleware, циклические зависимости, миграции) |
|
||||||
|
| Рантайм‑поведение | Не анализировала | Учёл DEV_MODE и реальные сценарии |
|
||||||
|
| Точность попаданий | 6/7 верных | Все найденные подтверждены + новые |
|
||||||
|
| Ложные срабатывания | 0 | 0 |
|
||||||
|
|
||||||
|
**Вывод:** простая модель дала добротный первый проход — нашла два критических бага и дала разумные рекомендации. Но не копнула глубже: пропустила утекший токен, не проверила рантайм‑поведение в DEV_MODE и не заметила архитектурные завязки. Для production‑review нужен более глубокий анализ.
|
||||||
@@ -108,6 +108,9 @@ async function initAuth() {
|
|||||||
DEV_SECRET: devSecret,
|
DEV_SECRET: devSecret,
|
||||||
// JWKS endpoint (только в mock-режиме)
|
// JWKS endpoint (только в mock-режиме)
|
||||||
jwksHandler: !isOidc ? (req, res) => res.json(mockJwks) : null,
|
jwksHandler: !isOidc ? (req, res) => res.json(mockJwks) : null,
|
||||||
|
// IAM API хелперы
|
||||||
|
fetchIamUser,
|
||||||
|
switchProfile,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -57,8 +57,7 @@ function createRouter({ auth, doubleCsrfProtection, generateCsrfToken, authLimit
|
|||||||
|
|
||||||
// Пытаемся обогатить через IAM API
|
// Пытаемся обогатить через IAM API
|
||||||
try {
|
try {
|
||||||
const { fetchIamUser } = require('../auth');
|
const iamData = await auth.fetchIamUser(accessToken);
|
||||||
const iamData = await fetchIamUser(accessToken);
|
|
||||||
req.session.user = {
|
req.session.user = {
|
||||||
email: iamData.email,
|
email: iamData.email,
|
||||||
clientId: iamData.clientId,
|
clientId: iamData.clientId,
|
||||||
|
|||||||
+11
-4
@@ -5,17 +5,24 @@
|
|||||||
#
|
#
|
||||||
# Использование:
|
# Использование:
|
||||||
# bash tests/api-crud.sh [BASE_URL] [TOKEN]
|
# bash tests/api-crud.sh [BASE_URL] [TOKEN]
|
||||||
|
# TOKEN=eyJ... bash tests/api-crud.sh
|
||||||
#
|
#
|
||||||
# Если токен не указан — использовать prod IAM токен (только для DEV_MODE)
|
# Токен обязателен. В DEV_MODE подойдёт любой декодируемый JWT.
|
||||||
|
# Получить: залогиниться на стенде, скопировать из session.token
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
BASE="${1:-https://italo.kube5s.ru}"
|
BASE="${1:-https://italo.kube5s.ru}"
|
||||||
TOKEN="${2:-}"
|
TOKEN="${2:-${TEST_TOKEN:-}}"
|
||||||
|
|
||||||
# ── Prod IAM токен (работает в DEV_MODE) ──────────────────────────────
|
|
||||||
if [ -z "$TOKEN" ]; then
|
if [ -z "$TOKEN" ]; then
|
||||||
TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhdXRoLWFwaSIsInN1YiI6IjAxOWNjMjY4LTZjNmEtNzgxZS04NjEzLTRiZWQ0ZWM3Y2QyMCIsImV4cCI6MTc5NjExODA2NywiaWF0IjoxNzgwNTY2MDY3LCJqdGkiOiJjMzJlNWIwMy05YjBlLTRjNGEtYTY0MS1lOTE0YWQ4MWRiMDQiLCJhdXRoX3RpbWUiOjAsInR5cCI6IiIsImF6cCI6IiIsInNlc3Npb25fc3RhdGUiOiIiLCJhY3IiOiIiLCJhbGxvd2VkLW9yaWdpbnMiOm51bGwsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6bnVsbH0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpudWxsfX0sInNjb3BlIjoiIiwic2lkIjoiIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiIiwiQ2xpZW50SUQiOiJXWjAzNzA5IiwiY29tcGFueV9pZCI6IjAxOWNjMjRhLTcyN2UtNzQwZi1iNDA3LWJlYzc5ZGFiNDE2MiIsImNvbXBhbnlfbmFtZSI6Im5hZWVsX3Rlc3QiLCJ0b2tlbl90eXBlIjoidGVjaCIsImlkcF91c3JfdWlkIjoiMDE5Y2MyNjgtNmM2YS03ODFlLTg2MTMtNGJlZDRlYzdjZDIwIiwibG9naW4iOiJ0YXpldGRpbm92bkBnbWFpbC5jb20iLCJmaXJzdG5hbWUiOiLQndCw0LjQu9GMIiwibWlkZGxlbmFtZSI6ItCk0LDRgNC40YLQvtCy0LjRhyIsImxhc3RuYW1lIjoi0KLQsNC30LXRgtC00LjQvdC-0LIiLCJncm91cHMiOm51bGwsInByZWZlcnJlZF91c2VybmFtZSI6IiIsImdpdmVuX25hbWUiOiIiLCJmYW1pbHlfbmFtZSI6IiIsImVtYWlsIjoidGF6ZXRkaW5vdm5AZ21haWwuY29tIn0.Pc0NJsauTQQApbPgDjFZd9phceMvfN4usaa8Rw9wGeMUvKR6EOAC6F_9in6zhcgK0zFsdbpsSzeGaNLoJNpadbshAWmGKVxprbMZbCuuxvDFzraIHTw0okgvh-4XPN8NhzqI0taTWjN9Wdl5hWBpHBQFpgRlf9u-jMwqmSQQ15wZHiOc_x1Xh_IKhtBGC4duYlvnzVzzWgRVvCNzs73dnPhCAKNn17epZujq0QHlzkkg2WeCo24iiiKrIpssW6YUiM9aKezwbYxGvZZOePX-RjcapBKFjNmpjI2UjEOMroQUtS7ei1cgU9VN5_QjmGKmSSXF2JBtcSGb52qAhYDwQQ"
|
echo "Ошибка: укажите TOKEN аргументом или через TEST_TOKEN=..."
|
||||||
|
echo ""
|
||||||
|
echo " # В DEV_MODE можно сгенерировать локально:"
|
||||||
|
echo ' TOKEN=$(node -e "const a=require(\"./src/auth\"); a.initAuth().then(x=>console.log(x.issueMockToken({clientId:\"WZ01112\",companyId:\"a\",companyName:\"Nubes\",email:\"a@b\"})))" 2>/dev/null)'
|
||||||
|
echo " bash tests/api-crud.sh https://italo.kube5s.ru \$TOKEN"
|
||||||
|
echo ""
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
PASS=0
|
PASS=0
|
||||||
|
|||||||
+8
-4
@@ -68,7 +68,7 @@ function createRouter({ auth, MOCK_USERS, authLimiter }) {
|
|||||||
router.get('/login-token', (req, res) => res.redirect('/login'));
|
router.get('/login-token', (req, res) => res.redirect('/login'));
|
||||||
|
|
||||||
// POST /login-token — вход по Bearer JWT токену (реальному) или clientId (отладка)
|
// POST /login-token — вход по Bearer JWT токену (реальному) или clientId (отладка)
|
||||||
router.post('/login-token', authLimiter, (req, res) => {
|
router.post('/login-token', authLimiter, async (req, res) => {
|
||||||
const { token, clientId, returnTo } = req.body;
|
const { token, clientId, returnTo } = req.body;
|
||||||
|
|
||||||
// ── Вход по реальному JWT токену ──────────────────────────────────────
|
// ── Вход по реальному JWT токену ──────────────────────────────────────
|
||||||
@@ -96,10 +96,10 @@ function createRouter({ auth, MOCK_USERS, authLimiter }) {
|
|||||||
|
|
||||||
req.session.token = raw;
|
req.session.token = raw;
|
||||||
|
|
||||||
// Пытаемся обогатить через IAM API
|
// Пытаемся обогатить через IAM API (только в OIDC-режиме)
|
||||||
|
if (auth.isOidc) {
|
||||||
try {
|
try {
|
||||||
const { fetchIamUser } = require('../../src/auth');
|
const iamData = await auth.fetchIamUser(raw);
|
||||||
const iamData = await fetchIamUser(raw);
|
|
||||||
req.session.user = {
|
req.session.user = {
|
||||||
email: iamData.email,
|
email: iamData.email,
|
||||||
clientId: iamData.clientId,
|
clientId: iamData.clientId,
|
||||||
@@ -116,6 +116,10 @@ function createRouter({ auth, MOCK_USERS, authLimiter }) {
|
|||||||
} catch (iamErr) {
|
} catch (iamErr) {
|
||||||
// IAM недоступен — fallback из JWT
|
// IAM недоступен — fallback из JWT
|
||||||
console.warn('[login-token] IAM enrichment failed, using JWT fallback:', iamErr.message);
|
console.warn('[login-token] IAM enrichment failed, using JWT fallback:', iamErr.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback (всегда): базовый user из JWT
|
||||||
|
if (!req.session.user) {
|
||||||
req.session.user = {
|
req.session.user = {
|
||||||
clientId: rawClientId,
|
clientId: rawClientId,
|
||||||
allClientIds,
|
allClientIds,
|
||||||
|
|||||||
Reference in New Issue
Block a user