< Summary

Information
Class: AsiBackbone.Analyzers.SignatureInputRequestAnalyzer
Assembly: AsiBackbone.Analyzers
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Analyzers/SignatureInputRequestAnalyzer.cs
Line coverage
100%
Covered lines: 70
Uncovered lines: 0
Coverable lines: 70
Total lines: 166
Line coverage: 100%
Branch coverage
79%
Covered branches: 59
Total branches: 74
Branch coverage: 79.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
get_SupportedDiagnostics()100%11100%
Initialize(...)100%11100%
AnalyzeObjectCreation(...)83.33%66100%
IsRequestType(...)100%44100%
InitializesSignatureInput(...)100%1212100%
IsStaticallyEmptySignatureInput(...)75%1616100%
IsArrayEmptyInvocation(...)100%88100%
IsStaticallyEmptyByteArray(...)62.5%1616100%
IsMemoryLike(...)60%1010100%
Unwrap(...)100%22100%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Analyzers/SignatureInputRequestAnalyzer.cs

#LineLine coverage
 1using System.Collections.Immutable;
 2using Microsoft.CodeAnalysis;
 3using Microsoft.CodeAnalysis.Diagnostics;
 4using Microsoft.CodeAnalysis.Operations;
 5
 6namespace AsiBackbone.Analyzers;
 7
 8/// <summary>
 9/// Reports signing and verification request construction that omits an explicit signature input.
 10/// </summary>
 11[DiagnosticAnalyzer(LanguageNames.CSharp)]
 12public sealed class SignatureInputRequestAnalyzer : DiagnosticAnalyzer
 13{
 14    /// <summary>
 15    /// Gets the diagnostic identifier reported when a supported request omits
 16    /// an explicit signature input.
 17    /// </summary>
 18    public const string DiagnosticId = "ASIB004";
 19
 20    /// <summary>
 21    /// Gets the name of the property that must be set to avoid falling back to the pre-6.0 hash-only signature input.
 22    /// </summary>
 23    private const string SignatureInputPropertyName = "SignatureInput";
 24
 25    /// <remarks>
 26    /// Each of these request types falls back to the pre-6.0 hash-only signature input when <c>SignatureInput</c> is no
 27    /// set. The fallback is internal, so it never surfaces the <c>ASIB902</c> deprecation warning on its own.
 28    /// </remarks>
 129    private static readonly string[] RequestTypeNames =
 130    [
 131        "AsiBackbone.Core.Signing.SigningRequest",
 132        "AsiBackbone.Core.Signing.SignatureVerificationRequest",
 133        "AsiBackbone.Signing.ManagedKey.ManagedKeySignRequest",
 134    ];
 35
 136    private static readonly DiagnosticDescriptor Rule = new(
 137        DiagnosticId,
 138        "Set SignatureInput on signing and verification requests",
 139        "'{0}' is created without setting SignatureInput, so it falls back to the pre-6.0 hash-only signature input; set
 140        "AsiBackbone.GovernanceSafety",
 141        DiagnosticSeverity.Warning,
 142        isEnabledByDefault: true,
 143        description: "A signing or verification request without an explicit SignatureInput signs or verifies the UTF-8 t
 44
 45    /// <summary>
 46    /// Gets the diagnostics supported by this analyzer.
 47    /// </summary>
 1448    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Rule];
 49
 50    /// <summary>
 51    /// Initializes the analyzer and registers operation analysis.
 52    /// </summary>
 53    /// <param name="context">The analysis context used to register analyzer actions.</param>
 54    public override void Initialize(AnalysisContext context)
 55    {
 1456        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
 1457        context.EnableConcurrentExecution();
 1458        context.RegisterOperationAction(AnalyzeObjectCreation, OperationKind.ObjectCreation);
 1459    }
 60
 61    private static void AnalyzeObjectCreation(OperationAnalysisContext context)
 62    {
 1663        var objectCreation = (IObjectCreationOperation)context.Operation;
 64
 1665        if (objectCreation.Type is not INamedTypeSymbol requestType || !IsRequestType(requestType))
 66        {
 367            return;
 68        }
 69
 1370        if (InitializesSignatureInput(objectCreation.Initializer))
 71        {
 272            return;
 73        }
 74
 1175        context.ReportDiagnostic(
 1176            Diagnostic.Create(
 1177                Rule,
 1178                objectCreation.Syntax.GetLocation(),
 1179                requestType.Name));
 1180    }
 81
 82    private static bool IsRequestType(INamedTypeSymbol type)
 83    {
 1684        string typeName = type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat);
 85
 7186        foreach (string requestTypeName in RequestTypeNames)
 87        {
 2688            if (typeName.Equals(requestTypeName, StringComparison.Ordinal))
 89            {
 1390                return true;
 91            }
 92        }
 93
 394        return false;
 95    }
 96
 97    private static bool InitializesSignatureInput(IObjectOrCollectionInitializerOperation? initializer)
 98    {
 1399        if (initializer is null)
 100        {
 4101            return false;
 102        }
 103
 34104        foreach (IOperation memberInitializer in initializer.Initializers)
 105        {
 9106            if (memberInitializer is ISimpleAssignmentOperation
 9107                {
 9108                    Target: IPropertyReferenceOperation propertyReference
 9109                } assignment
 9110                && propertyReference.Property.Name.Equals(SignatureInputPropertyName, StringComparison.Ordinal)
 9111                && !IsStaticallyEmptySignatureInput(assignment.Value))
 112            {
 2113                return true;
 114            }
 115        }
 116
 7117        return false;
 118    }
 119    private static bool IsStaticallyEmptySignatureInput(IOperation operation)
 120    {
 9121        operation = Unwrap(operation);
 122
 9123        return operation is IDefaultValueOperation || (operation is IPropertyReferenceOperation propertyReference
 9124            && propertyReference.Property.Name.Equals("Empty", StringComparison.Ordinal)
 9125            && IsMemoryLike(propertyReference.Property.ContainingType)) || (operation is IObjectCreationOperation object
 9126            && objectCreation.Arguments.Length == 0
 9127            && IsMemoryLike(objectCreation.Type)) || IsArrayEmptyInvocation(operation) || IsStaticallyEmptyByteArray(ope
 128    }
 129
 130    private static bool IsArrayEmptyInvocation(IOperation operation)
 131    {
 4132        return operation is IInvocationOperation invocation
 4133            && invocation.TargetMethod.Name.Equals("Empty", StringComparison.Ordinal)
 4134            && invocation.TargetMethod.ContainingType.SpecialType == SpecialType.System_Array
 4135            && invocation.TargetMethod.TypeArguments.Length == 1
 4136            && invocation.TargetMethod.TypeArguments[0].SpecialType == SpecialType.System_Byte;
 137    }
 138
 139    private static bool IsStaticallyEmptyByteArray(IOperation operation)
 140    {
 3141        return operation is IArrayCreationOperation arrayCreation
 3142            && arrayCreation.Type is IArrayTypeSymbol arrayType
 3143            && arrayType.ElementType.SpecialType == SpecialType.System_Byte && (arrayCreation.Initializer is { ElementVa
 3144            && arrayCreation.DimensionSizes[0].ConstantValue is { HasValue: true, Value: 0 }));
 145    }
 146
 147    private static bool IsMemoryLike(ITypeSymbol? type)
 148    {
 4149        return type is INamedTypeSymbol namedType
 4150            && namedType.Arity == 1
 4151            && (namedType.Name.Equals("Memory", StringComparison.Ordinal)
 4152                || namedType.Name.Equals("ReadOnlyMemory", StringComparison.Ordinal))
 4153            && namedType.ContainingNamespace?.ToDisplayString().Equals("System", StringComparison.Ordinal) == true;
 154    }
 155
 156    private static IOperation Unwrap(IOperation operation)
 157    {
 14158        while (operation is IConversionOperation conversion)
 159        {
 5160            operation = conversion.Operand;
 5161        }
 162
 9163        return operation;
 164    }
 165
 166}