Fix a few issues related to the integrations.

1. process_incoming_request can now return falsey (null, undefined, 0, false, etc) and nothing will be sent to the channel
2. integrations can post messages to channels without the user being in them (direct messages, etc)
3. users can't brute force check if a room exists by successfully sending a message there
pull/7793/head
Bradley Hilton 8 years ago
parent 25792e619b
commit ae3f82183d
No known key found for this signature in database
GPG Key ID: 0666B2C24C43C358
  1. 2
      packages/rocketchat-api/server/v1/chat.js
  2. 32
      packages/rocketchat-integrations/server/api/api.js
  3. 7
      packages/rocketchat-integrations/server/processWebhookMessage.js

@ -73,7 +73,7 @@ RocketChat.API.v1.addRoute('chat.pinMessage', { authRequired: true }, {
RocketChat.API.v1.addRoute('chat.postMessage', { authRequired: true }, { RocketChat.API.v1.addRoute('chat.postMessage', { authRequired: true }, {
post() { post() {
const messageReturn = processWebhookMessage(this.bodyParams, this.user)[0]; const messageReturn = processWebhookMessage(this.bodyParams, this.user, undefined, true)[0];
if (!messageReturn) { if (!messageReturn) {
return RocketChat.API.v1.failure('unknown-error'); return RocketChat.API.v1.failure('unknown-error');

@ -158,18 +158,21 @@ function executeIntegrationRest() {
logger.incoming.info('Post integration:', this.integration.name); logger.incoming.info('Post integration:', this.integration.name);
logger.incoming.debug('@urlParams:', this.urlParams); logger.incoming.debug('@urlParams:', this.urlParams);
logger.incoming.debug('@bodyParams:', this.bodyParams); logger.incoming.debug('@bodyParams:', this.bodyParams);
if (this.integration.enabled !== true) { if (this.integration.enabled !== true) {
return { return {
statusCode: 503, statusCode: 503,
body: 'Service Unavailable' body: 'Service Unavailable'
}; };
} }
const defaultValues = { const defaultValues = {
channel: this.integration.channel, channel: this.integration.channel,
alias: this.integration.alias, alias: this.integration.alias,
avatar: this.integration.avatar, avatar: this.integration.avatar,
emoji: this.integration.emoji emoji: this.integration.emoji
}; };
if (this.integration.scriptEnabled === true && this.integration.scriptCompiled && this.integration.scriptCompiled.trim() !== '') { if (this.integration.scriptEnabled === true && this.integration.scriptCompiled && this.integration.scriptCompiled.trim() !== '') {
let script; let script;
try { try {
@ -178,6 +181,7 @@ function executeIntegrationRest() {
logger.incoming.warn(e); logger.incoming.warn(e);
return RocketChat.API.v1.failure(e.message); return RocketChat.API.v1.failure(e.message);
} }
const request = { const request = {
url: { url: {
hash: this.request._parsedUrl.hash, hash: this.request._parsedUrl.hash,
@ -197,23 +201,29 @@ function executeIntegrationRest() {
username: this.user.username username: this.user.username
} }
}; };
try { try {
const { sandbox } = buildSandbox(compiledScripts[this.integration._id].store); const { sandbox } = buildSandbox(compiledScripts[this.integration._id].store);
sandbox.script = script; sandbox.script = script;
sandbox.request = request; sandbox.request = request;
const result = vm.runInNewContext('script.process_incoming_request({ request: request })', sandbox, { const result = vm.runInNewContext('script.process_incoming_request({ request: request })', sandbox, {
timeout: 3000 timeout: 3000
}); });
if (result && result.error) {
if (!result) {
logger.incoming.debug('[Process Incoming Request result of Trigger', this.integration.name, ':] No data');
return RocketChat.API.v1.success();
} else if (result && result.error) {
return RocketChat.API.v1.failure(result.error); return RocketChat.API.v1.failure(result.error);
} }
this.bodyParams = result && result.content; this.bodyParams = result && result.content;
if (typeof result !== 'undefined') {
this.scriptResponse = result.response; this.scriptResponse = result.response;
if (result.user) { if (result.user) {
this.user = result.user; this.user = result.user;
} }
}
logger.incoming.debug('[Process Incoming Request result of Trigger', this.integration.name, ':]'); logger.incoming.debug('[Process Incoming Request result of Trigger', this.integration.name, ':]');
logger.incoming.debug('result', this.bodyParams); logger.incoming.debug('result', this.bodyParams);
} catch ({stack}) { } catch ({stack}) {
@ -224,20 +234,26 @@ function executeIntegrationRest() {
return RocketChat.API.v1.failure('error-running-script'); return RocketChat.API.v1.failure('error-running-script');
} }
} }
if (this.bodyParams == null) {
return RocketChat.API.v1.failure('body-empty'); // TODO: Turn this into an option on the integrations - no body means a success
// TODO: Temporary fix for https://github.com/RocketChat/Rocket.Chat/issues/7770 until the above is implemented
if (!this.bodyParams) {
// return RocketChat.API.v1.failure('body-empty');
return RocketChat.API.v1.success();
} }
this.bodyParams.bot = {
i: this.integration._id this.bodyParams.bot = { i: this.integration._id };
};
try { try {
const message = processWebhookMessage(this.bodyParams, this.user, defaultValues); const message = processWebhookMessage(this.bodyParams, this.user, defaultValues);
if (_.isEmpty(message)) { if (_.isEmpty(message)) {
return RocketChat.API.v1.failure('unknown-error'); return RocketChat.API.v1.failure('unknown-error');
} }
if (this.scriptResponse) { if (this.scriptResponse) {
logger.incoming.debug('response', this.scriptResponse); logger.incoming.debug('response', this.scriptResponse);
} }
return RocketChat.API.v1.success(this.scriptResponse); return RocketChat.API.v1.success(this.scriptResponse);
} catch ({ error }) { } catch ({ error }) {
return RocketChat.API.v1.failure(error); return RocketChat.API.v1.failure(error);

@ -1,4 +1,4 @@
this.processWebhookMessage = function(messageObj, user, defaultValues = { channel: '', alias: '', avatar: '', emoji: '' }) { this.processWebhookMessage = function(messageObj, user, defaultValues = { channel: '', alias: '', avatar: '', emoji: '' }, mustBeJoined = false) {
const sentData = []; const sentData = [];
const channels = [].concat(messageObj.channel || messageObj.roomId || defaultValues.channel); const channels = [].concat(messageObj.channel || messageObj.roomId || defaultValues.channel);
@ -34,6 +34,11 @@ this.processWebhookMessage = function(messageObj, user, defaultValues = { channe
throw new Meteor.Error('invalid-channel'); throw new Meteor.Error('invalid-channel');
} }
if (mustBeJoined && !room.usernames.includes(user.username)) {
// throw new Meteor.Error('invalid-room', 'Invalid room provided to send a message to, must be joined.');
throw new Meteor.Error('invalid-channel'); // Throwing the generic one so people can't "brute force" find rooms
}
if (messageObj.attachments && !_.isArray(messageObj.attachments)) { if (messageObj.attachments && !_.isArray(messageObj.attachments)) {
console.log('Attachments should be Array, ignoring value'.red, messageObj.attachments); console.log('Attachments should be Array, ignoring value'.red, messageObj.attachments);
messageObj.attachments = undefined; messageObj.attachments = undefined;

Loading…
Cancel
Save