From 3a978441b6e6c633b475eef54e5e654a1c7723d7 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 7 Jun 2025 22:06:57 +0800 Subject: [PATCH] :sparkles: Sanitize text to remove hidden unicode and control characters --- DysonNetwork.Sphere/Chat/ChatController.cs | 18 +++++++++++---- DysonNetwork.Sphere/Post/PostController.cs | 3 +++ DysonNetwork.Sphere/Storage/TextSanitizer.cs | 24 ++++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 DysonNetwork.Sphere/Storage/TextSanitizer.cs diff --git a/DysonNetwork.Sphere/Chat/ChatController.cs b/DysonNetwork.Sphere/Chat/ChatController.cs index b1c4c92..e1df461 100644 --- a/DysonNetwork.Sphere/Chat/ChatController.cs +++ b/DysonNetwork.Sphere/Chat/ChatController.cs @@ -1,4 +1,6 @@ using System.ComponentModel.DataAnnotations; +using System.Globalization; +using System.Text; using System.Text.RegularExpressions; using DysonNetwork.Sphere.Permission; using DysonNetwork.Sphere.Storage; @@ -142,6 +144,8 @@ public partial class ChatController(AppDatabase db, ChatService cs, ChatRoomServ public async Task SendMessage([FromBody] SendMessageRequest request, Guid roomId) { if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); + + request.Content = TextSanitizer.Sanitize(request.Content); if (string.IsNullOrWhiteSpace(request.Content) && (request.AttachmentsId == null || request.AttachmentsId.Count == 0)) return BadRequest("You cannot send an empty message."); @@ -218,18 +222,24 @@ public partial class ChatController(AppDatabase db, ChatService cs, ChatRoomServ { if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); + request.Content = TextSanitizer.Sanitize(request.Content); + var message = await db.ChatMessages .Include(m => m.Sender) .Include(m => m.Sender.Account) .Include(m => m.Sender.Account.Profile) .Include(message => message.ChatRoom) .FirstOrDefaultAsync(m => m.Id == messageId && m.ChatRoomId == roomId); - + if (message == null) return NotFound(); if (message.Sender.AccountId != currentUser.Id) return StatusCode(403, "You can only edit your own messages."); + if (string.IsNullOrWhiteSpace(request.Content) && + (request.AttachmentsId == null || request.AttachmentsId.Count == 0)) + return BadRequest("You cannot send an empty message."); + if (request.RepliedMessageId.HasValue) { var repliedMessage = await db.ChatMessages @@ -248,8 +258,8 @@ public partial class ChatController(AppDatabase db, ChatService cs, ChatRoomServ // Call service method to update the message await cs.UpdateMessageAsync( - message, - request.Meta, + message, + request.Meta, request.Content, request.RepliedMessageId, request.ForwardedMessageId, @@ -269,7 +279,7 @@ public partial class ChatController(AppDatabase db, ChatService cs, ChatRoomServ .Include(m => m.Sender) .Include(m => m.ChatRoom) .FirstOrDefaultAsync(m => m.Id == messageId && m.ChatRoomId == roomId); - + if (message == null) return NotFound(); if (message.Sender.AccountId != currentUser.Id) diff --git a/DysonNetwork.Sphere/Post/PostController.cs b/DysonNetwork.Sphere/Post/PostController.cs index 273ba3a..6c8c7c8 100644 --- a/DysonNetwork.Sphere/Post/PostController.cs +++ b/DysonNetwork.Sphere/Post/PostController.cs @@ -3,6 +3,7 @@ using System.Text.Json; using DysonNetwork.Sphere.Account; using DysonNetwork.Sphere.Permission; using DysonNetwork.Sphere.Publisher; +using DysonNetwork.Sphere.Storage; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -149,6 +150,7 @@ public class PostController( [FromHeader(Name = "X-Pub")] string? publisherName ) { + request.Content = TextSanitizer.Sanitize(request.Content); if (string.IsNullOrWhiteSpace(request.Content) && request.Attachments is { Count: 0 }) return BadRequest("Content is required."); if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); @@ -290,6 +292,7 @@ public class PostController( [HttpPatch("{id:guid}")] public async Task> UpdatePost(Guid id, [FromBody] PostRequest request) { + request.Content = TextSanitizer.Sanitize(request.Content); if (string.IsNullOrWhiteSpace(request.Content) && request.Attachments is { Count: 0 }) return BadRequest("Content is required."); if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); diff --git a/DysonNetwork.Sphere/Storage/TextSanitizer.cs b/DysonNetwork.Sphere/Storage/TextSanitizer.cs new file mode 100644 index 0000000..6bc7382 --- /dev/null +++ b/DysonNetwork.Sphere/Storage/TextSanitizer.cs @@ -0,0 +1,24 @@ +using System.Globalization; +using System.Text; + +namespace DysonNetwork.Sphere.Storage; + +public abstract class TextSanitizer +{ + public static string? Sanitize(string? text) + { + if (string.IsNullOrEmpty(text)) return text; + + var filtered = new StringBuilder(); + foreach (var ch in from ch in text + let category = CharUnicodeInfo.GetUnicodeCategory(ch) + where category is not (UnicodeCategory.Control or UnicodeCategory.Format + or UnicodeCategory.NonSpacingMark) + select ch) + { + filtered.Append(ch); + } + + return filtered.ToString(); + } +} \ No newline at end of file