Emoji Picker is not not using CDN URLs for custom emojis

Description

Standard emoji paths aren’t converted to CDN URLs, while admin emoji paths are properly converted.

Evidence

Standard controller (no CDN conversion):

Admin controller (correct conversion using EmojiSerializer):

Standard emoji paths should be converted to CDN URLs like admin emoji paths.

2 Likes

Do you see these URLs being used somewhere in the UI? I suspect they are being converted to CDN URLs in the JS app before being displayed.

I checked and can confirm the emojis use S3 URLs directly (starting with //) in the rendered output, not CDN URLs.

Which page do you see this on?

You can see this on the post composer page when using the emoji picker. When custom emojis are loaded, they use S3 endpoints directly, like:

https://assets-meta-cdck-prod-meta.s3.dualstack.us-west-1.amazonaws.com/original/3X/1/d/1de8f76096f76cf393640bdcff387ec9ae33dd3e.gif

This shows the paths aren’t being converted to CDN URLs as expected.


Testing custom emoji :awthanks:

:up_arrow: this is being shipped from the S3 CDN… We have an issue with the picker itself @joffreyjaffeux

Is not using the S3 CDN from custom emojis

This UI is using the S3 CDN

So this is very specific to the emoji picker, improving title here.

1 Like

Thanks that should fix it:

3 Likes

Thank you for your PR that enables CDN usage for custom emojis.

While your frontend fix correctly requests emoji CDN addresses, I’m concerned about backend security.
The https://meta.discourse.org/emojis.json endpoint still exposes S3 source URLs like //assets-meta-cdck-prod-meta.s3.dualstack.us-west-1.amazonaws.com/original/3X/6/1/61e481320406f0f82ed780db3f04056128191613.png, which could potentially lead to malicious abuse and excessive S3 billing.

For better security, I suggest returning CDN addresses directly, similar to how non-custom emojis are handled.

Here’s my attempt at fixing the code:

class EmojisController < ApplicationController
  def index
    emojis = Emoji.allowed.group_by(&:group)
    
    emojis.each do |_, emoji_list|
      emoji_list.each do |emoji|
        emoji.url = Discourse.store.cdn_url(emoji.url) if emoji.url.present?
      end
    end
    
    render json: MultiJson.dump(emojis)
  end
  def search_aliases
    render json: MultiJson.dump(Emoji.search_aliases)
  end
end

Yes I know we do it directly for non custom emoji in emoji.rb which I would prefer than the controller. I want to ensure this is not toing to cause issues though, so I made the simplest fix for now, but will dig this in the future.

2 Likes