-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added Sanitized type to prevent CVE-117 during logging
- Loading branch information
1 parent
9b2cf31
commit a0dce13
Showing
2 changed files
with
289 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
|
||
/// <summary> | ||
/// Contains unit tests for the <see cref="Sanitized"/> struct to ensure it correctly sanitizes input strings. | ||
/// </summary> | ||
public class SanitizedTests | ||
{ | ||
/// <summary> | ||
/// Tests that the original string is returned when no special characters are present. | ||
/// </summary> | ||
[Fact] | ||
public void ToString_ShouldReturnOriginalString_WhenNoSpecialCharacters() | ||
{ | ||
const string input = "HelloWorld"; | ||
var sanitizedValue = new Sanitized(input); | ||
Assert.Equal(input, sanitizedValue.ToString()); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that control characters are removed from the string. | ||
/// </summary> | ||
[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()); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that newline characters are replaced with their escaped representation. | ||
/// </summary> | ||
[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()); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that carriage return characters are replaced with their escaped representation. | ||
/// </summary> | ||
[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()); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that tab characters are replaced with their escaped representation. | ||
/// </summary> | ||
[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()); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that double quote characters are replaced with their escaped representation. | ||
/// </summary> | ||
[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()); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that single quote characters are replaced with their escaped representation. | ||
/// </summary> | ||
[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()); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that backslash characters are replaced with their escaped representation. | ||
/// </summary> | ||
[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()); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that comma characters are replaced with their escaped representation. | ||
/// </summary> | ||
[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()); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that semicolon characters are replaced with their escaped representation. | ||
/// </summary> | ||
[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()); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that a null input returns null. | ||
/// </summary> | ||
[Fact] | ||
public void ToString_ShouldHandleNullInput() | ||
{ | ||
const string? input = null; | ||
var sanitizedValue = new Sanitized(input); | ||
Assert.Null(sanitizedValue.ToString()); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that an empty string remains unchanged. | ||
/// </summary> | ||
[Fact] | ||
public void ToString_ShouldHandleEmptyString() | ||
{ | ||
const string input = ""; | ||
var sanitizedValue = new Sanitized(input); | ||
Assert.Equal(input, sanitizedValue.ToString()); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that a string with only control characters is sanitized to an empty string. | ||
/// </summary> | ||
[Fact] | ||
public void ToString_ShouldHandleStringWithOnlyControlCharacters() | ||
{ | ||
const string input = "\x01\x02\x03"; | ||
const string expected = ""; | ||
var sanitizedValue = new Sanitized(input); | ||
Assert.Equal(expected, sanitizedValue.ToString()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
|
||
/// <summary> | ||
/// A type that sanitizes a given string by removing control characters and escaping special characters | ||
/// to prevent log injection attacks. | ||
/// </summary> | ||
public readonly record struct Sanitized | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="Sanitized"/> struct with the specified source string. | ||
/// </summary> | ||
/// <param name="source">The source string to be sanitized.</param> | ||
public Sanitized(string? source) | ||
{ | ||
_source = source; | ||
} | ||
|
||
private readonly string? _source; | ||
|
||
/// <summary> | ||
/// Returns the sanitized string representation of the source string. | ||
/// </summary> | ||
/// <returns>A sanitized string with control characters removed and special characters escaped.</returns> | ||
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); | ||
} | ||
} |