-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathindex.js
More file actions
179 lines (161 loc) · 4.98 KB
/
index.js
File metadata and controls
179 lines (161 loc) · 4.98 KB
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
"use strict";
/**
* Follow these steps to configure the webhook in Slack:
*
* 1. Navigate to https://<your-team-domain>.slack.com/services/new
*
* 2. Search for and select "Incoming WebHooks".
*
* 3. Choose the default channel where messages will be sent and click "Add Incoming WebHooks Integration".
*
* 4. Copy the webhook URL from the setup instructions and use it in the next section.
*
*
* To encrypt your secrets use the following steps:
*
* 1. Create or use an existing KMS Key - http://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html
*
* 2. Click the "Enable Encryption Helpers" checkbox
*
* 3. Paste <SLACK_HOOK_URL> into the kmsEncryptedHookUrl environment variable and click encrypt
*
* Note: You must exclude the protocol from the URL (e.g. "hooks.slack.com/services/abc123").
*
* 4. Give your function's role permission for the kms:Decrypt action.
* Example:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1443036478000",
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": [
"<your KMS key ARN>"
]
}
]
}
*/
const AWS = require("aws-sdk");
const url = require("url");
const https = require("https");
const crypto = require("crypto");
// Verification function to check if it is actually GitHub who is POSTing here
const verifyGitHub = (req) => {
console.log(req);
if (!req.headers["User-Agent"].includes("GitHub-Hookshot")) {
return false;
}
// Compare their hmac signature to our hmac signature
// (hmac = hash-based message authentication code)
const theirSignature = req.headers["X-Hub-Signature"];
const payload = req.body;
console.log(payload);
const secret = process.env.SECRET_TOKEN;
const ourSignature = `sha1=${crypto
.createHmac("sha1", secret)
.update(payload)
.digest("hex")}`;
return crypto.timingSafeEqual(
Buffer.from(theirSignature),
Buffer.from(ourSignature)
);
};
// The base-64 encoded, encrypted key (CiphertextBlob) stored in the kmsEncryptedHookUrl environment variable
const kmsEncryptedHookUrl = process.env.kmsEncryptedHookUrl;
// The Slack channel to send a message to stored in the slackChannel environment variable
const slackChannel = process.env.slackChannel;
let hookUrl;
function postMessage(message, callback) {
const body = JSON.stringify(message);
const options = url.parse(hookUrl);
options.method = "POST";
options.headers = {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(body),
};
const postReq = https.request(options, (res) => {
const chunks = [];
res.setEncoding("utf8");
res.on("data", (chunk) => chunks.push(chunk));
res.on("end", () => {
if (callback) {
callback({
body: chunks.join(""),
statusCode: res.statusCode,
statusMessage: res.statusMessage,
});
}
});
return res;
});
postReq.write(body);
postReq.end();
}
function processEvent(event, callback) {
if (!verifyGitHub(event)) {
callback(null, {
statusCode: 403,
body: "something is wrong, github secret does not match",
});
return;
}
const body = JSON.parse(event.body);
const repository = body.repository.full_name;
const refType = body.ref_type;
const ref = body.ref;
if (typeof ref === "undefined") {
callback(null, {
statusCode: 204,
body: "this is not an event type we handle",
});
return;
}
const slackMessage = {
channel: slackChannel,
text: `${repository} created a new ${refType} called ${ref}`,
};
postMessage(slackMessage, (response) => {
if (response.statusCode < 400) {
console.info("Message posted successfully");
callback(null);
} else if (response.statusCode < 500) {
console.error(
`Error posting message to Slack API: ${response.statusCode} - ${response.statusMessage}`
);
callback(null); // Don't retry because the error is due to a problem with the request
} else {
// Let Lambda retry
callback(
`Server error when processing message: ${response.statusCode} - ${response.statusMessage}`
);
}
});
callback(null, { statusCode: 200, body: "results" });
}
exports.handler = (event, context, callback) => {
if (hookUrl) {
// Container reuse, simply process the event with the key in memory
processEvent(event, callback);
} else if (
kmsEncryptedHookUrl &&
kmsEncryptedHookUrl !== "<kmsEncryptedHookUrl>"
) {
const encryptedBuf = new Buffer(kmsEncryptedHookUrl, "base64");
const cipherText = { CiphertextBlob: encryptedBuf };
const kms = new AWS.KMS();
kms.decrypt(cipherText, (err, data) => {
if (err) {
console.log("Decrypt error:", err);
return callback(err);
}
hookUrl = `https://${data.Plaintext.toString("ascii")}`;
processEvent(event, callback);
});
} else {
callback("Hook URL has not been set.");
}
};