Filtering New Chat Bypasses with the Introduction of TextChatService

With the prioritization of TextChatService some users have discovered a bypass that allows them to type profanity and normally filtered content using flaws in the RichText system.

This issue was originally brought to my attention by @King_GamingRobloxYT via this bug report: [CRITICAL] Chat Filter Bypass: Allows All Content (Slurs, Swears, PII, etc.)

How this works is a few symbols namely &, <, and, > are instead interpreted as & < and > respectively. Due to this the Roblox filtration system has fallen somewhat flat, and players are able to type profanity in game by spacing out inappropriate words with the symbols.

I have created an example place that uses features of TextChatService to filter these strings from messages, resulting in user messages containing these to be filtered as they should. he basic idea behind this is restricting users so that no messages are directly shared between clients and are instead interpreted, parsed, and filtered by the server manually and distributed to all clients as if they were sent from the original user. The user that tries to bypass the chat filter will still see their message as if they have bypassed it, but all other users will see the censored message.

Credit to @GFink for the ability to compute text chat name colors and @Geomaster for their function to convert Color3 instances to hex strings.

Here is the experience.:

It is publically available if you wish to download it and play around, but be aware that FilterStringAsync() only works in live-game instances.

[UPDATE: 1/24/2025]

  • filters applied to messages are now localized to fit user chat permissions (different filtering for 13+ and <13 users)
  • now supports different TextChannels
  • messages with the offending symbols, but not intended to bypass should not be filtered
  • message formatting moved to modulescript for cleanliness
5 Likes

You can’t use filteredTextResult:GetNonChatStringForBroadcastAsync() well for chats (should use filteredTextResult:GetChatForUserAsync()), but mainly, after april 30th, every message defined as a “Chat” will have to go through TextChatService’s api. This means, using TextChannel:SendAsync()

Sucks that there is basically no way to fix Roblox’s mess, at least with the default TextChatService ui

Currently working on adding filteredTextResult:GetChatForUserAsync() to serve different filtered messages to 13+ and <13 users respectively. Having GetNonChatStringForBroadcastAsync() instead was a temporary solution while I worked on that.

Aside from that, all of this is through TextChatService’s API, they are just served via remote events to clients rather than using TextChannel:SendAsync() to publish messages to be displayed to all users. I can’t add functionality for use of TextChannel:SendAsync() because it doesn’t allow for server interference. Further, this shouldn’t be a problem because all of this is done through Roblox’s new TextChatService API, but there is additional filtering aside from what already takes place normally.

1 Like

Correct me if I’m wrong, but are you blocking messages from going through the TextChannel, and replacing them with a message filtered on the server? From looking at the code, I can see that you are setting every TextSource’s CanSend to false (at least those in the general channel), and you got some functions to display messages and chat bubbles

I believe this goes against the up coming policies regarding TextChatService, as this is basically bypassing the filtering done by TextChatService’s api, so beware

I am blocking messages from going through the text channel, however they are still filtered via TextChatService’s API. They are grabbed from the TextChatService SendingMessage event, passed through my own filter, and then displayed using TextChatServices display methods. DisplaySystemMessage() actually parses it through TextChatService’s filters again and returns a TextChatMessage object in the process. A similar thing occurs with DisplayBubble(), but this takes in a string and filters it and returns no object.

Basically: TextChatService filter → my filter → TextChatService filter
I’m just injecting my own filter, there is no bypass of Roblox’s filters.