-
Notifications
You must be signed in to change notification settings - Fork 636
Hubot can DOS itself via conversations.info events #554
Description
Description
My company recently experienced an outage of our Hubot when we were rate-limited by surprise. After discussing it with Slack support, the reason is apparently that conversations.info was being called a large number of times.
What type of issue is this? (place an x in one of the [ ])
- bug
- enhancement (feature request)
- question
- documentation related
- testing related
- discussion
Requirements (place an x in each of the [ ])
- I've read and understood the Contributing guidelines and have done my best effort to follow them.
- I've read and agree to the Code of Conduct.
- I've searched for any related issues and avoided creating a duplicate issue.
Bug Report
It looks like hubot-slack calls this API method during the process of sending a message:
Lines 139 to 147 in dc7f2a8
| # The `envelope.room` property is intended to be a conversation ID. Even when that is not the case, this method will | |
| # makes a reasonable attempt at sending the message. If the property is set to a public or private channel name, it | |
| # will still work. When there's no `room` in the envelope, this method will fallback to the `id` property. That | |
| # affordance allows scripts to use Hubot User objects, Slack users (as obtained from the response to `users.info`), | |
| # and Slack conversations (as obtained from the response to `conversations.info`) as possible envelopes. In the first | |
| # two cases, envelope.id` will contain a user ID (`Uxxx` or `Wxxx`). Since Hubot runs using a bot token (`xoxb`), | |
| # passing a user ID as the `channel` argument to `chat.postMessage` (with `as_user=true`) results in a DM from the bot | |
| # user (if `as_user=false` it would instead result in a DM from slackbot). Leaving `as_user=true` has no effect when | |
| # the `channel` argument is a conversation ID. |
Lines 236 to 259 in dc7f2a8
| ###* | |
| # Fetch conversation info from conversation map. If not available, call conversations.info | |
| # @public | |
| ### | |
| fetchConversation: (conversationId) -> | |
| # Current date minus 5 minutes (time of expiration for conversation info) | |
| expiration = Date.now() - (5 * 60 * 1000) | |
| # Check whether conversation is held in client's channelData map and whether information is expired | |
| return Promise.resolve(@channelData[conversationId].channel) if @channelData[conversationId]?.channel? and | |
| expiration < @channelData[conversationId]?.updated | |
| # Delete data from map if it's expired | |
| delete @channelData[conversationId] if @channelData[conversationId]? | |
| # Return conversations.info promise | |
| @web.conversations.info(conversationId).then((r) => | |
| if r.channel? | |
| @channelData[conversationId] = { | |
| channel: r.channel, | |
| updated: Date.now() | |
| } | |
| r.channel | |
| ) |
I'm not clear why exactly this was; I don't have much visibility into it. Our application itself doesn't call this method at all except through whatever hubot-slack calls on its own. Since conversations.info has a much lower rate limit than actually posting messages, this has the possibility to reduce the number of messages that can be safely posted under some circumstances.
Reproducible in:
hubot-slack version: 4.6.0
node version: 8.2.0
OS version(s): Debian jesse
Steps to reproduce:
Unclear
Expected result:
Hubot doesn't rate-limit itself in normal operation.
Actual result:
Hubot was rate-limited.