From ce73e58a878dcbf704957444b2a07bf798d34484 Mon Sep 17 00:00:00 2001 From: ntark Date: Tue, 25 Nov 2025 21:16:23 +0400 Subject: [PATCH 1/5] feat: add other primitive/base type support --- src/JsonDataMasking.Test/JsonMaskTests.cs | 66 ++++++++++++++++--- .../MockData/BasicTypesMock.cs | 24 +++++++ .../MockData/BasicTypesSensitiveMock.cs | 41 ++++++++++++ src/JsonDataMasking/Masks/JsonMask.cs | 48 ++++++++++---- 4 files changed, 156 insertions(+), 23 deletions(-) create mode 100644 src/JsonDataMasking.Test/MockData/BasicTypesMock.cs create mode 100644 src/JsonDataMasking.Test/MockData/BasicTypesSensitiveMock.cs diff --git a/src/JsonDataMasking.Test/JsonMaskTests.cs b/src/JsonDataMasking.Test/JsonMaskTests.cs index 7a47269..279a74a 100644 --- a/src/JsonDataMasking.Test/JsonMaskTests.cs +++ b/src/JsonDataMasking.Test/JsonMaskTests.cs @@ -108,16 +108,6 @@ public void MaskSensitiveData_UsesCustomMask_WhenHasAttributeWithCustomMaskChara Assert.Equal(expectedMask, maskedCustomerData.CreditCardSecurityCode); } - [Fact] - public void MaskSensitiveData_ThrowsNotSupportedException_WhenPropertyWithNotSupportedTypeHasAttribute() - { - // Arrange - var creditCard = new CreditCardMock { SecurityCode = 999 }; - - // Act and Assert - Assert.Throws(() => JsonMask.MaskSensitiveData(creditCard)); - } - [Fact] public void MaskSensitiveData_MasksUsingDefaultSize_WhenHasAttributeWithInvalidShowFirstAndLastRange() { @@ -373,5 +363,61 @@ public void MaskSensitiveData_ThrowsNotSupportedException_WhenPropertyIsArray() // Act and assert Assert.Throws(() => JsonMask.MaskSensitiveData(passcodes)); } + + [Fact] + public void MaskSensitiveData_MasksOtherBaseTypes() + { + // Arrange + var kink = new BasicTypesSensitiveMock(); + + // Act and Assert + var maskedKink = JsonMask.MaskSensitiveData(kink); + + // Assert + Assert.False(maskedKink.Bool); + Assert.Equal(0, maskedKink.Byte); + Assert.Equal(0, maskedKink.Sbyte); + Assert.Equal(0, maskedKink.Short); + Assert.Equal(0, maskedKink.Ushort); + Assert.Equal(0, maskedKink.Int); + Assert.Equal(0u, maskedKink.Uint); + Assert.Equal(0, maskedKink.Long); + Assert.Equal(0u, maskedKink.Ulong); + Assert.Equal(0, maskedKink.Float); + Assert.Equal(0, maskedKink.Double); + Assert.Equal(0, maskedKink.Decimal); + Assert.Equal('0', maskedKink.Char); + Assert.Equal(new DateTime(), maskedKink.DateTime); + Assert.Equal(new DateTimeOffset(), maskedKink.DateTimeOffset); + Assert.Equal(Guid.Empty, maskedKink.Guid); + } + + [Fact] + public void MaskSensitiveData_DoesNotMasksOtherBaseTypes_WhenWithoutSensitiveAttrib() + { + // Arrange + var kink = new BasicTypesMock(); + + // Act and Assert + var maskedKink = JsonMask.MaskSensitiveData(kink); + + // Assert + Assert.True(maskedKink.Bool); + Assert.Equal(1, maskedKink.Byte); + Assert.Equal(1, maskedKink.Sbyte); + Assert.Equal(1, maskedKink.Short); + Assert.Equal(1, maskedKink.Ushort); + Assert.Equal(1, maskedKink.Int); + Assert.Equal(1u, maskedKink.Uint); + Assert.Equal(1, maskedKink.Long); + Assert.Equal(1u, maskedKink.Ulong); + Assert.Equal(1, maskedKink.Float); + Assert.Equal(1, maskedKink.Double); + Assert.Equal(1, maskedKink.Decimal); + Assert.Equal('1', maskedKink.Char); + Assert.Equal(new DateTime().AddYears(1), maskedKink.DateTime); + Assert.Equal(new DateTimeOffset().AddYears(1), maskedKink.DateTimeOffset); + Assert.Equal(Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), maskedKink.Guid); + } } } \ No newline at end of file diff --git a/src/JsonDataMasking.Test/MockData/BasicTypesMock.cs b/src/JsonDataMasking.Test/MockData/BasicTypesMock.cs new file mode 100644 index 0000000..b0d0da3 --- /dev/null +++ b/src/JsonDataMasking.Test/MockData/BasicTypesMock.cs @@ -0,0 +1,24 @@ +using System; + +namespace JsonDataMasking.Test.MockData +{ + public class BasicTypesMock + { + public bool Bool { get; set; } = true; + public byte Byte { get; set; } = 1; + public sbyte Sbyte { get; set; } = 1; + public short Short { get; set; } = 1; + public ushort Ushort { get; set; } = 1; + public int Int { get; set; } = 1; + public uint Uint { get; set; } = 1; + public long Long { get; set; } = 1; + public ulong Ulong { get; set; } = 1; + public float Float { get; set; } = 1; + public double Double { get; set; } = 1; + public decimal Decimal { get; set; } = 1; + public char Char { get; set; } = '1'; + public DateTime DateTime { get; set; } = new DateTime().AddYears(1); + public DateTimeOffset DateTimeOffset { get; set; } = new DateTimeOffset().AddYears(1); + public Guid Guid { get; set; } = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"); + } +} diff --git a/src/JsonDataMasking.Test/MockData/BasicTypesSensitiveMock.cs b/src/JsonDataMasking.Test/MockData/BasicTypesSensitiveMock.cs new file mode 100644 index 0000000..e4abe7c --- /dev/null +++ b/src/JsonDataMasking.Test/MockData/BasicTypesSensitiveMock.cs @@ -0,0 +1,41 @@ +using JsonDataMasking.Attributes; +using System; + +namespace JsonDataMasking.Test.MockData +{ + public class BasicTypesSensitiveMock + { + [SensitiveData] + public bool Bool { get; set; } = true; + [SensitiveData] + public byte Byte { get; set; } = 1; + [SensitiveData] + public sbyte Sbyte { get; set; } = 1; + [SensitiveData] + public short Short { get; set; } = 1; + [SensitiveData] + public ushort Ushort { get; set; } = 1; + [SensitiveData] + public int Int { get; set; } = 1; + [SensitiveData] + public uint Uint { get; set; } = 1; + [SensitiveData] + public long Long { get; set; } = 1; + [SensitiveData] + public ulong Ulong { get; set; } = 1; + [SensitiveData] + public float Float { get; set; } = 1; + [SensitiveData] + public double Double { get; set; } = 1; + [SensitiveData] + public decimal Decimal { get; set; } = 1; + [SensitiveData] + public char Char { get; set; } = '1'; + [SensitiveData] + public DateTime DateTime { get; set; } = new DateTime().AddYears(1); + [SensitiveData] + public DateTimeOffset DateTimeOffset { get; set; } = new DateTimeOffset().AddYears(1); + [SensitiveData] + public Guid Guid { get; set; } = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"); + } +} diff --git a/src/JsonDataMasking/Masks/JsonMask.cs b/src/JsonDataMasking/Masks/JsonMask.cs index 9edd4a4..839fd3f 100644 --- a/src/JsonDataMasking/Masks/JsonMask.cs +++ b/src/JsonDataMasking/Masks/JsonMask.cs @@ -18,7 +18,7 @@ public static class JsonMask #region Masking /// - /// Mask values of string and some string collections type class properties that have the [SensitiveData] attribute. + /// Mask values of primitive types and some string collections type class properties that have the [SensitiveData] attribute. /// Properties with null values or that don't have the attribute remain unchanged. /// /// @@ -61,16 +61,44 @@ private static T MaskPropertiesWithSensitiveDataAttribute(T data) { MaskDictionaryProperty(data, property); } - else if (propertyAttribute != null && IsSupportedBaseType(property.PropertyType)) + else if (propertyAttribute != null) { - var maskedPropertyValue = GetMaskedPropertyValue(propertyValue?.ToString(), propertyAttribute); + var maskedPropertyValue = GetMaskedPropertyValue(property.PropertyType, propertyValue, propertyAttribute); property.SetValue(data, maskedPropertyValue); } } return data; } - private static string? GetMaskedPropertyValue(string? currentPropertyValue, SensitiveDataAttribute attribute) + private static readonly Dictionary MaskDefaults = new Dictionary() + { + [typeof(bool)] = false, + [typeof(byte)] = (byte)0, + [typeof(sbyte)] = (sbyte)0, + [typeof(short)] = (short)0, + [typeof(ushort)] = (ushort)0, + [typeof(int)] = 0, + [typeof(uint)] = 0U, + [typeof(long)] = 0L, + [typeof(ulong)] = 0UL, + [typeof(float)] = 0F, + [typeof(double)] = 0D, + [typeof(decimal)] = 0M, + [typeof(char)] = '0', + [typeof(DateTime)] = new DateTime(), + [typeof(DateTimeOffset)] = new DateTimeOffset(), + [typeof(Guid)] = Guid.Empty + }; + + private static object? GetMaskedPropertyValue(Type type, object? currentPropertyValue, SensitiveDataAttribute attribute) + => type switch + { + _ when type == typeof(string) => GetMaskedPropertyValueString(currentPropertyValue?.ToString(), attribute), + _ when MaskDefaults.TryGetValue(type, out var defaultValue) => defaultValue, + _ => throw new NotSupportedException($"Masking of type {type.Name} is not supported") + }; + + private static string? GetMaskedPropertyValueString(string? currentPropertyValue, SensitiveDataAttribute attribute) { if (string.IsNullOrWhiteSpace(currentPropertyValue)) return currentPropertyValue; @@ -114,8 +142,8 @@ private static void MaskIEnumerableProperty(T data, PropertyInfo property) object? maskedCollectionValue = null; if (IsClassReferenceType(collectionType)) maskedCollectionValue = MaskPropertiesWithSensitiveDataAttribute(value); - else if (propertyAttribute != null && IsSupportedBaseType(collectionType)) - maskedCollectionValue = GetMaskedPropertyValue(value?.ToString(), propertyAttribute); + else if (propertyAttribute != null) + maskedCollectionValue = GetMaskedPropertyValue(collectionType, value, propertyAttribute); if (maskedCollectionValue != null) maskedCollection.Add(maskedCollectionValue); @@ -138,7 +166,7 @@ private static void MaskDictionaryProperty(T data, PropertyInfo property) foreach (var pair in collection) { - var maskedCollectionValue = GetMaskedPropertyValue(pair.Value, propertyAttribute); + var maskedCollectionValue = GetMaskedPropertyValueString(pair.Value, propertyAttribute); maskedCollection.Add(pair.Key, maskedCollectionValue); } property.SetValue(data, maskedCollection); @@ -160,12 +188,6 @@ private static IEnumerable ConvertCollectionType(IEnumerable collection, Type ty private static bool IsPropertyTypeEqualsToAnonymousType(PropertyInfo property) => property.ReflectedType.AssemblyQualifiedName.Contains("AnonymousType"); - private static bool IsSupportedBaseType(Type type) => type switch - { - Type _ when type == typeof(string) => true, - _ => throw new NotSupportedException("Masking of non-string base types is not supported") - }; - private static bool AreFirstAndLastParametersInValidRange(int propertySize, SensitiveDataAttribute attribute) => attribute.ShowFirst <= propertySize && attribute.ShowLast <= propertySize && (attribute.ShowFirst + attribute.ShowLast) <= propertySize; From dbd3a3cab28ad5cef5946a34fd26ebd2cdf6089b Mon Sep 17 00:00:00 2001 From: Luiza Engler Stadelhofer Date: Sun, 30 Nov 2025 22:21:59 -0300 Subject: [PATCH 2/5] chore: change MaskDefaults dict to use actual default() for base types --- src/JsonDataMasking.Test/JsonMaskTests.cs | 80 +++++++++++------------ src/JsonDataMasking/Masks/JsonMask.cs | 48 +++++++------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/JsonDataMasking.Test/JsonMaskTests.cs b/src/JsonDataMasking.Test/JsonMaskTests.cs index 279a74a..f8d75ea 100644 --- a/src/JsonDataMasking.Test/JsonMaskTests.cs +++ b/src/JsonDataMasking.Test/JsonMaskTests.cs @@ -365,59 +365,59 @@ public void MaskSensitiveData_ThrowsNotSupportedException_WhenPropertyIsArray() } [Fact] - public void MaskSensitiveData_MasksOtherBaseTypes() + public void MaskSensitiveData_MasksOtherBaseTypes_WhenTheyHaveAttribute() { // Arrange - var kink = new BasicTypesSensitiveMock(); + var baseTypes = new BasicTypesSensitiveMock(); - // Act and Assert - var maskedKink = JsonMask.MaskSensitiveData(kink); + // Act + var maskedBaseTypes = JsonMask.MaskSensitiveData(baseTypes); // Assert - Assert.False(maskedKink.Bool); - Assert.Equal(0, maskedKink.Byte); - Assert.Equal(0, maskedKink.Sbyte); - Assert.Equal(0, maskedKink.Short); - Assert.Equal(0, maskedKink.Ushort); - Assert.Equal(0, maskedKink.Int); - Assert.Equal(0u, maskedKink.Uint); - Assert.Equal(0, maskedKink.Long); - Assert.Equal(0u, maskedKink.Ulong); - Assert.Equal(0, maskedKink.Float); - Assert.Equal(0, maskedKink.Double); - Assert.Equal(0, maskedKink.Decimal); - Assert.Equal('0', maskedKink.Char); - Assert.Equal(new DateTime(), maskedKink.DateTime); - Assert.Equal(new DateTimeOffset(), maskedKink.DateTimeOffset); - Assert.Equal(Guid.Empty, maskedKink.Guid); + Assert.False(maskedBaseTypes.Bool); + Assert.Equal(0, maskedBaseTypes.Byte); + Assert.Equal(0, maskedBaseTypes.Sbyte); + Assert.Equal(0, maskedBaseTypes.Short); + Assert.Equal(0, maskedBaseTypes.Ushort); + Assert.Equal(0, maskedBaseTypes.Int); + Assert.Equal(0u, maskedBaseTypes.Uint); + Assert.Equal(0, maskedBaseTypes.Long); + Assert.Equal(0u, maskedBaseTypes.Ulong); + Assert.Equal(0, maskedBaseTypes.Float); + Assert.Equal(0, maskedBaseTypes.Double); + Assert.Equal(0, maskedBaseTypes.Decimal); + Assert.Equal('\0', maskedBaseTypes.Char); + Assert.Equal(new DateTime(), maskedBaseTypes.DateTime); + Assert.Equal(new DateTimeOffset(), maskedBaseTypes.DateTimeOffset); + Assert.Equal(Guid.Empty, maskedBaseTypes.Guid); } [Fact] - public void MaskSensitiveData_DoesNotMasksOtherBaseTypes_WhenWithoutSensitiveAttrib() + public void MaskSensitiveData_DoesNotMaskOtherBaseTypes_WhenWithoutSensitiveAttribute() { // Arrange - var kink = new BasicTypesMock(); + var nonSensitiveBaseTypes = new BasicTypesMock(); - // Act and Assert - var maskedKink = JsonMask.MaskSensitiveData(kink); + // Act + var maskedBaseTypes = JsonMask.MaskSensitiveData(nonSensitiveBaseTypes); // Assert - Assert.True(maskedKink.Bool); - Assert.Equal(1, maskedKink.Byte); - Assert.Equal(1, maskedKink.Sbyte); - Assert.Equal(1, maskedKink.Short); - Assert.Equal(1, maskedKink.Ushort); - Assert.Equal(1, maskedKink.Int); - Assert.Equal(1u, maskedKink.Uint); - Assert.Equal(1, maskedKink.Long); - Assert.Equal(1u, maskedKink.Ulong); - Assert.Equal(1, maskedKink.Float); - Assert.Equal(1, maskedKink.Double); - Assert.Equal(1, maskedKink.Decimal); - Assert.Equal('1', maskedKink.Char); - Assert.Equal(new DateTime().AddYears(1), maskedKink.DateTime); - Assert.Equal(new DateTimeOffset().AddYears(1), maskedKink.DateTimeOffset); - Assert.Equal(Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), maskedKink.Guid); + Assert.True(maskedBaseTypes.Bool); + Assert.Equal(1, maskedBaseTypes.Byte); + Assert.Equal(1, maskedBaseTypes.Sbyte); + Assert.Equal(1, maskedBaseTypes.Short); + Assert.Equal(1, maskedBaseTypes.Ushort); + Assert.Equal(1, maskedBaseTypes.Int); + Assert.Equal(1u, maskedBaseTypes.Uint); + Assert.Equal(1, maskedBaseTypes.Long); + Assert.Equal(1u, maskedBaseTypes.Ulong); + Assert.Equal(1, maskedBaseTypes.Float); + Assert.Equal(1, maskedBaseTypes.Double); + Assert.Equal(1, maskedBaseTypes.Decimal); + Assert.Equal('1', maskedBaseTypes.Char); + Assert.Equal(new DateTime().AddYears(1), maskedBaseTypes.DateTime); + Assert.Equal(new DateTimeOffset().AddYears(1), maskedBaseTypes.DateTimeOffset); + Assert.Equal(Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), maskedBaseTypes.Guid); } } } \ No newline at end of file diff --git a/src/JsonDataMasking/Masks/JsonMask.cs b/src/JsonDataMasking/Masks/JsonMask.cs index 839fd3f..718c27b 100644 --- a/src/JsonDataMasking/Masks/JsonMask.cs +++ b/src/JsonDataMasking/Masks/JsonMask.cs @@ -70,32 +70,32 @@ private static T MaskPropertiesWithSensitiveDataAttribute(T data) return data; } - private static readonly Dictionary MaskDefaults = new Dictionary() - { - [typeof(bool)] = false, - [typeof(byte)] = (byte)0, - [typeof(sbyte)] = (sbyte)0, - [typeof(short)] = (short)0, - [typeof(ushort)] = (ushort)0, - [typeof(int)] = 0, - [typeof(uint)] = 0U, - [typeof(long)] = 0L, - [typeof(ulong)] = 0UL, - [typeof(float)] = 0F, - [typeof(double)] = 0D, - [typeof(decimal)] = 0M, - [typeof(char)] = '0', - [typeof(DateTime)] = new DateTime(), - [typeof(DateTimeOffset)] = new DateTimeOffset(), - [typeof(Guid)] = Guid.Empty + private static readonly Dictionary MaskDefaults = new Dictionary() + { + [typeof(bool)] = default(bool), + [typeof(byte)] = default(byte), + [typeof(sbyte)] = default(sbyte), + [typeof(short)] = default(short), + [typeof(ushort)] = default(ushort), + [typeof(int)] = default(int), + [typeof(uint)] = default(uint), + [typeof(long)] = default(long), + [typeof(ulong)] = default(ulong), + [typeof(float)] = default(float), + [typeof(double)] = default(double), + [typeof(decimal)] = default(decimal), + [typeof(char)] = default(char), + [typeof(DateTime)] = default(DateTime), + [typeof(DateTimeOffset)] = default(DateTimeOffset), + [typeof(Guid)] = default(Guid) }; - private static object? GetMaskedPropertyValue(Type type, object? currentPropertyValue, SensitiveDataAttribute attribute) - => type switch - { - _ when type == typeof(string) => GetMaskedPropertyValueString(currentPropertyValue?.ToString(), attribute), - _ when MaskDefaults.TryGetValue(type, out var defaultValue) => defaultValue, - _ => throw new NotSupportedException($"Masking of type {type.Name} is not supported") + private static object? GetMaskedPropertyValue(Type type, object? currentPropertyValue, SensitiveDataAttribute attribute) + => type switch + { + _ when type == typeof(string) => GetMaskedPropertyValueString(currentPropertyValue?.ToString(), attribute), + _ when MaskDefaults.TryGetValue(type, out var defaultValue) => defaultValue, + _ => throw new NotSupportedException($"Masking of type {type.Name} is not supported") }; private static string? GetMaskedPropertyValueString(string? currentPropertyValue, SensitiveDataAttribute attribute) From 28f4b7ee05110211160ab7fb49de6b8cd82f5856 Mon Sep 17 00:00:00 2001 From: Luiza Engler Stadelhofer Date: Sun, 30 Nov 2025 22:45:44 -0300 Subject: [PATCH 3/5] Revert "chore: change MaskDefaults dict to use actual default() for base types" This reverts commit dbd3a3cab28ad5cef5946a34fd26ebd2cdf6089b. --- src/JsonDataMasking.Test/JsonMaskTests.cs | 80 +++++++++++------------ src/JsonDataMasking/Masks/JsonMask.cs | 48 +++++++------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/JsonDataMasking.Test/JsonMaskTests.cs b/src/JsonDataMasking.Test/JsonMaskTests.cs index f8d75ea..279a74a 100644 --- a/src/JsonDataMasking.Test/JsonMaskTests.cs +++ b/src/JsonDataMasking.Test/JsonMaskTests.cs @@ -365,59 +365,59 @@ public void MaskSensitiveData_ThrowsNotSupportedException_WhenPropertyIsArray() } [Fact] - public void MaskSensitiveData_MasksOtherBaseTypes_WhenTheyHaveAttribute() + public void MaskSensitiveData_MasksOtherBaseTypes() { // Arrange - var baseTypes = new BasicTypesSensitiveMock(); + var kink = new BasicTypesSensitiveMock(); - // Act - var maskedBaseTypes = JsonMask.MaskSensitiveData(baseTypes); + // Act and Assert + var maskedKink = JsonMask.MaskSensitiveData(kink); // Assert - Assert.False(maskedBaseTypes.Bool); - Assert.Equal(0, maskedBaseTypes.Byte); - Assert.Equal(0, maskedBaseTypes.Sbyte); - Assert.Equal(0, maskedBaseTypes.Short); - Assert.Equal(0, maskedBaseTypes.Ushort); - Assert.Equal(0, maskedBaseTypes.Int); - Assert.Equal(0u, maskedBaseTypes.Uint); - Assert.Equal(0, maskedBaseTypes.Long); - Assert.Equal(0u, maskedBaseTypes.Ulong); - Assert.Equal(0, maskedBaseTypes.Float); - Assert.Equal(0, maskedBaseTypes.Double); - Assert.Equal(0, maskedBaseTypes.Decimal); - Assert.Equal('\0', maskedBaseTypes.Char); - Assert.Equal(new DateTime(), maskedBaseTypes.DateTime); - Assert.Equal(new DateTimeOffset(), maskedBaseTypes.DateTimeOffset); - Assert.Equal(Guid.Empty, maskedBaseTypes.Guid); + Assert.False(maskedKink.Bool); + Assert.Equal(0, maskedKink.Byte); + Assert.Equal(0, maskedKink.Sbyte); + Assert.Equal(0, maskedKink.Short); + Assert.Equal(0, maskedKink.Ushort); + Assert.Equal(0, maskedKink.Int); + Assert.Equal(0u, maskedKink.Uint); + Assert.Equal(0, maskedKink.Long); + Assert.Equal(0u, maskedKink.Ulong); + Assert.Equal(0, maskedKink.Float); + Assert.Equal(0, maskedKink.Double); + Assert.Equal(0, maskedKink.Decimal); + Assert.Equal('0', maskedKink.Char); + Assert.Equal(new DateTime(), maskedKink.DateTime); + Assert.Equal(new DateTimeOffset(), maskedKink.DateTimeOffset); + Assert.Equal(Guid.Empty, maskedKink.Guid); } [Fact] - public void MaskSensitiveData_DoesNotMaskOtherBaseTypes_WhenWithoutSensitiveAttribute() + public void MaskSensitiveData_DoesNotMasksOtherBaseTypes_WhenWithoutSensitiveAttrib() { // Arrange - var nonSensitiveBaseTypes = new BasicTypesMock(); + var kink = new BasicTypesMock(); - // Act - var maskedBaseTypes = JsonMask.MaskSensitiveData(nonSensitiveBaseTypes); + // Act and Assert + var maskedKink = JsonMask.MaskSensitiveData(kink); // Assert - Assert.True(maskedBaseTypes.Bool); - Assert.Equal(1, maskedBaseTypes.Byte); - Assert.Equal(1, maskedBaseTypes.Sbyte); - Assert.Equal(1, maskedBaseTypes.Short); - Assert.Equal(1, maskedBaseTypes.Ushort); - Assert.Equal(1, maskedBaseTypes.Int); - Assert.Equal(1u, maskedBaseTypes.Uint); - Assert.Equal(1, maskedBaseTypes.Long); - Assert.Equal(1u, maskedBaseTypes.Ulong); - Assert.Equal(1, maskedBaseTypes.Float); - Assert.Equal(1, maskedBaseTypes.Double); - Assert.Equal(1, maskedBaseTypes.Decimal); - Assert.Equal('1', maskedBaseTypes.Char); - Assert.Equal(new DateTime().AddYears(1), maskedBaseTypes.DateTime); - Assert.Equal(new DateTimeOffset().AddYears(1), maskedBaseTypes.DateTimeOffset); - Assert.Equal(Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), maskedBaseTypes.Guid); + Assert.True(maskedKink.Bool); + Assert.Equal(1, maskedKink.Byte); + Assert.Equal(1, maskedKink.Sbyte); + Assert.Equal(1, maskedKink.Short); + Assert.Equal(1, maskedKink.Ushort); + Assert.Equal(1, maskedKink.Int); + Assert.Equal(1u, maskedKink.Uint); + Assert.Equal(1, maskedKink.Long); + Assert.Equal(1u, maskedKink.Ulong); + Assert.Equal(1, maskedKink.Float); + Assert.Equal(1, maskedKink.Double); + Assert.Equal(1, maskedKink.Decimal); + Assert.Equal('1', maskedKink.Char); + Assert.Equal(new DateTime().AddYears(1), maskedKink.DateTime); + Assert.Equal(new DateTimeOffset().AddYears(1), maskedKink.DateTimeOffset); + Assert.Equal(Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), maskedKink.Guid); } } } \ No newline at end of file diff --git a/src/JsonDataMasking/Masks/JsonMask.cs b/src/JsonDataMasking/Masks/JsonMask.cs index 718c27b..839fd3f 100644 --- a/src/JsonDataMasking/Masks/JsonMask.cs +++ b/src/JsonDataMasking/Masks/JsonMask.cs @@ -70,32 +70,32 @@ private static T MaskPropertiesWithSensitiveDataAttribute(T data) return data; } - private static readonly Dictionary MaskDefaults = new Dictionary() - { - [typeof(bool)] = default(bool), - [typeof(byte)] = default(byte), - [typeof(sbyte)] = default(sbyte), - [typeof(short)] = default(short), - [typeof(ushort)] = default(ushort), - [typeof(int)] = default(int), - [typeof(uint)] = default(uint), - [typeof(long)] = default(long), - [typeof(ulong)] = default(ulong), - [typeof(float)] = default(float), - [typeof(double)] = default(double), - [typeof(decimal)] = default(decimal), - [typeof(char)] = default(char), - [typeof(DateTime)] = default(DateTime), - [typeof(DateTimeOffset)] = default(DateTimeOffset), - [typeof(Guid)] = default(Guid) + private static readonly Dictionary MaskDefaults = new Dictionary() + { + [typeof(bool)] = false, + [typeof(byte)] = (byte)0, + [typeof(sbyte)] = (sbyte)0, + [typeof(short)] = (short)0, + [typeof(ushort)] = (ushort)0, + [typeof(int)] = 0, + [typeof(uint)] = 0U, + [typeof(long)] = 0L, + [typeof(ulong)] = 0UL, + [typeof(float)] = 0F, + [typeof(double)] = 0D, + [typeof(decimal)] = 0M, + [typeof(char)] = '0', + [typeof(DateTime)] = new DateTime(), + [typeof(DateTimeOffset)] = new DateTimeOffset(), + [typeof(Guid)] = Guid.Empty }; - private static object? GetMaskedPropertyValue(Type type, object? currentPropertyValue, SensitiveDataAttribute attribute) - => type switch - { - _ when type == typeof(string) => GetMaskedPropertyValueString(currentPropertyValue?.ToString(), attribute), - _ when MaskDefaults.TryGetValue(type, out var defaultValue) => defaultValue, - _ => throw new NotSupportedException($"Masking of type {type.Name} is not supported") + private static object? GetMaskedPropertyValue(Type type, object? currentPropertyValue, SensitiveDataAttribute attribute) + => type switch + { + _ when type == typeof(string) => GetMaskedPropertyValueString(currentPropertyValue?.ToString(), attribute), + _ when MaskDefaults.TryGetValue(type, out var defaultValue) => defaultValue, + _ => throw new NotSupportedException($"Masking of type {type.Name} is not supported") }; private static string? GetMaskedPropertyValueString(string? currentPropertyValue, SensitiveDataAttribute attribute) From 061d75394714ed7e73e3ee17c66fbfdad20094b0 Mon Sep 17 00:00:00 2001 From: Luiza Engler Stadelhofer Date: Sun, 30 Nov 2025 22:21:59 -0300 Subject: [PATCH 4/5] chore: change MaskDefaults dict to use actual default() for base types --- src/JsonDataMasking.Test/JsonMaskTests.cs | 80 +++++++++++------------ src/JsonDataMasking/Masks/JsonMask.cs | 48 +++++++------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/JsonDataMasking.Test/JsonMaskTests.cs b/src/JsonDataMasking.Test/JsonMaskTests.cs index 279a74a..f8d75ea 100644 --- a/src/JsonDataMasking.Test/JsonMaskTests.cs +++ b/src/JsonDataMasking.Test/JsonMaskTests.cs @@ -365,59 +365,59 @@ public void MaskSensitiveData_ThrowsNotSupportedException_WhenPropertyIsArray() } [Fact] - public void MaskSensitiveData_MasksOtherBaseTypes() + public void MaskSensitiveData_MasksOtherBaseTypes_WhenTheyHaveAttribute() { // Arrange - var kink = new BasicTypesSensitiveMock(); + var baseTypes = new BasicTypesSensitiveMock(); - // Act and Assert - var maskedKink = JsonMask.MaskSensitiveData(kink); + // Act + var maskedBaseTypes = JsonMask.MaskSensitiveData(baseTypes); // Assert - Assert.False(maskedKink.Bool); - Assert.Equal(0, maskedKink.Byte); - Assert.Equal(0, maskedKink.Sbyte); - Assert.Equal(0, maskedKink.Short); - Assert.Equal(0, maskedKink.Ushort); - Assert.Equal(0, maskedKink.Int); - Assert.Equal(0u, maskedKink.Uint); - Assert.Equal(0, maskedKink.Long); - Assert.Equal(0u, maskedKink.Ulong); - Assert.Equal(0, maskedKink.Float); - Assert.Equal(0, maskedKink.Double); - Assert.Equal(0, maskedKink.Decimal); - Assert.Equal('0', maskedKink.Char); - Assert.Equal(new DateTime(), maskedKink.DateTime); - Assert.Equal(new DateTimeOffset(), maskedKink.DateTimeOffset); - Assert.Equal(Guid.Empty, maskedKink.Guid); + Assert.False(maskedBaseTypes.Bool); + Assert.Equal(0, maskedBaseTypes.Byte); + Assert.Equal(0, maskedBaseTypes.Sbyte); + Assert.Equal(0, maskedBaseTypes.Short); + Assert.Equal(0, maskedBaseTypes.Ushort); + Assert.Equal(0, maskedBaseTypes.Int); + Assert.Equal(0u, maskedBaseTypes.Uint); + Assert.Equal(0, maskedBaseTypes.Long); + Assert.Equal(0u, maskedBaseTypes.Ulong); + Assert.Equal(0, maskedBaseTypes.Float); + Assert.Equal(0, maskedBaseTypes.Double); + Assert.Equal(0, maskedBaseTypes.Decimal); + Assert.Equal('\0', maskedBaseTypes.Char); + Assert.Equal(new DateTime(), maskedBaseTypes.DateTime); + Assert.Equal(new DateTimeOffset(), maskedBaseTypes.DateTimeOffset); + Assert.Equal(Guid.Empty, maskedBaseTypes.Guid); } [Fact] - public void MaskSensitiveData_DoesNotMasksOtherBaseTypes_WhenWithoutSensitiveAttrib() + public void MaskSensitiveData_DoesNotMaskOtherBaseTypes_WhenWithoutSensitiveAttribute() { // Arrange - var kink = new BasicTypesMock(); + var nonSensitiveBaseTypes = new BasicTypesMock(); - // Act and Assert - var maskedKink = JsonMask.MaskSensitiveData(kink); + // Act + var maskedBaseTypes = JsonMask.MaskSensitiveData(nonSensitiveBaseTypes); // Assert - Assert.True(maskedKink.Bool); - Assert.Equal(1, maskedKink.Byte); - Assert.Equal(1, maskedKink.Sbyte); - Assert.Equal(1, maskedKink.Short); - Assert.Equal(1, maskedKink.Ushort); - Assert.Equal(1, maskedKink.Int); - Assert.Equal(1u, maskedKink.Uint); - Assert.Equal(1, maskedKink.Long); - Assert.Equal(1u, maskedKink.Ulong); - Assert.Equal(1, maskedKink.Float); - Assert.Equal(1, maskedKink.Double); - Assert.Equal(1, maskedKink.Decimal); - Assert.Equal('1', maskedKink.Char); - Assert.Equal(new DateTime().AddYears(1), maskedKink.DateTime); - Assert.Equal(new DateTimeOffset().AddYears(1), maskedKink.DateTimeOffset); - Assert.Equal(Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), maskedKink.Guid); + Assert.True(maskedBaseTypes.Bool); + Assert.Equal(1, maskedBaseTypes.Byte); + Assert.Equal(1, maskedBaseTypes.Sbyte); + Assert.Equal(1, maskedBaseTypes.Short); + Assert.Equal(1, maskedBaseTypes.Ushort); + Assert.Equal(1, maskedBaseTypes.Int); + Assert.Equal(1u, maskedBaseTypes.Uint); + Assert.Equal(1, maskedBaseTypes.Long); + Assert.Equal(1u, maskedBaseTypes.Ulong); + Assert.Equal(1, maskedBaseTypes.Float); + Assert.Equal(1, maskedBaseTypes.Double); + Assert.Equal(1, maskedBaseTypes.Decimal); + Assert.Equal('1', maskedBaseTypes.Char); + Assert.Equal(new DateTime().AddYears(1), maskedBaseTypes.DateTime); + Assert.Equal(new DateTimeOffset().AddYears(1), maskedBaseTypes.DateTimeOffset); + Assert.Equal(Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), maskedBaseTypes.Guid); } } } \ No newline at end of file diff --git a/src/JsonDataMasking/Masks/JsonMask.cs b/src/JsonDataMasking/Masks/JsonMask.cs index 839fd3f..718c27b 100644 --- a/src/JsonDataMasking/Masks/JsonMask.cs +++ b/src/JsonDataMasking/Masks/JsonMask.cs @@ -70,32 +70,32 @@ private static T MaskPropertiesWithSensitiveDataAttribute(T data) return data; } - private static readonly Dictionary MaskDefaults = new Dictionary() - { - [typeof(bool)] = false, - [typeof(byte)] = (byte)0, - [typeof(sbyte)] = (sbyte)0, - [typeof(short)] = (short)0, - [typeof(ushort)] = (ushort)0, - [typeof(int)] = 0, - [typeof(uint)] = 0U, - [typeof(long)] = 0L, - [typeof(ulong)] = 0UL, - [typeof(float)] = 0F, - [typeof(double)] = 0D, - [typeof(decimal)] = 0M, - [typeof(char)] = '0', - [typeof(DateTime)] = new DateTime(), - [typeof(DateTimeOffset)] = new DateTimeOffset(), - [typeof(Guid)] = Guid.Empty + private static readonly Dictionary MaskDefaults = new Dictionary() + { + [typeof(bool)] = default(bool), + [typeof(byte)] = default(byte), + [typeof(sbyte)] = default(sbyte), + [typeof(short)] = default(short), + [typeof(ushort)] = default(ushort), + [typeof(int)] = default(int), + [typeof(uint)] = default(uint), + [typeof(long)] = default(long), + [typeof(ulong)] = default(ulong), + [typeof(float)] = default(float), + [typeof(double)] = default(double), + [typeof(decimal)] = default(decimal), + [typeof(char)] = default(char), + [typeof(DateTime)] = default(DateTime), + [typeof(DateTimeOffset)] = default(DateTimeOffset), + [typeof(Guid)] = default(Guid) }; - private static object? GetMaskedPropertyValue(Type type, object? currentPropertyValue, SensitiveDataAttribute attribute) - => type switch - { - _ when type == typeof(string) => GetMaskedPropertyValueString(currentPropertyValue?.ToString(), attribute), - _ when MaskDefaults.TryGetValue(type, out var defaultValue) => defaultValue, - _ => throw new NotSupportedException($"Masking of type {type.Name} is not supported") + private static object? GetMaskedPropertyValue(Type type, object? currentPropertyValue, SensitiveDataAttribute attribute) + => type switch + { + _ when type == typeof(string) => GetMaskedPropertyValueString(currentPropertyValue?.ToString(), attribute), + _ when MaskDefaults.TryGetValue(type, out var defaultValue) => defaultValue, + _ => throw new NotSupportedException($"Masking of type {type.Name} is not supported") }; private static string? GetMaskedPropertyValueString(string? currentPropertyValue, SensitiveDataAttribute attribute) From d86b6cf033e3c21aa3d7923d04173b050a7f08e5 Mon Sep 17 00:00:00 2001 From: Luiza Engler Stadelhofer Date: Sun, 30 Nov 2025 23:16:15 -0300 Subject: [PATCH 5/5] docs: update README to reflect the support of new base types --- README.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 91eed03..8d28287 100644 --- a/README.md +++ b/README.md @@ -17,20 +17,27 @@ After installing the Nuget package in your project, you need to take the followi - **SubstituteText**: If set, the entire property value will be override with this text. Note that using this setting will ignore all other settings. - **Mask**: Set to a character to use it when masking the property's value. By default, the character `*` is used. + > PS.: These customization fields only work for fields with a `string` base type. + 2. Call the `JsonMask.MaskSensitiveData()` function, passing in your object instance as a parameter. ## Support -This library supports masking of `string` fields only, although it also supports `List`/`IEnumerable` and `Dictionary`. Nested class properties are also masked, independently of depth. - -| Property Type | Support | -|:---: |:---: | -| string | ✅ | -| List\, where T is a class or string | ✅ | -| IEnumerable\, where T is a class or string | ✅ | -| Dictionary | ✅ | -| Any other collection type, such as Array, ArrayList\, etc | ❌ | -| Any other base type different from string | ❌ | +### Base Types +This library supports masking of the following base types: +- `string` fields, which are masked following the rules detailed in the [Usage](#usage) section. +- Other types such as `bool`, `(s)byte`, `(u)short`, `(u)int`, `(u)long`, `float`, `double`, `decimal`, `char`, `Datetime`, `DatetimeOffset`, and `Guid`, which are set to their respective default values when having the `[SensitiveData]` attribute. +- Any other base types are currently NOT supported. + +### Collections +The library also supports the masking of some collections, such as: +- `List`, where T is a class or `string`. +- `IEnumerable`, where T is a class or `string`. +- `Dictionary`. +- Any other collection type, such as `Array`, `ArrayList`, etc., is NOT supported. + +### Nested class fields +Nested class properties are also masked, independently of depth. ## Examples