132 lines
4.5 KiB
C#
132 lines
4.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CodeActions;
|
|
using Microsoft.CodeAnalysis.CodeFixes;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.Diagnostics;
|
|
using Microsoft.CodeAnalysis.Text;
|
|
using Xunit;
|
|
|
|
|
|
using StellaOps.TestKit;
|
|
namespace StellaOps.AirGap.Policy.Analyzers.Tests;
|
|
|
|
public sealed class HttpClientUsageAnalyzerTests
|
|
{
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task ReportsDiagnostic_ForNewHttpClient()
|
|
{
|
|
const string source = """
|
|
using System.Net.Http;
|
|
|
|
namespace Sample.App;
|
|
|
|
public sealed class Demo
|
|
{
|
|
public void Run()
|
|
{
|
|
var client = new HttpClient();
|
|
}
|
|
}
|
|
""";
|
|
|
|
var diagnostics = await AnalyzeAsync(source, assemblyName: "Sample.App");
|
|
Assert.Contains(diagnostics, d => d.Id == HttpClientUsageAnalyzer.DiagnosticId);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task DoesNotReportDiagnostic_InsidePolicyAssembly()
|
|
{
|
|
const string source = """
|
|
using System.Net.Http;
|
|
|
|
namespace StellaOps.AirGap.Policy.Internal;
|
|
|
|
internal static class Loopback
|
|
{
|
|
public static HttpClient Create() => new HttpClient();
|
|
}
|
|
""";
|
|
|
|
var diagnostics = await AnalyzeAsync(source, assemblyName: "StellaOps.AirGap.Policy");
|
|
Assert.DoesNotContain(diagnostics, d => d.Id == HttpClientUsageAnalyzer.DiagnosticId);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task DoesNotReportDiagnostic_ForTestingAssemblyNames()
|
|
{
|
|
const string source = """
|
|
using System.Net.Http;
|
|
|
|
namespace Sample.App;
|
|
|
|
public sealed class Demo
|
|
{
|
|
public void Run()
|
|
{
|
|
var client = new HttpClient();
|
|
}
|
|
}
|
|
""";
|
|
|
|
var diagnostics = await AnalyzeAsync(source, assemblyName: "Sample.App.Testing");
|
|
Assert.DoesNotContain(diagnostics, d => d.Id == HttpClientUsageAnalyzer.DiagnosticId);
|
|
}
|
|
|
|
private static async Task<ImmutableArray<Diagnostic>> AnalyzeAsync(string source, string assemblyName)
|
|
{
|
|
var compilation = CSharpCompilation.Create(
|
|
assemblyName,
|
|
new[]
|
|
{
|
|
CSharpSyntaxTree.ParseText(source),
|
|
CSharpSyntaxTree.ParseText(PolicyStubSource),
|
|
},
|
|
CreateMetadataReferences(),
|
|
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
|
|
|
|
var analyzer = new HttpClientUsageAnalyzer();
|
|
var compilationWithAnalyzers = compilation.WithAnalyzers(ImmutableArray.Create<DiagnosticAnalyzer>(analyzer));
|
|
return await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync();
|
|
}
|
|
|
|
private static IEnumerable<MetadataReference> CreateMetadataReferences()
|
|
{
|
|
yield return MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location);
|
|
yield return MetadataReference.CreateFromFile(typeof(Uri).GetTypeInfo().Assembly.Location);
|
|
yield return MetadataReference.CreateFromFile(typeof(HttpClient).GetTypeInfo().Assembly.Location);
|
|
yield return MetadataReference.CreateFromFile(typeof(Enumerable).GetTypeInfo().Assembly.Location);
|
|
}
|
|
|
|
private const string PolicyStubSource = """
|
|
namespace StellaOps.AirGap.Policy
|
|
{
|
|
public interface IEgressPolicy
|
|
{
|
|
void EnsureAllowed(EgressRequest request);
|
|
}
|
|
|
|
public readonly record struct EgressRequest(string Component, System.Uri Destination, string Intent);
|
|
|
|
public static class EgressHttpClientFactory
|
|
{
|
|
public static System.Net.Http.HttpClient Create(IEgressPolicy egressPolicy, EgressRequest request)
|
|
=> throw new System.NotImplementedException();
|
|
|
|
public static System.Net.Http.HttpClient Create(IEgressPolicy egressPolicy, EgressRequest request, System.Func<System.Net.Http.HttpClient> clientFactory)
|
|
=> throw new System.NotImplementedException();
|
|
}
|
|
}
|
|
""";
|
|
}
|