Check the route for a team query parameter while you are in there. The common v0 shape is /api/pages?team=sales, which looks like a filter but is user input, so changing it to hr returns HR pages with a straight face. Team comes from the session or from RLS. Never from the URL.
Internal wiki search shows pages from other teams, filter runs in the browser
Repo or live app
github.com/tomachterberg/team-wiki ↗
Each team has its own space. Someone in sales searched for a name and saw a snippet from an HR page. v0's search fetches /api/pages and filters by team in the component. So the snippet was on their machine all along, which I understand is the actual problem here, not the search box.
2 answers
You have diagnosed it correctly, which puts you ahead of most reports like this. The browser received HR's content; hiding it in the component was never a control.
Move the filter into the query and derive the team from the session, never from a parameter the client sends:
const { data } = await supabase.from('pages')
.select('id,title,excerpt')
.textSearch('body', q)with an RLS policy on pages restricting to the caller's team memberships. Then /api/pages cannot return HR rows even if someone calls it directly with curl, which they now know they can.
One more thing: treat the HR content as disclosed. Whoever searched saw it, and anyone with the browser console saw more.