Skip to content

Commit

Permalink
Fix Sanitization (#1688)
Browse files Browse the repository at this point in the history
fixes sanitization with regards to emotes and radio prefixes being
silly.
prevents color coding your chat by escaping the escape

---------

Co-authored-by: CaasGit <87243814+CaasGit@users.noreply.github.com>
Co-authored-by: Thomas <87614336+Aeshus@users.noreply.github.com>
Co-authored-by: Hyper B <137433177+HyperB1@users.noreply.github.com>
  • Loading branch information
4 people authored Feb 6, 2025
1 parent ebf13c2 commit 69554e0
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 12 deletions.
2 changes: 1 addition & 1 deletion Content.Server/Chat/Managers/ChatSanitizationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public sealed class ChatSanitizationManager : IChatSanitizationManager
{ ":D", "chatsan-smiles-widely" },
{ "D:", "chatsan-frowns-deeply" },
{ ":O", "chatsan-surprised" },
{ ":3", "chatsan-smiles" }, //nope
{ ":3", "chatsan-smiles" },
{ ":S", "chatsan-uncertain" },
{ ":>", "chatsan-grins" },
{ ":<", "chatsan-pouts" },
Expand Down
22 changes: 12 additions & 10 deletions Content.Server/Chat/Systems/ChatSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@ public void TrySendInGameICMessage(
}
}

message = FormattedMessage.EscapeText(message);

// Otherwise, send whatever type.
switch (desiredType)
{
Expand Down Expand Up @@ -408,7 +410,7 @@ private void SendEntitySpeak(
return;

// The original message
var message = TransformSpeech(source, FormattedMessage.RemoveMarkup(originalMessage), language);
var message = TransformSpeech(source, FormattedMessage.RemoveMarkupPermissive(originalMessage), language);

if (message.Length == 0)
return;
Expand Down Expand Up @@ -481,7 +483,7 @@ private void SendEntityWhisper(
if (!_actionBlocker.CanSpeak(source) && !ignoreActionBlocker)
return;

var message = TransformSpeech(source, FormattedMessage.RemoveMarkup(originalMessage), language);
var message = TransformSpeech(source, FormattedMessage.RemoveMarkupPermissive(originalMessage), language);
if (message.Length == 0)
return;

Expand Down Expand Up @@ -517,7 +519,7 @@ private void SendEntityWhisper(

var canUnderstandLanguage = _language.CanUnderstand(listener, language.ID);
// How the entity perceives the message depends on whether it can understand its language
var perceivedMessage = FormattedMessage.EscapeText(canUnderstandLanguage ? message : languageObfuscatedMessage);
var perceivedMessage = canUnderstandLanguage ? message : languageObfuscatedMessage;

// Result is the intermediate message derived from the perceived one via obfuscation
// Wrapped message is the result wrapped in an "x says y" string
Expand All @@ -544,7 +546,7 @@ private void SendEntityWhisper(
_chatManager.ChatMessageToOne(ChatChannel.Whisper, result, wrappedMessage, source, false, session.Channel);
}

var replayWrap = WrapWhisperMessage(source, "chat-manager-entity-whisper-wrap-message", name, FormattedMessage.EscapeText(message), language);
var replayWrap = WrapWhisperMessage(source, "chat-manager-entity-whisper-wrap-message", name, message, language);
_replay.RecordServerMessage(new ChatMessage(ChatChannel.Whisper, message, replayWrap, GetNetEntity(source), null, MessageRangeHideChatForReplay(range)));

var ev = new EntitySpokeEvent(source, message, channel, true, language);
Expand Down Expand Up @@ -591,7 +593,7 @@ private void SendEntityEmote(
var wrappedMessage = Loc.GetString("chat-manager-entity-me-wrap-message",
("entityName", name),
("entity", ent),
("message", FormattedMessage.RemoveMarkup(action)));
("message", FormattedMessage.RemoveMarkupPermissive(action)));

if (checkEmote)
TryEmoteChatInput(source, action);
Expand Down Expand Up @@ -771,8 +773,10 @@ private bool CanSendInGame(string message, IConsoleShell? shell = null, ICommonS
// ReSharper disable once InconsistentNaming
private string SanitizeInGameICMessage(EntityUid source, string message, out string? emoteStr, bool capitalize = true, bool punctuate = false, bool capitalizeTheWordI = true)
{
var newMessage = message.Trim();
newMessage = SanitizeMessageReplaceWords(newMessage);
var newMessage = SanitizeMessageReplaceWords(message.Trim());

GetRadioKeycodePrefix(source, newMessage, out newMessage, out var prefix);
_sanitizer.TrySanitizeOutSmilies(newMessage, source, out newMessage, out emoteStr);

if (capitalize)
newMessage = SanitizeMessageCapital(newMessage);
Expand All @@ -781,9 +785,7 @@ private string SanitizeInGameICMessage(EntityUid source, string message, out str
if (punctuate)
newMessage = SanitizeMessagePeriod(newMessage);

_sanitizer.TrySanitizeOutSmilies(newMessage, source, out newMessage, out emoteStr);

return newMessage;
return prefix + newMessage;
}

private string SanitizeInGameOOCMessage(string message)
Expand Down
29 changes: 29 additions & 0 deletions Content.Shared/Chat/SharedChatSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,35 @@ public SpeechVerbPrototype GetSpeechVerb(EntityUid source, string message, Speec
return current ?? _prototypeManager.Index(speech.SpeechVerb);
}

/// <summary>
/// Splits the input message into a radio prefix part and the rest to preserve it during sanitization.
/// </summary>
/// <remarks>
/// This is primarily for the chat emote sanitizer, which can match against ":b" as an emote, which is a valid radio keycode.
/// </remarks>
public void GetRadioKeycodePrefix(EntityUid source,
string input,
out string output,
out string prefix)
{
prefix = string.Empty;
output = input;

// If the string is less than 2, then it's probably supposed to be an emote.
// No one is sending empty radio messages!
if (input.Length <= 2)
return;

if (!(input.StartsWith(RadioChannelPrefix) || input.StartsWith(RadioChannelAltPrefix)))
return;

if (!_keyCodes.TryGetValue(input[1], out _))
return;

prefix = input[..2];
output = input[2..];
}

/// <summary>
/// Attempts to resolve radio prefixes in chat messages (e.g., remove a leading ":e" and resolve the requested
/// channel. Returns true if a radio message was attempted, even if the channel is invalid.
Expand Down
2 changes: 1 addition & 1 deletion Content.Shared/Preferences/HumanoidCharacterProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace Content.Shared.Preferences;
[Serializable, NetSerializable]
public sealed partial class HumanoidCharacterProfile : ICharacterProfile
{
private static readonly Regex RestrictedNameRegex = new("[^A-Z,a-z,0-9, -]");
private static readonly Regex RestrictedNameRegex = new(@"[^A-Za-z0-9 '\-]");
private static readonly Regex ICNameCaseRegex = new(@"^(?<word>\w)|\b(?<word>\w)(?=\w*$)");

public const int MaxNameLength = 64;
Expand Down

0 comments on commit 69554e0

Please sign in to comment.