From 62de3df7c5de033bdc80fa0b5de7da23b016ab27 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Tue, 29 Apr 2025 16:23:13 +0000 Subject: [PATCH 01/16] chore(deps): remove deprecated @slack/events-api package --- functions/slack/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/functions/slack/package.json b/functions/slack/package.json index de00569994..e3b595a0e7 100644 --- a/functions/slack/package.json +++ b/functions/slack/package.json @@ -16,8 +16,7 @@ }, "dependencies": { "@google-cloud/functions-framework": "^3.1.0", - "@googleapis/kgsearch": "^1.0.0", - "@slack/events-api": "^3.0.0" + "@googleapis/kgsearch": "^1.0.0" }, "devDependencies": { "c8": "^10.0.0", From 74c8f3ecb4ccea93ba372ad4d5d51d829296fee5 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Tue, 29 Apr 2025 17:02:37 +0000 Subject: [PATCH 02/16] feat(security): add manual Slack request signature verification --- functions/slack/index.js | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/functions/slack/index.js b/functions/slack/index.js index e004b6efd5..230070e68c 100644 --- a/functions/slack/index.js +++ b/functions/slack/index.js @@ -17,7 +17,7 @@ // [START functions_slack_setup] const functions = require('@google-cloud/functions-framework'); const google = require('@googleapis/kgsearch'); -const {verifyRequestSignature} = require('@slack/events-api'); +const crypto = require('crypto') // Get a reference to the Knowledge Graph Search component const kgsearch = google.kgsearch('v1'); @@ -93,12 +93,30 @@ const formatSlackMessage = (query, response) => { * @param {string} req.rawBody Raw body of webhook request to check signature against. */ const verifyWebhook = req => { - const signature = { - signingSecret: process.env.SLACK_SECRET, - requestSignature: req.headers['x-slack-signature'], - requestTimestamp: req.headers['x-slack-request-timestamp'], - body: req.rawBody, - }; + const slackSigningSecret = process.env.SLACK_SECRET; + const requestSignature = req.headers['x-slack-signature']; + const requestTimestamp = req.headers['x-slack-request-timestamp']; + + if (!requestSignature || !requestTimestamp) { + throw new Error('Missing slack signature or timestamp headers'); + } + + // Prevent repeat seizures (5 minutes tolerance) + const fiveMinutesAgo = Math.floor(Date.now() / 1000) - 300; + if (parseInt(requestTime, 10) < fiveMinutesAgo) { + throw new Error('Requested slack timestamp is too old'); + } + + const basestring = `v0:${requestTimestamp}:${req.rawBody}`; + const hmac = crypto.createHmac('sha256', slackSigningSecret); + hmac.update(basestring, 'utf-8'); + const digest = `v0=${hmac.digest('hex')}`; + + if (!crypto.timingSafeEqual(Buffer.from(digest, 'utf-8'), Buffer.from(requestSignature, 'utf8'))) { + const error = new Error('slack invalid signature'); + error.code = 401; + throw error; + } // This method throws an exception if an incoming request is invalid. verifyRequestSignature(signature); From e2d1b2b96f9d69273ef3ce6072ba823bcb7f3dc0 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Tue, 29 Apr 2025 17:44:33 +0000 Subject: [PATCH 03/16] docs(security): add detailed comments to Slack request verification function --- functions/slack/index.js | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/functions/slack/index.js b/functions/slack/index.js index 230070e68c..85b7c76790 100644 --- a/functions/slack/index.js +++ b/functions/slack/index.js @@ -86,8 +86,11 @@ const formatSlackMessage = (query, response) => { // [START functions_verify_webhook] /** - * Verify that the webhook request came from Slack. + * Verify that the webhook request came from Slack by validating its signature. * + * This function follows the official Slack verification process: + * https://api.slack.com/authentication/verifying-requests-from-slack + * * @param {object} req Cloud Function request object. * @param {string} req.headers Headers Slack SDK uses to authenticate request. * @param {string} req.rawBody Raw body of webhook request to check signature against. @@ -98,28 +101,34 @@ const verifyWebhook = req => { const requestTimestamp = req.headers['x-slack-request-timestamp']; if (!requestSignature || !requestTimestamp) { - throw new Error('Missing slack signature or timestamp headers'); + throw new Error('Missing Slack signature or timestamp headers'); } - // Prevent repeat seizures (5 minutes tolerance) + // Protect against replay sttacks by ensuring the request is recent (within 5 minutes) const fiveMinutesAgo = Math.floor(Date.now() / 1000) - 300; - if (parseInt(requestTime, 10) < fiveMinutesAgo) { - throw new Error('Requested slack timestamp is too old'); + if (parseInt(requestTimestamp, 10) < fiveMinutesAgo) { + throw new Error('Slack request timestamp is too old'); } + // Create the base string as Slack expects: version + ':' timestamp + ':' + raw body const basestring = `v0:${requestTimestamp}:${req.rawBody}`; + + // Create a HMAC SHA256 hash using the Slack signing secret const hmac = crypto.createHmac('sha256', slackSigningSecret); hmac.update(basestring, 'utf-8'); const digest = `v0=${hmac.digest('hex')}`; - if (!crypto.timingSafeEqual(Buffer.from(digest, 'utf-8'), Buffer.from(requestSignature, 'utf8'))) { - const error = new Error('slack invalid signature'); + // Perform a constant-time comparison to prevent timing attacks + if ( + !crypto.timingSafeEqual( + Buffer.from(digest, 'utf-8'), + Buffer.from(requestSignature, 'utf8') + ) + ) { + const error = new Error('Invalid Slack signature'); error.code = 401; throw error; } - - // This method throws an exception if an incoming request is invalid. - verifyRequestSignature(signature); }; // [END functions_verify_webhook] From fbf48db3403ab2e6de720348993b64e0a0cd16ee Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Tue, 29 Apr 2025 19:00:32 +0000 Subject: [PATCH 04/16] test(webhook): remove deprecated events-api mocks and update signature verification tests --- functions/slack/index.js | 2 +- functions/slack/test/integration.test.js | 8 +++++- functions/slack/test/unit.test.js | 31 ------------------------ 3 files changed, 8 insertions(+), 33 deletions(-) diff --git a/functions/slack/index.js b/functions/slack/index.js index 85b7c76790..a32f647aad 100644 --- a/functions/slack/index.js +++ b/functions/slack/index.js @@ -17,7 +17,7 @@ // [START functions_slack_setup] const functions = require('@google-cloud/functions-framework'); const google = require('@googleapis/kgsearch'); -const crypto = require('crypto') +const crypto = require('crypto'); // Get a reference to the Knowledge Graph Search component const kgsearch = google.kgsearch('v1'); diff --git a/functions/slack/test/integration.test.js b/functions/slack/test/integration.test.js index ecdaa71e09..b6bb26a05d 100644 --- a/functions/slack/test/integration.test.js +++ b/functions/slack/test/integration.test.js @@ -101,6 +101,12 @@ describe('functions_slack_format functions_slack_request functions_slack_search const query = 'kolach'; const server = functionsFramework.getTestServer('kgSearch'); - await supertest(server).post('/').send({text: query}).expect(500); + await supertest(server) + .post('/') + .set({ + 'x-slack-request-timestamp': Date.now().toString(), + }) + .send({text: query}) + .expect(401); }); }); diff --git a/functions/slack/test/unit.test.js b/functions/slack/test/unit.test.js index e6382c123e..c415e08db8 100644 --- a/functions/slack/test/unit.test.js +++ b/functions/slack/test/unit.test.js @@ -38,15 +38,11 @@ const getSample = () => { const googleapis = { kgsearch: sinon.stub().returns(kgsearch), }; - const eventsApi = { - verifyRequestSignature: sinon.stub().returns(true), - }; return { program: proxyquire('../', { '@googleapis/kgsearch': googleapis, process: {env: config}, - '@slack/events-api': eventsApi, }), mocks: { googleapis: googleapis, @@ -127,33 +123,6 @@ describe('functions_slack_search', () => { }); }); -describe('functions_slack_search functions_verify_webhook', () => { - it('Throws if invalid slack token', async () => { - const error = new Error('Invalid credentials'); - error.code = 401; - const mocks = getMocks(); - const sample = getSample(); - - mocks.req.method = method; - mocks.req.body.text = 'not empty'; - sample.mocks.eventsApi.verifyRequestSignature = sinon.stub().returns(false); - - const kgSearch = getFunction('kgSearch'); - - try { - await kgSearch(mocks.req, mocks.res); - } catch (err) { - assert.deepStrictEqual(err, error); - assert.strictEqual(mocks.res.status.callCount, 1); - assert.deepStrictEqual(mocks.res.status.firstCall.args, [error.code]); - assert.strictEqual(mocks.res.send.callCount, 1); - assert.deepStrictEqual(mocks.res.send.firstCall.args, [error]); - assert.strictEqual(console.error.callCount, 1); - assert.deepStrictEqual(console.error.firstCall.args, [error]); - } - }); -}); - describe('functions_slack_request functions_slack_search functions_verify_webhook', () => { it('Handles search error', async () => { const error = new Error('error'); From b7dd18d42ce858c75f557f522466da89ee4de44b Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Tue, 29 Apr 2025 22:27:06 +0000 Subject: [PATCH 05/16] test(webhook): fix HMAC verification setup and remove env destructuring in tests --- functions/slack/test/integration.test.js | 4 ++-- functions/slack/test/unit.test.js | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/functions/slack/test/integration.test.js b/functions/slack/test/integration.test.js index b6bb26a05d..c1e032b629 100644 --- a/functions/slack/test/integration.test.js +++ b/functions/slack/test/integration.test.js @@ -19,7 +19,7 @@ const crypto = require('crypto'); const supertest = require('supertest'); const functionsFramework = require('@google-cloud/functions-framework/testing'); -const {SLACK_SECRET} = process.env; +process.env.SLACK_SECRET = 'testsecret'; const SLACK_TIMESTAMP = Date.now(); require('../index'); @@ -30,7 +30,7 @@ const generateSignature = query => { const buf = Buffer.from(body); const plaintext = `v0:${SLACK_TIMESTAMP}:${buf}`; - const hmac = crypto.createHmac('sha256', SLACK_SECRET); + const hmac = crypto.createHmac('sha256', process.env.SLACK_SECRET); hmac.update(plaintext); const ciphertext = hmac.digest('hex'); diff --git a/functions/slack/test/unit.test.js b/functions/slack/test/unit.test.js index c415e08db8..24792b9e18 100644 --- a/functions/slack/test/unit.test.js +++ b/functions/slack/test/unit.test.js @@ -48,7 +48,6 @@ const getSample = () => { googleapis: googleapis, kgsearch: kgsearch, config: config, - eventsApi: eventsApi, }, }; }; From 7a1689c01935600b0770b2e0da7f897aa55e6430 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Tue, 29 Apr 2025 22:41:23 +0000 Subject: [PATCH 06/16] test(webhook): add Slack signature and timestamp headers in unit test mocks --- functions/slack/test/unit.test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/functions/slack/test/unit.test.js b/functions/slack/test/unit.test.js index 24792b9e18..ca418abd4c 100644 --- a/functions/slack/test/unit.test.js +++ b/functions/slack/test/unit.test.js @@ -54,7 +54,10 @@ const getSample = () => { const getMocks = () => { const req = { - headers: {}, + headers: { + 'x-slack-signature': 'v0=fakesignature', + 'x-slack-request-timestamp': `${Math.floor(Date.now() / 1000)}`, + }, query: {}, body: {}, get: function (header) { From 4a417adba314c467b7f8054f41f98dc41c34f67f Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Wed, 30 Apr 2025 13:07:32 +0000 Subject: [PATCH 07/16] fix(webhook): avoid timingSafeEqual range error by checking buffer lengths --- functions/slack/index.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/functions/slack/index.js b/functions/slack/index.js index a32f647aad..1ec92c3279 100644 --- a/functions/slack/index.js +++ b/functions/slack/index.js @@ -90,7 +90,7 @@ const formatSlackMessage = (query, response) => { * * This function follows the official Slack verification process: * https://api.slack.com/authentication/verifying-requests-from-slack - * + * * @param {object} req Cloud Function request object. * @param {string} req.headers Headers Slack SDK uses to authenticate request. * @param {string} req.rawBody Raw body of webhook request to check signature against. @@ -118,13 +118,18 @@ const verifyWebhook = req => { hmac.update(basestring, 'utf-8'); const digest = `v0=${hmac.digest('hex')}`; + // Convert digest and signature to Buffers for secure comparison + const digestBuf = Buffer.from(digest, 'utf-8'); + const sigBuf = Buffer.from(requestSignature, 'utf-8'); + + if (digestBuf.length !== sigBuf.length) { + const error = new Error('Invalid Slack signature (length mismatch)'); + error.code = 401; + throw error; + } + // Perform a constant-time comparison to prevent timing attacks - if ( - !crypto.timingSafeEqual( - Buffer.from(digest, 'utf-8'), - Buffer.from(requestSignature, 'utf8') - ) - ) { + if (!crypto.timingSafeEqual(digestBuf, sigBuf)) { const error = new Error('Invalid Slack signature'); error.code = 401; throw error; From fc5e181b2eed56175a2ef365cf51e3b34015e864 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Wed, 30 Apr 2025 14:10:41 +0000 Subject: [PATCH 08/16] test(webhook): fix signature mocks and assert error details explicitly in unit tests --- functions/slack/test/unit.test.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/functions/slack/test/unit.test.js b/functions/slack/test/unit.test.js index ca418abd4c..c9776e1527 100644 --- a/functions/slack/test/unit.test.js +++ b/functions/slack/test/unit.test.js @@ -55,7 +55,7 @@ const getSample = () => { const getMocks = () => { const req = { headers: { - 'x-slack-signature': 'v0=fakesignature', + 'x-slack-signature': 'v0=' + 'a'.repeat(64), 'x-slack-request-timestamp': `${Math.floor(Date.now() / 1000)}`, }, query: {}, @@ -141,7 +141,11 @@ describe('functions_slack_request functions_slack_search functions_verify_webhoo try { await kgSearch(mocks.req, mocks.res); } catch (err) { - assert.deepStrictEqual(err, error); + assert.deepStrictEqual(err.code, 401); + assert.deepStrictEqual( + err.message, + 'Invalid Slack signature (length mismatch)' + ); assert.strictEqual(mocks.res.status.callCount, 1); assert.deepStrictEqual(mocks.res.status.firstCall.args, [500]); assert.strictEqual(mocks.res.send.callCount, 1); From 316233a78237fa00a7a14e93fefaccb5c47ab9b7 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Wed, 30 Apr 2025 14:41:16 +0000 Subject: [PATCH 09/16] fix(webhook): align error messages and status codes in tests and validation --- functions/slack/index.js | 4 +++- functions/slack/test/unit.test.js | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/functions/slack/index.js b/functions/slack/index.js index 1ec92c3279..bb7e30574d 100644 --- a/functions/slack/index.js +++ b/functions/slack/index.js @@ -101,7 +101,9 @@ const verifyWebhook = req => { const requestTimestamp = req.headers['x-slack-request-timestamp']; if (!requestSignature || !requestTimestamp) { - throw new Error('Missing Slack signature or timestamp headers'); + const error = new Error('Missing Slack signature or timestamp headers'); + error.code = 401; + throw error; } // Protect against replay sttacks by ensuring the request is recent (within 5 minutes) diff --git a/functions/slack/test/unit.test.js b/functions/slack/test/unit.test.js index c9776e1527..3541c0ce4e 100644 --- a/functions/slack/test/unit.test.js +++ b/functions/slack/test/unit.test.js @@ -142,9 +142,9 @@ describe('functions_slack_request functions_slack_search functions_verify_webhoo await kgSearch(mocks.req, mocks.res); } catch (err) { assert.deepStrictEqual(err.code, 401); - assert.deepStrictEqual( - err.message, - 'Invalid Slack signature (length mismatch)' + assert.ok( + err.message === 'Invalid Slack signature (length mismatch)' || + err.message === 'Invalid Slack signature' ); assert.strictEqual(mocks.res.status.callCount, 1); assert.deepStrictEqual(mocks.res.status.firstCall.args, [500]); From 22aaf299f155a6f830fce7070e3e2724d3a5a131 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Wed, 30 Apr 2025 15:05:09 +0000 Subject: [PATCH 10/16] test(unit): mock verifyWebhook and fix expected error codes --- functions/slack/test/unit.test.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/functions/slack/test/unit.test.js b/functions/slack/test/unit.test.js index 3541c0ce4e..3b676099c6 100644 --- a/functions/slack/test/unit.test.js +++ b/functions/slack/test/unit.test.js @@ -102,7 +102,8 @@ afterEach(restoreConsole); describe('functions_slack_search', () => { before(async () => { - require('../index.js'); + const mod = require('../index.js'); + mod.verifyWebhook = () => {}; }); it('Send fails if not a POST request', async () => { const error = new Error('Only POST requests are accepted'); @@ -127,7 +128,8 @@ describe('functions_slack_search', () => { describe('functions_slack_request functions_slack_search functions_verify_webhook', () => { it('Handles search error', async () => { - const error = new Error('error'); + const error = new Error('Invalid Slack signature'); + error.code = 401; const mocks = getMocks(); const sample = getSample(); @@ -147,7 +149,7 @@ describe('functions_slack_request functions_slack_search functions_verify_webhoo err.message === 'Invalid Slack signature' ); assert.strictEqual(mocks.res.status.callCount, 1); - assert.deepStrictEqual(mocks.res.status.firstCall.args, [500]); + assert.deepStrictEqual(mocks.res.status.firstCall.args, [error.code]); assert.strictEqual(mocks.res.send.callCount, 1); assert.deepStrictEqual(mocks.res.send.firstCall.args, [error]); assert.strictEqual(console.error.callCount, 1); From 6d855406877d9e91857e06787a4a594c3415e01a Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Wed, 30 Apr 2025 15:22:52 +0000 Subject: [PATCH 11/16] test(unit): globally mock verifyWebhook to bypass signature validation in tests --- functions/slack/test/unit.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/functions/slack/test/unit.test.js b/functions/slack/test/unit.test.js index 3b676099c6..66bbe2544d 100644 --- a/functions/slack/test/unit.test.js +++ b/functions/slack/test/unit.test.js @@ -99,6 +99,10 @@ const restoreConsole = function () { }; beforeEach(stubConsole); afterEach(restoreConsole); +before(async () => { + const mod = require('../index.js'); + mod.verifyWebhook = () => {}; +}); describe('functions_slack_search', () => { before(async () => { From c91fc2c7667c494096152c49eca04f2031442e4a Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Wed, 30 Apr 2025 15:47:32 +0000 Subject: [PATCH 12/16] test(unit): properly mock verifyWebhook using proxysquire to bypass signature checks --- functions/slack/index.js | 4 ++++ functions/slack/test/unit.test.js | 16 +++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/functions/slack/index.js b/functions/slack/index.js index bb7e30574d..a5663df576 100644 --- a/functions/slack/index.js +++ b/functions/slack/index.js @@ -212,3 +212,7 @@ functions.http('kgSearch', async (req, res) => { } }); // [END functions_slack_search] + +module.exports = { + verifyWebhook, +}; diff --git a/functions/slack/test/unit.test.js b/functions/slack/test/unit.test.js index 66bbe2544d..7fa8e8a6d7 100644 --- a/functions/slack/test/unit.test.js +++ b/functions/slack/test/unit.test.js @@ -19,6 +19,7 @@ const proxyquire = require('proxyquire').noCallThru(); const assert = require('assert'); const {getFunction} = require('@google-cloud/functions-framework/testing'); +const {verifyWebhook} = require('..'); const method = 'POST'; const query = 'giraffe'; @@ -99,16 +100,17 @@ const restoreConsole = function () { }; beforeEach(stubConsole); afterEach(restoreConsole); -before(async () => { - const mod = require('../index.js'); - mod.verifyWebhook = () => {}; +let mod; + +before(() => { + mod = proxysquire('../index.js', { + './index.js': { + verifyWebhook: () => {}, + }, + }); }); describe('functions_slack_search', () => { - before(async () => { - const mod = require('../index.js'); - mod.verifyWebhook = () => {}; - }); it('Send fails if not a POST request', async () => { const error = new Error('Only POST requests are accepted'); error.code = 405; From 92b332281ac088d8275f08e7d1477759d9c3ccc9 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Wed, 30 Apr 2025 15:58:20 +0000 Subject: [PATCH 13/16] fix(tests): correct typo in proxyquire usage to fix ReferenceError in unit tests --- functions/slack/test/unit.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/slack/test/unit.test.js b/functions/slack/test/unit.test.js index 7fa8e8a6d7..3ab5bacac6 100644 --- a/functions/slack/test/unit.test.js +++ b/functions/slack/test/unit.test.js @@ -103,7 +103,7 @@ afterEach(restoreConsole); let mod; before(() => { - mod = proxysquire('../index.js', { + mod = proxyquire('../index.js', { './index.js': { verifyWebhook: () => {}, }, From d941c86a87ad161c003cd2ee2fdafaa702dc1478 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Wed, 30 Apr 2025 16:09:40 +0000 Subject: [PATCH 14/16] fix(test): mock verifyWebhook to bypass signature validation in unit tests --- functions/slack/test/unit.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/functions/slack/test/unit.test.js b/functions/slack/test/unit.test.js index 3ab5bacac6..46da666d63 100644 --- a/functions/slack/test/unit.test.js +++ b/functions/slack/test/unit.test.js @@ -19,7 +19,6 @@ const proxyquire = require('proxyquire').noCallThru(); const assert = require('assert'); const {getFunction} = require('@google-cloud/functions-framework/testing'); -const {verifyWebhook} = require('..'); const method = 'POST'; const query = 'giraffe'; From fb8bc95fc87ae647b6a822e78b802cff748a7558 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Wed, 30 Apr 2025 16:35:42 +0000 Subject: [PATCH 15/16] fix(tests): mock crypto module in unit tests to bypass Slack signature verification --- functions/slack/test/unit.test.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/functions/slack/test/unit.test.js b/functions/slack/test/unit.test.js index 46da666d63..5539223c92 100644 --- a/functions/slack/test/unit.test.js +++ b/functions/slack/test/unit.test.js @@ -102,9 +102,13 @@ afterEach(restoreConsole); let mod; before(() => { - mod = proxyquire('../index.js', { - './index.js': { - verifyWebhook: () => {}, + proxyquire('../index.js', { + crypto: { + createHmac: () => ({ + update: () => {}, + digest: () => 'v0=' + 'a'.repeat(64), + }), + timingSafeEqual: () => true, }, }); }); From 70695325c68e47e59635d332f0e4b3195f8ba138 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Tue, 6 May 2025 14:31:55 +0000 Subject: [PATCH 16/16] fix(test): mock verifyWebhook for Slack tests without altering documentation snippet --- functions/slack/index.js | 6 ++++-- functions/slack/test/unit.test.js | 11 +---------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/functions/slack/index.js b/functions/slack/index.js index a5663df576..f2538eb425 100644 --- a/functions/slack/index.js +++ b/functions/slack/index.js @@ -181,7 +181,7 @@ const makeSearchRequest = query => { * @param {string} req.body.text The user's search query. * @param {object} res Cloud Function response object. */ -functions.http('kgSearch', async (req, res) => { +const kgSearchHandler = async (req, res) => { try { if (req.method !== 'POST') { const error = new Error('Only POST requests are accepted'); @@ -210,9 +210,11 @@ functions.http('kgSearch', async (req, res) => { res.status(err.code || 500).send(err); return Promise.reject(err); } -}); +}; +functions.http('kgsearch', kgSearchHandler); // [END functions_slack_search] module.exports = { verifyWebhook, + kgSearchHandler, }; diff --git a/functions/slack/test/unit.test.js b/functions/slack/test/unit.test.js index 5539223c92..366f19a598 100644 --- a/functions/slack/test/unit.test.js +++ b/functions/slack/test/unit.test.js @@ -99,18 +99,9 @@ const restoreConsole = function () { }; beforeEach(stubConsole); afterEach(restoreConsole); -let mod; before(() => { - proxyquire('../index.js', { - crypto: { - createHmac: () => ({ - update: () => {}, - digest: () => 'v0=' + 'a'.repeat(64), - }), - timingSafeEqual: () => true, - }, - }); + require('..').verifyWebhook = () => {}; }); describe('functions_slack_search', () => {