-
Notifications
You must be signed in to change notification settings - Fork 123
Add ability to use syft binary instead of container for Linux detector #1776
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| namespace Microsoft.ComponentDetection.Detectors.Linux; | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.ComponentDetection.Contracts; | ||
| using Microsoft.Extensions.Logging; | ||
|
|
||
| /// <summary> | ||
| /// Runs Syft by invoking a local Syft binary. | ||
| /// </summary> | ||
| internal class BinarySyftRunner : ISyftRunner | ||
| { | ||
| private static readonly SemaphoreSlim BinarySemaphore = new(2); | ||
|
|
||
| private static readonly int SemaphoreTimeout = Convert.ToInt32( | ||
| TimeSpan.FromHours(1).TotalMilliseconds); | ||
|
|
||
| private readonly string syftBinaryPath; | ||
| private readonly ICommandLineInvocationService commandLineInvocationService; | ||
| private readonly ILogger<BinarySyftRunner> logger; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="BinarySyftRunner"/> class. | ||
| /// </summary> | ||
| /// <param name="syftBinaryPath">The path to the Syft binary.</param> | ||
| /// <param name="commandLineInvocationService">The command line invocation service.</param> | ||
| /// <param name="logger">The logger.</param> | ||
| public BinarySyftRunner( | ||
| string syftBinaryPath, | ||
| ICommandLineInvocationService commandLineInvocationService, | ||
| ILogger<BinarySyftRunner> logger) | ||
| { | ||
| this.syftBinaryPath = syftBinaryPath; | ||
| this.commandLineInvocationService = commandLineInvocationService; | ||
| this.logger = logger; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public async Task<bool> CanRunAsync(CancellationToken cancellationToken = default) | ||
| { | ||
| if (!await this.commandLineInvocationService.CanCommandBeLocatedAsync(this.syftBinaryPath, null, "--version")) | ||
| { | ||
| this.logger.LogInformation("Syft binary at {SyftBinaryPath} was not found in the local environment.", this.syftBinaryPath); | ||
| return false; | ||
| } | ||
|
|
||
| var result = await this.commandLineInvocationService.ExecuteCommandAsync( | ||
| this.syftBinaryPath, | ||
| null, | ||
| null, | ||
| cancellationToken, | ||
| "--version"); | ||
|
|
||
| if (result.ExitCode != 0) | ||
| { | ||
| this.logger.LogInformation( | ||
| "Syft binary at {SyftBinaryPath} failed version check with exit code {ExitCode}. Stderr: {StdErr}", | ||
| this.syftBinaryPath, | ||
| result.ExitCode, | ||
| result.StdErr); | ||
| return false; | ||
| } | ||
|
|
||
| this.logger.LogInformation( | ||
| "Using Syft binary at {SyftBinaryPath}: {SyftVersion}", | ||
| this.syftBinaryPath, | ||
| result.StdOut?.Trim()); | ||
| return true; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public async Task<(string Stdout, string Stderr)> RunSyftAsync( | ||
| ImageReference imageReference, | ||
| IList<string> arguments, | ||
| CancellationToken cancellationToken = default) | ||
| { | ||
| var (syftSourceKind, syftSource) = GetSyftSource(imageReference); | ||
| var acquired = false; | ||
|
|
||
| try | ||
| { | ||
| acquired = await BinarySemaphore.WaitAsync(SemaphoreTimeout, cancellationToken); | ||
| if (!acquired) | ||
| { | ||
| this.logger.LogWarning( | ||
| "Failed to enter the binary semaphore for image {ImageReference}", | ||
| imageReference.Reference); | ||
| return (string.Empty, string.Empty); | ||
| } | ||
|
Comment on lines
+83
to
+92
|
||
|
|
||
| var parameters = new[] { syftSource, ISyftRunner.SyftSourceKindArgument, syftSourceKind } | ||
| .Concat(arguments) | ||
| .ToArray(); | ||
|
|
||
| var result = await this.commandLineInvocationService.ExecuteCommandAsync( | ||
| this.syftBinaryPath, | ||
| null, | ||
| null, | ||
| cancellationToken, | ||
| parameters); | ||
|
|
||
| if (result.ExitCode != 0) | ||
| { | ||
| this.logger.LogError( | ||
| "Syft binary exited with code {ExitCode}. Stderr: {StdErr}", | ||
| result.ExitCode, | ||
| result.StdErr); | ||
| } | ||
|
|
||
| return (result.StdOut, result.StdErr); | ||
| } | ||
| finally | ||
| { | ||
| if (acquired) | ||
| { | ||
| BinarySemaphore.Release(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private static (string SyftSourceKind, string SyftSource) GetSyftSource(ImageReference imageReference) => | ||
| (imageReference.GetSyftSourceKind(), imageReference.Reference); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| namespace Microsoft.ComponentDetection.Detectors.Linux; | ||
|
|
||
| using Microsoft.ComponentDetection.Contracts; | ||
| using Microsoft.Extensions.Logging; | ||
|
|
||
| /// <summary> | ||
| /// Factory for creating <see cref="BinarySyftRunner"/> instances. | ||
| /// </summary> | ||
| internal class BinarySyftRunnerFactory : IBinarySyftRunnerFactory | ||
| { | ||
| private readonly ICommandLineInvocationService commandLineInvocationService; | ||
| private readonly ILoggerFactory loggerFactory; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="BinarySyftRunnerFactory"/> class. | ||
| /// </summary> | ||
| /// <param name="commandLineInvocationService">The command line invocation service.</param> | ||
| /// <param name="loggerFactory">The logger factory.</param> | ||
| public BinarySyftRunnerFactory( | ||
| ICommandLineInvocationService commandLineInvocationService, | ||
| ILoggerFactory loggerFactory) | ||
| { | ||
| this.commandLineInvocationService = commandLineInvocationService; | ||
| this.loggerFactory = loggerFactory; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public ISyftRunner Create(string binaryPath) => | ||
| new BinarySyftRunner( | ||
| binaryPath, | ||
| this.commandLineInvocationService, | ||
| this.loggerFactory.CreateLogger<BinarySyftRunner>()); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,144 @@ | ||||||||||||||||||||
| namespace Microsoft.ComponentDetection.Detectors.Linux; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| using System; | ||||||||||||||||||||
| using System.Collections.Generic; | ||||||||||||||||||||
| using System.IO; | ||||||||||||||||||||
| using System.Linq; | ||||||||||||||||||||
| using System.Runtime.InteropServices; | ||||||||||||||||||||
| using System.Threading; | ||||||||||||||||||||
| using System.Threading.Tasks; | ||||||||||||||||||||
| using Microsoft.ComponentDetection.Common.Telemetry.Records; | ||||||||||||||||||||
| using Microsoft.ComponentDetection.Contracts; | ||||||||||||||||||||
| using Microsoft.Extensions.Logging; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Runs Syft by executing a Docker container with the Syft image. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| internal class DockerSyftRunner : IDockerSyftRunner | ||||||||||||||||||||
| { | ||||||||||||||||||||
| internal const string ScannerImage = | ||||||||||||||||||||
| "governancecontainerregistry.azurecr.io/syft:v1.37.0@sha256:48d679480c6d272c1801cf30460556959c01d4826795be31d4fd8b53750b7d91"; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private const string LocalImageMountPoint = "/image"; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private static readonly SemaphoreSlim ContainerSemaphore = new(2); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private static readonly int SemaphoreTimeout = Convert.ToInt32( | ||||||||||||||||||||
| TimeSpan.FromHours(1).TotalMilliseconds); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private readonly IDockerService dockerService; | ||||||||||||||||||||
| private readonly ILogger<DockerSyftRunner> logger; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Initializes a new instance of the <see cref="DockerSyftRunner"/> class. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| /// <param name="dockerService">The docker service.</param> | ||||||||||||||||||||
| /// <param name="logger">The logger.</param> | ||||||||||||||||||||
| public DockerSyftRunner(IDockerService dockerService, ILogger<DockerSyftRunner> logger) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| this.dockerService = dockerService; | ||||||||||||||||||||
| this.logger = logger; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <inheritdoc/> | ||||||||||||||||||||
| public async Task<bool> CanRunAsync(CancellationToken cancellationToken = default) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| if (await this.dockerService.CanRunLinuxContainersAsync(cancellationToken)) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| return true; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| using var record = new LinuxContainerDetectorUnsupportedOs | ||||||||||||||||||||
| { | ||||||||||||||||||||
| Os = RuntimeInformation.OSDescription, | ||||||||||||||||||||
| }; | ||||||||||||||||||||
| this.logger.LogInformation("Linux containers are not available on this host."); | ||||||||||||||||||||
| return false; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <inheritdoc/> | ||||||||||||||||||||
| public async Task<(string Stdout, string Stderr)> RunSyftAsync( | ||||||||||||||||||||
| ImageReference imageReference, | ||||||||||||||||||||
| IList<string> arguments, | ||||||||||||||||||||
| CancellationToken cancellationToken = default) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| var (syftSourceKind, syftSource, additionalBinds) = GetSyftSourceAndBinds(imageReference); | ||||||||||||||||||||
| var acquired = false; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| try | ||||||||||||||||||||
| { | ||||||||||||||||||||
| acquired = await ContainerSemaphore.WaitAsync(SemaphoreTimeout, cancellationToken); | ||||||||||||||||||||
| if (!acquired) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| this.logger.LogWarning( | ||||||||||||||||||||
| "Failed to enter the container semaphore for image {ImageReference}", | ||||||||||||||||||||
| imageReference.Reference); | ||||||||||||||||||||
| return (string.Empty, string.Empty); | ||||||||||||||||||||
|
Comment on lines
+73
to
+76
|
||||||||||||||||||||
| this.logger.LogWarning( | |
| "Failed to enter the container semaphore for image {ImageReference}", | |
| imageReference.Reference); | |
| return (string.Empty, string.Empty); | |
| var stderr = $"Failed to acquire the container semaphore for image {imageReference.Reference}."; | |
| this.logger.LogWarning( | |
| "{SemaphoreFailureMessage}", | |
| stderr); | |
| return (string.Empty, stderr); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| namespace Microsoft.ComponentDetection.Detectors.Linux; | ||
|
|
||
| /// <summary> | ||
| /// Factory for creating binary-based Syft runners. | ||
| /// </summary> | ||
| public interface IBinarySyftRunnerFactory | ||
| { | ||
| /// <summary> | ||
| /// Creates a binary Syft runner configured to use the specified binary path. | ||
| /// </summary> | ||
| /// <param name="binaryPath">The path to the Syft binary.</param> | ||
| /// <returns>An <see cref="ISyftRunner"/> that invokes the specified Syft binary.</returns> | ||
| ISyftRunner Create(string binaryPath); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| namespace Microsoft.ComponentDetection.Detectors.Linux; | ||
|
|
||
| /// <summary> | ||
| /// Marker interface for the Docker-based Syft runner. | ||
| /// Runs Syft by executing a Docker container with the Syft image. | ||
| /// </summary> | ||
| public interface IDockerSyftRunner : ISyftRunner | ||
| { | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.