From eadeee52350cddb5a8465ca76f52e0d7c181e056 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Dec 2025 16:17:05 +0000 Subject: [PATCH 1/7] Initial plan From 5203f684765b71b084bfbe10f398b802631db9d5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Dec 2025 16:25:08 +0000 Subject: [PATCH 2/7] Add detection for logging types with DataClassification members Co-authored-by: meziantou <509220+meziantou@users.noreply.github.com> --- docs/Rules/MA0153.md | 22 ++- .../Rules/DoNotLogClassifiedDataAnalyzer.cs | 40 +++++ .../DoNotLogClassifiedDataAnalyzerTests.cs | 144 ++++++++++++++++++ 3 files changed, 203 insertions(+), 3 deletions(-) diff --git a/docs/Rules/MA0153.md b/docs/Rules/MA0153.md index dae274b1..d6228036 100644 --- a/docs/Rules/MA0153.md +++ b/docs/Rules/MA0153.md @@ -3,8 +3,9 @@ Source: [DoNotLogClassifiedDataAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs) -Detects when a log parameter is decorated with an attribute that inherits from `Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute`. -Most of the time, these values should not be used with `[LogProperties]` to redact values. +Detects when a log parameter is decorated with an attribute that inherits from `Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute`, or when logging an object whose type contains members (properties or fields) decorated with such attributes. + +Since there could be multiple log providers, any type that has sensitive attributes should not be logged directly, as some providers could log inner fields/properties and ignore the attributes. ````c# using Microsoft.Extensions.Logging; @@ -14,15 +15,30 @@ ILogger logger; // non-compliant as Prop is decorated with an attribute that inherits from DataClassificationAttribute logger.LogInformation("{Prop}", new Dummy().Prop); +// non-compliant as PatientInfo contains properties decorated with DataClassificationAttribute +PatientInfo patient = new(); +logger.LogInformation("{Patient}", patient); + class Dummy { [PiiAttribute] public string Prop { get; set; } } +class PatientInfo +{ + [PiiAttribute] + public string PatientId { get; set; } + + public ulong RecordId { get; set; } + + [PiiAttribute] + public string FirstName { get; set; } +} + class PiiAttribute : Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute { - public TaxonomyAttribute() : base(default) + public PiiAttribute() : base(default) { } } diff --git a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs index 3349c5a2..508d1c62 100644 --- a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs @@ -99,6 +99,10 @@ static void ValidateDataClassification(DiagnosticReporter diagnosticReporter, IO { diagnosticReporter.ReportDiagnostic(Rule, reportOperation); } + else if (TypeContainsMembersWithDataClassification(parameter.Type, dataClassificationAttributeSymbol)) + { + diagnosticReporter.ReportDiagnostic(Rule, reportOperation); + } } else if (operation is IPropertyReferenceOperation { Property: var property }) { @@ -118,7 +122,43 @@ static void ValidateDataClassification(DiagnosticReporter diagnosticReporter, IO { ValidateDataClassification(diagnosticReporter, arrayElementReferenceOperation.ArrayReference, reportOperation, dataClassificationAttributeSymbol); } + else + { + // Check if the operation's type contains members with DataClassificationAttribute + var type = operation.Type; + if (type is not null && TypeContainsMembersWithDataClassification(type, dataClassificationAttributeSymbol)) + { + diagnosticReporter.ReportDiagnostic(Rule, reportOperation); + } + } + } + } + + private static bool TypeContainsMembersWithDataClassification(ITypeSymbol type, INamedTypeSymbol dataClassificationAttributeSymbol) + { + if (type is null) + return false; + + // Don't check primitive types, strings, or common system types + if (type.SpecialType != SpecialType.None) + return false; + + // Check all properties + foreach (var member in type.GetMembers()) + { + if (member is IPropertySymbol property) + { + if (property.HasAttribute(dataClassificationAttributeSymbol, inherits: true)) + return true; + } + else if (member is IFieldSymbol field) + { + if (field.HasAttribute(dataClassificationAttributeSymbol, inherits: true)) + return true; + } } + + return false; } private static bool FindLogParameters(IMethodSymbol methodSymbol, out IParameterSymbol? arguments) diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs index 0f085143..f21ce5da 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs @@ -190,6 +190,150 @@ class TaxonomyAttribute : Microsoft.Extensions.Compliance.Classification.DataCla { public TaxonomyAttribute() : base(Microsoft.Extensions.Compliance.Classification.DataClassification.Unknown) { } } +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task Logger_LogInformation_DataClassification_TypeWithClassifiedProperty() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +ILogger logger = null; +PatientInfo p = new(); +logger.LogInformation("{Patient}", [|p|]); + +class PatientInfo +{ + [PiiData] public string PatientId { get; set; } + public ulong RecordId { get; set; } + [PiiData] public string FirstName { get; set; } +} + +class PiiData : Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute +{ + public PiiData() : base(Microsoft.Extensions.Compliance.Classification.DataClassification.Unknown) { } +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task Logger_LogInformation_DataClassification_TypeWithClassifiedField() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +ILogger logger = null; +PatientInfo p = new(); +logger.LogInformation("{Patient}", [|p|]); + +class PatientInfo +{ + [PiiData] public string PatientId; + public ulong RecordId; +} + +class PiiData : Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute +{ + public PiiData() : base(Microsoft.Extensions.Compliance.Classification.DataClassification.Unknown) { } +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task Logger_LogInformation_NoDataClassification_TypeWithNoClassifiedMembers() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +ILogger logger = null; +PatientInfo p = new(); +logger.LogInformation("{Patient}", p); + +class PatientInfo +{ + public string PatientId { get; set; } + public ulong RecordId { get; set; } +} + +class PiiData : Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute +{ + public PiiData() : base(Microsoft.Extensions.Compliance.Classification.DataClassification.Unknown) { } +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task Logger_LogInformation_DataClassification_ObjectCreationWithClassifiedProperty() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +ILogger logger = null; +logger.LogInformation("{Patient}", [|new PatientInfo()|]); + +class PatientInfo +{ + [PiiData] public string PatientId { get; set; } + public ulong RecordId { get; set; } +} + +class PiiData : Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute +{ + public PiiData() : base(Microsoft.Extensions.Compliance.Classification.DataClassification.Unknown) { } +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task Logger_LogInformation_NoDataClassification_PrimitiveType() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +ILogger logger = null; +int value = 42; +logger.LogInformation("{Value}", value); + +class PiiData : Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute +{ + public PiiData() : base(Microsoft.Extensions.Compliance.Classification.DataClassification.Unknown) { } +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task Logger_LogInformation_NoDataClassification_StringType() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +ILogger logger = null; +string value = "test"; +logger.LogInformation("{Value}", value); + +class PiiData : Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute +{ + public PiiData() : base(Microsoft.Extensions.Compliance.Classification.DataClassification.Unknown) { } +} """; await CreateProjectBuilder() .WithSourceCode(SourceCode) From b190d5b5536717f1d39249ebb5b3151562d24c58 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Dec 2025 16:32:11 +0000 Subject: [PATCH 3/7] Address code review feedback: improve performance and fix comment Co-authored-by: meziantou <509220+meziantou@users.noreply.github.com> --- .../Rules/DoNotLogClassifiedDataAnalyzer.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs index 508d1c62..a7bfcba5 100644 --- a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs @@ -126,9 +126,22 @@ static void ValidateDataClassification(DiagnosticReporter diagnosticReporter, IO { // Check if the operation's type contains members with DataClassificationAttribute var type = operation.Type; - if (type is not null && TypeContainsMembersWithDataClassification(type, dataClassificationAttributeSymbol)) + if (type is not null) { - diagnosticReporter.ReportDiagnostic(Rule, reportOperation); + // Early exit for value types (enums, structs) from well-known namespaces + if (type.IsValueType && type is INamedTypeSymbol namedType) + { + var ns = namedType.ContainingNamespace?.ToDisplayString(); + if (ns is not null && (ns.StartsWith("System.", StringComparison.Ordinal) || ns == "System")) + { + return; + } + } + + if (TypeContainsMembersWithDataClassification(type, dataClassificationAttributeSymbol)) + { + diagnosticReporter.ReportDiagnostic(Rule, reportOperation); + } } } } @@ -143,7 +156,7 @@ private static bool TypeContainsMembersWithDataClassification(ITypeSymbol type, if (type.SpecialType != SpecialType.None) return false; - // Check all properties + // Check all members (properties and fields) foreach (var member in type.GetMembers()) { if (member is IPropertySymbol property) From 32c4185184a9bc21e09d08f1fe7ee6ff980bbd6c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 03:39:56 +0000 Subject: [PATCH 4/7] Add configuration option to enable/disable type-level data classification detection Co-authored-by: meziantou <509220+meziantou@users.noreply.github.com> --- docs/Rules/MA0153.md | 8 ++ .../Rules/DoNotLogClassifiedDataAnalyzer.cs | 32 +++++--- .../DoNotLogClassifiedDataAnalyzerTests.cs | 80 +++++++++++++++++++ 3 files changed, 111 insertions(+), 9 deletions(-) diff --git a/docs/Rules/MA0153.md b/docs/Rules/MA0153.md index d6228036..3839911e 100644 --- a/docs/Rules/MA0153.md +++ b/docs/Rules/MA0153.md @@ -43,3 +43,11 @@ class PiiAttribute : Microsoft.Extensions.Compliance.Classification.DataClassifi } } ```` + +# Configuration (`.editorconfig`) + +```` +[*.cs] +# Enable or disable reporting objects containing properties/fields with DataClassificationAttribute +MA0153.report_types_with_data_classification_attributes = true|false # Default: true +```` diff --git a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs index a7bfcba5..e6e8192f 100644 --- a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs @@ -61,6 +61,9 @@ public void AnalyzeInvocationDeclaration(OperationAnalysisContext context) var operation = (IInvocationOperation)context.Operation; if (operation.TargetMethod.ContainingType.IsEqualTo(LoggerExtensionsSymbol) && FindLogParameters(operation.TargetMethod, out var argumentsParameter)) { + var options = context.Options.AnalyzerConfigOptionsProvider.GetOptions(operation.Syntax.SyntaxTree); + var reportTypesWithDataClassification = GetReportTypesWithDataClassificationConfiguration(options); + foreach (var argument in operation.Arguments) { var parameter = argument.Parameter; @@ -71,26 +74,37 @@ public void AnalyzeInvocationDeclaration(OperationAnalysisContext context) { if (argument.ArgumentKind == ArgumentKind.ParamArray && argument.Value is IArrayCreationOperation arrayCreation && arrayCreation.Initializer is not null) { - ValidateDataClassification(context, arrayCreation.Initializer.ElementValues); + ValidateDataClassification(context, arrayCreation.Initializer.ElementValues, reportTypesWithDataClassification); } } } } } - private void ValidateDataClassification(DiagnosticReporter diagnosticReporter, IEnumerable operations) + private static bool GetReportTypesWithDataClassificationConfiguration(AnalyzerConfigOptions options) + { + if (options.TryGetValue(RuleIdentifiers.DoNotLogClassifiedData + ".report_types_with_data_classification_attributes", out var value)) + { + if (bool.TryParse(value, out var result)) + return result; + } + + return true; // Default to true (enabled by default) + } + + private void ValidateDataClassification(DiagnosticReporter diagnosticReporter, IEnumerable operations, bool reportTypesWithDataClassification) { foreach (var operation in operations) { - ValidateDataClassification(diagnosticReporter, operation); + ValidateDataClassification(diagnosticReporter, operation, reportTypesWithDataClassification); } } - private void ValidateDataClassification(DiagnosticReporter diagnosticReporter, IOperation operation) + private void ValidateDataClassification(DiagnosticReporter diagnosticReporter, IOperation operation, bool reportTypesWithDataClassification) { - ValidateDataClassification(diagnosticReporter, operation, operation, DataClassificationAttributeSymbol!); + ValidateDataClassification(diagnosticReporter, operation, operation, DataClassificationAttributeSymbol!, reportTypesWithDataClassification); - static void ValidateDataClassification(DiagnosticReporter diagnosticReporter, IOperation operation, IOperation reportOperation, INamedTypeSymbol dataClassificationAttributeSymbol) + static void ValidateDataClassification(DiagnosticReporter diagnosticReporter, IOperation operation, IOperation reportOperation, INamedTypeSymbol dataClassificationAttributeSymbol, bool reportTypesWithDataClassification) { operation = operation.UnwrapConversionOperations(); if (operation is IParameterReferenceOperation { Parameter: var parameter }) @@ -99,7 +113,7 @@ static void ValidateDataClassification(DiagnosticReporter diagnosticReporter, IO { diagnosticReporter.ReportDiagnostic(Rule, reportOperation); } - else if (TypeContainsMembersWithDataClassification(parameter.Type, dataClassificationAttributeSymbol)) + else if (reportTypesWithDataClassification && TypeContainsMembersWithDataClassification(parameter.Type, dataClassificationAttributeSymbol)) { diagnosticReporter.ReportDiagnostic(Rule, reportOperation); } @@ -120,9 +134,9 @@ static void ValidateDataClassification(DiagnosticReporter diagnosticReporter, IO } else if (operation is IArrayElementReferenceOperation arrayElementReferenceOperation) { - ValidateDataClassification(diagnosticReporter, arrayElementReferenceOperation.ArrayReference, reportOperation, dataClassificationAttributeSymbol); + ValidateDataClassification(diagnosticReporter, arrayElementReferenceOperation.ArrayReference, reportOperation, dataClassificationAttributeSymbol, reportTypesWithDataClassification); } - else + else if (reportTypesWithDataClassification) { // Check if the operation's type contains members with DataClassificationAttribute var type = operation.Type; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs index f21ce5da..9dc5bc1d 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs @@ -339,4 +339,84 @@ await CreateProjectBuilder() .WithSourceCode(SourceCode) .ValidateAsync(); } + + [Fact] + public async Task Logger_LogInformation_DataClassification_TypeWithClassifiedProperty_ConfigDisabled() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +ILogger logger = null; +PatientInfo p = new(); +logger.LogInformation("{Patient}", p); + +class PatientInfo +{ + [PiiData] public string PatientId { get; set; } + public ulong RecordId { get; set; } +} + +class PiiData : Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute +{ + public PiiData() : base(Microsoft.Extensions.Compliance.Classification.DataClassification.Unknown) { } +} +"""; + await CreateProjectBuilder() + .AddAnalyzerConfiguration("MA0153.report_types_with_data_classification_attributes", "false") + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task Logger_LogInformation_DataClassification_TypeWithClassifiedProperty_ConfigEnabled() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +ILogger logger = null; +PatientInfo p = new(); +logger.LogInformation("{Patient}", [|p|]); + +class PatientInfo +{ + [PiiData] public string PatientId { get; set; } + public ulong RecordId { get; set; } +} + +class PiiData : Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute +{ + public PiiData() : base(Microsoft.Extensions.Compliance.Classification.DataClassification.Unknown) { } +} +"""; + await CreateProjectBuilder() + .AddAnalyzerConfiguration("MA0153.report_types_with_data_classification_attributes", "true") + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task Logger_LogInformation_DataClassification_DirectProperty_ConfigDisabled() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +ILogger logger = null; +logger.LogInformation("{Prop}", [|new Dummy().Prop|]); + +class Dummy +{ + [TaxonomyAttribute()] + public string Prop { get; set; } +} + +class TaxonomyAttribute : Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute +{ + public TaxonomyAttribute() : base(Microsoft.Extensions.Compliance.Classification.DataClassification.Unknown) { } +} +"""; + await CreateProjectBuilder() + .AddAnalyzerConfiguration("MA0153.report_types_with_data_classification_attributes", "false") + .WithSourceCode(SourceCode) + .ValidateAsync(); + } } From 2518ec214197af78622230bbb4bceefea74f4a85 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 03:57:30 +0000 Subject: [PATCH 5/7] Change default value of report_types_with_data_classification_attributes to false Co-authored-by: meziantou <509220+meziantou@users.noreply.github.com> --- docs/Rules/MA0153.md | 8 ++++---- .../Rules/DoNotLogClassifiedDataAnalyzer.cs | 2 +- .../Rules/DoNotLogClassifiedDataAnalyzerTests.cs | 3 +++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/Rules/MA0153.md b/docs/Rules/MA0153.md index 3839911e..5a694738 100644 --- a/docs/Rules/MA0153.md +++ b/docs/Rules/MA0153.md @@ -3,9 +3,9 @@ Source: [DoNotLogClassifiedDataAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs) -Detects when a log parameter is decorated with an attribute that inherits from `Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute`, or when logging an object whose type contains members (properties or fields) decorated with such attributes. +Detects when a log parameter is decorated with an attribute that inherits from `Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute`. -Since there could be multiple log providers, any type that has sensitive attributes should not be logged directly, as some providers could log inner fields/properties and ignore the attributes. +Optionally (when configured), it can also detect when logging an object whose type contains members (properties or fields) decorated with such attributes. Since there could be multiple log providers, any type that has sensitive attributes should not be logged directly, as some providers could log inner fields/properties and ignore the attributes. ````c# using Microsoft.Extensions.Logging; @@ -15,7 +15,7 @@ ILogger logger; // non-compliant as Prop is decorated with an attribute that inherits from DataClassificationAttribute logger.LogInformation("{Prop}", new Dummy().Prop); -// non-compliant as PatientInfo contains properties decorated with DataClassificationAttribute +// non-compliant as PatientInfo contains properties decorated with DataClassificationAttribute (when MA0153.report_types_with_data_classification_attributes is enabled) PatientInfo patient = new(); logger.LogInformation("{Patient}", patient); @@ -49,5 +49,5 @@ class PiiAttribute : Microsoft.Extensions.Compliance.Classification.DataClassifi ```` [*.cs] # Enable or disable reporting objects containing properties/fields with DataClassificationAttribute -MA0153.report_types_with_data_classification_attributes = true|false # Default: true +MA0153.report_types_with_data_classification_attributes = true|false # Default: false ```` diff --git a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs index e6e8192f..42b8b53d 100644 --- a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs @@ -89,7 +89,7 @@ private static bool GetReportTypesWithDataClassificationConfiguration(AnalyzerCo return result; } - return true; // Default to true (enabled by default) + return false; // Default to false (disabled by default) } private void ValidateDataClassification(DiagnosticReporter diagnosticReporter, IEnumerable operations, bool reportTypesWithDataClassification) diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs index 9dc5bc1d..be9719af 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs @@ -219,6 +219,7 @@ public PiiData() : base(Microsoft.Extensions.Compliance.Classification.DataClass } """; await CreateProjectBuilder() + .AddAnalyzerConfiguration("MA0153.report_types_with_data_classification_attributes", "true") .WithSourceCode(SourceCode) .ValidateAsync(); } @@ -245,6 +246,7 @@ public PiiData() : base(Microsoft.Extensions.Compliance.Classification.DataClass } """; await CreateProjectBuilder() + .AddAnalyzerConfiguration("MA0153.report_types_with_data_classification_attributes", "true") .WithSourceCode(SourceCode) .ValidateAsync(); } @@ -296,6 +298,7 @@ public PiiData() : base(Microsoft.Extensions.Compliance.Classification.DataClass } """; await CreateProjectBuilder() + .AddAnalyzerConfiguration("MA0153.report_types_with_data_classification_attributes", "true") .WithSourceCode(SourceCode) .ValidateAsync(); } From cb5885fef31aa628ce361219f937cb40f97e0ce5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 04:04:14 +0000 Subject: [PATCH 6/7] Use GetConfigurationValue extension method to simplify configuration reading Co-authored-by: meziantou <509220+meziantou@users.noreply.github.com> --- .../Rules/DoNotLogClassifiedDataAnalyzer.cs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs index 42b8b53d..9e6daa41 100644 --- a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -61,8 +62,7 @@ public void AnalyzeInvocationDeclaration(OperationAnalysisContext context) var operation = (IInvocationOperation)context.Operation; if (operation.TargetMethod.ContainingType.IsEqualTo(LoggerExtensionsSymbol) && FindLogParameters(operation.TargetMethod, out var argumentsParameter)) { - var options = context.Options.AnalyzerConfigOptionsProvider.GetOptions(operation.Syntax.SyntaxTree); - var reportTypesWithDataClassification = GetReportTypesWithDataClassificationConfiguration(options); + var reportTypesWithDataClassification = context.Options.GetConfigurationValue(operation, RuleIdentifiers.DoNotLogClassifiedData + ".report_types_with_data_classification_attributes", defaultValue: false); foreach (var argument in operation.Arguments) { @@ -81,17 +81,6 @@ public void AnalyzeInvocationDeclaration(OperationAnalysisContext context) } } - private static bool GetReportTypesWithDataClassificationConfiguration(AnalyzerConfigOptions options) - { - if (options.TryGetValue(RuleIdentifiers.DoNotLogClassifiedData + ".report_types_with_data_classification_attributes", out var value)) - { - if (bool.TryParse(value, out var result)) - return result; - } - - return false; // Default to false (disabled by default) - } - private void ValidateDataClassification(DiagnosticReporter diagnosticReporter, IEnumerable operations, bool reportTypesWithDataClassification) { foreach (var operation in operations) From a30a69968e01afbe34390fa5c74481f6f4df17ee Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 05:16:54 +0000 Subject: [PATCH 7/7] Use GetAllMembers to check inherited members for DataClassificationAttribute Co-authored-by: meziantou <509220+meziantou@users.noreply.github.com> --- .../Rules/DoNotLogClassifiedDataAnalyzer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs index 9e6daa41..78cb5dc2 100644 --- a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs @@ -159,8 +159,8 @@ private static bool TypeContainsMembersWithDataClassification(ITypeSymbol type, if (type.SpecialType != SpecialType.None) return false; - // Check all members (properties and fields) - foreach (var member in type.GetMembers()) + // Check all members (properties and fields) including inherited members + foreach (var member in type.GetAllMembers()) { if (member is IPropertySymbol property) {