From e6761d953f01c4ae12fcf2c9e737f64bb8d494ee Mon Sep 17 00:00:00 2001 From: Julian Pinzer Date: Wed, 30 Sep 2026 14:56:53 -0400 Subject: [PATCH 1/5] Add exception handling for non-Dockerfile formats and add tests for non-dockerfiles --- .../dockerfile/DockerfileComponentDetector.cs | 7 ++++++- .../DockerfileComponentDetectorTests.cs | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.ComponentDetection.Detectors/dockerfile/DockerfileComponentDetector.cs b/src/Microsoft.ComponentDetection.Detectors/dockerfile/DockerfileComponentDetector.cs index 031358105..ca972b607 100644 --- a/src/Microsoft.ComponentDetection.Detectors/dockerfile/DockerfileComponentDetector.cs +++ b/src/Microsoft.ComponentDetection.Detectors/dockerfile/DockerfileComponentDetector.cs @@ -10,6 +10,7 @@ namespace Microsoft.ComponentDetection.Detectors.Dockerfile; using Microsoft.ComponentDetection.Contracts.Internal; using Microsoft.ComponentDetection.Contracts.TypedComponent; using Microsoft.Extensions.Logging; +using Sprache; using Valleysoft.DockerfileModel; public class DockerfileComponentDetector : FileComponentDetector, IExperimentalDetector @@ -59,9 +60,13 @@ protected override async Task OnFileFoundAsync(ProcessRequest processRequest, ID var stageNameMap = new Dictionary(); var dockerFileComponent = this.ParseDockerFileAsync(contents, file.Location, singleFileComponentRecorder, stageNameMap); } + catch (ParseException e) + { + this.Logger.LogWarning(e, "Ignoring file that doesn't appear to be a Dockerfile: {Location}", filePath); + } catch (Exception e) { - this.Logger.LogError(e, "The file doesn't appear to be a Dockerfile: {Location}", filePath); + this.Logger.LogError(e, "Failed to process Dockerfile: {Location}", filePath); } } diff --git a/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs b/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs index 62bf38b24..d83296580 100644 --- a/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs +++ b/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs @@ -210,6 +210,19 @@ public async Task TestDockerfile_PrefixedFilenameAsync() componentRecorder.GetDetectedComponents().Should().ContainSingle(); } + [DataTestMethod] + [DataRow("dockerfile.mjs")] + [DataRow("dockerfile.d.mts")] + public async Task TestDockerfile_ShikiLanguageDefinitionIsIgnoredAsync(string fileName) + { + var (scanResult, componentRecorder) = await this.DetectorTestUtility + .WithFile(fileName, "export default Object.freeze({ name: 'dockerfile' });") + .ExecuteDetectorAsync(); + + scanResult.ResultCode.Should().Be(ProcessingResultCode.Success); + componentRecorder.GetDetectedComponents().Should().BeEmpty(); + } + [TestMethod] public async Task TestDockerfile_NoFromInstructionsAsync() { From 6f3d301a7019f7724c6cd9ecc2527dcf629b24f1 Mon Sep 17 00:00:00 2001 From: Julian Pinzer Date: Wed, 30 Sep 2026 15:30:02 -0400 Subject: [PATCH 2/5] Change DataTestMethod to TestMethod for Shiki language definition test --- .../DockerfileComponentDetectorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs b/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs index d83296580..bdc0c16b8 100644 --- a/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs +++ b/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs @@ -210,7 +210,7 @@ public async Task TestDockerfile_PrefixedFilenameAsync() componentRecorder.GetDetectedComponents().Should().ContainSingle(); } - [DataTestMethod] + [TestMethod] [DataRow("dockerfile.mjs")] [DataRow("dockerfile.d.mts")] public async Task TestDockerfile_ShikiLanguageDefinitionIsIgnoredAsync(string fileName) From 76d160c21f1ccbc84be2881792724d677ec7921a Mon Sep 17 00:00:00 2001 From: Julian Pinzer Date: Wed, 30 Sep 2026 16:02:39 -0400 Subject: [PATCH 3/5] Don't check for dockerfiles in `node_modules` or subfolders of it. --- .../dockerfile/DockerfileComponentDetector.cs | 17 +++++++++++++++++ .../DockerfileComponentDetectorTests.cs | 14 ++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/Microsoft.ComponentDetection.Detectors/dockerfile/DockerfileComponentDetector.cs b/src/Microsoft.ComponentDetection.Detectors/dockerfile/DockerfileComponentDetector.cs index ca972b607..00760e336 100644 --- a/src/Microsoft.ComponentDetection.Detectors/dockerfile/DockerfileComponentDetector.cs +++ b/src/Microsoft.ComponentDetection.Detectors/dockerfile/DockerfileComponentDetector.cs @@ -3,6 +3,8 @@ namespace Microsoft.ComponentDetection.Detectors.Dockerfile; using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Reactive.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.ComponentDetection.Common; @@ -42,6 +44,15 @@ public DockerfileComponentDetector( public override int Version => 1; + protected override IList SkippedFolders => ["node_modules"]; + + protected override Task> OnPrepareDetectionAsync( + IObservable processRequests, + IDictionary detectorArgs, + CancellationToken cancellationToken = default) => + Task.FromResult(processRequests.Where(processRequest => + !this.IsInSkippedFolder(processRequest.ComponentStream.Location))); + protected override async Task OnFileFoundAsync(ProcessRequest processRequest, IDictionary detectorArgs, CancellationToken cancellationToken = default) { var singleFileComponentRecorder = processRequest.SingleFileComponentRecorder; @@ -70,6 +81,12 @@ protected override async Task OnFileFoundAsync(ProcessRequest processRequest, ID } } + private bool IsInSkippedFolder(string filePath) + { + var pathSegments = filePath.Split(['/', '\\'], StringSplitOptions.RemoveEmptyEntries); + return this.SkippedFolders.Any(skippedFolder => pathSegments.Contains(skippedFolder, StringComparer.OrdinalIgnoreCase)); + } + private Task ParseDockerFileAsync(string fileContents, string fileLocation, ISingleFileComponentRecorder singleFileComponentRecorder, Dictionary stageNameMap) { var dockerfileModel = Dockerfile.Parse(fileContents); diff --git a/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs b/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs index bdc0c16b8..582e4b27e 100644 --- a/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs +++ b/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs @@ -1,6 +1,7 @@ #nullable enable namespace Microsoft.ComponentDetection.Detectors.Tests; +using System.IO; using System.Linq; using System.Threading.Tasks; using AwesomeAssertions; @@ -223,6 +224,19 @@ public async Task TestDockerfile_ShikiLanguageDefinitionIsIgnoredAsync(string fi componentRecorder.GetDetectedComponents().Should().BeEmpty(); } + [TestMethod] + public async Task TestDockerfile_UnderNodeModulesIsIgnoredAsync() + { + var fileLocation = Path.Combine(Path.GetTempPath(), "node_modules", "shiki", "dist", "langs", "dockerfile.mjs"); + + var (scanResult, componentRecorder) = await this.DetectorTestUtility + .WithFile("dockerfile.mjs", "FROM redis:7-alpine", fileLocation: fileLocation) + .ExecuteDetectorAsync(); + + scanResult.ResultCode.Should().Be(ProcessingResultCode.Success); + componentRecorder.GetDetectedComponents().Should().BeEmpty(); + } + [TestMethod] public async Task TestDockerfile_NoFromInstructionsAsync() { From 16fec9966d1fbace98d745ab09c92c586673288a Mon Sep 17 00:00:00 2001 From: Julian Pinzer Date: Wed, 30 Sep 2026 16:53:40 -0400 Subject: [PATCH 4/5] Add logging verification for Shiki language definition test --- .../DockerfileComponentDetectorTests.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs b/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs index 582e4b27e..6b47125ec 100644 --- a/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs +++ b/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs @@ -1,6 +1,7 @@ #nullable enable namespace Microsoft.ComponentDetection.Detectors.Tests; +using System; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -9,6 +10,7 @@ namespace Microsoft.ComponentDetection.Detectors.Tests; using Microsoft.ComponentDetection.Contracts.TypedComponent; using Microsoft.ComponentDetection.Detectors.Dockerfile; using Microsoft.ComponentDetection.TestsUtilities; +using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -216,12 +218,31 @@ public async Task TestDockerfile_PrefixedFilenameAsync() [DataRow("dockerfile.d.mts")] public async Task TestDockerfile_ShikiLanguageDefinitionIsIgnoredAsync(string fileName) { + var loggerMock = new Mock>(); + var (scanResult, componentRecorder) = await this.DetectorTestUtility .WithFile(fileName, "export default Object.freeze({ name: 'dockerfile' });") + .AddServiceMock(loggerMock) .ExecuteDetectorAsync(); scanResult.ResultCode.Should().Be(ProcessingResultCode.Success); componentRecorder.GetDetectedComponents().Should().BeEmpty(); + loggerMock.Verify( + logger => logger.Log( + LogLevel.Warning, + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>()), + Times.Once); + loggerMock.Verify( + logger => logger.Log( + LogLevel.Error, + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>()), + Times.Never); } [TestMethod] From 8a9bd5187447c014ed2cfebba00571b0e087168a Mon Sep 17 00:00:00 2001 From: Julian Pinzer Date: Wed, 30 Sep 2026 17:01:36 -0400 Subject: [PATCH 5/5] Fix logger verification to handle nullable exceptions in Dockerfile tests --- .../DockerfileComponentDetectorTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs b/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs index 6b47125ec..90d685172 100644 --- a/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs +++ b/test/Microsoft.ComponentDetection.Detectors.Tests/DockerfileComponentDetectorTests.cs @@ -233,7 +233,7 @@ public async Task TestDockerfile_ShikiLanguageDefinitionIsIgnoredAsync(string fi It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>()), + It.IsAny>()), Times.Once); loggerMock.Verify( logger => logger.Log( @@ -241,7 +241,7 @@ public async Task TestDockerfile_ShikiLanguageDefinitionIsIgnoredAsync(string fi It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>()), + It.IsAny>()), Times.Never); }