-
Notifications
You must be signed in to change notification settings - Fork 124
Expand file tree
/
Copy pathBinarySyftRunner.cs
More file actions
133 lines (118 loc) · 4.54 KB
/
BinarySyftRunner.cs
File metadata and controls
133 lines (118 loc) · 4.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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)
{
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 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);
}
var parameters = new[] { syftSource }
.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();
}
}
}
/// <summary>
/// Constructs the Syft source argument from an image reference.
/// For local images, the host path is used directly with the appropriate scheme prefix.
/// </summary>
private static string GetSyftSource(ImageReference imageReference) =>
imageReference.Kind switch
{
ImageReferenceKind.DockerImage => imageReference.Reference,
ImageReferenceKind.OciLayout => $"oci-dir:{imageReference.Reference}",
ImageReferenceKind.OciArchive => $"oci-archive:{imageReference.Reference}",
ImageReferenceKind.DockerArchive => $"docker-archive:{imageReference.Reference}",
_ => throw new ArgumentOutOfRangeException(
nameof(imageReference),
$"Unsupported image reference kind '{imageReference.Kind}'."),
};
}