// ----------------------------------------------------------------------------- // ToolManager.cs // Sprint: SPRINT_5100_0003_0001_sbom_interop_roundtrip // Task: T1 - Interop Test Harness // Description: Manages execution of external tools (Syft, Grype, cosign). // ----------------------------------------------------------------------------- using System.Diagnostics; namespace StellaOps.Interop.Tests; public sealed class ToolManager { private readonly string _workDir; public ToolManager(string workDir) { _workDir = workDir; } public async Task VerifyToolAsync(string tool, string versionArg) { var result = await RunAsync(tool, versionArg, CancellationToken.None); if (!result.Success) { throw new InvalidOperationException( $"Tool '{tool}' is not available or failed verification: {result.Error}"); } } public async Task RunAsync( string tool, string arguments, CancellationToken ct, int timeoutSeconds = 300) { try { using var process = new Process { StartInfo = new ProcessStartInfo { FileName = tool, Arguments = arguments, WorkingDirectory = _workDir, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true } }; process.Start(); var outputTask = process.StandardOutput.ReadToEndAsync(ct); var errorTask = process.StandardError.ReadToEndAsync(ct); var completed = await Task.WhenAny( process.WaitForExitAsync(ct), Task.Delay(TimeSpan.FromSeconds(timeoutSeconds), ct)); if (!process.HasExited) { process.Kill(entireProcessTree: true); return new ToolResult(false, "", "Process timed out"); } var output = await outputTask; var error = await errorTask; if (process.ExitCode != 0) { return new ToolResult(false, output, error); } return new ToolResult(true, output); } catch (Exception ex) { return new ToolResult(false, "", ex.Message); } } }