WS8 PR F review fix: enforce firm model policy on the chat routes

↗ view on GitHub · sloth-ninja · 2026-07-27 · 3fa1d9eb

Blocker from independent review: firm model config was filtered client-side
only. POST /chat and POST /projects/:id/chat accepted a raw body.model, so a
policy-OFF member (or a policy-ON member under a provider restriction) could
bypass the policy with a crafted request ("gate the routes, not the tabs").

Fix: new resolveOrgChatModel(userId, requested, db) in userSettings.ts, applied
right after model resolution on both streaming chat routes. Rules: local ids
pass through untouched; fail-open on org-lookup error / orgless; policy-OFF +
firm default -> forced to firm default; requested provider outside a non-empty
offeredProviders -> substituted (firm default -> default main model -> first
offered provider's default). models.ts gains safeProviderForModel +
defaultMainModelForProvider.

Coherence: PATCH /admin/model-config now rejects (400) a MERGED config whose
cloud default model's provider sits outside a non-empty offeredProviders, so the
clamp can treat a set firm default as an in-set substitute; a local default is
exempt.

Routes checked for raw client model ids: tabular resolves via getUserModelSettings
(already firm-aware); workflows accepts no client model - neither needs a clamp.

Tests: +14 (309 total, 24 files) - resolveOrgChatModel clamp matrix and the
model-config coherence 400s. Backend tsc + vitest green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Repository Sloth-ninja/JessicaOSS
Author sloth-ninja <ezanahaddis@gmail.com>
Authored
Parents e504926a
Stats 8 files changed , +293 , -19
Part of WS8 PR F: firm model preferences

Capture this commit into my fork

Download a Markdown prompt that tells Claude how to port this exact commit into your working tree. Run it via claude -p < capture-commit-3fa1d9eb.md from inside the repo you want the change in.

⬇ Download capture-commit-3fa1d9eb.md