fix(mcp): security review follow-ups (#24) #48
Reference in New Issue
Block a user
Delete Branch "fix/mcp-security-followups"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Post-merge security-review follow-ups для mcp-per-user-auth (#13). Ветка от develop. Closes #24.
Что сделано (6 подпунктов чек-листа)
isInitializeRequestBodyтеперь делегирует SDK-предикатуisInitializeRequest(тот же, что в packages/mcp/http.ts). Голый{method:'initialize'}без id/params больше не триггерит side-effectinglogin()(audit-spam + рост user_sessions) до того, как http.ts его 400-ит..env.example— приложение сtrustProxy: trueдоверяет X-Forwarded-For; деплоить за доверенным прокси, иначе per-IP throttling (вкл. /mcp brute-force) спуфится. (doc-only, runtime не тронут.)verifyBearerAccessотвергает токен сpayload.workspaceId != workspace инстанса(резолв черезworkspaceRepo.findFirst(), как Basic-путь). Опциональный параметр → no-op когда не задан (single-workspace OSS).verifyUserCredentialsветка missing/disabled теперь делает bcrypt-compare против dummy-хеша. По замечанию ревью cost dummy-хеша выставлен 12 (== продакшен saltRounds), иначе тайминг не выровнен; тест проверяет$2b$12$. Сообщение CREDENTIALS_MISMATCH_MESSAGE сохранено (его матчит /mcp-лимитер).mcp-basic-login-gate.spec.tsгоняет РЕАЛЬНЫЙenforceBasicLoginGate(SSO enforced / EE-MFA bundled vs not / user-MFA / workspace-enforced-MFA), вместо заглушки гейта. EE-модуль мокается черезjest.mock(..., {virtual:true}).Косяк, пойманный на ревью
Первая версия timing-fix использовала dummy-хеш cost 10, а прод — cost 12 → bcrypt-сравнение быстрее → оракул частично оставался. Поправил на cost 12 + тест на префикс
$2b$12$.Не вошло (осознанно, отмечено ревью как SUGGESTION)
workspaceRepo.findFirst()на каждый Bearer-запрос не кешируется (лишний round-trip в single-workspace сборке) — принято как trade-off ради простоты/консистентности с Basic-путём.Проверка
tsc --noEmit— без новых ошибок.jest src/integrations/mcp+ contract spec — 71 passed.🤖 Generated with Claude Code
After develop merged, mcp.service.ts calls decideBasicGate from mcp-auth.helpers. The gate spec mocked the whole module returning only FailedLoginLimiter, so the merged code crashed with 'decideBasicGate is not a function' (7/7 failing). Spread jest.requireActual('./mcp-auth.helpers') so the real helpers are kept and the gate exercises real logic; keep only FailedLoginLimiter stubbed so its constructor runs without a real sweep timer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>Ghost referenced this pull request2026-06-21 02:05:01 +03:00