Skip to content

[bolt-ktor] toBoltRequest will receive invalid event request json from enterprise grid workspaces if it contains unicode symbols #966

@tg-dclarke

Description

@tg-dclarke

There is a bug in Ktor 1.x.x currently (fixed in 2.0.0 I believe) where the behaviour of call.receiveText() can potentially lead to invalid request signature matching on event requests sent by an Enterprise Grid workspace.

For example, a lot of Slack users might use the Google/Outlook calendar apps to sync Slack with their meetings. These plugins both set the user status text to be In a meeting • Google/Outlook Calendar. When this is sent from a normal workspace, the event contains a unicode code point (\u2022) for the dot. When sent from a grid, the dot is sent exactly as is.

The bolt-ktor function toBoltRequest uses call.receiveText() to get the request body for the event. But because the request does not come with an explicit charset applied to the Content-Type header, Ktor's receiveText function ignores the fact that the data is json and uses default plaintext encoding using ISO_8859-1. This turns the dot into � which changes the bytes. This then fails the request verifier stage.

The workaround is to enforce UTF-8 encoding on requests with a Content-Type of application/json unless a specific charset is applied. Here is the Ktor youtrack issue. We used the following workaround from there:

/**
 * Receive the request as String.
 * If there is no Content-Type in the HTTP header specified use ISO_8859_1 as default charset, see https://www.w3.org/International/articles/http-charset/index#charset.
 * But use UTF-8 as default charset for application/json, see https://tools.ietf.org/html/rfc4627#section-3
 */
private suspend fun ApplicationCall.receiveTextWithCorrectEncoding(): String {
  fun ContentType.defaultCharset(): Charset = when (this) {
    ContentType.Application.Json -> Charsets.UTF_8
    else -> Charsets.ISO_8859_1
  }
  
  val contentType = request.contentType()
  val suitableCharset = contentType.charset() ?: contentType.defaultCharset()
  return receiveStream().bufferedReader(charset = suitableCharset).readText()
}

Reproducible in:

The Slack SDK version

Latest (v1.21.1 at time of writing)

Java Runtime version

Java 11 (Azul and Coretto) is what we're using

Steps to reproduce:

  1. Create a simple Slack app using Ktor < 2.0.0
  2. Setup app to use user_change event
  3. Install the app in an enterprise grid workspace
  4. Update your profile status in the enterprise grid to say In a meeting • Google Calendar

Expected result:

Event should be sent to Slack app without issue and not produce a 401 due to invalid signature

Actual result:

Request will fail with an invalid request signature and a 401

Metadata

Metadata

Assignees

Labels

bugM-T: confirmed bug report. Issues are confirmed when the reproduction steps are documentedproject:bolt

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions