fix(routers): enforce the user's saved selection on the env-key spend path
resolveModel accepts router-prefixed ids by SHAPE alone, so any authenticated user could hand-craft a chat/title/tabular request naming any `openrouter/x/y` or `vercel/x/y` model and it would run - on the operator's env key when one is configured. WHY THIS MATTERS (what a cost-abuse vector via unvalidated model ids is) On server-key deployments the operator pays per token for whatever model a request names. The Settings UI curates a small allowlist per user, but nothing at request time consulted it: the model string in the request body went straight to the gateway. A user (or a leaked JWT) could point every request at the most expensive frontier model on the gateway's catalog and run up the operator's bill - a classic unvalidated- identifier cost-abuse vector. First-party models don't have this problem because resolveModel checks them against a closed catalog. HOW IT WORKS - routerModels.resolveRequestedModel is the single request-time choke point: resolve the id as before, then, only for router-prefixed results, require membership in that user's saved user_router_models selection; otherwise warn and degrade to the caller's default - exactly the path an invalid model id already takes. BYOK users get the same rule (uniform semantics; their saved list is one Settings save away). - runLLMStream (chat, project chat, Word chat, tabular streams) awaits it where it previously called resolveModel inline. - getUserModelSettings applies the same membership guard to the stored title/tabular preferences using the selections it already fetched, so the profile row can't smuggle an unselected router model either. Tests: streamingModelAllowlist.test.ts (saved member passes through to the adapter; non-member falls back to the default, with and without a BYOK key; first-party models skip the lookup) and userSettings.test.ts (stored router preference outside the selection falls back). Verified by stashing this fix: 4 of the new tests fail on the ported code because the unselected router model reaches the adapter / settings unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Repository | open-legal-products/mike |
|---|---|
| Author | Amal <mamalanand3@gmail.com> |
| Authored | |
| Committed | |
| Parents | 434e6b68 |
| Stats | 5 files changed , +304 , -7 |
| Part of | Harden model-router selection and failure handling |
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-af64aca5.md
from inside the repo you want the change in.