diff --git a/browse/src/cookie-import-browser.ts b/browse/src/cookie-import-browser.ts index e8d793ea6..9029c2fcd 100644 --- a/browse/src/cookie-import-browser.ts +++ b/browse/src/cookie-import-browser.ts @@ -989,6 +989,7 @@ export class CdpPipeTransport { }); this.readStream.on('end', () => { if (!this.closed) { + this.closed = true; this.abortAll(new CookieImportError('CDP read stream ended unexpectedly', 'cdp_error')); } }); @@ -1113,7 +1114,13 @@ export async function extractCookiesViaCdpPipe( targetId: pageTarget.targetId, flatten: true, }); - const sessionId = attached.sessionId as string; + const sessionId = attached.sessionId as string | undefined; + if (typeof sessionId !== 'string' || sessionId.length === 0) { + throw new CookieImportError( + 'Target.attachToTarget returned no sessionId', + 'cdp_error', + ); + } await transport.send('Network.enable', undefined, sessionId); const cookiesResp = await transport.send('Network.getAllCookies', undefined, sessionId); @@ -1126,7 +1133,7 @@ export async function extractCookiesViaCdpPipe( } const result: PlaywrightCookie[] = []; - for (const c of cookiesResp.cookies as CdpCookie[]) { + for (const c of (cookiesResp.cookies ?? []) as CdpCookie[]) { if (!domainSet.has(c.domain)) continue; result.push({ name: c.name, diff --git a/browse/test/cookie-import-browser.test.ts b/browse/test/cookie-import-browser.test.ts index d9776bbb9..f8035ea26 100644 --- a/browse/test/cookie-import-browser.test.ts +++ b/browse/test/cookie-import-browser.test.ts @@ -649,6 +649,25 @@ describe('CdpPipeTransport', () => { expect(caught!.code).toBe('cdp_error'); }); + test('send after read-stream end rejects immediately (closed flag flipped)', async () => { + const pipes = makeMockPipes(); + const t = new CdpPipeTransport(pipes.writeToChild, pipes.readFromChild); + + pipes.closeFromChild(); + // Give the 'end' handler a tick to fire. + await flush(); + + let caught: any = null; + try { + await t.send('Network.enable'); + } catch (e) { + caught = e; + } + expect(caught).toBeInstanceOf(CookieImportError); + expect(caught.code).toBe('cdp_error'); + expect(caught.message.toLowerCase()).toContain('closed'); + }); + test('send with explicit sessionId includes it in the frame', async () => { const pipes = makeMockPipes(); const t = new CdpPipeTransport(pipes.writeToChild, pipes.readFromChild); @@ -784,6 +803,33 @@ describe('extractCookiesViaCdpPipe', () => { expect(cookies).toEqual([]); expect(transport.sent).toHaveLength(0); }); + + test('returns empty array when Network.getAllCookies response has no cookies field', async () => { + const transport = new ScriptedTransport(); + transport.script('Target.getTargets', { targetInfos: [{ targetId: 'P', type: 'page' }] }); + transport.script('Target.attachToTarget', { sessionId: 'S1' }); + transport.script('Network.enable', {}); + transport.script('Network.getAllCookies', {}); // no 'cookies' field + + const cookies = await extractCookiesViaCdpPipe(transport as any, ['example.com']); + expect(cookies).toEqual([]); + }); + + test('throws cdp_error when attachToTarget returns no sessionId', async () => { + const transport = new ScriptedTransport(); + transport.script('Target.getTargets', { targetInfos: [{ targetId: 'P', type: 'page' }] }); + transport.script('Target.attachToTarget', {}); // missing sessionId + + let caught: any = null; + try { + await extractCookiesViaCdpPipe(transport as any, ['example.com']); + } catch (e) { + caught = e; + } + expect(caught).toBeInstanceOf(CookieImportError); + expect(caught.code).toBe('cdp_error'); + expect(caught.message.toLowerCase()).toContain('sessionid'); + }); }); describe('buildCdpSpawnArgs', () => {