diff --git a/config.universaltest3.json b/config.universaltest3.json new file mode 100644 index 000000000..13dcc339c --- /dev/null +++ b/config.universaltest3.json @@ -0,0 +1,34 @@ +{ + "BROKER_CLIENT_CONFIGURATION": { + "common": { + "default": { + "BROKER_SERVER_URL": "https://broker2.dev.snyk.io", + "BROKER_HA_MODE_ENABLED": "false", + "BROKER_DISPATCHER_BASE_URL": "https://api.dev.snyk.io" + } + }, + "github": { + "validations":[{ + "url": "https://notexists.notexists/no-such-url-ever" + }] + }, + "gitlab": { + "validations":[{ + "url": "https://notexists.notexists/no-such-url-ever" + }] + } + }, + "CONNECTIONS": { + "my github connection": { + "type": "github-enterprise", + "identifier": "${BROKER_TOKEN_1}", + "GITHUB_TOKEN": "${GITHUB_TOKEN}" + }, + "my gitlab connection": { + "type": "gitlab", + "identifier": "${BROKER_TOKEN_2}", + "GITLAB_TOKEN": "${GITLAB_TOKEN}", + "GITLAB":"gitlab.dev.snyk.io" + } + } + } \ No newline at end of file diff --git a/lib/client/hooks/startup/processHooks.ts b/lib/client/hooks/startup/processHooks.ts index 530ac36c6..adaaff277 100644 --- a/lib/client/hooks/startup/processHooks.ts +++ b/lib/client/hooks/startup/processHooks.ts @@ -12,8 +12,8 @@ export const processStartUpHooks = async ( ): Promise => { try { clientOpts.config.API_BASE_URL = - clientOpts.config.BROKER_DISPATCHER_BASE_URL ?? clientOpts.config.API_BASE_URL ?? + clientOpts.config.BROKER_DISPATCHER_BASE_URL ?? clientOpts.config.BROKER_SERVER_URL?.replace( '//broker.', '//api.', diff --git a/lib/client/routesHandler/websocketConnectionMiddlewares.ts b/lib/client/routesHandler/websocketConnectionMiddlewares.ts index 956fe02f6..297f928f9 100644 --- a/lib/client/routesHandler/websocketConnectionMiddlewares.ts +++ b/lib/client/routesHandler/websocketConnectionMiddlewares.ts @@ -15,19 +15,34 @@ export const websocketConnectionSelectorMiddleware = ( } else { const websocketConnections = res.locals .websocketConnections as WebSocketConnection[]; + const availableConnectionsTypes = websocketConnections.map( + (x) => x.supportedIntegrationType, + ); let inboundRequestType = ''; - //config.supportedBrokerTypes // 2 cases: webhooks/TYPE, /v[1-2] Container registry. if (req.path.startsWith('/webhook')) { const splitUrl = req.path.split('/'); + if ( splitUrl.length > 2 && - config.supportedBrokerTypes.includes(splitUrl[2]) + availableConnectionsTypes.includes(splitUrl[2]) ) { inboundRequestType = req.path.split('/')[2]; + } else if ( + splitUrl.length > 2 && + splitUrl[2] == 'github' && + availableConnectionsTypes.includes('github-enterprise') + ) { + inboundRequestType = 'github-enterprise'; } else { logger.warn({ url: req.path }, 'Unexpected type in webhook request'); + res + .status(401) + .send( + 'Unexpected type in webhook request, unable to forward to server.', + ); + return; } } else if ( req.path.startsWith('/api/v1/') || diff --git a/test/fixtures/client/filters-webhook.json b/test/fixtures/client/filters-webhook.json index 4663b1b90..72bc93e2f 100644 --- a/test/fixtures/client/filters-webhook.json +++ b/test/fixtures/client/filters-webhook.json @@ -6,6 +6,10 @@ { "path": "/webhook/github/:webhookId", "method": "POST" + }, + { + "path": "/webhook/gitlab/:webhookId", + "method": "POST" } ] } diff --git a/test/functional/webhook-universal.test.ts b/test/functional/webhook-universal.test.ts new file mode 100644 index 000000000..ce9a597d6 --- /dev/null +++ b/test/functional/webhook-universal.test.ts @@ -0,0 +1,127 @@ +const PORT = 9999; +process.env.BROKER_SERVER_URL = `http://localhost:${PORT}`; +import path from 'path'; +import { axiosClient } from '../setup/axios-client'; +import { + BrokerClient, + closeBrokerClient, + waitForBrokerServerConnections, +} from '../setup/broker-client'; +import { + BrokerServer, + closeBrokerServer, + createBrokerServer, + waitForUniversalBrokerClientsConnection, +} from '../setup/broker-server'; +import { TestWebServer, createTestWebServer } from '../setup/test-web-server'; +import { DEFAULT_TEST_WEB_SERVER_PORT } from '../setup/constants'; +import { createUniversalBrokerClient } from '../setup/broker-universal-client'; + +const fixtures = path.resolve(__dirname, '..', 'fixtures'); +const serverAccept = path.join(fixtures, 'server', 'filters-webhook.json'); +const clientAccept = path.join(fixtures, 'client', 'filters-webhook.json'); + +describe('proxy requests originating from behind the broker client', () => { + let tws: TestWebServer; + let bs: BrokerServer; + let bc: BrokerClient; + process.env.API_BASE_URL = `http://localhost:${DEFAULT_TEST_WEB_SERVER_PORT}`; + + beforeAll(async () => { + tws = await createTestWebServer(); + + bs = await createBrokerServer({ port: PORT, filters: serverAccept }); + + process.env.SNYK_BROKER_SERVER_UNIVERSAL_CONFIG_ENABLED = 'true'; + process.env.UNIVERSAL_BROKER_ENABLED = 'true'; + process.env.SERVICE_ENV = 'universaltest3'; + process.env.BROKER_TOKEN_1 = 'brokertoken1'; + process.env.BROKER_TOKEN_2 = 'brokertoken2'; + process.env.GITHUB_TOKEN = 'ghtoken'; + process.env.GITLAB_TOKEN = 'gltoken'; + process.env.SNYK_BROKER_CLIENT_CONFIGURATION__common__default__BROKER_SERVER_URL = `http://localhost:${bs.port}`; + process.env.SNYK_FILTER_RULES_PATHS__gitlab = clientAccept; + process.env['SNYK_FILTER_RULES_PATHS__github-enterprise'] = clientAccept; + + bc = await createUniversalBrokerClient(); + await waitForUniversalBrokerClientsConnection(bs, 2); + }); + + afterAll(async () => { + await tws.server.close(); + await closeBrokerClient(bc); + await closeBrokerServer(bs); + delete process.env.BROKER_SERVER_URL; + }); + + it('server identifies self to client', async () => { + const serverMetadata = await waitForBrokerServerConnections(bc); + expect(serverMetadata.map((x) => x.brokertoken)).toEqual( + expect.arrayContaining(['brokertoken1', 'brokertoken2']), + ); + expect(serverMetadata.map((x) => x.capabilities)).toEqual( + expect.arrayContaining([ + ['receive-post-streams'], + ['receive-post-streams'], + ]), + ); + }); + + it('successfully broker Webhook call via tunnel', async () => { + const response = await axiosClient.post( + `http://localhost:${bc.port}/webhook/gitlab/12345678-1234-1234-1234-123456789abc`, + { some: { example: 'json' } }, + ); + + expect(response.status).toEqual(200); + expect(response.data).toStrictEqual('Received webhook via websocket'); + }); + + it('successfully broker GHE Webhook call via tunnel without github conn', async () => { + const response = await axiosClient.post( + `http://localhost:${bc.port}/webhook/github/12345678-1234-1234-1234-123456789abc`, + { some: { example: 'json' } }, + ); + + expect(response.status).toEqual(200); + expect(response.data).toStrictEqual('Received webhook via websocket'); + }); + + it('successfully broker Webhook call via API without github conn', async () => { + await closeBrokerServer(bs); + + const response = await axiosClient.post( + `http://localhost:${bc.port}/webhook/github/12345678-1234-1234-1234-000000000000`, + { some: { example: 'json' } }, + ); + + expect(response.status).toEqual(200); + expect(response.data).toStrictEqual('Received webhook via API'); + }); + + it('successfully fails Webhook call without matching type', async () => { + const response = await axiosClient.post( + `http://localhost:${bc.port}/webhook/githubd/12345678-1234-1234-1234-000000000000`, + { some: { example: 'json' } }, + ); + + expect(response.status).toEqual(401); + expect(response.data).toStrictEqual( + 'Unexpected type in webhook request, unable to forward to server.', + ); + }); + + it('successfully fails Webhook call via API without matching type', async () => { + await closeBrokerServer(bs); + + const response = await axiosClient.post( + `http://localhost:${bc.port}/webhook/githubd/12345678-1234-1234-1234-000000000000`, + { some: { example: 'json' } }, + ); + + expect(response.status).toEqual(401); + expect(response.data).toStrictEqual( + 'Unexpected type in webhook request, unable to forward to server.', + ); + }); +}); diff --git a/test/setup/test-web-server.ts b/test/setup/test-web-server.ts index 3beafceb2..01456ad54 100644 --- a/test/setup/test-web-server.ts +++ b/test/setup/test-web-server.ts @@ -103,6 +103,13 @@ const applyEchoRoutes = (app: Express) => { resp.send('Received webhook via websocket'); }, ); + echoRouter.post( + '/webhook/gitlab/12345678-1234-1234-1234-123456789abc', + (_: express.Request, resp: express.Response) => { + resp.status(200); + resp.send('Received webhook via websocket'); + }, + ); echoRouter.post( '/webhook/github/12345678-1234-1234-1234-000000000000', (_: express.Request, resp: express.Response) => {