-
Notifications
You must be signed in to change notification settings - Fork 2
/
handler.js
138 lines (121 loc) · 3.85 KB
/
handler.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
"use strict";
// adapted from https://github.com/serverless/examples/blob/master/aws-node-github-webhook-listener/handler.js
if (process.env.SENTRY_DSN) {
const Sentry = require("@sentry/node");
Sentry.init({ dsn: process.env.SENTRY_DSN });
}
const axios = require("axios");
const crypto = require("crypto");
const signString = (key, string, hash) => {
if (crypto.getHashes().indexOf(hash) === -1) {
throw new Error(`Unsupported signing hash: ${hash}`);
}
return `${hash}=${crypto
.createHmac(hash, key)
.update(string, "utf-8")
.digest("hex")}`;
};
module.exports.signString = signString;
module.exports.klaxon = async (event, context, callback) => {
const SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL;
const GITHUB_VERIFICATION_TOKEN = process.env.GITHUB_VERIFICATION_TOKEN;
if (typeof GITHUB_VERIFICATION_TOKEN !== "string") {
return callback(null, {
statusCode: 401,
headers: { "Content-Type": "text/plain" },
body: "GITHUB_VERIFICATION_TOKEN is a required environment variable"
});
}
const body = JSON.parse(event.body);
const { action, alert, repository } = body;
const { headers } = event;
const signature = headers["X-Hub-Signature"];
if (!signature) {
return callback(null, {
statusCode: 422,
headers: { "Content-Type": "text/plain" },
body: "X-Hub-Signature header not found"
});
}
if (!headers["X-GitHub-Event"]) {
return callback(null, {
statusCode: 422,
headers: { "Content-Type": "text/plain" },
body: "X-GitHub-Event header not found"
});
}
const signatureHash = signature.split("=").shift();
if (
signString(GITHUB_VERIFICATION_TOKEN, event.body, signatureHash) !==
signature
) {
return callback(null, {
statusCode: 401,
headers: { "Content-Type": "text/plain" },
body: "X-Hub-Signature header failed verification"
});
}
if (headers["X-GitHub-Event"] === "ping") {
return callback(null, {
statusCode: 200,
headers: { "Content-Type": "text/plain" },
body: "pong"
});
}
if (headers["X-GitHub-Event"] !== "repository_vulnerability_alert") {
const response = {
statusCode: 418,
headers: { "Content-Type": "text/plain" },
body: `X-GitHub-Event ${headers["X-GitHub-Event"]} not supported`
};
return callback(null, response);
}
if (!headers["X-GitHub-Delivery"]) {
return callback(null, {
statusCode: 422,
headers: { "Content-Type": "text/plain" },
body: "X-GitHub-Delivery header not found"
});
}
const slackColors = { create: "danger", dismiss: "warning", resolve: "good" };
const slackVerbs = {
create: "found",
dismiss: "dismissed",
resolve: "fixed"
};
const slackMessage = {
attachments: [
{
fallback: `A vulnerability in ${alert.affected_package_name} has been ${
slackVerbs[action]
} in ${repository.full_name}.`,
color: slackColors[action],
title: "GitHub Vulnerability Alert",
title_link: `${repository.html_url}/network/alerts`,
text: `A <${alert.external_reference}|vulnerability> with ${
alert.affected_package_name
} has been ${slackVerbs[action]} in <${repository.html_url}|${
repository.full_name
}>.`
}
],
username: "GitHub",
icon_url: "https://ca.slack-edge.com/T35D8CZQR-UA6QSB1JB-946b161caa44-72"
};
if (process.env.SLACK_CHANNEL_ID) {
slackMessage.channel = process.env.SLACK_CHANNEL_ID;
}
const slackResponse = await axios.post(SLACK_WEBHOOK_URL, slackMessage);
return callback(null, {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
input: event,
slackMessage: slackMessage,
slackResponse: {
status: slackResponse.status,
statusText: slackResponse.statusText
}
})
});
};