Skip to content

Add component detectors for Docker Compose, Helm, and Kubernetes#1759

Open
jpinz wants to merge 25 commits intomainfrom
jupinzer/add_container_detectors
Open

Add component detectors for Docker Compose, Helm, and Kubernetes#1759
jpinz wants to merge 25 commits intomainfrom
jupinzer/add_container_detectors

Conversation

@jpinz
Copy link
Copy Markdown
Member

@jpinz jpinz commented Apr 2, 2026

This pull request adds new detectors for Docker Compose, Helm, and Kubernetes YAML files, enabling the system to automatically discover and register Docker image references found in these types of configuration files. It also updates the service registration and the DetectorClass enum to support these new detectors.

It also adds tests for the new detectors plus the Dockerfile one.

Image references containing unresolved variable placeholders (e.g., ${REGISTRY}/app:${TAG}, {{ .Values.image.tag }}) are silently skipped in all four Docker-related detectors (DockerCompose, Helm, Kubernetes, and Dockerfile) to avoid noisy warnings for otherwise-valid real-world manifests. A shared DockerReferenceUtility.HasUnresolvedVariables helper was added to Microsoft.ComponentDetection.Common to consolidate this logic, and all detectors—including the existing DockerfileComponentDetector—now use it.

The Helm detector uses the combination of a Chart.yaml/Chart.yml file and a *values*.(yaml|yml) file in the same directory to identify Helm charts. Values files found without a co-located Chart file are skipped, avoiding false positives from unrelated values.yaml files in non-Helm projects. The co-location check is implemented via a two-pass OnPrepareDetectionAsync: all matching files are materialized first, chart directories are identified in pass 1, then only values files from those directories are emitted for processing in pass 2. This eliminates any dependency on file enumeration order, so values files are correctly detected regardless of whether they are observed before or after their co-located Chart.yaml.

A Containers detector category was added to DetectorClass, allowing all container-related DefaultOff detectors (Dockerfile, DockerCompose, Helm, Kubernetes) to be enabled together via --DetectorCategories Containers.

A bug in DetectorRestrictionService was also fixed: when both AllowedDetectorIds and AllowedDetectorCategories are specified, DefaultOff detectors introduced via category expansion are now also constrained by the ID allow-list, producing an intersection rather than an unintended union.

New Detector Checklist

  • I have gone through the docs for creating a new detector here
  • My new detector implements IDefaultOffComponentDetector
  • I have created a PR to the verification repo with components that my new detector can find
  • (If necessary) I have updated the Feature Overview table in the README

@jpinz jpinz self-assigned this Apr 2, 2026
Copilot AI review requested due to automatic review settings April 2, 2026 21:44
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 2, 2026

👋 Hi! It looks like you modified some files in the Detectors folder.
You may need to bump the detector versions if any of the following scenarios apply:

  • The detector detects more or fewer components than before
  • The detector generates different parent/child graph relationships than before
  • The detector generates different devDependencies values than before

If none of the above scenarios apply, feel free to ignore this comment 🙂

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 new YAML-based detectors to Component Detection so Docker image references can be discovered from Docker Compose, Helm values, and Kubernetes manifests, plus updates orchestration wiring and expands detector test coverage.

Changes:

  • Introduce new DockerCompose, Helm, and Kubernetes detectors that parse YAML and register DockerReferenceComponent usages.
  • Register the new detectors in Orchestrator DI and extend DetectorClass with new categories.
  • Add MSTest coverage for the new detectors and add/expand tests for the existing Dockerfile detector.

Reviewed changes

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

Show a summary per file
File Description
test/Microsoft.ComponentDetection.Detectors.Tests/KubernetesComponentDetectorTests.cs Adds unit tests for K8s YAML image extraction and non-K8s YAML ignoring behavior.
test/Microsoft.ComponentDetection.Detectors.Tests/HelmComponentDetectorTests.cs Adds unit tests for Helm values.yaml image patterns (scalar, structured, digest, sequences).
test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs Adds Dockerfile detector tests for FROM, multi-stage, digests, COPY --from, and file patterns.
test/Microsoft.ComponentDetection.Detectors.Tests/DockerComposeComponentDetectorTests.cs Adds tests for Compose service images, overrides, digests, and build-only services.
src/Microsoft.ComponentDetection.Orchestrator/Extensions/ServiceCollectionExtensions.cs Registers the three new detectors with DI so they can be executed by the orchestrator.
src/Microsoft.ComponentDetection.Detectors/kubernetes/KubernetesComponentDetector.cs New detector implementation for parsing Kubernetes manifests and extracting container images.
src/Microsoft.ComponentDetection.Detectors/helm/HelmComponentDetector.cs New detector implementation for parsing Helm values.yaml and extracting image references.
src/Microsoft.ComponentDetection.Detectors/dockercompose/DockerComposeComponentDetector.cs New detector implementation for parsing Compose YAML and extracting service image: references.
src/Microsoft.ComponentDetection.Contracts/DetectorClass.cs Adds new detector classes/categories for Docker Compose, Helm, and Kubernetes.
Comments suppressed due to low confidence (5)

src/Microsoft.ComponentDetection.Detectors/kubernetes/KubernetesComponentDetector.cs:104

  • This file is compiled with nullable reference types enabled, but apiVersion/kind are declared as non-nullable string and initialized to null, which will fail the build with warnings-as-errors. Make these string? (and similarly update any other locals/returns that can be null).

    private bool IsKubernetesManifest(YamlMappingNode rootMapping)
    {
        string apiVersion = null;
        string kind = null;

        foreach (var entry in rootMapping.Children)
        {
            var key = (entry.Key as YamlScalarNode)?.Value;
            if (string.Equals(key, "apiVersion", StringComparison.OrdinalIgnoreCase))
            {
                apiVersion = (entry.Value as YamlScalarNode)?.Value;
            }
            else if (string.Equals(key, "kind", StringComparison.OrdinalIgnoreCase))
            {
                kind = (entry.Value as YamlScalarNode)?.Value;
            }

src/Microsoft.ComponentDetection.Detectors/kubernetes/KubernetesComponentDetector.cs:217

  • GetMappingChild returns null when the key isn't present, but the return type is non-nullable YamlMappingNode. With nullable enabled + warnings-as-errors this will fail compilation. Change the return type to YamlMappingNode? (and update call sites accordingly).

    private static YamlMappingNode GetMappingChild(YamlMappingNode parent, string key)
    {
        foreach (var entry in parent.Children)
        {
            if (entry.Key is YamlScalarNode scalarKey && string.Equals(scalarKey.Value, key, StringComparison.OrdinalIgnoreCase))
            {
                return entry.Value as YamlMappingNode;
            }
        }

        return null;

src/Microsoft.ComponentDetection.Detectors/kubernetes/KubernetesComponentDetector.cs:230

  • GetSequenceChild returns null when the key isn't present, but the return type is non-nullable YamlSequenceNode. With nullable enabled + warnings-as-errors this will fail compilation. Change the return type to YamlSequenceNode? (and update call sites accordingly).

    private static YamlSequenceNode GetSequenceChild(YamlMappingNode parent, string key)
    {
        foreach (var entry in parent.Children)
        {
            if (entry.Key is YamlScalarNode scalarKey && string.Equals(scalarKey.Value, key, StringComparison.OrdinalIgnoreCase))
            {
                return entry.Value as YamlSequenceNode;
            }
        }

        return null;

src/Microsoft.ComponentDetection.Detectors/helm/HelmComponentDetector.cs:135

  • This method initializes non-nullable string locals (repository, tag, digest, registry) to null. With nullable enabled + TreatWarningsAsErrors, this will fail the build. Use string? for these locals (and keep the subsequent null/whitespace checks).

    private void TryRegisterStructuredImageReference(YamlMappingNode imageMapping, ISingleFileComponentRecorder recorder, string fileLocation)
    {
        string repository = null;
        string tag = null;
        string digest = null;
        string registry = null;

src/Microsoft.ComponentDetection.Detectors/dockercompose/DockerComposeComponentDetector.cs:137

  • GetMappingChild returns null when the key isn't present, but the return type is non-nullable YamlMappingNode. With nullable enabled + warnings-as-errors this will fail compilation. Change the return type to YamlMappingNode? and update call sites accordingly.

    private static YamlMappingNode GetMappingChild(YamlMappingNode parent, string key)
    {
        foreach (var entry in parent.Children)
        {
            if (entry.Key is YamlScalarNode scalarKey && string.Equals(scalarKey.Value, key, StringComparison.OrdinalIgnoreCase))
            {
                return entry.Value as YamlMappingNode;
            }
        }

        return null;


public override int Version => 1;

public override IEnumerable<string> Categories => [Enum.GetName(typeof(DetectorClass), DetectorClass.Kubernetes)];
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Enum.GetName(...) returns string? under nullable reference types. Returning it directly here can fail compilation (and can propagate null into Categories). Use the null-forgiving operator (!) or an alternate non-null approach (e.g., nameof(DetectorClass.Kubernetes)).

This issue also appears in the following locations of the same file:

  • line 88
  • line 206
  • line 219
Suggested change
public override IEnumerable<string> Categories => [Enum.GetName(typeof(DetectorClass), DetectorClass.Kubernetes)];
public override IEnumerable<string> Categories => [nameof(DetectorClass.Kubernetes)];

Copilot uses AI. Check for mistakes.

public override int Version => 1;

public override IEnumerable<string> Categories => [Enum.GetName(typeof(DetectorClass), DetectorClass.Helm)];
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Enum.GetName(...) returns string? under nullable reference types. Returning it directly here can fail compilation and can propagate null into Categories. Use ! (as some other detectors do) or another guaranteed-non-null approach (e.g., nameof(DetectorClass.Helm)).

This issue also appears on line 129 of the same file.

Suggested change
public override IEnumerable<string> Categories => [Enum.GetName(typeof(DetectorClass), DetectorClass.Helm)];
public override IEnumerable<string> Categories => [nameof(DetectorClass.Helm)];

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +41

public override IEnumerable<ComponentType> SupportedComponentTypes => [ComponentType.DockerReference];

public override int Version => 1;

public override IEnumerable<string> Categories => [Enum.GetName(typeof(DetectorClass), DetectorClass.DockerCompose)];
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Enum.GetName(...) returns string? under nullable reference types. Returning it directly here can fail compilation and can propagate null into Categories. Use the null-forgiving operator (!) or an alternate non-null approach.

This issue also appears on line 126 of the same file.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 3, 2026 15:54
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

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

Comment on lines +50 to +72
this.Logger.LogInformation("Discovered Helm file: {Location}", file.Location);

string contents;
using (var reader = new StreamReader(file.Stream))
{
contents = await reader.ReadToEndAsync(cancellationToken);
}

var yaml = new YamlStream();
yaml.Load(new StringReader(contents));

if (yaml.Documents.Count == 0)
{
return;
}

var fileName = Path.GetFileName(file.Location);

if (fileName.Contains("values", StringComparison.OrdinalIgnoreCase) &&
(fileName.EndsWith(".yaml", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".yml", StringComparison.OrdinalIgnoreCase)))
{
this.ExtractImageReferencesFromValues(yaml, singleFileComponentRecorder, file.Location);
}
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

This method parses YAML for every matched file (including Chart.yaml/Chart.yml) even though only *values*.(yml|yaml) files are actually processed for image extraction. Since the filename decision is based solely on file.Location, you can short-circuit before reading/parsing the stream for non-values files to avoid unnecessary work and noise in logs.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 3, 2026 16:13
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

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/Microsoft.ComponentDetection.Detectors/kubernetes/KubernetesComponentDetector.cs:86

  • The exception handler logs "Skipping non-Kubernetes YAML file" for any exception, but exceptions here primarily represent YAML parse/processing failures (a file can be a Kubernetes manifest but malformed). Consider adjusting the message to reflect parse errors (or splitting try/catch so non-Kubernetes vs parse-failure are distinguished) to avoid misleading diagnostics.
        catch (Exception e)
        {
            // Not all YAML files are Kubernetes manifests; silently skip parse errors
            this.Logger.LogDebug(e, "Skipping non-Kubernetes YAML file: {Location}", file.Location);
        }

Copilot AI review requested due to automatic review settings April 3, 2026 16:40
@jpinz jpinz marked this pull request as ready for review April 3, 2026 16:41
@jpinz jpinz requested a review from a team as a code owner April 3, 2026 16:41
@jpinz jpinz requested a review from edgarrs April 3, 2026 16:41
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

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Comment on lines +125 to +138
private void TryRegisterImageReference(string imageReference, ISingleFileComponentRecorder recorder, string fileLocation)
{
try
{
var dockerRef = DockerReferenceUtility.ParseFamiliarName(imageReference);
if (dockerRef != null)
{
recorder.RegisterUsage(new DetectedComponent(dockerRef.ToTypedDockerReferenceComponent()));
}
}
catch (Exception e)
{
this.Logger.LogWarning(e, "Failed to parse image reference '{ImageReference}' in {Location}", imageReference, fileLocation);
}
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

Docker Compose files commonly use variable interpolation in image (e.g., ${REGISTRY}/app:${TAG}). DockerReferenceUtility.ParseFamiliarName throws on these, which will generate warnings for otherwise valid compose files. Consider skipping registration when the image reference contains unresolved variables (similar to DockerfileComponentDetector.HasUnresolvedVariables) instead of logging a warning for each occurrence.

Copilot uses AI. Check for mistakes.
Comment on lines +195 to +208
private void TryRegisterImageReference(string imageReference, ISingleFileComponentRecorder recorder, string fileLocation)
{
try
{
var dockerRef = DockerReferenceUtility.ParseFamiliarName(imageReference);
if (dockerRef != null)
{
recorder.RegisterUsage(new DetectedComponent(dockerRef.ToTypedDockerReferenceComponent()));
}
}
catch (Exception e)
{
this.Logger.LogWarning(e, "Failed to parse image reference '{ImageReference}' in {Location}", imageReference, fileLocation);
}
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

Helm values often use templated/variable image strings (e.g., ${REGISTRY} or {{ .Values.image.tag }} via preprocessing). Passing these through DockerReferenceUtility.ParseFamiliarName will throw and can create noisy warning logs. Consider detecting unresolved variables/templates in imageReference and skipping registration (without logging) to match the Dockerfile detector’s behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +228 to +241
private void TryRegisterImageReference(string imageReference, ISingleFileComponentRecorder recorder, string fileLocation)
{
try
{
var dockerRef = DockerReferenceUtility.ParseFamiliarName(imageReference);
if (dockerRef != null)
{
recorder.RegisterUsage(new DetectedComponent(dockerRef.ToTypedDockerReferenceComponent()));
}
}
catch (Exception e)
{
this.Logger.LogWarning(e, "Failed to parse image reference '{ImageReference}' in {Location}", imageReference, fileLocation);
}
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

Kubernetes manifests frequently use variable placeholders in image: (e.g., ${IMAGE}) via kustomize/CI substitution. DockerReferenceUtility.ParseFamiliarName throws on these, so this will log warnings for many real-world manifests. Consider skipping image references containing unresolved variables/templates (rather than logging) before calling ParseFamiliarName, consistent with DockerfileComponentDetector.HasUnresolvedVariables.

Copilot uses AI. Check for mistakes.
Copilot finished work on behalf of jpinz April 3, 2026 19:39
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 new DefaultOff container-focused detectors (Docker Compose, Helm values, Kubernetes manifests) to discover Docker image references from YAML, plus introduces a shared “Containers” category to enable them via --DetectorCategories.

Changes:

  • Added DockerComposeComponentDetector, HelmComponentDetector, and KubernetesComponentDetector and registered them in orchestrator DI.
  • Expanded DetectorClass with DockerCompose, Helm, Kubernetes, and Containers, and updated restriction logic/docs to support enabling DefaultOff detectors by category.
  • Added new unit tests and detector documentation pages for the new detectors (plus Dockerfile detector tests).
Show a summary per file
File Description
test/Microsoft.ComponentDetection.Orchestrator.Tests/Services/DetectorRestrictionServiceTests.cs Adds tests validating DefaultOff detectors can be included via category restrictions.
test/Microsoft.ComponentDetection.Detectors.Tests/KubernetesComponentDetectorTests.cs New test coverage for Kubernetes YAML image extraction scenarios.
test/Microsoft.ComponentDetection.Detectors.Tests/HelmComponentDetectorTests.cs New test coverage for Helm values.yaml image extraction scenarios.
test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs Adds unit tests for Dockerfile image reference parsing behavior.
test/Microsoft.ComponentDetection.Detectors.Tests/DockerComposeComponentDetectorTests.cs New test coverage for Docker Compose image extraction scenarios.
src/Microsoft.ComponentDetection.Orchestrator/Services/DetectorRestrictionService.cs Updates restriction logic to include DefaultOff detectors when filtering by categories.
src/Microsoft.ComponentDetection.Orchestrator/Extensions/ServiceCollectionExtensions.cs Registers the new detectors in DI.
src/Microsoft.ComponentDetection.Detectors/kubernetes/KubernetesComponentDetector.cs Implements Kubernetes YAML detector for container image references.
src/Microsoft.ComponentDetection.Detectors/helm/HelmComponentDetector.cs Implements Helm values YAML detector for image references.
src/Microsoft.ComponentDetection.Detectors/dockerfile/DockerfileComponentDetector.cs Adds “Containers” category to Dockerfile detector.
src/Microsoft.ComponentDetection.Detectors/dockercompose/DockerComposeComponentDetector.cs Implements Docker Compose YAML detector for service image references.
src/Microsoft.ComponentDetection.Contracts/DetectorClass.cs Adds new detector categories: DockerCompose/Helm/Kubernetes/Containers.
src/Microsoft.ComponentDetection.Common/DockerReference/DockerReferenceUtility.cs Adds shared HasUnresolvedVariables helper for skipping templated refs.
docs/detectors/README.md Lists the new detectors in detector documentation index.
docs/detectors/kubernetes.md Adds Kubernetes detector documentation.
docs/detectors/helm.md Adds Helm detector documentation.
docs/detectors/dockerfile.md Notes container-category enabling path for Dockerfile detector.
docs/detectors/dockercompose.md Adds Docker Compose detector documentation.
docs/detector-arguments.md Documents that category selection enables matching DefaultOff detectors; adds Containers to supported categories list.
.gitignore Ignores local .nuget/ folder.

Copilot's findings

  • Files reviewed: 19/20 changed files
  • Comments generated: 2

Comment on lines +59 to +77
@@ -68,7 +68,13 @@ public IEnumerable<IComponentDetector> ApplyRestrictions(DetectorRestrictions re
}

return false;
}).ToList();
}

detectors = detectors.Where(CategoryFilter).ToList();

// Also include DefaultOff detectors whose categories match
detectors = detectors.Union(defaultOffDetectors.Where(CategoryFilter)).ToList();

Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

--DetectorsFilter (AllowedDetectorIds) is applied before category filtering, but the new Union(defaultOffDetectors.Where(CategoryFilter)) can re-introduce DefaultOff detectors even when a user explicitly provided a detector allow-list. If both AllowedDetectorIds and AllowedDetectorCategories are set, this effectively turns the result into a union rather than an intersection. Consider applying the AllowedDetectorIds filter after category/DefaultOff expansion (or applying it to the DefaultOff set as well) and add a regression test covering the combined options.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +48
/// <summary>
/// Returns true if the reference contains unresolved variable placeholders (e.g., ${VAR}, {{ .Values.tag }}).
/// Such references should be skipped before calling <see cref="ParseFamiliarName"/> or <see cref="ParseQualifiedName"/>.
/// </summary>
/// <param name="reference">The image reference string to check.</param>
/// <returns><c>true</c> if the reference contains variable placeholder characters; otherwise <c>false</c>.</returns>
public static bool HasUnresolvedVariables(string reference) =>
reference.IndexOfAny(['$', '{', '}']) >= 0;
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

This new helper duplicates the Dockerfile detector’s existing HasUnresolvedVariables implementation (Regex("[${}]")). To actually consolidate the logic across Docker-related detectors (as described in the PR), consider switching the Dockerfile detector to call this shared helper and removing the duplicate method so the criteria can’t drift over time.

Copilot uses AI. Check for mistakes.
@jpinz
Copy link
Copy Markdown
Member Author

jpinz commented Apr 3, 2026

@copilot apply changes based on the comments in this thread

Copilot AI review requested due to automatic review settings April 3, 2026 19:55
@jpinz jpinz review requested due to automatic review settings April 3, 2026 19:55
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 3, 2026

@copilot apply changes based on the comments in this thread

Applied in commit dff7d5f.

  • DetectorRestrictionService: when AllowedDetectorIds is set alongside AllowedDetectorCategories, DefaultOff detectors introduced via category expansion are now also filtered by the ID allow-list — producing an intersection rather than a union. The HasUnresolvedVariables consolidation was already done in the previous commit (70762f1).
  • Added WithRestrictions_DetectorIdAllowListTakesPrecedenceOverCategoryForDefaultOff regression test covering the combined options.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 4myvsblobprodcus32.vsblob.vsassets.io
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/B9D91EC3F00925E83B2F176B451EEAFF/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/D47EE5A44C32F70BEBA9A6371300F374/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/CF4B41BED05CC519E28614CCFB1B4E53/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
  • azuresdkartifacts.blob.core.windows.net
    • Triggering command: /opt/hostedtoolcache/CodeQL/2.25.1/x64/codeql/csharp/tools/linux64/Semmle.Autobuild.CSharp /opt/hostedtoolcache/CodeQL/2.25.1/x64/codeql/csharp/tools/linux64/Semmle.Autobuild.CSharp (dns block)
  • c78vsblobprodcus322.vsblob.vsassets.io
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/2D3586D8C912F6D195254B43F0F0042B/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/86123D718A0A7D5E3D8DDB87D50A845A/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/4EB20032E82B2584A5C53CFFE5CD470A/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
  • k4kvsblobprodcus344.vsblob.vsassets.io
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/86123D718A0A7D5E3D8DDB87D50A845A/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/4EB20032E82B2584A5C53CFFE5CD470A/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/D93C69A3F90DACDB68555AD6306FA9CB/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
  • pdfvsblobprodcus380.vsblob.vsassets.io
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/2D3586D8C912F6D195254B43F0F0042B/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/4EB20032E82B2584A5C53CFFE5CD470A/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/D93C69A3F90DACDB68555AD6306FA9CB/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
  • st8vsblobprodcus339.vsblob.vsassets.io
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/EE9D343C76C38EDF0AC64A832F8C101A/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot finished work on behalf of jpinz April 3, 2026 19:56
Copilot AI review requested due to automatic review settings April 3, 2026 20:12
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

This PR extends Component Detection’s container scanning capabilities by introducing new DefaultOff detectors for Docker Compose, Helm, and Kubernetes YAML, consolidating “unresolved variable” skipping logic, and adding a Containers detector category to enable all container-related detectors together. It also fixes detector restriction behavior so that category expansion respects an explicit detector ID allow-list (intersection instead of union).

Changes:

  • Added new DefaultOff detectors: DockerComposeComponentDetector, HelmComponentDetector, KubernetesComponentDetector, and registered them in orchestrator DI.
  • Added DetectorClass.Containers (and related per-detector categories) and updated CLI/docs accordingly.
  • Centralized unresolved-variable skipping with DockerReferenceUtility.HasUnresolvedVariables and updated Dockerfile detector + added tests.
Show a summary per file
File Description
test/Microsoft.ComponentDetection.Orchestrator.Tests/Services/DetectorRestrictionServiceTests.cs Adds test coverage for category-based enabling of DefaultOff detectors and for the allow-list intersection fix.
test/Microsoft.ComponentDetection.Detectors.Tests/KubernetesComponentDetectorTests.cs Adds unit tests for Kubernetes YAML image extraction and skipping unresolved variables.
test/Microsoft.ComponentDetection.Detectors.Tests/HelmComponentDetectorTests.cs Adds unit tests for Helm values parsing patterns (direct/structured images, digests, skipping).
test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs Adds unit tests for Dockerfile detector behavior including unresolved-variable skipping and digest handling.
test/Microsoft.ComponentDetection.Detectors.Tests/DockerComposeComponentDetectorTests.cs Adds unit tests for Docker Compose YAML image extraction and skipping unresolved variables.
src/Microsoft.ComponentDetection.Orchestrator/Services/DetectorRestrictionService.cs Fixes restriction logic so DefaultOff detectors introduced via category expansion are constrained by AllowedDetectorIds when provided.
src/Microsoft.ComponentDetection.Orchestrator/Extensions/ServiceCollectionExtensions.cs Registers the new detectors in DI.
src/Microsoft.ComponentDetection.Detectors/kubernetes/KubernetesComponentDetector.cs New Kubernetes YAML detector that extracts container image references.
src/Microsoft.ComponentDetection.Detectors/helm/HelmComponentDetector.cs New Helm detector intended to parse values files when a Chart file is co-located.
src/Microsoft.ComponentDetection.Detectors/dockerfile/DockerfileComponentDetector.cs Updates categories to include Containers and switches unresolved-variable detection to shared utility.
src/Microsoft.ComponentDetection.Detectors/dockercompose/DockerComposeComponentDetector.cs New Docker Compose detector that extracts service image: references.
src/Microsoft.ComponentDetection.Contracts/DetectorClass.cs Adds DockerCompose, Helm, Kubernetes, and Containers detector classes/categories.
src/Microsoft.ComponentDetection.Common/DockerReference/DockerReferenceUtility.cs Adds HasUnresolvedVariables helper used across container-related detectors.
docs/detectors/README.md Documents the new detectors in the detector docs index.
docs/detectors/kubernetes.md Adds Kubernetes detector documentation.
docs/detectors/helm.md Adds Helm detector documentation.
docs/detectors/dockerfile.md Updates Dockerfile docs to mention enabling via --DetectorCategories Containers.
docs/detectors/dockercompose.md Adds Docker Compose detector documentation.
docs/detector-arguments.md Updates --DetectorCategories documentation to mention DefaultOff category enabling and Containers.
.gitignore Ignores .nuget/.

Copilot's findings

  • Files reviewed: 19/20 changed files
  • Comments generated: 6

Copilot AI review requested due to automatic review settings April 3, 2026 20:41
@jpinz jpinz review requested due to automatic review settings April 3, 2026 20:41
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 3, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 5rqvsblobprodcus385.vsblob.vsassets.io
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/4EB20032E82B2584A5C53CFFE5CD470A/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/86123D718A0A7D5E3D8DDB87D50A845A/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/D93C69A3F90DACDB68555AD6306FA9CB/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
  • azuresdkartifacts.blob.core.windows.net
    • Triggering command: /opt/hostedtoolcache/CodeQL/2.25.1/x64/codeql/csharp/tools/linux64/Semmle.Autobuild.CSharp /opt/hostedtoolcache/CodeQL/2.25.1/x64/codeql/csharp/tools/linux64/Semmle.Autobuild.CSharp (dns block)
  • c78vsblobprodcus322.vsblob.vsassets.io
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/4EB20032E82B2584A5C53CFFE5CD470A/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/86123D718A0A7D5E3D8DDB87D50A845A/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
  • k4kvsblobprodcus344.vsblob.vsassets.io
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/2D3586D8C912F6D195254B43F0F0042B/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/EAC58970BF56570ECF9A3336E1CC2C51/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/4EB20032E82B2584A5C53CFFE5CD470A/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
  • pdfvsblobprodcus380.vsblob.vsassets.io
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/2D3586D8C912F6D195254B43F0F0042B/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/4EB20032E82B2584A5C53CFFE5CD470A/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/C8FA87C461E8D947D5E484A1822A0126/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
  • s8mvsblobprodcus38.vsblob.vsassets.io
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/50B984F0373F12A39D028856910588FC/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
  • ytvvsblobprodcus310.vsblob.vsassets.io
    • Triggering command: /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/A57A5A89CDBA36A2878C8A42DA58182C/missingpackages_workingdir --packages /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-fe72422a9cf5c763/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

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

This PR expands Component Detection’s container-image discovery by introducing new DefaultOff detectors for Docker Compose, Helm values, and Kubernetes manifests, and consolidates “skip unresolved template variables” logic into a shared Docker reference utility.

Changes:

  • Added new detectors: DockerComposeComponentDetector, HelmComponentDetector, and KubernetesComponentDetector to parse YAML and register DockerReferenceComponent usages.
  • Added shared DockerReferenceUtility.HasUnresolvedVariables helper and updated the existing Dockerfile detector to use it.
  • Added documentation and unit tests covering the new detectors (plus Dockerfile detector coverage).
Show a summary per file
File Description
test/Microsoft.ComponentDetection.Detectors.Tests/KubernetesComponentDetectorTests.cs New unit tests for Kubernetes YAML image extraction.
test/Microsoft.ComponentDetection.Detectors.Tests/HelmComponentDetectorTests.cs New unit tests for Helm values image extraction + chart co-location behavior.
test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs New unit tests for Dockerfile image parsing and variable-skipping behavior.
test/Microsoft.ComponentDetection.Detectors.Tests/DockerComposeComponentDetectorTests.cs New unit tests for Docker Compose service image extraction.
src/Microsoft.ComponentDetection.Orchestrator/Extensions/ServiceCollectionExtensions.cs Registers the new detectors in DI.
src/Microsoft.ComponentDetection.Detectors/kubernetes/KubernetesComponentDetector.cs New Kubernetes detector implementation.
src/Microsoft.ComponentDetection.Detectors/helm/HelmComponentDetector.cs New Helm detector implementation with two-pass preparation.
src/Microsoft.ComponentDetection.Detectors/dockerfile/DockerfileComponentDetector.cs Switches unresolved-variable detection to shared helper; removes local regex.
src/Microsoft.ComponentDetection.Detectors/dockercompose/DockerComposeComponentDetector.cs New Docker Compose detector implementation.
src/Microsoft.ComponentDetection.Contracts/DetectorClass.cs Adds new detector classes for DockerCompose/Helm/Kubernetes.
src/Microsoft.ComponentDetection.Common/DockerReference/DockerReferenceUtility.cs Adds HasUnresolvedVariables helper.
docs/detectors/README.md Adds detector docs links/entries for Docker Compose, Helm, Kubernetes.
docs/detectors/kubernetes.md New Kubernetes detector documentation.
docs/detectors/helm.md New Helm detector documentation.
docs/detectors/dockercompose.md New Docker Compose detector documentation.
.gitignore Ignores .nuget/ directory.

Copilot's findings

  • Files reviewed: 15/16 changed files
  • Comments generated: 2

Comment on lines +37 to +39
"Chart.yaml", "Chart.yml",
"chart.yaml", "chart.yml",
"*values*.yaml", "*values*.yml",
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

SearchPatterns uses *values*.yaml / *values*.yml, but the repo’s PathUtilityService.MatchesPattern only supports * as a leading or trailing wildcard; mid-string * is treated literally, so values files (e.g., values.yaml, values.production.yaml) will never be discovered in real scans and the detector will effectively do no work. Consider switching to supported patterns (e.g., *.yaml/*.yml with IsValuesFile filtering) or extending the matcher to support contains patterns.

Suggested change
"Chart.yaml", "Chart.yml",
"chart.yaml", "chart.yml",
"*values*.yaml", "*values*.yml",
"*.yaml", "*.yml",

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +36
public override IList<string> SearchPatterns { get; } =
[
"docker-compose.yml", "docker-compose.yaml",
"docker-compose.*.yml", "docker-compose.*.yaml",
"compose.yml", "compose.yaml",
"compose.*.yml", "compose.*.yaml",
];
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

SearchPatterns contains docker-compose.*.yml / compose.*.yaml variants, but the repo’s PathUtilityService.MatchesPattern only supports * at the beginning or end of a pattern. As written, override/env-specific compose files like docker-compose.override.yml or compose.prod.yaml won’t be discovered in real scans. Use patterns the matcher can handle (e.g., docker-compose.* / compose.*) or broaden to *.yml/*.yaml and filter by filename.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,145 @@
#nullable disable
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Enable nullability for new files please

Comment on lines +37 to +38
"Chart.yaml", "Chart.yml",
"chart.yaml", "chart.yml",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are the lowercase chart.yaml / chart.yml entries here for Linux case-sensitivity? IsChartFile() already does OrdinalIgnoreCase, so once a file is matched by the walker, it'll be handled correctly. The question is whether the walker's glob is case-sensitive.

Comment on lines +19 to +29
private static readonly HashSet<string> KubernetesKinds = new(StringComparer.OrdinalIgnoreCase)
{
"Pod",
"Deployment",
"StatefulSet",
"DaemonSet",
"ReplicaSet",
"Job",
"CronJob",
"ReplicationController",
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Missing PodTemplate from the KubernetesKinds set. It's not common but it does embed a pod spec with container images.

}

[TestMethod]
public async Task TestHelm_ValuesFileObservedBeforeChartYamlAsync()
Copy link
Copy Markdown
Member

@JamieMagee JamieMagee Apr 4, 2026

Choose a reason for hiding this comment

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

This passes because both files default to tmp as their directory. They're co-located by coincidence, not by test design.

I'd add a test where Chart.yaml is in /tmp/chartA/ and values.yaml is in /tmp/chartB/ (via the fileLocation parameter) to actually verify the co-location filtering rejects mismatched directories. TestHelm_ValuesWithoutChartYamlSkippedAsync tests "no chart at all" but not "chart exists in a different directory."

}
}

private void TryRegisterImageReference(string imageReference, ISingleFileComponentRecorder recorder, string fileLocation)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

TryRegisterImageReference is copy-pasted across all three detectors. Consider pulling it into a shared static method on DockerReferenceUtility. Something like TryParseImageReference(string) -> DockerReference? that handles the unresolved-variable check, parsing, and exception swallowing. The Dockerfile detector does the same work inline too, so it'd consolidate four copies.

@JamieMagee
Copy link
Copy Markdown
Member

I think you're going to need to solve #201 before you can merge this.

See #1091. It was originally set to merge in v5.0.0, but accidentally got dropped before release.

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.

4 participants