From 9df5fb88fee5b8a9428afe90ce13a0680d50471f Mon Sep 17 00:00:00 2001 From: Nathaniel McAuliffe Date: Thu, 21 Oct 2021 16:38:30 -0400 Subject: [PATCH] fix(logging): Adjusting webhook logs and levels (#1287) * fix(logging): Adjusting webhook logs and levels * Correcting object key * Minor refactoring to support additional logging --- modules/webhook/lambdas/webhook/src/lambda.ts | 8 +- .../lambdas/webhook/src/webhook/handler.ts | 94 ++++++++----------- 2 files changed, 41 insertions(+), 61 deletions(-) diff --git a/modules/webhook/lambdas/webhook/src/lambda.ts b/modules/webhook/lambdas/webhook/src/lambda.ts index 29a96dd0bd..3c4599222e 100644 --- a/modules/webhook/lambdas/webhook/src/lambda.ts +++ b/modules/webhook/lambdas/webhook/src/lambda.ts @@ -1,14 +1,14 @@ import { handle } from './webhook/handler'; -import { APIGatewayEvent, Context } from 'aws-lambda'; +import { APIGatewayEvent, Context, Callback } from 'aws-lambda'; // eslint-disable-next-line @typescript-eslint/no-explicit-any -export const githubWebhook = async (event: APIGatewayEvent, context: Context, callback: any): Promise => { +export const githubWebhook = async (event: APIGatewayEvent, context: Context, callback: Callback): Promise => { try { - const statusCode = await handle(event.headers, event.body); + const statusCode = await handle(event.headers, event.body as string); callback(null, { statusCode: statusCode, }); } catch (e) { - callback(e); + callback(e as Error); } }; diff --git a/modules/webhook/lambdas/webhook/src/webhook/handler.ts b/modules/webhook/lambdas/webhook/src/webhook/handler.ts index 038df6a1b1..5150da7e3d 100644 --- a/modules/webhook/lambdas/webhook/src/webhook/handler.ts +++ b/modules/webhook/lambdas/webhook/src/webhook/handler.ts @@ -1,76 +1,59 @@ import { IncomingHttpHeaders } from 'http'; import { Webhooks } from '@octokit/webhooks'; import { sendActionRequest } from '../sqs'; -import { CheckRunEvent } from '@octokit/webhooks-types'; +import { CheckRunEvent, WorkflowJobEvent } from '@octokit/webhooks-types'; import { getParameterValue } from '../ssm'; -// Event type not available yet in SDK -export interface WorkflowJob { - action: 'queued' | 'created' | 'completed'; - workflow_job: { - id: number; - labels: [string]; - }; - repository: { - id: number; - name: string; - full_name: string; - owner: { - login: string; - }; - }; - organization: { - login: string; - }; - installation?: { - id?: number; - }; -} - -export const handle = async (headers: IncomingHttpHeaders, payload: any): Promise => { +export const handle = async (headers: IncomingHttpHeaders, body: string): Promise => { // ensure header keys lower case since github headers can contain capitals. for (const key in headers) { headers[key.toLowerCase()] = headers[key]; } - const signature = headers['x-hub-signature'] as string; - if (!signature) { - console.error("Github event doesn't have signature. This webhook requires a secret to be configured."); - return 500; - } - - const secret = await getParameterValue(process.env.ENVIRONMENT as string, 'github_app_webhook_secret'); + const githubEvent = headers['x-github-event'] as string; - const webhooks = new Webhooks({ - secret: secret, - }); - if (!(await webhooks.verify(payload as string, signature))) { - console.error('Unable to verify signature!'); - return 401; + let status = await verifySignature(githubEvent, headers['x-hub-signature'] as string, body); + if (status != 200) { + return status; } + const payload = JSON.parse(body); + console.info(`Received Github event ${githubEvent} from ${payload.repository.full_name}`); - const githubEvent = headers['x-github-event'] as string; - - console.debug(`Received Github event: "${githubEvent}"`); + if (isRepoNotAllowed(payload.repository.full_name)) { + console.error(`Received event from unauthorized repository ${payload.repository.full_name}`); + return 403; + } - let status = 200; if (githubEvent == 'workflow_job') { - status = await handleWorkflowJob(JSON.parse(payload) as WorkflowJob, githubEvent); + status = await handleWorkflowJob(payload as WorkflowJobEvent, githubEvent); } else if (githubEvent == 'check_run') { - status = await handleCheckRun(JSON.parse(payload) as CheckRunEvent, githubEvent); + status = await handleCheckRun(payload as CheckRunEvent, githubEvent); } else { - console.debug('Ignore event ' + githubEvent); + console.warn(`Ignoring unsupported event ${githubEvent}`); } return status; }; -async function handleWorkflowJob(body: WorkflowJob, githubEvent: string): Promise { - if (isRepoNotAllowed(body)) { - console.error(`Received event from unauthorized repository ${body.repository.full_name}`); - return 403; +async function verifySignature(githubEvent: string, signature: string, body: string): Promise { + if (!signature) { + console.error("Github event doesn't have signature. This webhook requires a secret to be configured."); + return 500; + } + + const secret = await getParameterValue(process.env.ENVIRONMENT as string, 'github_app_webhook_secret'); + + const webhooks = new Webhooks({ + secret: secret, + }); + if (!(await webhooks.verify(body, signature))) { + console.error('Unable to verify signature!'); + return 401; } + return 200; +} +async function handleWorkflowJob(body: WorkflowJobEvent, githubEvent: string): Promise { const disableCheckWorkflowJobLabelsEnv = process.env.DISABLE_CHECK_WORKFLOW_JOB_LABELS || 'false'; const disableCheckWorkflowJobLabels = JSON.parse(disableCheckWorkflowJobLabelsEnv) as boolean; if (!disableCheckWorkflowJobLabels && !canRunJob(body)) { @@ -91,15 +74,11 @@ async function handleWorkflowJob(body: WorkflowJob, githubEvent: string): Promis installationId: installationId, }); } + console.info(`Successfully queued job for ${body.repository.full_name}`); return 200; } async function handleCheckRun(body: CheckRunEvent, githubEvent: string): Promise { - if (isRepoNotAllowed(body)) { - console.error(`Received event from unauthorized repository ${body.repository.full_name}`); - return 403; - } - let installationId = body.installation?.id; if (installationId == null) { installationId = 0; @@ -113,17 +92,18 @@ async function handleCheckRun(body: CheckRunEvent, githubEvent: string): Promise installationId: installationId, }); } + console.info(`Successfully queued job for ${body.repository.full_name}`); return 200; } -function isRepoNotAllowed(body: WorkflowJob | CheckRunEvent): boolean { +function isRepoNotAllowed(repo_full_name: string): boolean { const repositoryWhiteListEnv = process.env.REPOSITORY_WHITE_LIST || '[]'; const repositoryWhiteList = JSON.parse(repositoryWhiteListEnv) as Array; - return repositoryWhiteList.length > 0 && !repositoryWhiteList.includes(body.repository.full_name); + return repositoryWhiteList.length > 0 && !repositoryWhiteList.includes(repo_full_name); } -function canRunJob(job: WorkflowJob): boolean { +function canRunJob(job: WorkflowJobEvent): boolean { const runnerLabelsEnv = process.env.RUNNER_LABELS || '[]'; const runnerLabels = new Set(JSON.parse(runnerLabelsEnv) as Array);