Originless localhost GETs can read protected conversation and schedule data
Summary
Protected app routes allow originless GET requests as long as they come from loopback. That means a local process can read protected BrowserOS data without presenting an extension/app origin.
This affects routes that return private app state, including full conversation bodies and scheduled job run results.
Evidence
packages/browseros-agent/apps/server/src/api/utils/request-auth.ts treats originless GET, HEAD, and OPTIONS requests as trusted app requests after the loopback/host checks pass:
const origin = c.req.header('origin')
if (origin) return isTrustedAppOrigin(origin)
return ['GET', 'HEAD', 'OPTIONS'].includes(c.req.method)Those checks are used on protected route prefixes in packages/browseros-agent/apps/server/src/api/routes/index.ts:
.use('/conversations/*', requireTrustedAppOrigin())
.use('/providers/*', requireTrustedAppOrigin())
.use('/scheduled-jobs/*', requireTrustedAppOrigin())
.use('/scheduled-job-runs/*', requireTrustedAppOrigin())The routes include read endpoints that return private data:
GET /conversations/:conversationIdreturns the fullmessagesblob frompackages/browseros-agent/apps/server/src/api/routes/conversations.tsGET /scheduled-job-runs/:runIdreturns stored runresult,finalResult,executionLog,toolCalls, anderrorfrompackages/browseros-agent/apps/server/src/api/routes/scheduled-job-runs.ts
There are tests showing the protected routes reject when no Bun server.requestIP context is present, but there does not appear to be coverage for a real loopback request with no Origin header.
Repro sketch
From a local process on the same machine, request a protected read route without an Origin header, for example:
curl -H 'Host: 127.0.0.1:<port>' http://127.0.0.1:<port>/conversations/<conversation-id>Because this is a loopback GET with no Origin, isTrustedAppRequest() returns true and the route can serve the protected data.
Expected
Protected app-state routes should require a trusted BrowserOS app/extension origin even for reads, or split genuinely public/status reads onto separate unprotected routes.
Actual
Originless loopback reads are accepted for protected route prefixes that expose user conversation and scheduled-task data.
Source: browseros-ai/BrowserOS