diff --git a/Abblix.Utils.UnitTests/SanitizedTests.cs b/Abblix.Utils.UnitTests/SanitizedTests.cs
new file mode 100644
index 00000000..099144ed
--- /dev/null
+++ b/Abblix.Utils.UnitTests/SanitizedTests.cs
@@ -0,0 +1,184 @@
+// Abblix OIDC Server Library
+// Copyright (c) Abblix LLP. All rights reserved.
+//
+// DISCLAIMER: This software is provided 'as-is', without any express or implied
+// warranty. Use at your own risk. Abblix LLP is not liable for any damages
+// arising from the use of this software.
+//
+// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed
+// in any form outside of the official GitHub repository at:
+// https://github.com/Abblix/OIDC.Server. All development and modifications
+// must occur within the official repository and are managed solely by Abblix LLP.
+//
+// Unauthorized use, modification, or distribution of this software is strictly
+// prohibited and may be subject to legal action.
+//
+// For full licensing terms, please visit:
+//
+// https://oidc.abblix.com/license
+//
+// CONTACT: For license inquiries or permissions, contact Abblix LLP at
+// info@abblix.com
+
+namespace Abblix.Utils.UnitTests;
+
+using Xunit;
+
+///
+/// Contains unit tests for the struct to ensure it correctly sanitizes input strings.
+///
+public class SanitizedTests
+{
+ ///
+ /// Tests that the original string is returned when no special characters are present.
+ ///
+ [Fact]
+ public void ToString_ShouldReturnOriginalString_WhenNoSpecialCharacters()
+ {
+ const string input = "HelloWorld";
+ var sanitizedValue = new Sanitized(input);
+ Assert.Equal(input, sanitizedValue.ToString());
+ }
+
+ ///
+ /// Tests that control characters are removed from the string.
+ ///
+ [Fact]
+ public void ToString_ShouldRemoveControlCharacters()
+ {
+ const string input = "Hello\x01\x02\x03World";
+ const string expected = "HelloWorld";
+ var sanitizedValue = new Sanitized(input);
+ Assert.Equal(expected, sanitizedValue.ToString());
+ }
+
+ ///
+ /// Tests that newline characters are replaced with their escaped representation.
+ ///
+ [Fact]
+ public void ToString_ShouldReplaceNewline()
+ {
+ const string input = "Hello\nWorld";
+ const string expected = "Hello\\nWorld";
+ var sanitizedValue = new Sanitized(input);
+ Assert.Equal(expected, sanitizedValue.ToString());
+ }
+
+ ///
+ /// Tests that carriage return characters are replaced with their escaped representation.
+ ///
+ [Fact]
+ public void ToString_ShouldReplaceCarriageReturn()
+ {
+ const string input = "Hello\rWorld";
+ const string expected = "Hello\\rWorld";
+ var sanitizedValue = new Sanitized(input);
+ Assert.Equal(expected, sanitizedValue.ToString());
+ }
+
+ ///
+ /// Tests that tab characters are replaced with their escaped representation.
+ ///
+ [Fact]
+ public void ToString_ShouldReplaceTab()
+ {
+ const string input = "Hello\tWorld";
+ const string expected = "Hello\\tWorld";
+ var sanitizedValue = new Sanitized(input);
+ Assert.Equal(expected, sanitizedValue.ToString());
+ }
+
+ ///
+ /// Tests that double quote characters are replaced with their escaped representation.
+ ///
+ [Fact]
+ public void ToString_ShouldReplaceDoubleQuote()
+ {
+ const string input = "Hello\"World";
+ const string expected = "Hello\\\"World";
+ var sanitizedValue = new Sanitized(input);
+ Assert.Equal(expected, sanitizedValue.ToString());
+ }
+
+ ///
+ /// Tests that single quote characters are replaced with their escaped representation.
+ ///
+ [Fact]
+ public void ToString_ShouldReplaceSingleQuote()
+ {
+ const string input = "Hello'World";
+ const string expected = "Hello\\'World";
+ var sanitizedValue = new Sanitized(input);
+ Assert.Equal(expected, sanitizedValue.ToString());
+ }
+
+ ///
+ /// Tests that backslash characters are replaced with their escaped representation.
+ ///
+ [Fact]
+ public void ToString_ShouldReplaceBackslash()
+ {
+ const string input = "Hello\\World";
+ const string expected = "Hello\\\\World";
+ var sanitizedValue = new Sanitized(input);
+ Assert.Equal(expected, sanitizedValue.ToString());
+ }
+
+ ///
+ /// Tests that comma characters are replaced with their escaped representation.
+ ///
+ [Fact]
+ public void ToString_ShouldReplaceComma()
+ {
+ const string input = "Hello,World";
+ const string expected = "Hello\\,World";
+ var sanitizedValue = new Sanitized(input);
+ Assert.Equal(expected, sanitizedValue.ToString());
+ }
+
+ ///
+ /// Tests that semicolon characters are replaced with their escaped representation.
+ ///
+ [Fact]
+ public void ToString_ShouldReplaceSemicolon()
+ {
+ const string input = "Hello;World";
+ const string expected = "Hello\\;World";
+ var sanitizedValue = new Sanitized(input);
+ Assert.Equal(expected, sanitizedValue.ToString());
+ }
+
+ ///
+ /// Tests that a null input returns null.
+ ///
+ [Fact]
+ public void ToString_ShouldHandleNullInput()
+ {
+ const string? input = null;
+ var sanitizedValue = new Sanitized(input);
+ Assert.Null(sanitizedValue.ToString());
+ }
+
+ ///
+ /// Tests that an empty string remains unchanged.
+ ///
+ [Fact]
+ public void ToString_ShouldHandleEmptyString()
+ {
+ const string input = "";
+ var sanitizedValue = new Sanitized(input);
+ Assert.Equal(input, sanitizedValue.ToString());
+ }
+
+ ///
+ /// Tests that a string with only control characters is sanitized to an empty string.
+ ///
+ [Fact]
+ public void ToString_ShouldHandleStringWithOnlyControlCharacters()
+ {
+ const string input = "\x01\x02\x03";
+ const string expected = "";
+ var sanitizedValue = new Sanitized(input);
+ Assert.Equal(expected, sanitizedValue.ToString());
+ }
+}
diff --git a/Abblix.Utils/Sanitized.cs b/Abblix.Utils/Sanitized.cs
new file mode 100644
index 00000000..1c0f7927
--- /dev/null
+++ b/Abblix.Utils/Sanitized.cs
@@ -0,0 +1,105 @@
+// Abblix OIDC Server Library
+// Copyright (c) Abblix LLP. All rights reserved.
+//
+// DISCLAIMER: This software is provided 'as-is', without any express or implied
+// warranty. Use at your own risk. Abblix LLP is not liable for any damages
+// arising from the use of this software.
+//
+// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed
+// in any form outside of the official GitHub repository at:
+// https://github.com/Abblix/OIDC.Server. All development and modifications
+// must occur within the official repository and are managed solely by Abblix LLP.
+//
+// Unauthorized use, modification, or distribution of this software is strictly
+// prohibited and may be subject to legal action.
+//
+// For full licensing terms, please visit:
+//
+// https://oidc.abblix.com/license
+//
+// CONTACT: For license inquiries or permissions, contact Abblix LLP at
+// info@abblix.com
+
+using System.Text;
+
+namespace Abblix.Utils;
+
+///
+/// A type that sanitizes a given string by removing control characters and escaping special characters
+/// to prevent log injection attacks.
+///
+public readonly record struct Sanitized
+{
+ ///
+ /// Initializes a new instance of the struct with the specified source string.
+ ///
+ /// The source string to be sanitized.
+ public Sanitized(string? source)
+ {
+ _source = source;
+ }
+
+ private readonly string? _source;
+
+ ///
+ /// Returns the sanitized string representation of the source string.
+ ///
+ /// A sanitized string with control characters removed and special characters escaped.
+ public override string? ToString()
+ {
+ if (string.IsNullOrEmpty(_source))
+ {
+ return _source;
+ }
+
+ StringBuilder? resultBuilder = null;
+ var source = _source;
+
+ for (var i = 0; i < _source.Length; i++)
+ {
+ var c = _source[i];
+
+ switch (c)
+ {
+ case '\n':
+ ReplaceTo("\\n", ref resultBuilder, source, i);
+ break;
+ case '\r':
+ ReplaceTo("\\r", ref resultBuilder, source, i);
+ break;
+ case '\t':
+ ReplaceTo("\\t", ref resultBuilder, source, i);
+ break;
+ case '\"':
+ ReplaceTo("\\\"", ref resultBuilder, source, i);
+ break;
+ case '\'':
+ ReplaceTo("\\'", ref resultBuilder, source, i);
+ break;
+ case '\\':
+ ReplaceTo(@"\\", ref resultBuilder, source, i);
+ break;
+ case ',':
+ ReplaceTo("\\,", ref resultBuilder, source, i);
+ break;
+ case ';':
+ ReplaceTo("\\;", ref resultBuilder, source, i);
+ break;
+ default:
+ if (0x00 <= c && c <= 0x1f || c == 0x7f)
+ ReplaceTo(null, ref resultBuilder, source, i);
+ else
+ resultBuilder?.Append(c);
+ break;
+ }
+ }
+
+ return resultBuilder != null ? resultBuilder.ToString() : _source;
+ }
+
+ private void ReplaceTo(string? replacement, ref StringBuilder? resultBuilder, string source, int i)
+ {
+ resultBuilder ??= new StringBuilder(source, 0, i, source.Length + (replacement?.Length ?? 0) - 1);
+ resultBuilder.Append(replacement);
+ }
+}