Skip to content

Add ability to use syft binary instead of container for Linux detector#1776

Draft
jasonpaulos wants to merge 1 commit intomainfrom
users/jasonpaulos/linux-syft-binary-support
Draft

Add ability to use syft binary instead of container for Linux detector#1776
jasonpaulos wants to merge 1 commit intomainfrom
users/jasonpaulos/linux-syft-binary-support

Conversation

@jasonpaulos
Copy link
Copy Markdown
Member

By passing the detector argument Linux.SyftBinaryPath, the Linux detector will invoke the given syft binary instead of using the syft docker image to scan images.

Copilot AI review requested due to automatic review settings April 9, 2026 20:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an alternate execution path for the Linux detector to run Syft via a user-specified local binary (Linux.SyftBinaryPath) instead of always invoking Syft through a Docker container, improving compatibility for environments where the Syft image/container approach isn’t desirable.

Changes:

  • Introduces ISyftRunner plus Docker-based and binary-based Syft runner implementations, with DI registration for both.
  • Refactors LinuxScanner/LinuxContainerDetector to scan using an ImageReference model and a selected ISyftRunner.
  • Updates and adds unit tests to cover runner behavior and the new invocation flow.
Show a summary per file
File Description
test/Microsoft.ComponentDetection.Orchestrator.Tests/Experiments/LinuxApplicationLayerExperimentTests.cs Updates detector constructors in experiment tests to match new DI signatures.
test/Microsoft.ComponentDetection.Detectors.Tests/LinuxScannerTests.cs Refactors scanner tests to mock ISyftRunner and use ImageReference.
test/Microsoft.ComponentDetection.Detectors.Tests/LinuxContainerDetectorTests.cs Updates detector tests for new runner selection and new scanner signatures.
test/Microsoft.ComponentDetection.Detectors.Tests/DockerSyftRunnerTests.cs Adds tests validating Docker runner source/bind construction for each image kind.
test/Microsoft.ComponentDetection.Detectors.Tests/BinarySyftRunnerTests.cs Adds tests validating binary runner version checks and command-line construction.
src/Microsoft.ComponentDetection.Orchestrator/Extensions/ServiceCollectionExtensions.cs Registers new runner services/factory and ensures logging services are available.
src/Microsoft.ComponentDetection.Detectors/linux/LinuxScanner.cs Moves Syft execution behind ISyftRunner; updates telemetry fields and call sites.
src/Microsoft.ComponentDetection.Detectors/linux/LinuxContainerDetector.cs Selects runner based on Linux.SyftBinaryPath; passes runner through scan pipeline; refactors local image scanning to use ImageReference.
src/Microsoft.ComponentDetection.Detectors/linux/LinuxApplicationLayerDetector.cs Wires new runner dependencies through the experimental detector subclass.
src/Microsoft.ComponentDetection.Detectors/linux/ISyftRunner.cs Introduces the runner abstraction for executing Syft.
src/Microsoft.ComponentDetection.Detectors/linux/ImageReference.cs Makes ImageReference/ImageReferenceKind public and keeps parsing/normalization logic here.
src/Microsoft.ComponentDetection.Detectors/linux/ILinuxScanner.cs Updates scanner API to accept ImageReference and ISyftRunner.
src/Microsoft.ComponentDetection.Detectors/linux/IDockerSyftRunner.cs Adds marker interface for Docker-backed runner.
src/Microsoft.ComponentDetection.Detectors/linux/IBinarySyftRunnerFactory.cs Adds factory interface for creating binary-backed runners by path.
src/Microsoft.ComponentDetection.Detectors/linux/DockerSyftRunner.cs Implements Docker container execution of Syft with per-kind source/bind mapping.
src/Microsoft.ComponentDetection.Detectors/linux/BinarySyftRunnerFactory.cs Implements binary runner factory using ICommandLineInvocationService and ILoggerFactory.
src/Microsoft.ComponentDetection.Detectors/linux/BinarySyftRunner.cs Implements local-binary execution of Syft, including --version availability check.

Copilot's findings

  • Files reviewed: 17/17 changed files
  • Comments generated: 4

Comment on lines +44 to +65
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;
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CanRunAsync calls ICommandLineInvocationService.ExecuteCommandAsync, which throws (e.g., InvalidOperationException) when the command path cannot be located. If Linux.SyftBinaryPath is missing/invalid, this will bubble up and fail the Linux detector instead of treating the runner as unavailable. Consider catching command-location exceptions (or calling CanCommandBeLocatedAsync first) and returning false so the detector can skip cleanly (or fall back to the Docker runner).

Suggested change
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;
if (string.IsNullOrWhiteSpace(this.syftBinaryPath))
{
this.logger.LogInformation("Syft binary path is not configured.");
return false;
}
try
{
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;
}
catch (InvalidOperationException ex)
{
this.logger.LogInformation(
ex,
"Syft binary at {SyftBinaryPath} could not be located or invoked.",
this.syftBinaryPath);
return false;
}

Copilot uses AI. Check for mistakes.
Comment on lines 5 to 9
/// <summary>
/// Specifies the type of image reference.
/// </summary>
internal enum ImageReferenceKind
public enum ImageReferenceKind
{
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ImageReferenceKind/ImageReference were changed from internal to public, expanding the public API surface for what looks like an internal Linux-detector detail. Since Microsoft.ComponentDetection.Detectors already grants internals access to Microsoft.ComponentDetection.Orchestrator and test assemblies via InternalsVisibleTo, consider keeping these types (and related Syft runner abstractions) internal unless you explicitly want to support them as public API.

Copilot uses AI. Check for mistakes.
Comment on lines 77 to 81
using var record = new LinuxScannerTelemetryRecord
{
ImageToScan = imageHash,
ScannerVersion = ScannerImage,
ImageToScan = imageReference.Reference,
ScannerVersion = DockerSyftRunner.ScannerImage,
};
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LinuxScannerTelemetryRecord uses ImageToScan = imageReference.Reference and ScannerVersion = DockerSyftRunner.ScannerImage. For local images, Reference is a full normalized host path (can leak machine/user path details into telemetry), and when using the binary runner the scanner version will be incorrect. Consider using a sanitized/non-host identifier (e.g., OriginalInput) and capturing runner identity/version based on the ISyftRunner used.

See below for a potential fix:

        var scannerVersion = syftRunner is DockerSyftRunner
            ? DockerSyftRunner.ScannerImage
            : syftRunner.GetType().Name;

        using var record = new LinuxScannerTelemetryRecord
        {
            ImageToScan = imageReference.OriginalInput,
            ScannerVersion = scannerVersion,

Copilot uses AI. Check for mistakes.
Comment on lines +267 to +271
var arguments = CmdParameters
.Concat(scopeParameters)
.ToList();
(stdout, stderr) = await syftRunner.RunSyftAsync(
imageReference,
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LinuxScannerTelemetryRecord still has a SemaphoreFailure field, but semaphore acquisition now happens inside the Syft runners and the scanner no longer sets this telemetry signal. Consider propagating a clear “semaphore not acquired” result/exception from ISyftRunner.RunSyftAsync so the scanner can set record.SemaphoreFailure (or otherwise preserve this telemetry).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants