diff --git a/build/_build.csproj b/build/_build.csproj
index d5eadde9..dbc52a56 100644
--- a/build/_build.csproj
+++ b/build/_build.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net10.0
False
CS0649;CS0169
@@ -10,15 +10,17 @@
-
-
+
+
+
+
all
runtime; build; native; contentfiles; analyzers
-
+
diff --git a/global.json b/global.json
index 4c686a52..971b5004 100644
--- a/global.json
+++ b/global.json
@@ -1,5 +1,6 @@
{
"sdk": {
- "version": "9.0.301"
+ "version": "10.0.100",
+ "rollForward": "latestPatch"
}
}
\ No newline at end of file
diff --git a/src/AutoColumnizer/AutoColumnizer.csproj b/src/AutoColumnizer/AutoColumnizer.csproj
index 3f5dd983..cb537385 100644
--- a/src/AutoColumnizer/AutoColumnizer.csproj
+++ b/src/AutoColumnizer/AutoColumnizer.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
AutoColumnizer
$(SolutionDir)..\bin\$(Configuration)\plugins
diff --git a/src/ColumnizerLib.UnitTests/ColumnizerLib.UnitTests.csproj b/src/ColumnizerLib.UnitTests/ColumnizerLib.UnitTests.csproj
index af5aa1ac..96ae6dae 100644
--- a/src/ColumnizerLib.UnitTests/ColumnizerLib.UnitTests.csproj
+++ b/src/ColumnizerLib.UnitTests/ColumnizerLib.UnitTests.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
true
ColumnizerLib.UnitTests
diff --git a/src/ColumnizerLib/ColumnizerLib.csproj b/src/ColumnizerLib/ColumnizerLib.csproj
index 960aa76e..5f0376de 100644
--- a/src/ColumnizerLib/ColumnizerLib.csproj
+++ b/src/ColumnizerLib/ColumnizerLib.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
$(SolutionDir)..\bin\Docs\ColumnizerLib.xml
diff --git a/src/CsvColumnizer/CsvColumnizer.csproj b/src/CsvColumnizer/CsvColumnizer.csproj
index 005a350a..bb1e079f 100644
--- a/src/CsvColumnizer/CsvColumnizer.csproj
+++ b/src/CsvColumnizer/CsvColumnizer.csproj
@@ -1,6 +1,6 @@
- net8.0-windows
+ net10.0-windows
true
true
diff --git a/src/DefaultPlugins/DefaultPlugins.csproj b/src/DefaultPlugins/DefaultPlugins.csproj
index 20255a67..3f53e80e 100644
--- a/src/DefaultPlugins/DefaultPlugins.csproj
+++ b/src/DefaultPlugins/DefaultPlugins.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
$(SolutionDir)..\bin\$(Configuration)\plugins
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index d357ea89..100bf68d 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -9,18 +9,18 @@
-
+
-
-
+
+
-
+
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/src/FlashIconHighlighter/FlashIconHighlighter.csproj b/src/FlashIconHighlighter/FlashIconHighlighter.csproj
index 50489d9a..af0c5828 100644
--- a/src/FlashIconHighlighter/FlashIconHighlighter.csproj
+++ b/src/FlashIconHighlighter/FlashIconHighlighter.csproj
@@ -1,7 +1,7 @@
- net8.0-windows
+ net10.0-windows
true
true
true
diff --git a/src/GlassfishColumnizer/GlassfishColumnizer.csproj b/src/GlassfishColumnizer/GlassfishColumnizer.csproj
index 23bf3af2..3f0c708b 100644
--- a/src/GlassfishColumnizer/GlassfishColumnizer.csproj
+++ b/src/GlassfishColumnizer/GlassfishColumnizer.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
$(SolutionDir)..\bin\$(Configuration)\plugins
GlassfishColumnizer
diff --git a/src/JsonColumnizer/JsonColumnizer.csproj b/src/JsonColumnizer/JsonColumnizer.csproj
index 0092f405..8a882ff2 100644
--- a/src/JsonColumnizer/JsonColumnizer.csproj
+++ b/src/JsonColumnizer/JsonColumnizer.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
$(SolutionDir)..\bin\$(Configuration)\plugins
JsonColumnizer
diff --git a/src/JsonCompactColumnizer/JsonCompactColumnizer.csproj b/src/JsonCompactColumnizer/JsonCompactColumnizer.csproj
index bd18a3cc..e46da3ba 100644
--- a/src/JsonCompactColumnizer/JsonCompactColumnizer.csproj
+++ b/src/JsonCompactColumnizer/JsonCompactColumnizer.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
JsonColumnizer
$(SolutionDir)..\bin\$(Configuration)\plugins
diff --git a/src/Log4jXmlColumnizer/Log4jXmlColumnizer.csproj b/src/Log4jXmlColumnizer/Log4jXmlColumnizer.csproj
index 88d38b28..3598fe21 100644
--- a/src/Log4jXmlColumnizer/Log4jXmlColumnizer.csproj
+++ b/src/Log4jXmlColumnizer/Log4jXmlColumnizer.csproj
@@ -1,6 +1,6 @@
- net8.0-windows
+ net10.0-windows
true
true
diff --git a/src/LogExpert.Core/Classes/Filter/FilterParams.cs b/src/LogExpert.Core/Classes/Filter/FilterParams.cs
index 68376518..e8aaa1af 100644
--- a/src/LogExpert.Core/Classes/Filter/FilterParams.cs
+++ b/src/LogExpert.Core/Classes/Filter/FilterParams.cs
@@ -4,6 +4,7 @@
using System.Text.RegularExpressions;
using LogExpert.Core.Classes.JsonConverters;
+using LogExpert.Core.Helpers;
using Newtonsoft.Json;
@@ -130,12 +131,12 @@ public void CreateRegex ()
{
if (SearchText != null)
{
- Regex = new Regex(SearchText, IsCaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase);
+ Regex = RegexHelper.GetOrCreateCached(SearchText, IsCaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase);
}
if (RangeSearchText != null && IsRangeSearch)
{
- RangeRex = new Regex(RangeSearchText, IsCaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase);
+ RangeRex = RegexHelper.GetOrCreateCached(RangeSearchText, IsCaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase);
}
}
diff --git a/src/LogExpert.Core/Classes/Highlight/HighlightEntry.cs b/src/LogExpert.Core/Classes/Highlight/HighlightEntry.cs
index cd0391ee..b4f18867 100644
--- a/src/LogExpert.Core/Classes/Highlight/HighlightEntry.cs
+++ b/src/LogExpert.Core/Classes/Highlight/HighlightEntry.cs
@@ -1,6 +1,8 @@
using System.Drawing;
using System.Text.RegularExpressions;
+using LogExpert.Core.Helpers;
+
using Newtonsoft.Json;
namespace LogExpert.Core.Classes.Highlight;
@@ -54,10 +56,10 @@ public Regex Regex
get
{
_regex ??= IsRegex
- ? new Regex(SearchText, IsCaseSensitive
+ ? RegexHelper.GetOrCreateCached(SearchText, IsCaseSensitive
? RegexOptions.None
: RegexOptions.IgnoreCase)
- : new Regex(Regex.Escape(SearchText),
+ : RegexHelper.GetOrCreateCached(System.Text.RegularExpressions.Regex.Escape(SearchText),
IsCaseSensitive
? RegexOptions.None
: RegexOptions.IgnoreCase);
diff --git a/src/LogExpert.Core/Classes/ParamParser.cs b/src/LogExpert.Core/Classes/ParamParser.cs
index 6ca9731f..10ede7c6 100644
--- a/src/LogExpert.Core/Classes/ParamParser.cs
+++ b/src/LogExpert.Core/Classes/ParamParser.cs
@@ -1,6 +1,8 @@
using System.Text;
using System.Text.RegularExpressions;
+using LogExpert.Core.Helpers;
+
namespace LogExpert.Core.Classes;
public class ParamParser (string argTemplate)
@@ -38,8 +40,18 @@ public string ReplaceParams (ILogLine logLine, int lineNum, string fileName)
replace = GetNextGroup(builder, ref sPos);
if (reg != null && replace != null)
{
- var result = Regex.Replace(logLine.FullLine, reg, replace);
- builder.Insert(sPos, result);
+ // Use RegexHelper for safe regex operations with timeout protection
+ try
+ {
+ var regex = RegexHelper.GetOrCreateCached(reg);
+ var result = regex.Replace(logLine.FullLine, replace);
+ builder.Insert(sPos, result);
+ }
+ catch (RegexMatchTimeoutException)
+ {
+ // If regex times out, insert the original pattern as fallback
+ builder.Insert(sPos, $"{{timeout: {reg}}}");
+ }
}
} while (replace != null);
return builder.ToString();
diff --git a/src/LogExpert.Core/Classes/xml/XmlLogReader.cs b/src/LogExpert.Core/Classes/xml/XmlLogReader.cs
index 99ed640f..58832449 100644
--- a/src/LogExpert.Core/Classes/xml/XmlLogReader.cs
+++ b/src/LogExpert.Core/Classes/xml/XmlLogReader.cs
@@ -58,17 +58,37 @@ public override int ReadChar()
}
public override string ReadLine()
+ {
+ // Call async version synchronously for backward compatibility
+ // This maintains the interface but uses the improved async implementation internally
+ return ReadLineAsync(CancellationToken.None).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Reads a complete XML block asynchronously.
+ /// Replaces Thread.Sleep with Task.Delay for non-blocking waits.
+ ///
+ /// Cancellation token for graceful cancellation
+ /// Complete XML block or null if not available
+ public async Task ReadLineAsync(CancellationToken cancellationToken = default)
{
short state = 0;
var tagIndex = 0;
var blockComplete = false;
var eof = false;
var tryCounter = 5;
+ const int delayMs = 100;
StringBuilder builder = new();
while (!eof && !blockComplete)
{
+ // Check for cancellation
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return null;
+ }
+
var readInt = ReadChar();
if (readInt == -1)
{
@@ -77,11 +97,21 @@ public override string ReadLine()
{
if (--tryCounter > 0)
{
- Thread.Sleep(100);
+ // Use Task.Delay instead of Thread.Sleep for non-blocking wait
+ try
+ {
+ await Task.Delay(delayMs, cancellationToken);
+ }
+ catch (OperationCanceledException)
+ {
+ // Gracefully handle cancellation
+ return null;
+ }
continue;
}
else
{
+ // Timeout - return partial block if available
break;
}
}
diff --git a/src/LogExpert.Core/Helpers/RegexHelper.cs b/src/LogExpert.Core/Helpers/RegexHelper.cs
new file mode 100644
index 00000000..13b93cb5
--- /dev/null
+++ b/src/LogExpert.Core/Helpers/RegexHelper.cs
@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Concurrent;
+using System.Text.RegularExpressions;
+
+namespace LogExpert.Core.Helpers;
+
+///
+/// Helper class for creating and managing regex instances with safety features.
+/// Provides timeout protection against catastrophic backtracking (DoS attacks)
+/// and caching for improved performance.
+///
+public static class RegexHelper
+{
+ ///
+ /// Default timeout for all regex operations to prevent DoS attacks.
+ /// This prevents catastrophic backtracking from freezing the application.
+ ///
+ public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(2);
+
+ private static readonly ConcurrentDictionary _cache = new();
+ private const int MaxCacheSize = 100;
+
+ ///
+ /// Creates a regex with timeout protection.
+ ///
+ /// The regular expression pattern.
+ /// Regex options to use.
+ /// Optional timeout override. Uses DefaultTimeout if not specified.
+ /// A Regex instance with timeout protection.
+ /// Thrown if pattern is null.
+ /// Thrown if pattern is invalid.
+ public static Regex CreateSafeRegex(
+ string pattern,
+ RegexOptions options = RegexOptions.None,
+ TimeSpan? timeout = null)
+ {
+ ArgumentNullException.ThrowIfNull(pattern);
+
+ return new Regex(
+ pattern,
+ options,
+ timeout ?? DefaultTimeout);
+ }
+
+ ///
+ /// Gets or creates a cached regex instance.
+ /// This improves performance by reusing compiled regex patterns.
+ ///
+ /// The regular expression pattern.
+ /// Regex options to use.
+ /// A cached Regex instance with timeout protection.
+ public static Regex GetOrCreateCached(
+ string pattern,
+ RegexOptions options = RegexOptions.None)
+ {
+ var key = new RegexCacheKey(pattern, options);
+
+ return _cache.GetOrAdd(key, k =>
+ {
+ // Evict oldest entries if cache is full
+ if (_cache.Count >= MaxCacheSize)
+ {
+ TrimCache();
+ }
+
+ return CreateSafeRegex(k.Pattern, k.Options);
+ });
+ }
+
+ ///
+ /// Validates a regex pattern without executing it.
+ ///
+ /// The pattern to validate.
+ /// Output parameter containing error message if validation fails.
+ /// True if the pattern is valid, false otherwise.
+ public static bool IsValidPattern(string pattern, out string? error)
+ {
+ if (string.IsNullOrEmpty(pattern))
+ {
+ error = "Pattern cannot be null or empty.";
+ return false;
+ }
+
+ try
+ {
+ _ = new Regex(pattern, RegexOptions.None, TimeSpan.FromMilliseconds(100));
+ error = null;
+ return true;
+ }
+ catch (ArgumentException ex)
+ {
+ error = ex.Message;
+ return false;
+ }
+ catch (RegexMatchTimeoutException)
+ {
+ // Pattern is valid syntactically, but may be complex
+ error = null;
+ return true;
+ }
+ }
+
+ ///
+ /// Clears the regex cache. Useful for testing or memory management.
+ ///
+ public static void ClearCache()
+ {
+ _cache.Clear();
+ }
+
+ ///
+ /// Gets the current cache size.
+ ///
+ public static int CacheSize => _cache.Count;
+
+ private static void TrimCache()
+ {
+ // Keep most recent 50 entries (half of max)
+ var toRemove = _cache.Keys.Take(_cache.Count - MaxCacheSize / 2).ToList();
+ foreach (var key in toRemove)
+ {
+ _cache.TryRemove(key, out _);
+ }
+ }
+
+ private record RegexCacheKey(string Pattern, RegexOptions Options);
+}
diff --git a/src/LogExpert.Core/LogExpert.Core.csproj b/src/LogExpert.Core/LogExpert.Core.csproj
index 8b8e1e8f..09862a29 100644
--- a/src/LogExpert.Core/LogExpert.Core.csproj
+++ b/src/LogExpert.Core/LogExpert.Core.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
true
LogExpert.Core
diff --git a/src/LogExpert.Resources/LogExpert.Resources.csproj b/src/LogExpert.Resources/LogExpert.Resources.csproj
index 1337b7d9..1af925eb 100644
--- a/src/LogExpert.Resources/LogExpert.Resources.csproj
+++ b/src/LogExpert.Resources/LogExpert.Resources.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
True
False
..\Solution Items\Key.snk
diff --git a/src/LogExpert.Tests/Helpers/RegexHelperTests.cs b/src/LogExpert.Tests/Helpers/RegexHelperTests.cs
new file mode 100644
index 00000000..573755d0
--- /dev/null
+++ b/src/LogExpert.Tests/Helpers/RegexHelperTests.cs
@@ -0,0 +1,271 @@
+using System.Text.RegularExpressions;
+
+using LogExpert.Core.Helpers;
+
+using NUnit.Framework;
+
+namespace LogExpert.Tests.Helpers;
+
+[TestFixture]
+public class RegexHelperTests
+{
+ [SetUp]
+ public void Setup()
+ {
+ // Clear cache before each test to ensure isolation
+ RegexHelper.ClearCache();
+ }
+
+ [Test]
+ public void CreateSafeRegex_ShouldHaveDefaultTimeout()
+ {
+ // Arrange & Act
+ var regex = RegexHelper.CreateSafeRegex("test");
+
+ // Assert
+ Assert.That(regex.MatchTimeout, Is.EqualTo(RegexHelper.DefaultTimeout));
+ }
+
+ [Test]
+ public void CreateSafeRegex_WithCustomTimeout_ShouldUseCustomTimeout()
+ {
+ // Arrange
+ var customTimeout = TimeSpan.FromSeconds(5);
+
+ // Act
+ var regex = RegexHelper.CreateSafeRegex("test", RegexOptions.None, customTimeout);
+
+ // Assert
+ Assert.That(regex.MatchTimeout, Is.EqualTo(customTimeout));
+ }
+
+ [Test]
+ public void CreateSafeRegex_WithNullPattern_ShouldThrowArgumentNullException()
+ {
+ // Act & Assert
+ Assert.Throws(() => RegexHelper.CreateSafeRegex(null!));
+ }
+
+ [Test]
+ public void CreateSafeRegex_ShouldPreventCatastrophicBacktracking()
+ {
+ // Arrange
+ var maliciousPattern = "^(a+)+$";
+ var maliciousInput = "aaaaaaaaaaaaaaaaaX";
+ var regex = RegexHelper.CreateSafeRegex(maliciousPattern);
+
+ // Act & Assert
+ Assert.Throws(() =>
+ {
+ regex.IsMatch(maliciousInput);
+ });
+ }
+
+ [Test]
+ public void CreateSafeRegex_WithComplexPattern_ShouldTimeout()
+ {
+ // Arrange - Another catastrophic backtracking pattern
+ var pattern = "(x+x+)+y";
+ var input = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxX";
+ var regex = RegexHelper.CreateSafeRegex(pattern);
+
+ // Act & Assert
+ Assert.Throws(() =>
+ {
+ regex.IsMatch(input);
+ });
+ }
+
+ [Test]
+ public void GetOrCreateCached_ShouldReturnSameInstance()
+ {
+ // Arrange & Act
+ var regex1 = RegexHelper.GetOrCreateCached("test");
+ var regex2 = RegexHelper.GetOrCreateCached("test");
+
+ // Assert
+ Assert.That(regex1, Is.SameAs(regex2));
+ }
+
+ [Test]
+ public void GetOrCreateCached_WithDifferentPatterns_ShouldReturnDifferentInstances()
+ {
+ // Arrange & Act
+ var regex1 = RegexHelper.GetOrCreateCached("test1");
+ var regex2 = RegexHelper.GetOrCreateCached("test2");
+
+ // Assert
+ Assert.That(regex1, Is.Not.SameAs(regex2));
+ }
+
+ [Test]
+ public void GetOrCreateCached_WithDifferentOptions_ShouldReturnDifferentInstances()
+ {
+ // Arrange & Act
+ var regex1 = RegexHelper.GetOrCreateCached("test", RegexOptions.None);
+ var regex2 = RegexHelper.GetOrCreateCached("test", RegexOptions.IgnoreCase);
+
+ // Assert
+ Assert.That(regex1, Is.Not.SameAs(regex2));
+ }
+
+ [Test]
+ public void GetOrCreateCached_ShouldCacheUpToMaxSize()
+ {
+ // Arrange - Create more patterns than cache size
+ var cacheSize = 100;
+
+ // Act - Fill the cache
+ for (int i = 0; i < cacheSize; i++)
+ {
+ RegexHelper.GetOrCreateCached($"pattern{i}");
+ }
+
+ // Assert - Cache should be at max size
+ Assert.That(RegexHelper.CacheSize, Is.EqualTo(cacheSize));
+
+ // Act - Add more to trigger eviction
+ RegexHelper.GetOrCreateCached("pattern_overflow");
+
+ // Assert - Cache should have evicted some entries
+ Assert.That(RegexHelper.CacheSize, Is.LessThanOrEqualTo(cacheSize));
+ }
+
+ [Test]
+ public void IsValidPattern_WithValidPattern_ShouldReturnTrue()
+ {
+ // Arrange
+ var pattern = @"\d{4}-\d{2}-\d{2}";
+
+ // Act
+ var result = RegexHelper.IsValidPattern(pattern, out var error);
+
+ // Assert
+ Assert.That(result, Is.True);
+ Assert.That(error, Is.Null);
+ }
+
+ [Test]
+ public void IsValidPattern_WithInvalidPattern_ShouldReturnFalse()
+ {
+ // Arrange
+ var pattern = "[invalid";
+
+ // Act
+ var result = RegexHelper.IsValidPattern(pattern, out var error);
+
+ // Assert
+ Assert.That(result, Is.False);
+ Assert.That(error, Is.Not.Null);
+ Assert.That(error, Does.Contain("parsing"));
+ }
+
+ [Test]
+ public void IsValidPattern_WithNullPattern_ShouldReturnFalse()
+ {
+ // Act
+ var result = RegexHelper.IsValidPattern(null!, out var error);
+
+ // Assert
+ Assert.That(result, Is.False);
+ Assert.That(error, Is.Not.Null);
+ }
+
+ [Test]
+ public void IsValidPattern_WithEmptyPattern_ShouldReturnFalse()
+ {
+ // Act
+ var result = RegexHelper.IsValidPattern(string.Empty, out var error);
+
+ // Assert
+ Assert.That(result, Is.False);
+ Assert.That(error, Is.Not.Null);
+ }
+
+ [Test]
+ public void ClearCache_ShouldRemoveAllCachedRegex()
+ {
+ // Arrange
+ RegexHelper.GetOrCreateCached("test1");
+ RegexHelper.GetOrCreateCached("test2");
+ RegexHelper.GetOrCreateCached("test3");
+ Assert.That(RegexHelper.CacheSize, Is.GreaterThan(0));
+
+ // Act
+ RegexHelper.ClearCache();
+
+ // Assert
+ Assert.That(RegexHelper.CacheSize, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void CachedRegex_ShouldWorkCorrectly()
+ {
+ // Arrange
+ var pattern = @"(\d{4})-(\d{2})-(\d{2})";
+ var input = "Date: 2025-11-11";
+ var regex = RegexHelper.GetOrCreateCached(pattern);
+
+ // Act
+ var match = regex.Match(input);
+
+ // Assert
+ Assert.That(match.Success, Is.True);
+ Assert.That(match.Groups[1].Value, Is.EqualTo("2025"));
+ Assert.That(match.Groups[2].Value, Is.EqualTo("11"));
+ Assert.That(match.Groups[3].Value, Is.EqualTo("11"));
+ }
+
+ [Test]
+ public void CachedRegex_WithIgnoreCase_ShouldMatchCaseInsensitively()
+ {
+ // Arrange
+ var pattern = "test";
+ var regex = RegexHelper.GetOrCreateCached(pattern, RegexOptions.IgnoreCase);
+
+ // Act
+ var match1 = regex.IsMatch("TEST");
+ var match2 = regex.IsMatch("Test");
+ var match3 = regex.IsMatch("test");
+
+ // Assert
+ Assert.That(match1, Is.True);
+ Assert.That(match2, Is.True);
+ Assert.That(match3, Is.True);
+ }
+
+ [Test]
+ public void CachedRegex_ShouldHaveTimeout()
+ {
+ // Arrange & Act
+ var regex = RegexHelper.GetOrCreateCached("test");
+
+ // Assert
+ Assert.That(regex.MatchTimeout, Is.EqualTo(RegexHelper.DefaultTimeout));
+ }
+
+ [Test]
+ public void DefaultTimeout_ShouldBeTwoSeconds()
+ {
+ // Assert
+ Assert.That(RegexHelper.DefaultTimeout, Is.EqualTo(TimeSpan.FromSeconds(2)));
+ }
+
+ [TestCase(@"\d+", "123", true)]
+ [TestCase(@"\d+", "abc", false)]
+ [TestCase(@"[A-Z]+", "ABC", true)]
+ [TestCase(@"[A-Z]+", "abc", false)]
+ [TestCase(@"^\w+@\w+\.\w+$", "test@example.com", true)]
+ [TestCase(@"^\w+@\w+\.\w+$", "invalid-email", false)]
+ public void CreateSafeRegex_CommonPatterns_ShouldWorkCorrectly(string pattern, string input, bool expectedMatch)
+ {
+ // Arrange
+ var regex = RegexHelper.CreateSafeRegex(pattern);
+
+ // Act
+ var result = regex.IsMatch(input);
+
+ // Assert
+ Assert.That(result, Is.EqualTo(expectedMatch));
+ }
+}
diff --git a/src/LogExpert.Tests/LogExpert.Tests.csproj b/src/LogExpert.Tests/LogExpert.Tests.csproj
index 71fdb0dc..ca38ba90 100644
--- a/src/LogExpert.Tests/LogExpert.Tests.csproj
+++ b/src/LogExpert.Tests/LogExpert.Tests.csproj
@@ -1,7 +1,7 @@
- net8.0-windows
+ net10.0-windows
true
true
diff --git a/src/LogExpert.UI/Controls/BufferedDataGridView.cs b/src/LogExpert.UI/Controls/BufferedDataGridView.cs
index d2f557da..5d5d4a83 100644
--- a/src/LogExpert.UI/Controls/BufferedDataGridView.cs
+++ b/src/LogExpert.UI/Controls/BufferedDataGridView.cs
@@ -6,6 +6,7 @@
using LogExpert.UI.Controls;
using NLog;
+using System.ComponentModel;
namespace LogExpert.Dialogs;
@@ -61,8 +62,10 @@ public Graphics Buffer
}
*/
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public ContextMenuStrip EditModeMenuStrip { get; set; }
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool PaintWithOverlays { get; set; }
#endregion
diff --git a/src/LogExpert.UI/Controls/ColorComboBox.cs b/src/LogExpert.UI/Controls/ColorComboBox.cs
index ecafb2a1..af12e836 100644
--- a/src/LogExpert.UI/Controls/ColorComboBox.cs
+++ b/src/LogExpert.UI/Controls/ColorComboBox.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Runtime.Versioning;
@@ -49,6 +50,7 @@ public ColorComboBox ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Color CustomColor
{
get => _customColor;
diff --git a/src/LogExpert.UI/Controls/DateTimeDragControl.cs b/src/LogExpert.UI/Controls/DateTimeDragControl.cs
index dc6ca00d..6b52da7e 100644
--- a/src/LogExpert.UI/Controls/DateTimeDragControl.cs
+++ b/src/LogExpert.UI/Controls/DateTimeDragControl.cs
@@ -77,16 +77,19 @@ public DateTimeDragControl ()
///
/// Gets or sets the minimum allowable date and time value.
///
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public DateTime MinDateTime { get; set; } = DateTime.MinValue;
///
/// Gets or sets the maximum allowable date and time value.
///
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public DateTime MaxDateTime { get; set; } = DateTime.MaxValue;
///
/// Gets or sets the orientation for drag operations.
///
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public DragOrientations DragOrientation
{
get => _dragOrientation;
@@ -100,11 +103,13 @@ public DragOrientations DragOrientation
///
/// Gets or sets the color used to highlight an element when the mouse hovers over it.
///
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Color HoverColor { get; set; }
///
/// Gets or sets the date and time value, adjusted to exclude milliseconds.
///
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public DateTime DateTime
{
get => _dateTime.Subtract(TimeSpan.FromMilliseconds(_dateTime.Millisecond));
diff --git a/src/LogExpert.UI/Controls/KnobControl.cs b/src/LogExpert.UI/Controls/KnobControl.cs
index c816e4ad..5701a9e4 100644
--- a/src/LogExpert.UI/Controls/KnobControl.cs
+++ b/src/LogExpert.UI/Controls/KnobControl.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Runtime.Versioning;
namespace LogExpert.UI.Controls;
@@ -36,10 +37,13 @@ public KnobControl ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int MinValue { get; set; }
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int MaxValue { get; set; }
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int Value
{
get => _value;
@@ -53,6 +57,7 @@ public int Value
public int Range => MaxValue - MinValue;
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int DragSensitivity { get; set; } = 3;
#endregion
diff --git a/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs b/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs
index 417830cc..f87807f0 100644
--- a/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs
+++ b/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs
@@ -319,6 +319,7 @@ public LogWindow (LogTabWindow.LogTabWindow parent, string fileName, bool isTemp
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Color BookmarkColor { get; set; } = Color.FromArgb(165, 200, 225);
public ILogLineColumnizer CurrentColumnizer
@@ -335,6 +336,7 @@ private set
}
[SupportedOSPlatform("windows")]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool ShowBookmarkBubbles
{
get => _guiStateArgs.ShowBookmarkBubbles;
@@ -351,6 +353,7 @@ public bool ShowBookmarkBubbles
public string FileName { get; private set; }
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string SessionFileName { get; set; }
public bool IsMultiFile
@@ -363,8 +366,10 @@ public bool IsMultiFile
private readonly IConfigManager ConfigManager;
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string TempTitleName { get; set; } = string.Empty;
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
internal FilterPipe FilterPipe { get; set; }
public string Title => IsTempFile
@@ -373,12 +378,15 @@ public bool IsMultiFile
public ColumnizerCallback ColumnizerCallbackObject { get; }
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool ForcePersistenceLoading { get; set; }
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string ForcedPersistenceFileName { get; set; }
public Preferences Preferences => _parentLogTabWin.Preferences;
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string GivenFileName { get; set; }
public TimeSyncList TimeSyncList { get; private set; }
diff --git a/src/LogExpert.UI/Controls/LogWindow/PatternWindow.cs b/src/LogExpert.UI/Controls/LogWindow/PatternWindow.cs
index 5a5cfe3e..1347a645 100644
--- a/src/LogExpert.UI/Controls/LogWindow/PatternWindow.cs
+++ b/src/LogExpert.UI/Controls/LogWindow/PatternWindow.cs
@@ -1,4 +1,5 @@
using System.Globalization;
+using System.ComponentModel;
using System.Runtime.Versioning;
using LogExpert.Core.Classes;
@@ -57,24 +58,28 @@ public PatternWindow (LogWindow logWindow)
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int Fuzzy
{
set => fuzzyKnobControl.Value = value;
get => fuzzyKnobControl.Value;
}
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int MaxDiff
{
set => maxDiffKnobControl.Value = value;
get => maxDiffKnobControl.Value;
}
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int MaxMisses
{
set => maxMissesKnobControl.Value = value;
get => maxMissesKnobControl.Value;
}
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int Weight
{
set => weigthKnobControl.Value = value;
diff --git a/src/LogExpert.UI/Controls/LogWindow/TimeSpreadigControl.cs b/src/LogExpert.UI/Controls/LogWindow/TimeSpreadigControl.cs
index 2a13a311..3a8cd7f9 100644
--- a/src/LogExpert.UI/Controls/LogWindow/TimeSpreadigControl.cs
+++ b/src/LogExpert.UI/Controls/LogWindow/TimeSpreadigControl.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Globalization;
using System.Runtime.Versioning;
@@ -58,8 +59,10 @@ public TimeSpreadingControl ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool ReverseAlpha { get; set; }
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
internal TimeSpreadCalculator TimeSpreadCalc
{
get => _timeSpreadCalc;
diff --git a/src/LogExpert.UI/Dialogs/BookmarkCommentDlg.cs b/src/LogExpert.UI/Dialogs/BookmarkCommentDlg.cs
index ae55621f..cecbdbf4 100644
--- a/src/LogExpert.UI/Dialogs/BookmarkCommentDlg.cs
+++ b/src/LogExpert.UI/Dialogs/BookmarkCommentDlg.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Runtime.Versioning;
namespace LogExpert.Dialogs;
@@ -31,6 +32,7 @@ private void ApplyResources ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string Comment
{
set => textBoxComment.Text = value;
diff --git a/src/LogExpert.UI/Dialogs/BookmarkWindow.cs b/src/LogExpert.UI/Dialogs/BookmarkWindow.cs
index 8124461f..d8e451ac 100644
--- a/src/LogExpert.UI/Dialogs/BookmarkWindow.cs
+++ b/src/LogExpert.UI/Dialogs/BookmarkWindow.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Runtime.Versioning;
using LogExpert.Core.Config;
@@ -64,11 +65,13 @@ private void ApplyResources ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool LineColumnVisible
{
set => bookmarkDataGridView.Columns[2].Visible = value;
}
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool ShowBookmarkCommentColumn
{
get => checkBoxCommentColumn.Checked;
diff --git a/src/LogExpert.UI/Dialogs/ChooseIconDlg.cs b/src/LogExpert.UI/Dialogs/ChooseIconDlg.cs
index f8be593b..5b529089 100644
--- a/src/LogExpert.UI/Dialogs/ChooseIconDlg.cs
+++ b/src/LogExpert.UI/Dialogs/ChooseIconDlg.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Runtime.Versioning;
using LogExpert.UI.Extensions;
@@ -37,8 +38,10 @@ private void ApplyResources ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string FileName { get; set; }
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int IconIndex { get; set; }
#endregion
diff --git a/src/LogExpert.UI/Dialogs/Eminus/EminusConfigDlg.cs b/src/LogExpert.UI/Dialogs/Eminus/EminusConfigDlg.cs
index ffb56c4d..35c22716 100644
--- a/src/LogExpert.UI/Dialogs/Eminus/EminusConfigDlg.cs
+++ b/src/LogExpert.UI/Dialogs/Eminus/EminusConfigDlg.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Globalization;
using System.Runtime.Versioning;
@@ -43,6 +44,7 @@ private void LoadResources ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public EminusConfig Config { get; set; }
#endregion
diff --git a/src/LogExpert.UI/Dialogs/HighlightDialog.cs b/src/LogExpert.UI/Dialogs/HighlightDialog.cs
index ce596646..1b418605 100644
--- a/src/LogExpert.UI/Dialogs/HighlightDialog.cs
+++ b/src/LogExpert.UI/Dialogs/HighlightDialog.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Globalization;
using System.Runtime.Versioning;
using System.Security;
@@ -5,6 +6,7 @@
using LogExpert.Core.Classes.Highlight;
using LogExpert.Core.Entities;
+using LogExpert.Core.Helpers;
using LogExpert.Core.Interface;
using LogExpert.UI.Controls;
using LogExpert.UI.Dialogs;
@@ -96,6 +98,7 @@ private void ApplyResources ()
#region Properties / Indexers
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public List HighlightGroupList
{
get => _highlightGroupList;
@@ -110,8 +113,10 @@ public List HighlightGroupList
}
}
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public IList KeywordActionList { get; set; }
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string PreSelectedGroupName { get; set; }
private bool IsDirty => btnApply.Image == _applyButtonImage;
@@ -571,7 +576,11 @@ private void CheckRegex ()
throw new ArgumentException(Resources.HighlightDialog_RegexError);
}
- _ = Regex.IsMatch(string.Empty, textBoxSearchString.Text);
+ // Use RegexHelper for safer validation with timeout protection
+ if (!RegexHelper.IsValidPattern(textBoxSearchString.Text, out var error))
+ {
+ throw new ArgumentException(error ?? Resources.HighlightDialog_RegexError);
+ }
}
}
diff --git a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs
index 24f2eb0f..bbf9db26 100644
--- a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs
+++ b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs
@@ -211,6 +211,7 @@ public LogTabWindow (string[] fileNames, int instanceNumber, bool showInstanceNu
#region Properties
[SupportedOSPlatform("windows")]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public LogWindow.LogWindow CurrentLogWindow
{
get => _currentLogWindow;
@@ -228,7 +229,9 @@ public LogWindow.LogWindow CurrentLogWindow
// get { return ConfigManager.Settings; }
//}
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public ILogExpertProxy LogExpertProxy { get; set; }
+
public IConfigManager ConfigManager { get; }
#endregion
diff --git a/src/LogExpert.UI/Dialogs/MultiFileMaskDialog.cs b/src/LogExpert.UI/Dialogs/MultiFileMaskDialog.cs
index 13edef11..316c1567 100644
--- a/src/LogExpert.UI/Dialogs/MultiFileMaskDialog.cs
+++ b/src/LogExpert.UI/Dialogs/MultiFileMaskDialog.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Runtime.Versioning;
namespace LogExpert.UI.Dialogs;
@@ -44,8 +45,10 @@ private void ApplyResources ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string FileNamePattern { get; set; }
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int MaxDays { get; set; }
#endregion
diff --git a/src/LogExpert.UI/Dialogs/OpenUriDialog.cs b/src/LogExpert.UI/Dialogs/OpenUriDialog.cs
index 0172d8ef..49230368 100644
--- a/src/LogExpert.UI/Dialogs/OpenUriDialog.cs
+++ b/src/LogExpert.UI/Dialogs/OpenUriDialog.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Runtime.Versioning;
namespace LogExpert.UI.Dialogs;
@@ -32,6 +33,7 @@ public OpenUriDialog ()
//TODO Convert to System.Uri
public string Uri => cmbUri.Text;
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public IList UriHistory { get; set; }
#endregion
diff --git a/src/LogExpert.UI/Dialogs/ParamRequesterDialog.cs b/src/LogExpert.UI/Dialogs/ParamRequesterDialog.cs
index a8ec5cc7..50df740a 100644
--- a/src/LogExpert.UI/Dialogs/ParamRequesterDialog.cs
+++ b/src/LogExpert.UI/Dialogs/ParamRequesterDialog.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Runtime.Versioning;
namespace LogExpert.Dialogs;
@@ -47,6 +48,7 @@ private void ApplyResources ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string ParamValue { get; set; }
#endregion
diff --git a/src/LogExpert.UI/Dialogs/ProjectLoadDlg.cs b/src/LogExpert.UI/Dialogs/ProjectLoadDlg.cs
index 1b77b0a6..304b2e3d 100644
--- a/src/LogExpert.UI/Dialogs/ProjectLoadDlg.cs
+++ b/src/LogExpert.UI/Dialogs/ProjectLoadDlg.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Runtime.Versioning;
using LogExpert.Core.Enums;
@@ -41,6 +42,7 @@ private void ApplyResources ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public ProjectLoadDlgResult ProjectLoadResult { get; set; } = ProjectLoadDlgResult.Cancel;
#endregion
diff --git a/src/LogExpert.UI/Dialogs/RegexHelperDialog.cs b/src/LogExpert.UI/Dialogs/RegexHelperDialog.cs
index 9d17ba78..9770072b 100644
--- a/src/LogExpert.UI/Dialogs/RegexHelperDialog.cs
+++ b/src/LogExpert.UI/Dialogs/RegexHelperDialog.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
@@ -47,6 +48,7 @@ private void ApplyResources ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool CaseSensitive
{
get => _caseSensitive;
@@ -57,14 +59,17 @@ public bool CaseSensitive
}
}
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string Pattern
{
get => comboBoxRegex.Text;
set => comboBoxRegex.Text = value;
}
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public List ExpressionHistoryList { get; set; } = [];
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public List TesttextHistoryList { get; set; } = [];
#endregion
diff --git a/src/LogExpert.UI/Dialogs/SearchDialog.cs b/src/LogExpert.UI/Dialogs/SearchDialog.cs
index 62f6ba13..71e64638 100644
--- a/src/LogExpert.UI/Dialogs/SearchDialog.cs
+++ b/src/LogExpert.UI/Dialogs/SearchDialog.cs
@@ -1,7 +1,9 @@
+using System.ComponentModel;
using System.Globalization;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
+using LogExpert.Core.Helpers;
using LogExpert.Entities;
using LogExpert.UI.Dialogs;
@@ -56,6 +58,7 @@ private void ApplyResources ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public SearchParams SearchParams { get; set; } = new();
#endregion
@@ -131,7 +134,11 @@ private void OnButtonOkClick (object sender, EventArgs e)
throw new ArgumentException(Resources.SearchDialog_UI_Error_SearchTextEmpty);
}
- _ = Regex.IsMatch("", comboBoxSearchFor.Text);
+ // Use RegexHelper for safer validation with timeout protection
+ if (!RegexHelper.IsValidPattern(comboBoxSearchFor.Text, out var error))
+ {
+ throw new ArgumentException($"Invalid regex pattern: {error}");
+ }
}
SearchParams.SearchText = comboBoxSearchFor.Text;
diff --git a/src/LogExpert.UI/Dialogs/TabRenameDialog.cs b/src/LogExpert.UI/Dialogs/TabRenameDialog.cs
index a974f149..8dd1a382 100644
--- a/src/LogExpert.UI/Dialogs/TabRenameDialog.cs
+++ b/src/LogExpert.UI/Dialogs/TabRenameDialog.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Runtime.Versioning;
namespace LogExpert.UI.Dialogs;
@@ -35,6 +36,7 @@ private void ApplyResources ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string TabName
{
get => textBoxTabName.Text;
diff --git a/src/LogExpert.UI/Dialogs/ToolArgsDialog.cs b/src/LogExpert.UI/Dialogs/ToolArgsDialog.cs
index 2d3148b6..c1eefe87 100644
--- a/src/LogExpert.UI/Dialogs/ToolArgsDialog.cs
+++ b/src/LogExpert.UI/Dialogs/ToolArgsDialog.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.Runtime.Versioning;
using LogExpert.UI.Controls.LogTabWindow;
@@ -49,6 +50,7 @@ private void ApplyResources ()
#region Properties
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string Arg { get; set; }
#endregion
diff --git a/src/LogExpert.UI/LogExpert.UI.csproj b/src/LogExpert.UI/LogExpert.UI.csproj
index 4d401ccd..d3b2a4d1 100644
--- a/src/LogExpert.UI/LogExpert.UI.csproj
+++ b/src/LogExpert.UI/LogExpert.UI.csproj
@@ -4,7 +4,7 @@
true
true
true
- net8.0-windows
+ net10.0-windows
true
diff --git a/src/LogExpert/LogExpert.csproj b/src/LogExpert/LogExpert.csproj
index 20ea2108..f63f4f8d 100644
--- a/src/LogExpert/LogExpert.csproj
+++ b/src/LogExpert/LogExpert.csproj
@@ -9,7 +9,7 @@
Auto
$(SolutionDir)..\bin\$(Configuration)
WinExe
- net8.0-windows
+ net10.0-windows
False
diff --git a/src/LogExpert/Program.cs b/src/LogExpert/Program.cs
index 8ce3a1a4..7db33f95 100644
--- a/src/LogExpert/Program.cs
+++ b/src/LogExpert/Program.cs
@@ -44,6 +44,9 @@ internal static class Program
[SupportedOSPlatform("windows")]
private static void Main (string[] args)
{
+ // Set global regex timeout to prevent DoS attacks from catastrophic backtracking
+ AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT", TimeSpan.FromSeconds(2));
+
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
Application.ThreadException += Application_ThreadException;
@@ -149,7 +152,12 @@ or ArgumentException
_logger.Error($"IpcClientChannel error: {ex}");
errMsg = ex;
counter--;
- Thread.Sleep(500);
+
+ // Use Task.Delay instead of Thread.Sleep for non-blocking wait
+ if (counter > 0)
+ {
+ Task.Delay(500).Wait();
+ }
}
}
diff --git a/src/PluginRegistry/LogExpert.PluginRegistry.csproj b/src/PluginRegistry/LogExpert.PluginRegistry.csproj
index cf09bc7b..7ffeb844 100644
--- a/src/PluginRegistry/LogExpert.PluginRegistry.csproj
+++ b/src/PluginRegistry/LogExpert.PluginRegistry.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
diff --git a/src/RegexColumnizer.UnitTests/RegexColumnizer.UnitTests.csproj b/src/RegexColumnizer.UnitTests/RegexColumnizer.UnitTests.csproj
index be3904ad..5ff20bb2 100644
--- a/src/RegexColumnizer.UnitTests/RegexColumnizer.UnitTests.csproj
+++ b/src/RegexColumnizer.UnitTests/RegexColumnizer.UnitTests.csproj
@@ -1,6 +1,6 @@
- net8.0-windows
+ net10.0-windows
true
diff --git a/src/RegexColumnizer/RegexColumnizer.cs b/src/RegexColumnizer/RegexColumnizer.cs
index bc66b146..0febb7e5 100644
--- a/src/RegexColumnizer/RegexColumnizer.cs
+++ b/src/RegexColumnizer/RegexColumnizer.cs
@@ -1,4 +1,5 @@
-using LogExpert;
+using LogExpert;
+using LogExpert.Core.Helpers;
using System;
using System.IO;
using System.Linq;
@@ -188,7 +189,7 @@ public void Init(RegexColumnizerConfig config)
try
{
- Regex = new Regex(Config.Expression, RegexOptions.Compiled);
+ Regex = RegexHelper.GetOrCreateCached(Config.Expression, RegexOptions.Compiled);
var skip = Regex.GetGroupNames().Length == 1 ? 0 : 1;
columns = Regex.GetGroupNames().Skip(skip).ToArray();
}
diff --git a/src/RegexColumnizer/RegexColumnizer.csproj b/src/RegexColumnizer/RegexColumnizer.csproj
index ecd68455..b3244bd0 100644
--- a/src/RegexColumnizer/RegexColumnizer.csproj
+++ b/src/RegexColumnizer/RegexColumnizer.csproj
@@ -1,6 +1,6 @@
- net8.0-windows
+ net10.0-windows
true
true
@@ -10,6 +10,7 @@
+
diff --git a/src/RegexColumnizer/RegexColumnizer.manifest.json b/src/RegexColumnizer/RegexColumnizer.manifest.json
new file mode 100644
index 00000000..8c21171a
--- /dev/null
+++ b/src/RegexColumnizer/RegexColumnizer.manifest.json
@@ -0,0 +1,20 @@
+{
+ "name": "RegexColumnizer",
+ "version": "1.0.0",
+ "author": "LogExpert Team",
+ "description": "Highly configurable columnizer using regular expressions to parse log lines into columns",
+ "apiVersion": "1.0",
+ "requires": {
+ "logExpert": ">=1.10.0",
+ "dotnet": ">=8.0"
+ },
+ "permissions": [
+ "filesystem:read",
+ "config:read",
+ "config:write"
+ ],
+ "dependencies": {},
+ "main": "RegexColumnizer.dll",
+ "url": "https://github.com/LogExperts/LogExpert",
+ "license": "MIT"
+}
diff --git a/src/RegexColumnizer/RegexColumnizerConfigDialog.cs b/src/RegexColumnizer/RegexColumnizerConfigDialog.cs
index e9cee9ea..58519135 100644
--- a/src/RegexColumnizer/RegexColumnizerConfigDialog.cs
+++ b/src/RegexColumnizer/RegexColumnizerConfigDialog.cs
@@ -1,3 +1,12 @@
+using System;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
+
+using LogExpert.Core.Helpers;
using System.Data;
using System.Text.RegularExpressions;
@@ -30,6 +39,7 @@ private void ApplyResources ()
btnCancel.Text = Resources.RegexColumnizerConfigDialog_UI_Button_Cancel;
}
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public RegexColumnizerConfig Config { get; set; }
private void OnBtnOkClick (object sender, EventArgs e)
@@ -59,7 +69,8 @@ private bool Check ()
try
{
- Regex regex = new(tbExpression.Text);
+ // Use RegexHelper for safe regex creation with timeout protection
+ Regex regex = RegexHelper.CreateSafeRegex(tbExpression.Text);
var groupNames = regex.GetGroupNames();
var offset = groupNames.Length > 1 ? 1 : 0;
diff --git a/src/SftpFileSystemx64/LoginDialog.cs b/src/SftpFileSystemx64/LoginDialog.cs
index ec4fbc8a..dd51cd44 100644
--- a/src/SftpFileSystemx64/LoginDialog.cs
+++ b/src/SftpFileSystemx64/LoginDialog.cs
@@ -1,3 +1,9 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+using System.ComponentModel;
+
namespace SftpFileSystem;
public partial class LoginDialog : Form
@@ -59,6 +65,7 @@ private void ApplyResources ()
public string Password { get; private set; }
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string Username
{
get => _username;
diff --git a/src/SftpFileSystemx64/SftpFileSystemx64.csproj b/src/SftpFileSystemx64/SftpFileSystemx64.csproj
index dd0197fb..cb1aaa9f 100644
--- a/src/SftpFileSystemx64/SftpFileSystemx64.csproj
+++ b/src/SftpFileSystemx64/SftpFileSystemx64.csproj
@@ -1,6 +1,6 @@
- net8.0-windows
+ net10.0-windows
true
SftpFileSystem
diff --git a/src/SftpFileSystemx86/SftpFileSystemx86.csproj b/src/SftpFileSystemx86/SftpFileSystemx86.csproj
index 7f60bbd8..f3f5dfe9 100644
--- a/src/SftpFileSystemx86/SftpFileSystemx86.csproj
+++ b/src/SftpFileSystemx86/SftpFileSystemx86.csproj
@@ -1,6 +1,6 @@
- net8.0-windows
+ net10.0-windows
true
SftpFileSystem