diff --git a/src/entry/webtriggers/process-gitlab-event.test.ts b/src/entry/webtriggers/process-gitlab-event.test.ts index 4effbf5..258a6a7 100644 --- a/src/entry/webtriggers/process-gitlab-event.test.ts +++ b/src/entry/webtriggers/process-gitlab-event.test.ts @@ -87,6 +87,17 @@ describe('processGitlabEvent', () => { expect(serverResponse).toHaveBeenCalledWith('Invalid event format', 400); }); + it('returns server response error in case of unexpected error', async () => { + const webtriggerRequest = generateWebtriggerRequest('

Invalid body

'); + + storage.getSecret.mockRejectedValue(new Error()); + + await processGitlabEvent(webtriggerRequest, MOCK_CONTEXT); + + expect(mockHandlePushEvent).not.toHaveBeenCalled(); + expect(serverResponse).toHaveBeenCalledWith('The webhook could not be processed', 500); + }); + it('handles pipeline event when FF is enabled', async () => { const webtriggerRequest = generateWebtriggerRequest(JSON.stringify(MOCK_PIPELINE_EVENT)); diff --git a/src/entry/webtriggers/process-gitlab-event.ts b/src/entry/webtriggers/process-gitlab-event.ts index 6e2d6ec..c874a12 100644 --- a/src/entry/webtriggers/process-gitlab-event.ts +++ b/src/entry/webtriggers/process-gitlab-event.ts @@ -19,59 +19,71 @@ import { handlePipelineEvent, } from './gitlab-event-handlers'; import { listFeatures } from '../../services/feature-flags'; +import { ParseWebhookEventPayloadError, ValidateWebhookSignatureError } from '../../models/errors'; type Context = { principal: undefined; installContext: string; }; -class ValidateWebhookSignatureError extends Error {} - const validateWebhookSignature = (eventSignature: string, controlSignature: string): void | never => { if (eventSignature !== controlSignature) { throw new ValidateWebhookSignatureError(); } }; -export const processGitlabEvent = async (event: WebtriggerRequest, context: Context): Promise => { - const { installContext } = context; - const cloudId = parse(installContext).resourceId; - const groupId = event.queryParameters.groupId[0]; - const groupToken = await storage.getSecret(`${STORAGE_SECRETS.GROUP_TOKEN_KEY_PREFIX}${groupId}`); - const eventPayload = event.body; - let parsedEvent: GitlabEvent; +const parseEventPayload = (eventPayload: string): GitlabEvent | never => { + try { + return JSON.parse(eventPayload); + } catch { + throw new ParseWebhookEventPayloadError(); + } +}; +export const processGitlabEvent = async (event: WebtriggerRequest, context: Context): Promise => { try { + const { installContext } = context; + const cloudId = parse(installContext).resourceId; + const groupId = event.queryParameters.groupId[0]; + const groupToken = await storage.getSecret(`${STORAGE_SECRETS.GROUP_TOKEN_KEY_PREFIX}${groupId}`); + const eventPayload = event.body; + validateWebhookSignature( event.headers['x-gitlab-token'][0], await storage.get(`${STORAGE_KEYS.WEBHOOK_SIGNATURE_PREFIX}${groupId}`), ); - parsedEvent = JSON.parse(eventPayload); + + const parsedEvent = parseEventPayload(eventPayload); + + if (parsedEvent.object_kind === 'push') { + await handlePushEvent(parsedEvent as PushEvent, groupToken, cloudId); + } + + if (parsedEvent.object_kind === 'merge_request') { + await handleMergeRequestEvent(parsedEvent as MergeRequestEvent, groupToken, cloudId); + } + + if (parsedEvent.object_kind === 'pipeline') { + await handlePipelineEvent(parsedEvent as PipelineEvent, groupToken, cloudId); + } + + if (parsedEvent.object_kind === 'deployment') { + await handleDeploymentEvent(parsedEvent as DeploymentEvent, groupToken, cloudId); + } + + return serverResponse('Processed webhook event'); } catch (error) { if (error instanceof ValidateWebhookSignatureError) { console.error({ message: 'Webhook event secret is invalid', error }); return serverResponse('Invalid webhook secret', 403); } - console.error({ message: 'Failed parsing webhook event', error }); - return serverResponse('Invalid event format', 400); - } - - if (parsedEvent.object_kind === 'push') { - await handlePushEvent(parsedEvent as PushEvent, groupToken, cloudId); - } - - if (parsedEvent.object_kind === 'merge_request') { - await handleMergeRequestEvent(parsedEvent as MergeRequestEvent, groupToken, cloudId); - } - - if (parsedEvent.object_kind === 'pipeline') { - await handlePipelineEvent(parsedEvent as PipelineEvent, groupToken, cloudId); - } + if (error instanceof ParseWebhookEventPayloadError) { + console.error({ message: 'Failed parsing webhook event', error }); + return serverResponse('Invalid event format', 400); + } - if (parsedEvent.object_kind === 'deployment') { - await handleDeploymentEvent(parsedEvent as DeploymentEvent, groupToken, cloudId); + console.error({ message: 'Unexpected error while processing webhook', error }); + return serverResponse('The webhook could not be processed', 500); } - - return serverResponse('Processed webhook event'); }; diff --git a/src/models/errors.ts b/src/models/errors.ts index 5b415dc..1e3d95f 100644 --- a/src/models/errors.ts +++ b/src/models/errors.ts @@ -55,3 +55,7 @@ export class GitlabHttpMethodError extends Error { this.statusText = statusText; } } + +export class ValidateWebhookSignatureError extends Error {} + +export class ParseWebhookEventPayloadError extends Error {}