diff --git a/plugins/gateway-backend/src/plugin.test.ts b/plugins/gateway-backend/src/plugin.test.ts index b7619e3dfc..8db99e39a5 100644 --- a/plugins/gateway-backend/src/plugin.test.ts +++ b/plugins/gateway-backend/src/plugin.test.ts @@ -176,6 +176,26 @@ describe('gateway', () => { }); }); + it('should detect looped requests with intentional negative hop count', async () => { + const response = await fetch( + 'http://localhost:7777/api/nonexistent-plugin/foo', + { + headers: { + 'backstage-gateway-hops': '-1000000', + }, + }, + ); + expect(response.status).toBe(508); + + const data = await response.json(); + expect(data).toEqual({ + error: { + name: 'LoopDetectedError', + message: 'Maximum proxy hop count exceeded (3)', + }, + }); + }); + it('should close the response for sse connections', async () => { const eventSource = new EventSource( 'http://localhost:7777/api/external-plugin/endpoint-sse', diff --git a/plugins/gateway-backend/src/router.ts b/plugins/gateway-backend/src/router.ts index f04bd3ee58..6c9b4ba6c7 100644 --- a/plugins/gateway-backend/src/router.ts +++ b/plugins/gateway-backend/src/router.ts @@ -47,7 +47,7 @@ export async function createRouter({ on: { proxyReq(proxyReq, req: Request<{ pluginId: string }>) { const currentHops = - parseInt(req.headers[HOPS_HEADER] as string, 10) || 0; + Math.max(parseInt(req.headers[HOPS_HEADER] as string, 10), 0) || 0; proxyReq.setHeader(HOPS_HEADER, currentHops + 1); },