Skip to content

Commit 4d7a1d9

Browse files
committed
Remove references to filtered out components from dependency roots
1 parent 301a27f commit 4d7a1d9

2 files changed

Lines changed: 61 additions & 0 deletions

File tree

src/Microsoft.ComponentDetection.Orchestrator/Services/GraphTranslation/DefaultGraphTranslationService.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public ScanResult GenerateScanResultFromProcessingResult(
4646
{
4747
componentsToOutput = FilterOutBaseImageComponents(componentsToOutput, detectorProcessingResult.ContainersDetailsMap);
4848
PruneFilteredComponentsFromGraphs(dependencyGraphs, componentsToOutput);
49+
PruneFilteredComponentReferrers(componentsToOutput);
4950
}
5051

5152
return new DefaultGraphScanResult
@@ -162,6 +163,22 @@ private static void PruneFilteredComponentsFromGraphs(DependencyGraphCollection
162163
}
163164
}
164165

166+
/// <summary>
167+
/// Removes references to filtered-out components from the DependencyRoots and AncestralDependencyRoots
168+
/// of retained components, so that TopLevelReferrers and AncestralReferrers in the output don't
169+
/// reference components that were removed from ComponentsFound.
170+
/// </summary>
171+
private static void PruneFilteredComponentReferrers(List<DetectedComponent> retainedComponents)
172+
{
173+
var retainedIds = new HashSet<string>(retainedComponents.Select(c => c.Component.Id));
174+
175+
foreach (var component in retainedComponents)
176+
{
177+
component.DependencyRoots?.RemoveWhere(root => !retainedIds.Contains(root.Id));
178+
component.AncestralDependencyRoots?.RemoveWhere(root => !retainedIds.Contains(root.Id));
179+
}
180+
}
181+
165182
private static ConcurrentHashSet<string> MergeTargetFrameworks(ConcurrentHashSet<string> left, ConcurrentHashSet<string> right)
166183
{
167184
if (left == null && right == null)

test/Microsoft.ComponentDetection.Orchestrator.Tests/Services/DefaultGraphTranslationServiceTests.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,4 +778,48 @@ public void FilterBaseImageComponents_DependencyGraphsUnchangedWhenFlagDisabled(
778778
graph.Should().ContainKey(retainedComponent.Component.Id);
779779
graph.Should().ContainKey(baseImageComponent.Component.Id);
780780
}
781+
782+
[TestMethod]
783+
public void FilterBaseImageComponents_PrunesReferrersToFilteredComponents()
784+
{
785+
var filePath = Path.Join(this.sourceDirectory.FullName, "file1");
786+
var singleFileRecorder = this.componentRecorder.CreateSingleFileComponentRecorder(filePath);
787+
788+
// base-pkg is a root that depends on child-pkg (non-base-image).
789+
var baseImageComponent = new DetectedComponent(new NpmComponent("base-pkg", "1.0.0"), containerDetailsId: 1, containerLayerId: 0);
790+
var childComponent = new DetectedComponent(new NpmComponent("child-pkg", "1.0.0"), containerDetailsId: 1, containerLayerId: 1);
791+
792+
singleFileRecorder.RegisterUsage(baseImageComponent, isExplicitReferencedDependency: true);
793+
singleFileRecorder.RegisterUsage(childComponent, parentComponentId: baseImageComponent.Component.Id);
794+
795+
var containerDetailsMap = new Dictionary<int, ContainerDetails>
796+
{
797+
[1] = new ContainerDetails
798+
{
799+
Id = 1,
800+
Layers = [new DockerLayer { LayerIndex = 0, IsBaseImage = true }, new DockerLayer { LayerIndex = 1, IsBaseImage = false }],
801+
},
802+
};
803+
804+
var processingResult = new DetectorProcessingResult
805+
{
806+
ResultCode = ProcessingResultCode.Success,
807+
ContainersDetailsMap = containerDetailsMap,
808+
ComponentRecorders = [(this.componentDetectorMock.Object, this.componentRecorder)],
809+
};
810+
811+
var result = this.serviceUnderTest.GenerateScanResultFromProcessingResult(
812+
processingResult, new ScanSettings { SourceDirectory = this.sourceDirectory, FilterBaseImageComponents = true });
813+
814+
// Only child-pkg should remain (base-pkg is exclusively from base image layer).
815+
result.ComponentsFound.Should().HaveCount(1);
816+
var child = result.ComponentsFound.Single();
817+
((NpmComponent)child.Component).Name.Should().Be("child-pkg");
818+
819+
// TopLevelReferrers should not reference the filtered base-image component.
820+
child.TopLevelReferrers?.Should().NotContain(c => c.Id == baseImageComponent.Component.Id);
821+
822+
// AncestralReferrers should not reference the filtered base-image component.
823+
child.AncestralReferrers?.Should().NotContain(c => c.Id == baseImageComponent.Component.Id);
824+
}
781825
}

0 commit comments

Comments
 (0)