Skip to content

Commit

Permalink
Slackbot api sigfix (#279)
Browse files Browse the repository at this point in the history
* chore: Update isValidSlackRequest to prevent replay attacks and improve error handling

* version bump
  • Loading branch information
mlhaufe authored Jul 29, 2024
1 parent 6865c4c commit 0f9fa39
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 14 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@final-hill/cathedral",
"version": "0.10.0",
"version": "0.10.1",
"description": "Requirements management system",
"keywords": [],
"private": true,
Expand Down Expand Up @@ -67,4 +67,4 @@
"typescript": "^5.5.4",
"vue-tsc": "^2.0.28"
}
}
}
36 changes: 26 additions & 10 deletions server/api/slack-bot/index.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,26 @@ async function sendResponse(slackEvent: NonNullable<typeof bodySchema._type.even
}
}

function isValidSlackRequest(headers: Headers, body: typeof bodySchema._type) {
// https://api.slack.com/authentication/verifying-requests-from-slack#validating-a-request
function isValidSlackRequest(headers: Headers, rawBody: string) {
const signingSecret = process.env.SLACK_SIGNING_SECRET!,
timestamp = headers.get('X-Slack-Request-Timestamp')!,
slackSignature = headers.get('X-Slack-Signature')!,
base = `v0:${timestamp}:${JSON.stringify(body)}`,
timestamp = headers.get('X-Slack-Request-Timestamp')!

// Prevent replay attacks by checking the timestamp
// to verify that it does not differ from local time by more than five minutes.
const curTimestamp = Math.floor(Date.now() / 1000),
reqTimestamp = parseInt(timestamp, 10);

if (Math.abs(curTimestamp - reqTimestamp) > 300) {
throw createError({
statusCode: 403,
statusMessage: 'Forbidden',
message: 'Invalid Slack request timestamp'
});
}

const slackSignature = headers.get('X-Slack-Signature')!,
base = `v0:${timestamp}:${rawBody}`,
hmac = crypto
.createHmac('sha256', signingSecret)
.update(base)
Expand All @@ -66,6 +81,7 @@ function isValidSlackRequest(headers: Headers, body: typeof bodySchema._type) {
*/
export default defineEventHandler(async (event) => {
const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)),
rawBody = (await readRawBody(event))!,
headers = event.headers

if (!body.success)
Expand All @@ -75,18 +91,18 @@ export default defineEventHandler(async (event) => {
message: JSON.stringify(body.error.errors)
})

const requestType = body.data.type

if (requestType === 'url_verification')
return { challenge: body.data.challenge };

if (!isValidSlackRequest(headers, body.data))
if (!isValidSlackRequest(headers, rawBody))
throw createError({
statusCode: 403,
statusMessage: 'Forbidden',
message: 'Invalid Slack request signature'
})

const requestType = body.data.type

if (requestType === 'url_verification')
return { challenge: body.data.challenge };

if (requestType === 'event_callback') {
const eventType = body.data.event!.type
if (eventType === 'app_mention') {
Expand Down

0 comments on commit 0f9fa39

Please sign in to comment.