#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member using System; using System.Text; using System.Threading; namespace Cysharp.Threading.Tasks { // UniTask has no scheduler like TaskScheduler. // Only handle unobserved exception. public static class UniTaskScheduler { /// /// 安全清理异常消息字符串,防止日志注入攻击 /// 清理控制字符、限制长度、转义特殊字符 /// /// 原始异常消息 /// 最大长度限制,默认1000字符 /// 清理后的安全消息字符串 static string SanitizeExceptionMessage(string message, int maxLength = 1000) { if (string.IsNullOrEmpty(message)) { return string.Empty; } // 创建StringBuilder用于构建清理后的消息 var sanitized = new StringBuilder(message.Length); // 遍历每个字符进行清理 for (int i = 0; i < message.Length && sanitized.Length < maxLength; i++) { char c = message[i]; // 保留可打印字符(ASCII 32-126)和常见的Unicode字符 // 移除控制字符(ASCII 0-31,除了换行符和制表符) if (char.IsControl(c) && c != '\n' && c != '\r' && c != '\t') { // 将控制字符替换为安全的转义表示 sanitized.Append($"\\u{(int)c:X4}"); } else if (c == '\n') { // 将换行符替换为安全表示,防止日志注入 sanitized.Append("\\n"); } else if (c == '\r') { // 将回车符替换为安全表示 sanitized.Append("\\r"); } else if (c == '\t') { // 将制表符替换为空格 sanitized.Append(" "); } else { // 保留正常的可打印字符 sanitized.Append(c); } } // 如果消息被截断,添加截断标记 if (message.Length > maxLength) { sanitized.Append("...[消息已截断]"); } return sanitized.ToString(); } public static event Action UnobservedTaskException; /// /// Propagate OperationCanceledException to UnobservedTaskException when true. Default is false. /// public static bool PropagateOperationCanceledException = false; #if UNITY_2018_3_OR_NEWER /// /// Write log type when catch unobserved exception and not registered UnobservedTaskException. Default is Exception. /// public static UnityEngine.LogType UnobservedExceptionWriteLogType = UnityEngine.LogType.Exception; /// /// Dispatch exception event to Unity MainThread. Default is true. /// public static bool DispatchUnityMainThread = true; // cache delegate. static readonly SendOrPostCallback handleExceptionInvoke = InvokeUnobservedTaskException; static void InvokeUnobservedTaskException(object state) { UnobservedTaskException((Exception)state); } #endif internal static void PublishUnobservedTaskException(Exception ex) { if (ex != null) { if (!PropagateOperationCanceledException && ex is OperationCanceledException) { return; } if (UnobservedTaskException != null) { #if UNITY_2018_3_OR_NEWER if (!DispatchUnityMainThread || Thread.CurrentThread.ManagedThreadId == PlayerLoopHelper.MainThreadId) { // allows inlining call. UnobservedTaskException.Invoke(ex); } else { // Post to MainThread. PlayerLoopHelper.UnitySynchronizationContext.Post(handleExceptionInvoke, ex); } #else UnobservedTaskException.Invoke(ex); #endif } else { #if UNITY_2018_3_OR_NEWER string msg = null; if (UnobservedExceptionWriteLogType != UnityEngine.LogType.Exception) { // 使用安全清理方法处理异常消息,防止日志注入攻击 string safeExceptionMessage = SanitizeExceptionMessage(ex.ToString()); msg = "UnobservedTaskException: " + safeExceptionMessage; } switch (UnobservedExceptionWriteLogType) { case UnityEngine.LogType.Error: UnityEngine.Debug.LogError(msg); break; case UnityEngine.LogType.Assert: UnityEngine.Debug.LogAssertion(msg); break; case UnityEngine.LogType.Warning: UnityEngine.Debug.LogWarning(msg); break; case UnityEngine.LogType.Log: UnityEngine.Debug.Log(msg); break; case UnityEngine.LogType.Exception: UnityEngine.Debug.LogException(ex); break; default: break; } #else // 使用安全清理方法处理异常消息,防止日志注入攻击 string safeMessage = SanitizeExceptionMessage(ex.ToString()); Console.WriteLine("UnobservedTaskException: " + safeMessage); #endif } } } } }