File: Microsoft.NetCore.Analyzers\Security\DoNotAlwaysSkipTokenValidationInDelegatesTests.cs
Web Access
Project: ..\..\..\src\Microsoft.CodeAnalysis.NetAnalyzers\tests\Microsoft.CodeAnalysis.NetAnalyzers.UnitTests\Microsoft.CodeAnalysis.NetAnalyzers.UnitTests.csproj (Microsoft.CodeAnalysis.NetAnalyzers.UnitTests)
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the MIT license.  See License.txt in the project root for license information.
 
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Testing;
using Test.Utilities;
using Xunit;
using VerifyCS = Test.Utilities.CSharpSecurityCodeFixVerifier<
    Microsoft.NetCore.Analyzers.Security.DoNotAlwaysSkipTokenValidationInDelegates,
    Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>;
 
namespace Microsoft.NetCore.Analyzers.Security.UnitTests
{
    public class DoNotAlwaysSkipTokenValidationInDelegatesTests
    {
        [Theory]
        [InlineData("AudienceValidator = [|(a, b, c)")]
        [InlineData("LifetimeValidator = [|(a, b, c, d)")]
        public async Task TestLambdaDiagnostic(string declaration)
        {
            string code = @$"
using System;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{{
    public void TestMethod()
    {{
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.{declaration} => {{ return true; }}|];
    }}
}}";
 
            await VerifyCSharpAnalyzerAsync(code);
        }
 
        [Theory]
        [InlineData("AudienceValidator = [|(a, b, c)")]
        [InlineData("LifetimeValidator = [|(a, b, c, d)")]
        public async Task TestLambdaWithLiteralValueDiagnostic(string declaration)
        {
            string code = @$"
using System;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{{
    public void TestMethod()
    {{
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.{declaration} => true|];
    }}
}}";
            await VerifyCSharpAnalyzerAsync(code);
        }
 
        [Theory]
        [InlineData("AudienceValidator")]
        [InlineData("LifetimeValidator")]
        public async Task TestAnonymousMethodDiagnostic(string declaration)
        {
            string code = @$"
using System;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{{
    public void TestMethod()
    {{
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.{declaration} = [|delegate {{ return true; }}|];
    }}
}}";
            await VerifyCSharpAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task TestDelegateCreationLocalFunctionDiagnostic_LifetimeValidator()
        {
            await VerifyCSharpAnalyzerAsync(@"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.LifetimeValidator = [|new LifetimeValidator(AcceptAllLifetimes)|];
 
        bool AcceptAllLifetimes(
            DateTime? start,
            DateTime? end,
            SecurityToken securityToken,
            TokenValidationParameters validationParameters)
        {
            return true;
        }
    }
}");
        }
 
        [Fact]
        public async Task TestDelegateCreationLocalFunctionDiagnostic_AudienceValidator()
        {
            await VerifyCSharpAnalyzerAsync(@"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.AudienceValidator = [|new AudienceValidator(AcceptAllAudiences)|];
 
        bool AcceptAllAudiences(
            IEnumerable<string> audiences,
            SecurityToken securityToken,
            TokenValidationParameters validationParameters)
        {
            return true;
        }
    }
}");
        }
 
        [Fact]
        public async Task TestDelegateCreationDiagnostic_LifetimeValidator()
        {
            await VerifyCSharpAnalyzerAsync(@"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{
    bool AcceptAllLifetimes(
        DateTime? start,
        DateTime? end,
        SecurityToken securityToken,
        TokenValidationParameters validationParameters)
    {
        return true;
    }
 
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.LifetimeValidator = [|new LifetimeValidator(AcceptAllLifetimes)|];
    }
}");
        }
 
        [Fact]
        public async Task TestDelegateCreationDiagnostic_AudienceValidator()
        {
            await VerifyCSharpAnalyzerAsync(@"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{
    bool AcceptAllAudiences(
        IEnumerable<string> audiences,
        SecurityToken securityToken,
        TokenValidationParameters validationParameters)
    {
        return true;
    }
 
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.AudienceValidator = [|new AudienceValidator(AcceptAllAudiences)|];
    }
}");
        }
 
        [Fact]
        public async Task TestDelegateCreationNormalMethodWithLambdaDiagnostic_AudienceValidator()
        {
            await VerifyCSharpAnalyzerAsync(@"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{
    bool AcceptAllAudiences(
        IEnumerable<string> audiences,
        SecurityToken securityToken,
        TokenValidationParameters validationParameters) => true;
 
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.AudienceValidator = [|new AudienceValidator(AcceptAllAudiences)|];
    }
}");
        }
 
        // Ideally we could detect this but we'll have to rely on CodeQL instead for more robust detection.
        [Fact]
        public async Task TestDelegatedMethodFromDifferentAssemblyNoDiagnostic()
        {
            string source1 = @"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
namespace AcceptAllAudiencesNamespace
{
    public class AcceptAllAudiencesClass
    {
        public static bool AcceptAllAudiences(
            IEnumerable<string> audiences,
            SecurityToken securityToken,
            TokenValidationParameters validationParameters)
        {
            return true;
        }
    }
}";
 
            var source2 = @"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
using AcceptAllAudiencesNamespace;
 
class TestClass
{
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.AudienceValidator = new AudienceValidator(AcceptAllAudiencesClass.AcceptAllAudiences);
    }
}";
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithWilson,
                TestState =
                {
                    Sources = { source2 },
                },
                SolutionTransforms =
                {
                    (solution, projectId) =>
                    {
                        var sideProject = solution.AddProject("DependencyProject", "DependencyProject", LanguageNames.CSharp)
                            .AddDocument("Dependency.cs", source1).Project
                            .AddMetadataReferences(solution.GetProject(projectId).MetadataReferences)
                            .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
 
                        return sideProject.Solution.GetProject(projectId)
                            .AddProjectReference(new ProjectReference(sideProject.Id))
                            .Solution;
                    }
                }
            }.RunAsync();
        }
 
        // Ideally we could detect this but we'll have to rely on CodeQL instead for more robust detection.
        [Fact]
        public async Task TestDelegatedMethodFromLocalFromDifferentAssemblyNoDiagnostic()
        {
            string source1 = @"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
namespace AcceptAllAudiencesNamespace
{
    public class AcceptAllAudiencesClass
    {
        public static bool AcceptAllAudiences2(
            IEnumerable<string> audiences,
            SecurityToken securityToken,
            TokenValidationParameters validationParameters)
        {
            return true;
        }
    }
}";
 
            var source2 = @"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
using AcceptAllAudiencesNamespace;
 
class TestClass
{
    public bool AcceptAllAudiences(
        IEnumerable<string> audiences,
        SecurityToken securityToken,
        TokenValidationParameters validationParameters)
    {
        return AcceptAllAudiencesClass.AcceptAllAudiences2(audiences, securityToken, validationParameters);
    }
 
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.AudienceValidator = new AudienceValidator(AcceptAllAudiences);
    }
}";
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithWilson,
                TestState =
                {
                    Sources = { source2 },
                },
                SolutionTransforms =
                {
                    (solution, projectId) =>
                    {
                        var sideProject = solution.AddProject("DependencyProject", "DependencyProject", LanguageNames.CSharp)
                            .AddDocument("Dependency.cs", source1).Project
                            .AddMetadataReferences(solution.GetProject(projectId).MetadataReferences)
                            .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
 
                        return sideProject.Solution.GetProject(projectId)
                            .AddProjectReference(new ProjectReference(sideProject.Id))
                            .Solution;
                    }
                }
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestLambdaNoDiagnostic()
        {
            await VerifyCSharpAnalyzerAsync(@"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.AudienceValidator = (a, b, c) => { if(a != null) {return true;} return false;};
    }
}");
        }
 
        [Fact]
        public async Task TestLambdaWithLiteralValueNoDiagnostic()
        {
            await VerifyCSharpAnalyzerAsync(@"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.AudienceValidator = (a, b, c) => false;
    }
}");
        }
 
        [Fact]
        public async Task TestAnonymousMethodNoDiagnostic()
        {
            await VerifyCSharpAnalyzerAsync(@"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.AudienceValidator += delegate { return false; };
    }
}");
        }
 
        [Fact]
        public async Task TestDelegateCreationLocalFunctionNoDiagnostic()
        {
            await VerifyCSharpAnalyzerAsync(@"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.AudienceValidator  = new AudienceValidator(AcceptAllAudiences);
 
        bool AcceptAllAudiences(
            IEnumerable<string> audiences,
            SecurityToken securityToken,
            TokenValidationParameters validationParameters)
        {
            if(audiences != null)
            {
                return true;
            }
 
            return false;
        }
    }
}");
        }
 
        [Fact]
        public async Task TestDelegateCreationNoDiagnostic()
        {
            await VerifyCSharpAnalyzerAsync(@"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{
    public bool AcceptAllAudiences(
        IEnumerable<string> audiences,
        SecurityToken securityToken,
        TokenValidationParameters validationParameters)
    {
        if(audiences != null)
        {
            return true;
        }
 
        return false;
    }
 
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.AudienceValidator  = new AudienceValidator(AcceptAllAudiences);
    }
}");
        }
 
        [Fact]
        public async Task TestDelegateCreationNoDiagnostic2()
        {
            await VerifyCSharpAnalyzerAsync(@"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{
    public bool AcceptAllAudiences(
        IEnumerable<string> audiences,
        SecurityToken securityToken,
        TokenValidationParameters validationParameters)
    {
        if(audiences != null)
        {
            return true;
        }
 
        return false;
    }
 
    public void TestMethod()
    {
    }
}");
        }
 
        [Fact]
        public async Task TestDelegateCreationNormalMethodWithLambdaNoDiagnostic()
        {
            await VerifyCSharpAnalyzerAsync(@"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{
    public bool AcceptAllAudiences(
        IEnumerable<string> audiences,
        SecurityToken securityToken,
        TokenValidationParameters validationParameters) => false;
 
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.AudienceValidator  = new AudienceValidator(AcceptAllAudiences);
    }
}");
        }
 
        // Ideally we could detect this but we'll have to rely on CodeQL instead for more robust detection.
        [Fact]
        public async Task TestDelegateCreationFromLocalFromLocalNoDiagnostic()
        {
            await VerifyCSharpAnalyzerAsync(@"
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
 
class TestClass
{
    public bool AcceptAllAudiences2(
        IEnumerable<string> audiences,
        SecurityToken securityToken,
        TokenValidationParameters validationParameters)
    {
        return true;
    }
 
    public bool AcceptAllAudiences(
        IEnumerable<string> audiences,
        SecurityToken securityToken,
        TokenValidationParameters validationParameters)
    {
        return AcceptAllAudiences2(
          audiences,
          securityToken,
          validationParameters);
    }
 
    public void TestMethod()
    {
        TokenValidationParameters parameters = new TokenValidationParameters();
        parameters.AudienceValidator  = new AudienceValidator(AcceptAllAudiences);
    }
}");
        }
 
        private static async Task VerifyCSharpAnalyzerAsync(string source, params DiagnosticResult[] expected)
        {
            var csharpTest = new VerifyCS.Test
            {
                ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithWilson,
                TestState =
                {
                    Sources = { source },
                },
            };
 
            csharpTest.ExpectedDiagnostics.AddRange(expected);
 
            await csharpTest.RunAsync();
        }
    }
}