fix(web): bound the free-text context fed into the research prompt - #1530
Conversation
name and email came straight from req.json() and were interpolated into the grok-4-fast prompt with no length cap and no runtime type check, so an oversized value was billed as prompt tokens and a non-string arrived as [object Object]. Route both through sanitizeContextField: non-strings become empty, runs of whitespace collapse to a single space so a value cannot forge an extra prompt line, and the result is trimmed to 200 characters.
…pt context Parse and normalize urls with new URL() and drop duplicates before calling the paid Exa API, and collapse whitespace in name/email so a newline can't forge extra prompt lines. Both improvements are adapted from #1528 and #1530. Co-Authored-By: SEPURI-SAI-KRISHNA <206394534+SEPURI-SAI-KRISHNA@users.noreply.github.com>
|
Thanks @SEPURI-SAI-KRISHNA — closing this in favour of #1589, which bundles the research-route bounds together with the session-verification fix for the same route. That route was reachable unauthenticated, so the two changes needed to land together. Your #1589 is still in draft while it goes through review — happy to reopen this if that changes. |
Cherry-picks #1579 and #1580 from @Sravanjangam (security audit #1578), plus improvements on top. - `/api/og`, `/api/onboarding/extract-content` and `/api/onboarding/research` now verify the session against the auth backend; the middleware only checked that a cookie was present, so a forged cookie reached handlers that spend metered Exa/xAI quota. - Bounds those routes: 2MB cap on fetched HTML, max 10 http(s) URLs per request, name/email length limits and a 60s timeout on the LLM call. - De-duplicates URLs before calling Exa, and collapses whitespace in `name`/`email` so a newline can't forge extra prompt lines. Both adapted from @SEPURI-SAI-KRISHNA's #1528 and #1530. - Deletes the unused, unauthenticated `account-status` route. Verified locally: pre-fix `/api/og` returned 200 for a forged cookie, post-fix it returns 401. Five duplicate URLs collapse to two before reaching Exa, and a newline-laden `name` arrives as a single prompt line. Supersedes #1528 and #1530.
|
Thanks for folding the Same note as on #1528: the merged commit ( |
Summary
/api/onboarding/researchinterpolates the caller-suppliednameandemailstraightinto the prompt sent to
grok-4-fast:ResearchRequestdeclares both asstring, but nothing enforces that at runtime — thevalues come from
await req.json(). Two consequences:as prompt tokens on a model call that also runs the
web_searchandx_searchtools.xUrlis already constrained (the derived handle must match/^[A-Za-z0-9_]{1,15}$/); these two fields are the remaining unbounded input.the prompt as
Name: [object Object].Changes
Route both fields through a
sanitizeContextFieldhelper that:""for anything that is not a string, so only strings reach the prompt;MAX_CONTEXT_FIELD_LENGTH(200).The whitespace collapse matters beyond tidiness: each value is meant to occupy one
Name:/Email:line, and an embedded newline would let it forge an additional line ofprompt instead of staying inside its own field.
To be explicit about what this does not do: it bounds cost and keeps each value on
its intended line, but it is not a defence against prompt injection. 200 characters is
ample for an injected instruction, and the fields are free text by design.
Note for reviewers: this route appears to be unused
I could not find any caller for this route in the monorepo — the only
/api/*route theweb client fetches is
/api/og(apps/web/components/memories-grid.tsx:157). The samelooks true of
/api/onboarding/extract-contentand/api/onboarding/account-status. Allthree were added in #672 (Jan 2026), and onboarding has been rebuilt since — #904
(
remove unused old onboarding flow), #1067, #1178 — which appears to have dropped thecallers while leaving the routes.
I have only grepped this repository and cannot rule out a caller outside the monorepo,
which is why this hardens rather than removes. If these routes are dead, deleting all
three would be the better fix, and I am happy to open that PR instead. Same note appears
on #1528, which bounds the sibling
extract-contentroute.Testing
biome checkpasses on the changed file.apps/webhas no test runner configured (novitestdependency, notestscript), so no tests were added.