From 675b14c3aa17dc6d9121215f29255d745d9d07ef Mon Sep 17 00:00:00 2001 From: laurentiu021 Date: Tue, 19 May 2026 17:41:53 +0300 Subject: [PATCH] fix: dynamic user-path sanitization in LogService and cache FontFamily in MarkdownTextBlock --- CHANGELOG.md | 6 +++++ .../SysManager/Helpers/MarkdownTextBlock.cs | 5 +++- SysManager/SysManager/Services/LogService.cs | 24 +++++++++++++++---- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90bfa403..f4fe72d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,12 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). now uses `long` before clamping to the 0–20 deduction cap. - **SpeedTestService** — documented pinned Ookla CLI version (`1.2.0`) with maintenance comment explaining update procedure and Authenticode verification. +- **LogService** — path sanitization regex now dynamically derives the user + profile directory from `Environment.GetFolderPath` instead of assuming a + hardcoded `:\Users\` pattern; falls back to the generic regex if the + environment variable is unavailable. +- **MarkdownTextBlock** — cached `FontFamily("Consolas")` as a static field to + eliminate per-render allocation in code span formatting. ### Added - **ServicesViewModelTests** — 20 unit tests covering ApplyFilter logic: category diff --git a/SysManager/SysManager/Helpers/MarkdownTextBlock.cs b/SysManager/SysManager/Helpers/MarkdownTextBlock.cs index 4fd86c11..fd29240f 100644 --- a/SysManager/SysManager/Helpers/MarkdownTextBlock.cs +++ b/SysManager/SysManager/Helpers/MarkdownTextBlock.cs @@ -85,6 +85,9 @@ private static void OnMarkdownChanged(DependencyObject d, DependencyPropertyChan private static readonly Regex InlineFormattingRegex = new( @"\*\*(.+?)\*\*|`([^`]+)`", RegexOptions.Compiled); + // PERF-007: Cache FontFamily to avoid allocating a new instance per code span render. + private static readonly FontFamily CodeFontFamily = new("Consolas"); + /// /// Parses a single line for **bold** and `code` inline formatting /// and appends the resulting Inlines to the TextBlock. @@ -111,7 +114,7 @@ private static void AddFormattedText(TextBlock tb, string text) // `code` tb.Inlines.Add(new Run(m.Groups[2].Value) { - FontFamily = new FontFamily("Consolas"), + FontFamily = CodeFontFamily, Foreground = Application.Current?.TryFindResource("Accent") as Brush ?? Brushes.CornflowerBlue }); } diff --git a/SysManager/SysManager/Services/LogService.cs b/SysManager/SysManager/Services/LogService.cs index f7e31105..e5a5a9b0 100644 --- a/SysManager/SysManager/Services/LogService.cs +++ b/SysManager/SysManager/Services/LogService.cs @@ -16,10 +16,26 @@ public static class LogService public static string LogDir { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "SysManager", "logs"); - // Matches :\Users\\ and replaces the username with [user]. - private static readonly Regex UserPathRegex = new( - @"(?i)([A-Z]:\\Users\\)[^\\]+", - RegexOptions.Compiled); + // Dynamically build regex from the actual user profile parent directory + // (e.g. C:\Users) so it works even if Windows is installed on a non-standard + // drive or the Users folder has a custom path. + private static readonly Regex UserPathRegex = BuildUserPathRegex(); + + private static Regex BuildUserPathRegex() + { + var userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + if (!string.IsNullOrEmpty(userProfile)) + { + var usersDir = Path.GetDirectoryName(userProfile); + if (!string.IsNullOrEmpty(usersDir)) + { + var escaped = Regex.Escape(usersDir + @"\"); + return new Regex($@"(?i)({escaped})[^\\]+", RegexOptions.Compiled); + } + } + // Fallback: match any drive letter followed by \Users\ + return new Regex(@"(?i)([A-Z]:\\Users\\)[^\\]+", RegexOptions.Compiled); + } public static void Init() {