diff --git a/.changeset/real-badgers-clap.md b/.changeset/real-badgers-clap.md new file mode 100644 index 0000000000000..aa2249da67efb --- /dev/null +++ b/.changeset/real-badgers-clap.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes receiving webhook payloads encoded as x-www-form-urlencoded JSON. diff --git a/apps/meteor/app/integrations/server/api/api.js b/apps/meteor/app/integrations/server/api/api.js index 80a88b8757c9f..a94cb55bd8677 100644 --- a/apps/meteor/app/integrations/server/api/api.js +++ b/apps/meteor/app/integrations/server/api/api.js @@ -319,19 +319,21 @@ Api.router.use((req, res, next) => { return next(); } - const payloadKeys = Object.keys(req.body); - if (payloadKeys.length !== 1) { + // make sure body has only one key and it is 'payload' + if (!req.body || typeof req.body !== 'object' || !('payload' in req.body) || Object.keys(req.body).length !== 1) { return next(); } try { - // need to compose the full payload in this weird way because body-parser thought it was a form - req.bodyParams = JSON.parse(payloadKeys[0] + req.body[payloadKeys[0]]); + req.bodyParams = JSON.parse(req.body.payload); + return next(); } catch (e) { res.writeHead(400); res.end(JSON.stringify({ success: false, error: e.message })); } + + return next(); }); Api.addRoute( diff --git a/apps/meteor/tests/end-to-end/api/incoming-integrations.ts b/apps/meteor/tests/end-to-end/api/incoming-integrations.ts index 814f246623bb4..952d21b386843 100644 --- a/apps/meteor/tests/end-to-end/api/incoming-integrations.ts +++ b/apps/meteor/tests/end-to-end/api/incoming-integrations.ts @@ -305,6 +305,31 @@ describe('[Incoming Integrations]', () => { expect(!!(res.body.messages as IMessage[]).find((m) => m.msg === successfulMesssage)).to.be.true; }); }); + + it('should send a message if the payload is a application/x-www-form-urlencoded JSON', async () => { + const payload = { msg: `Message as x-www-form-urlencoded JSON sent successfully at #${Date.now()}` }; + + await request + .post(`/hooks/${integration._id}/${integration.token}`) + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(`payload=${JSON.stringify(payload)}`) + .expect(200) + .expect(async () => { + return request + .get(api('channels.messages')) + .set(credentials) + .query({ + roomId: 'GENERAL', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('messages').and.to.be.an('array'); + expect(!!(res.body.messages as IMessage[]).find((m) => m.msg === payload.msg)).to.be.true; + }); + }); + }); }); describe('[/integrations.history]', () => {