Skip to content

Commit

Permalink
fix(logging): Adjusting webhook logs and levels (philips-labs#1287)
Browse files Browse the repository at this point in the history
* fix(logging): Adjusting webhook logs and levels

* Correcting object key

* Minor refactoring to support additional logging
  • Loading branch information
mcaulifn authored Oct 21, 2021
1 parent 7dd662c commit 9df5fb8
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 61 deletions.
8 changes: 4 additions & 4 deletions modules/webhook/lambdas/webhook/src/lambda.ts
Original file line number Diff line number Diff line change
@@ -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<void> => {
export const githubWebhook = async (event: APIGatewayEvent, context: Context, callback: Callback): Promise<void> => {
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);
}
};
94 changes: 37 additions & 57 deletions modules/webhook/lambdas/webhook/src/webhook/handler.ts
Original file line number Diff line number Diff line change
@@ -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<number> => {
export const handle = async (headers: IncomingHttpHeaders, body: string): Promise<number> => {
// 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<number> {
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<number> {
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<number> {
const disableCheckWorkflowJobLabelsEnv = process.env.DISABLE_CHECK_WORKFLOW_JOB_LABELS || 'false';
const disableCheckWorkflowJobLabels = JSON.parse(disableCheckWorkflowJobLabelsEnv) as boolean;
if (!disableCheckWorkflowJobLabels && !canRunJob(body)) {
Expand All @@ -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<number> {
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;
Expand All @@ -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<string>;

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<string>);

Expand Down

0 comments on commit 9df5fb8

Please sign in to comment.