Skip to content

Reapply "Add OCI image support to Linux scanner (#1708)" (#1716)#1717

Open
jasonpaulos wants to merge 1 commit intomainfrom
users/jasonpaulos/reapply-1708
Open

Reapply "Add OCI image support to Linux scanner (#1708)" (#1716)#1717
jasonpaulos wants to merge 1 commit intomainfrom
users/jasonpaulos/reapply-1708

Conversation

@jasonpaulos
Copy link
Member

Reapply #1708 which was reverted in #1716

Copy link
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

Reapplies OCI image support for the Linux container detector by allowing --DockerImagesToScan inputs to reference OCI layout directories and OCI archive tarballs, running Syft against those sources, and mapping detected packages back to image layers.

Changes:

  • Add ImageReference parsing (Docker image vs oci-dir: / oci-archive:) and update LinuxContainerDetector to resolve/scan local OCI inputs via Syft volume binds.
  • Extend ILinuxScanner/LinuxScanner to support returning raw Syft output and processing it separately (needed to extract source metadata for OCI inputs).
  • Extend IDockerService/DockerService to support additional bind mounts and to create empty ContainerDetails for non-Docker-inspect image sources; add/expand unit tests and docs.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/Microsoft.ComponentDetection.Detectors.Tests/LinuxScannerTests.cs Updates mocks for new DockerService overload and adds tests for raw Syft output + layer mapping behavior.
test/Microsoft.ComponentDetection.Detectors.Tests/LinuxContainerDetectorTests.cs Adds comprehensive OCI layout/archive detector tests (path normalization, metadata presence/absence, mixed inputs).
test/Microsoft.ComponentDetection.Detectors.Tests/ImageReferenceTests.cs Adds unit tests for parsing Docker vs OCI references and validating empty-path errors.
src/Microsoft.ComponentDetection.Detectors/linux/LinuxScanner.cs Refactors to support “run Syft” vs “process Syft output” paths; adds bind support and a safe fallback when no layers are provided.
src/Microsoft.ComponentDetection.Detectors/linux/LinuxContainerDetector.cs Implements OCI scanning flow: validate local paths, bind-mount into Syft container, extract metadata, and record components.
src/Microsoft.ComponentDetection.Detectors/linux/ImageReference.cs Introduces parsing and classification of image inputs (Docker vs OCI directory/archive).
src/Microsoft.ComponentDetection.Detectors/linux/ILinuxScanner.cs Adds new public methods to return raw Syft output and process it separately.
src/Microsoft.ComponentDetection.Detectors/linux/Contracts/SyftSourceMetadata.cs Adds typed model for Syft source.metadata (image ID, layers, tags, labels, etc.).
src/Microsoft.ComponentDetection.Detectors/linux/Contracts/SyftSourceLayer.cs Adds typed model for Syft source layer entries.
src/Microsoft.ComponentDetection.Detectors/linux/Contracts/SourceClassExtensions.cs Adds helper to deserialize the untyped SourceClass.Metadata into SyftSourceMetadata.
src/Microsoft.ComponentDetection.Contracts/IDockerService.cs Adds container-run overload supporting bind mounts and adds GetEmptyContainerDetails().
src/Microsoft.ComponentDetection.Common/DockerService.cs Implements new IDockerService members and plumbs additional bind mounts into container creation.
docs/detectors/linux.md Documents supported image input types including oci-dir: and oci-archive:.

Comment on lines +59 to +80
var path = input[OciDirPrefix.Length..];
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentException($"Input with '{OciDirPrefix}' prefix must include a path.", nameof(input));
}

return new ImageReference
{
OriginalInput = input,
Reference = path,
Kind = ImageReferenceKind.OciLayout,
};
}

if (input.StartsWith(OciArchivePrefix, StringComparison.OrdinalIgnoreCase))
{
var path = input[OciArchivePrefix.Length..];
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentException($"Input with '{OciArchivePrefix}' prefix must include a path.", nameof(input));
}

Comment on lines +221 to +224
if (additionalBinds != null)
{
binds.AddRange(additionalBinds);
}
Comment on lines 59 to +76
Task<(string Stdout, string Stderr)> CreateAndRunContainerAsync(string image, IList<string> command, CancellationToken cancellationToken = default);

/// <summary>
/// Creates and runs a container with the given image, command, and additional volume binds.
/// </summary>
/// <param name="image">The image to run.</param>
/// <param name="command">The command to run in the container.</param>
/// <param name="additionalBinds">Additional volume bind mounts to add to the container (e.g., "/host/path:/container/path:ro").</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A tuple of stdout and stderr from the container.</returns>
Task<(string Stdout, string Stderr)> CreateAndRunContainerAsync(string image, IList<string> command, IList<string> additionalBinds, CancellationToken cancellationToken = default);

/// <summary>
/// Creates an empty <see cref="ContainerDetails"/> with a unique ID assigned.
/// Used for image types where details are not obtained from Docker inspect (e.g., OCI layout images).
/// </summary>
/// <returns>A <see cref="ContainerDetails"/> with only the <see cref="ContainerDetails.Id"/> populated.</returns>
ContainerDetails GetEmptyContainerDetails();
Comment on lines +35 to +62
/// <summary>
/// Runs the Syft scanner and returns the raw parsed output without processing components.
/// Use this when the caller needs access to the full Syft output (e.g., to extract source metadata for OCI images).
/// </summary>
/// <param name="syftSource">The source argument passed to Syft (e.g., an image hash or "oci-dir:/oci-image").</param>
/// <param name="additionalBinds">Additional volume bind mounts for the Syft container (e.g., for mounting OCI directories).</param>
/// <param name="scope">The scope for scanning the image.</param>
/// <param name="cancellationToken">A token to monitor for cancellation requests.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the parsed <see cref="SyftOutput"/>.</returns>
public Task<SyftOutput> GetSyftOutputAsync(
string syftSource,
IList<string> additionalBinds,
LinuxScannerScope scope,
CancellationToken cancellationToken = default
);

/// <summary>
/// Processes parsed Syft output into layer-mapped components.
/// </summary>
/// <param name="syftOutput">The parsed Syft output.</param>
/// <param name="containerLayers">The layers to map components to.</param>
/// <param name="enabledComponentTypes">The set of component types to include in the results.</param>
/// <returns>A collection of <see cref="LayerMappedLinuxComponents"/> representing the components found and their associated layers.</returns>
public IEnumerable<LayerMappedLinuxComponents> ProcessSyftOutput(
SyftOutput syftOutput,
IEnumerable<DockerLayer> containerLayers,
ISet<ComponentType> enabledComponentTypes
);
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