diff --git a/PCL.Core/UI/IconManager.cs b/PCL.Core/UI/IconManager.cs index 3134ef676..b0bf3cf4c 100644 --- a/PCL.Core/UI/IconManager.cs +++ b/PCL.Core/UI/IconManager.cs @@ -1,8 +1,14 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Xaml; using System.Windows; -using System.Windows.Markup; +using System.Windows.Controls; +using System.Windows.Data; +using WpfXamlReader = System.Windows.Markup.XamlReader; namespace PCL.Core.UI; @@ -55,7 +61,8 @@ public static bool TryLoadIconFromXaml(string xamlString, out UIElement? icon) { } try { - icon = (UIElement)XamlReader.Parse(xamlString); + ValidateSafeLooseXaml(xamlString); + icon = (UIElement)WpfXamlReader.Parse(xamlString); return true; } catch (Exception) { @@ -75,10 +82,82 @@ public static bool LoadIconFromXaml(string xamlString, out UIElement? icon) { throw new InvalidOperationException("XAML 解析需要在 UI 线程执行。"); } - icon = (UIElement)XamlReader.Parse(xamlString); + ValidateSafeLooseXaml(xamlString); + icon = (UIElement)WpfXamlReader.Parse(xamlString); return true; } + private static readonly Type[] DisallowedLooseXamlTypes = + [ + typeof(WebBrowser), + typeof(Frame), + typeof(MediaElement), + typeof(ObjectDataProvider), + typeof(XmlDataProvider), + typeof(WpfXamlReader), + typeof(Window), + typeof(Process), + typeof(ProcessStartInfo) + ]; + + private static readonly string[] DisallowedLooseXamlMembers = + [ + "Code", + "FactoryMethod", + "Static" + ]; + + private static readonly string[] DisallowedLooseXamlAssemblies = + [ + "Microsoft.Xaml.Behaviors" + ]; + + private static void ValidateSafeLooseXaml(string xamlString) { + using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(xamlString)); + using var reader = new XamlXmlReader(stream); + + while (reader.Read()) { + if (reader.Type?.UnderlyingType is { } nodeType) { + ValidateLooseXamlType(nodeType); + } + + if (reader.Member is { } member) { + if (DisallowedLooseXamlMembers.Contains(member.Name)) { + throw new UnauthorizedAccessException($"不允许使用 {member.Name} 成员。"); + } + + if (member.DeclaringType?.UnderlyingType is { } declaringType) { + ValidateLooseXamlType(declaringType); + } + } + + if (reader.Value is string value) { + ValidateLooseXamlValue(value); + } + } + } + + private static void ValidateLooseXamlType(Type type) { + if (DisallowedLooseXamlTypes.Any(disallowedType => disallowedType.IsAssignableFrom(type))) { + throw new UnauthorizedAccessException($"不允许使用 {type.Name} 类型。"); + } + + var assemblyName = type.Assembly.GetName().Name; + if (assemblyName is not null && DisallowedLooseXamlAssemblies.Any(disallowedAssembly => assemblyName.StartsWith(disallowedAssembly, StringComparison.Ordinal))) { + throw new UnauthorizedAccessException($"不允许使用 {assemblyName} 程序集中的类型。"); + } + } + + private static void ValidateLooseXamlValue(string value) { + if (DisallowedLooseXamlTypes.Any(disallowedType => string.Equals(value, disallowedType.Name, StringComparison.Ordinal))) { + throw new UnauthorizedAccessException($"不允许使用 {value} 值。"); + } + + if (DisallowedLooseXamlAssemblies.Any(disallowedAssembly => value.Contains(disallowedAssembly, StringComparison.Ordinal))) { + throw new UnauthorizedAccessException($"不允许引用 {value}。"); + } + } + public event PropertyChangedEventHandler? PropertyChanged; protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); diff --git a/Plain Craft Launcher 2/Modules/Base/ModBase.cs b/Plain Craft Launcher 2/Modules/Base/ModBase.cs index 8aaba2f17..a7bef50e1 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModBase.cs +++ b/Plain Craft Launcher 2/Modules/Base/ModBase.cs @@ -3223,7 +3223,8 @@ public static object GetObjectFromXML(string str) foreach (var blackListType in new[] { typeof(WebBrowser), typeof(Frame), typeof(MediaElement), typeof(ObjectDataProvider), - typeof(XamlReader), typeof(Window), typeof(XmlDataProvider) + typeof(XamlReader), typeof(Window), typeof(XmlDataProvider), + typeof(Process), typeof(ProcessStartInfo) }) { if (reader.Type is not null && blackListType.IsAssignableFrom(reader.Type.UnderlyingType)) @@ -3232,6 +3233,16 @@ public static object GetObjectFromXML(string str) throw new UnauthorizedAccessException($"不允许使用 {blackListType.Name} 值。"); } + foreach (var blackListAssembly in new[] { "Microsoft.Xaml.Behaviors" }) + { + if (reader.Type?.UnderlyingType?.Assembly.GetName().Name?.StartsWith(blackListAssembly, StringComparison.Ordinal) == true) + throw new UnauthorizedAccessException($"不允许使用 {blackListAssembly} 程序集中的类型。"); + if (reader.Member?.DeclaringType?.UnderlyingType?.Assembly.GetName().Name?.StartsWith(blackListAssembly, StringComparison.Ordinal) == true) + throw new UnauthorizedAccessException($"不允许使用 {blackListAssembly} 程序集中的成员。"); + if (reader.Value is string value && value.Contains(blackListAssembly, StringComparison.Ordinal)) + throw new UnauthorizedAccessException($"不允许引用 {blackListAssembly} 程序集。"); + } + foreach (var blackListMember in new[] { "Code", "FactoryMethod", "Static" }) if (reader.Member is not null && (reader.Member.Name ?? "") == (blackListMember ?? "")) throw new UnauthorizedAccessException($"不允许使用 {blackListMember} 成员。");