Files
git.stella-ops.org/src/Scanner/StellaOps.Scanner.Analyzers.Native/ServiceCollectionExtensions.cs
2026-01-13 18:53:39 +02:00

203 lines
6.8 KiB
C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using StellaOps.Determinism;
using StellaOps.Scanner.Analyzers.Native.Plugin;
using StellaOps.Scanner.Analyzers.Native.RuntimeCapture;
namespace StellaOps.Scanner.Analyzers.Native;
/// <summary>
/// Extension methods for registering native analyzer services with DI.
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// Configuration section name for native analyzer options.
/// </summary>
public const string ConfigSectionName = "Scanner:Analyzers:Native";
/// <summary>
/// Adds the native analyzer services to the service collection.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configuration">Configuration for binding options.</param>
/// <returns>Service collection for chaining.</returns>
public static IServiceCollection AddNativeAnalyzer(
this IServiceCollection services,
IConfiguration? configuration = null)
{
return services.AddNativeAnalyzer(configuration, null);
}
/// <summary>
/// Adds the native analyzer services to the service collection.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configure">Optional action to configure options.</param>
/// <returns>Service collection for chaining.</returns>
public static IServiceCollection AddNativeAnalyzer(
this IServiceCollection services,
Action<NativeAnalyzerServiceOptions>? configure)
{
return services.AddNativeAnalyzer(null, configure);
}
/// <summary>
/// Adds the native analyzer services to the service collection.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configuration">Configuration for binding options.</param>
/// <param name="configure">Optional action to configure options.</param>
/// <returns>Service collection for chaining.</returns>
public static IServiceCollection AddNativeAnalyzer(
this IServiceCollection services,
IConfiguration? configuration,
Action<NativeAnalyzerServiceOptions>? configure)
{
// Register options
var optionsBuilder = services.AddOptions<NativeAnalyzerServiceOptions>();
if (configuration != null)
{
optionsBuilder.Bind(configuration.GetSection(ConfigSectionName));
}
if (configure != null)
{
optionsBuilder.Configure(configure);
}
// Register core services
services.TryAddSingleton<INativeAnalyzerPluginCatalog, NativeAnalyzerPluginCatalog>();
services.TryAddSingleton<INativeAnalyzer, NativeAnalyzer>();
services.TryAddSingleton<IValidateOptions<ElfSectionHashOptions>, ElfSectionHashOptionsValidator>();
var sectionOptionsBuilder = services.AddOptions<ElfSectionHashOptions>();
if (configuration != null)
{
sectionOptionsBuilder.Bind(configuration.GetSection($"{ConfigSectionName}:SectionHashes"));
}
sectionOptionsBuilder.ValidateOnStart();
services.TryAddSingleton<IElfSectionHashExtractor, ElfSectionHashExtractor>();
return services;
}
/// <summary>
/// Adds runtime capture adapter services (optional, requires elevated privileges).
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configure">Optional action to configure runtime capture options.</param>
/// <returns>Service collection for chaining.</returns>
public static IServiceCollection AddNativeRuntimeCapture(
this IServiceCollection services,
Action<RuntimeCaptureOptions>? configure = null)
{
services.AddDeterminismDefaults();
var optionsBuilder = services.AddOptions<RuntimeCaptureOptions>();
if (configure != null)
{
optionsBuilder.Configure(configure);
}
// Register platform-appropriate capture adapter
services.TryAddSingleton<IRuntimeCaptureAdapter>(sp =>
{
var timeProvider = sp.GetRequiredService<TimeProvider>();
var guidProvider = sp.GetRequiredService<IGuidProvider>();
var adapter = RuntimeCaptureAdapterFactory.CreateForCurrentPlatform(timeProvider, guidProvider);
if (adapter == null)
{
throw new PlatformNotSupportedException(
"Runtime capture is not supported on this platform.");
}
return adapter;
});
return services;
}
}
/// <summary>
/// Configuration options for native analyzer services.
/// </summary>
public sealed class NativeAnalyzerServiceOptions
{
/// <summary>
/// Directory for loading additional native analyzer plugins.
/// Default: plugins/scanner/analyzers/native
/// </summary>
public string PluginDirectory { get; set; } = "plugins/scanner/analyzers/native";
/// <summary>
/// Whether to enable heuristic scanning by default.
/// Default: true.
/// </summary>
public bool EnableHeuristicScanning { get; set; } = true;
/// <summary>
/// Whether to enable dependency resolution by default.
/// Default: true.
/// </summary>
public bool EnableResolution { get; set; } = true;
/// <summary>
/// Default timeout per binary analysis.
/// Default: 30 seconds.
/// </summary>
public TimeSpan DefaultTimeout { get; set; } = TimeSpan.FromSeconds(30);
/// <summary>
/// Default search paths for Linux (ELF).
/// </summary>
public List<string> LinuxDefaultSearchPaths { get; set; } =
[
"/lib",
"/lib64",
"/usr/lib",
"/usr/lib64",
"/usr/local/lib",
"/lib/x86_64-linux-gnu",
"/usr/lib/x86_64-linux-gnu"
];
/// <summary>
/// Default search paths for Windows (PE).
/// </summary>
public List<string> WindowsDefaultSearchPaths { get; set; } =
[
@"C:\Windows\System32",
@"C:\Windows\SysWOW64",
@"C:\Windows"
];
/// <summary>
/// Default search paths for macOS (Mach-O).
/// </summary>
public List<string> MacOSDefaultSearchPaths { get; set; } =
[
"/usr/lib",
"/usr/local/lib",
"/Library/Frameworks",
"/System/Library/Frameworks"
];
/// <summary>
/// Gets the default search paths for the specified format.
/// </summary>
public IReadOnlyList<string> GetDefaultSearchPathsForFormat(NativeFormat format)
{
return format switch
{
NativeFormat.Elf => LinuxDefaultSearchPaths,
NativeFormat.Pe => WindowsDefaultSearchPaths,
NativeFormat.MachO => MacOSDefaultSearchPaths,
_ => []
};
}
}