fix: validate response in protocol.handle() (#38587)

fix: validate response in protocol.handle()
This commit is contained in:
Shelley Vohr 2023-06-07 09:29:04 +02:00 committed by GitHub
parent 5931f69f18
commit 86824c070e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 4 deletions

View file

@ -60,6 +60,26 @@ function convertToRequestBody (uploadData: ProtocolRequest['uploadData']): Reque
}) as RequestInit['body']; }) as RequestInit['body'];
} }
// TODO(codebytere): Use Object.hasOwn() once we update to ECMAScript 2022.
function validateResponse (res: Response) {
if (!res || typeof res !== 'object') return false;
if (res.type === 'error') return true;
const exists = (key: string) => Object.prototype.hasOwnProperty.call(res, key);
if (exists('status') && typeof res.status !== 'number') return false;
if (exists('statusText') && typeof res.statusText !== 'string') return false;
if (exists('headers') && typeof res.headers !== 'object') return false;
if (exists('body')) {
if (typeof res.body !== 'object') return false;
if (res.body !== null && !(res.body instanceof ReadableStream)) return false;
}
return true;
}
Protocol.prototype.handle = function (this: Electron.Protocol, scheme: string, handler: (req: Request) => Response | Promise<Response>) { Protocol.prototype.handle = function (this: Electron.Protocol, scheme: string, handler: (req: Request) => Response | Promise<Response>) {
const register = isBuiltInScheme(scheme) ? this.interceptProtocol : this.registerProtocol; const register = isBuiltInScheme(scheme) ? this.interceptProtocol : this.registerProtocol;
const success = register.call(this, scheme, async (preq: ProtocolRequest, cb: any) => { const success = register.call(this, scheme, async (preq: ProtocolRequest, cb: any) => {
@ -73,13 +93,14 @@ Protocol.prototype.handle = function (this: Electron.Protocol, scheme: string, h
duplex: body instanceof ReadableStream ? 'half' : undefined duplex: body instanceof ReadableStream ? 'half' : undefined
} as any); } as any);
const res = await handler(req); const res = await handler(req);
if (!res || typeof res !== 'object') { if (!validateResponse(res)) {
return cb({ error: ERR_UNEXPECTED }); return cb({ error: ERR_UNEXPECTED });
} } else if (res.type === 'error') {
if (res.type === 'error') { cb({ error: ERR_FAILED }); } else { cb({ error: ERR_FAILED });
} else {
cb({ cb({
data: res.body ? Readable.fromWeb(res.body as ReadableStream<ArrayBufferView>) : null, data: res.body ? Readable.fromWeb(res.body as ReadableStream<ArrayBufferView>) : null,
headers: Object.fromEntries(res.headers), headers: res.headers ? Object.fromEntries(res.headers) : {},
statusCode: res.status, statusCode: res.status,
statusText: res.statusText, statusText: res.statusText,
mimeType: (res as any).__original_resp?._responseHead?.mimeType mimeType: (res as any).__original_resp?._responseHead?.mimeType

View file

@ -1211,6 +1211,42 @@ describe('protocol module', () => {
await expect(net.fetch('test-scheme://foo')).to.eventually.be.rejectedWith('net::ERR_FAILED'); await expect(net.fetch('test-scheme://foo')).to.eventually.be.rejectedWith('net::ERR_FAILED');
}); });
it('handles invalid protocol response status', async () => {
protocol.handle('test-scheme', () => {
return { status: [] } as any;
});
defer(() => { protocol.unhandle('test-scheme'); });
await expect(net.fetch('test-scheme://foo')).to.be.rejectedWith('net::ERR_UNEXPECTED');
});
it('handles invalid protocol response statusText', async () => {
protocol.handle('test-scheme', () => {
return { statusText: false } as any;
});
defer(() => { protocol.unhandle('test-scheme'); });
await expect(net.fetch('test-scheme://foo')).to.be.rejectedWith('net::ERR_UNEXPECTED');
});
it('handles invalid protocol response header parameters', async () => {
protocol.handle('test-scheme', () => {
return { headers: false } as any;
});
defer(() => { protocol.unhandle('test-scheme'); });
await expect(net.fetch('test-scheme://foo')).to.be.rejectedWith('net::ERR_UNEXPECTED');
});
it('handles invalid protocol response body parameters', async () => {
protocol.handle('test-scheme', () => {
return { body: false } as any;
});
defer(() => { protocol.unhandle('test-scheme'); });
await expect(net.fetch('test-scheme://foo')).to.be.rejectedWith('net::ERR_UNEXPECTED');
});
it('handles a synchronous error in the handler', async () => { it('handles a synchronous error in the handler', async () => {
protocol.handle('test-scheme', () => { throw new Error('test'); }); protocol.handle('test-scheme', () => { throw new Error('test'); });
defer(() => { protocol.unhandle('test-scheme'); }); defer(() => { protocol.unhandle('test-scheme'); });