mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-10-26 16:55:08 +00:00
fix(liveshare-protocol): forward only real route segments
The carrier matched forwarded routes by substring, so /api/present.hostile and /x/open-in-app/apps-foo were posted to the owner as if they were the read routes. Match only where the route starts a path segment and ends at the pathname or a following slash, keeping the Relay base path prefix working; add hit, subpath, prefix-neighbour and query cases.
This commit is contained in:
1 parent
d218ba88f0
commit
eef30f8462
2 files changed
+46
-4
No files matched your search
@@ -135,17 +135,36 @@ export function installViewerCarrier(options: ViewerCarrierOptions): void {
|
||||
*/
|
||||
const forwardedRoutes = ['/open-in-app/apps', '/api/present.host']
|
||||
/**
|
||||
* Owner path one page request forwards, matched by suffix because the page URL carries the Relay's own
|
||||
* base path in front of it.
|
||||
* Offset at which `route` starts a path segment run, ignoring a Relay base path in front of it.
|
||||
* @param pathname - Resolved request pathname.
|
||||
* @param route - Owner route to find.
|
||||
* @returns The offset, or undefined when the route appears only inside a longer segment.
|
||||
*/
|
||||
const segmentOffset = (pathname: string, route: string): number | undefined => {
|
||||
let from = 0
|
||||
for (;;) {
|
||||
const at = pathname.indexOf(route, from)
|
||||
if (at === -1) return undefined
|
||||
// The route starts with '/', so its own slash is the segment boundary; only the tail decides
|
||||
// whether a longer segment (e.g. `/api/present.hostile`) merely shares the prefix.
|
||||
const next = pathname[at + route.length]
|
||||
if (next === undefined || next === '/') return at
|
||||
from = at + 1
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Owner path one page request forwards, matched at path-segment boundaries because the page URL carries
|
||||
* the Relay's own base path in front of it. Prefix-neighbouring paths such as `/api/present.hostile` or
|
||||
* `/x/open-in-app/apps-foo` are not this route.
|
||||
* @param pathname - Resolved request pathname (query and hash already excluded).
|
||||
* @param method - Request method, already upper-cased.
|
||||
* @returns The owner path to post, or undefined when this request is not one of the forwarded routes.
|
||||
*/
|
||||
const forwardedRoute = (pathname: string, method: string): string | undefined => {
|
||||
if (method !== 'GET') return undefined
|
||||
for (const route of forwardedRoutes) {
|
||||
const at = pathname.lastIndexOf(route)
|
||||
if (at !== -1) return pathname.slice(at)
|
||||
const at = segmentOffset(pathname, route)
|
||||
if (at !== undefined) return pathname.slice(at)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -541,6 +541,29 @@ describe('viewer carrier forwarded page routes', () => {
|
||||
expect(posted[0]).toMatchObject({ op: 'call', path: '/open-in-app/apps' })
|
||||
})
|
||||
|
||||
it('forwards a route only when it starts a path segment', async () => {
|
||||
const below = carrier(() => json({ apps: ['finder'] }))
|
||||
expect(await (await globalThis.fetch('/relay/s/abc/open-in-app/apps/foo')).json()).toEqual({ apps: ['finder'] })
|
||||
expect(below.posted[0]).toMatchObject({ op: 'call', path: '/open-in-app/apps/foo' })
|
||||
const exact = carrier(() => json({ name: 'owner-host' }))
|
||||
expect(await (await globalThis.fetch('/relay/s/abc/api/present.host')).json()).toEqual({ name: 'owner-host' })
|
||||
expect(exact.posted[0]).toMatchObject({ op: 'call', path: '/api/present.host' })
|
||||
})
|
||||
|
||||
it('does not forward a prefix neighbour of a forwarded route', async () => {
|
||||
const direct = directFetch()
|
||||
expect(await (await globalThis.fetch('/api/present.hostile')).text()).toBe('direct:/api/present.hostile')
|
||||
expect(await (await globalThis.fetch('/x/open-in-app/apps-foo')).text()).toBe('direct:/x/open-in-app/apps-foo')
|
||||
expect(await (await globalThis.fetch('/api/present.hostile?x=1')).text()).toBe('direct:/api/present.hostile?x=1')
|
||||
expect(direct).toHaveBeenCalledTimes(3)
|
||||
})
|
||||
|
||||
it('forwards a hit whose page URL carries a query without passing it to the owner', async () => {
|
||||
const { posted } = carrier(() => json({ name: 'owner-host', available: true, fileManager: 'finder' }))
|
||||
expect(await (await globalThis.fetch('/relay/s/abc/api/present.host?x=1#frag')).json()).toMatchObject({ name: 'owner-host' })
|
||||
expect(posted[0]).toMatchObject({ op: 'call', path: '/api/present.host' })
|
||||
})
|
||||
|
||||
it('leaves every other request to the page\u2019s own fetch', async () => {
|
||||
const direct = directFetch()
|
||||
expect(await (await globalThis.fetch('health')).text()).toBe('direct:health')
|
||||
|
||||
Reference in new issue
Block a user