@@ -568,7 +568,7 @@ public void GenerateScanResult_MultipleRichAndBare_BareGraphDataAbsorbedByAllRic
568568 [ TestMethod ]
569569 public void FilterBaseImageComponents_RemovesComponentsExclusivelyFromBaseImageLayers ( )
570570 {
571- var singleFileRecorder = this . componentRecorder . CreateSingleFileComponentRecorder ( Path . Join ( this . sourceDirectory . FullName , "/ file1" ) ) ;
571+ var singleFileRecorder = this . componentRecorder . CreateSingleFileComponentRecorder ( Path . Join ( this . sourceDirectory . FullName , "file1" ) ) ;
572572
573573 var baseImageComponent = new DetectedComponent ( new NpmComponent ( "base-pkg" , "1.0.0" ) , containerDetailsId : 1 , containerLayerId : 0 ) ;
574574 singleFileRecorder . RegisterUsage ( baseImageComponent ) ;
@@ -598,7 +598,7 @@ public void FilterBaseImageComponents_RemovesComponentsExclusivelyFromBaseImageL
598598 [ TestMethod ]
599599 public void FilterBaseImageComponents_RetainsComponentsWithMixedLayers ( )
600600 {
601- var singleFileRecorder = this . componentRecorder . CreateSingleFileComponentRecorder ( Path . Join ( this . sourceDirectory . FullName , "/ file1" ) ) ;
601+ var singleFileRecorder = this . componentRecorder . CreateSingleFileComponentRecorder ( Path . Join ( this . sourceDirectory . FullName , "file1" ) ) ;
602602
603603 var mixedComponent = new DetectedComponent ( new NpmComponent ( "mixed-pkg" , "1.0.0" ) , containerDetailsId : 1 , containerLayerId : 0 ) ;
604604 mixedComponent . ContainerLayerIds [ 1 ] = [ 0 , 1 ] ;
@@ -630,7 +630,7 @@ public void FilterBaseImageComponents_RetainsComponentsWithMixedLayers()
630630 [ TestMethod ]
631631 public void FilterBaseImageComponents_RetainsComponentsWithNoContainerReferences ( )
632632 {
633- var singleFileRecorder = this . componentRecorder . CreateSingleFileComponentRecorder ( Path . Join ( this . sourceDirectory . FullName , "/ file1" ) ) ;
633+ var singleFileRecorder = this . componentRecorder . CreateSingleFileComponentRecorder ( Path . Join ( this . sourceDirectory . FullName , "file1" ) ) ;
634634
635635 var filesystemComponent = new DetectedComponent ( new NpmComponent ( "fs-pkg" , "2.0.0" ) ) ;
636636 singleFileRecorder . RegisterUsage ( filesystemComponent ) ;
@@ -661,7 +661,7 @@ public void FilterBaseImageComponents_RetainsComponentsWithNoContainerReferences
661661 [ TestMethod ]
662662 public void FilterBaseImageComponents_NoOpWhenFlagIsDisabled ( )
663663 {
664- var singleFileRecorder = this . componentRecorder . CreateSingleFileComponentRecorder ( Path . Join ( this . sourceDirectory . FullName , "/ file1" ) ) ;
664+ var singleFileRecorder = this . componentRecorder . CreateSingleFileComponentRecorder ( Path . Join ( this . sourceDirectory . FullName , "file1" ) ) ;
665665
666666 var baseImageComponent = new DetectedComponent ( new NpmComponent ( "base-pkg" , "1.0.0" ) , containerDetailsId : 1 , containerLayerId : 0 ) ;
667667 singleFileRecorder . RegisterUsage ( baseImageComponent ) ;
@@ -688,4 +688,94 @@ public void FilterBaseImageComponents_NoOpWhenFlagIsDisabled()
688688 result . ComponentsFound . Should ( ) . HaveCount ( 1 ) ;
689689 ( ( NpmComponent ) result . ComponentsFound . Single ( ) . Component ) . Name . Should ( ) . Be ( "base-pkg" ) ;
690690 }
691+
692+ [ TestMethod ]
693+ public void FilterBaseImageComponents_PrunesFilteredComponentsFromDependencyGraphs ( )
694+ {
695+ var filePath = Path . Join ( this . sourceDirectory . FullName , "file1" ) ;
696+ var singleFileRecorder = this . componentRecorder . CreateSingleFileComponentRecorder ( filePath ) ;
697+
698+ // Register a retained (non-base-image) component and a base-image component with a dependency edge.
699+ var retainedComponent = new DetectedComponent ( new NpmComponent ( "app-pkg" , "1.0.0" ) , containerDetailsId : 1 , containerLayerId : 1 ) ;
700+ var baseImageComponent = new DetectedComponent ( new NpmComponent ( "base-pkg" , "2.0.0" ) , containerDetailsId : 1 , containerLayerId : 0 ) ;
701+
702+ singleFileRecorder . RegisterUsage ( retainedComponent , isExplicitReferencedDependency : true ) ;
703+ singleFileRecorder . RegisterUsage ( baseImageComponent , parentComponentId : retainedComponent . Component . Id ) ;
704+
705+ var containerDetailsMap = new Dictionary < int , ContainerDetails >
706+ {
707+ [ 1 ] = new ContainerDetails
708+ {
709+ Id = 1 ,
710+ Layers = [ new DockerLayer { LayerIndex = 0 , IsBaseImage = true } , new DockerLayer { LayerIndex = 1 , IsBaseImage = false } ] ,
711+ } ,
712+ } ;
713+
714+ var processingResult = new DetectorProcessingResult
715+ {
716+ ResultCode = ProcessingResultCode . Success ,
717+ ContainersDetailsMap = containerDetailsMap ,
718+ ComponentRecorders = [ ( this . componentDetectorMock . Object , this . componentRecorder ) ] ,
719+ } ;
720+
721+ var result = ( DefaultGraphScanResult ) this . serviceUnderTest . GenerateScanResultFromProcessingResult (
722+ processingResult , new ScanSettings { SourceDirectory = this . sourceDirectory , FilterBaseImageComponents = true } ) ;
723+
724+ // Only the non-base-image component should remain.
725+ result . ComponentsFound . Should ( ) . HaveCount ( 1 ) ;
726+ ( ( NpmComponent ) result . ComponentsFound . Single ( ) . Component ) . Name . Should ( ) . Be ( "app-pkg" ) ;
727+
728+ // The dependency graph should not reference the filtered component.
729+ var graphEntry = result . DependencyGraphs . Should ( ) . ContainSingle ( ) . Which ;
730+ var graph = graphEntry . Value . Graph ;
731+ graph . Should ( ) . ContainKey ( retainedComponent . Component . Id ) ;
732+ graph . Should ( ) . NotContainKey ( baseImageComponent . Component . Id ) ;
733+
734+ // The retained component's edge set should not reference the removed component.
735+ var retainedEdges = graph [ retainedComponent . Component . Id ] ;
736+ retainedEdges ? . Should ( ) . NotContain ( baseImageComponent . Component . Id ) ;
737+
738+ // Metadata sets should also be cleaned.
739+ graphEntry . Value . ExplicitlyReferencedComponentIds . Should ( ) . NotContain ( baseImageComponent . Component . Id ) ;
740+ }
741+
742+ [ TestMethod ]
743+ public void FilterBaseImageComponents_DependencyGraphsUnchangedWhenFlagDisabled ( )
744+ {
745+ var filePath = Path . Join ( this . sourceDirectory . FullName , "file1" ) ;
746+ var singleFileRecorder = this . componentRecorder . CreateSingleFileComponentRecorder ( filePath ) ;
747+
748+ var retainedComponent = new DetectedComponent ( new NpmComponent ( "app-pkg" , "1.0.0" ) , containerDetailsId : 1 , containerLayerId : 1 ) ;
749+ var baseImageComponent = new DetectedComponent ( new NpmComponent ( "base-pkg" , "2.0.0" ) , containerDetailsId : 1 , containerLayerId : 0 ) ;
750+
751+ singleFileRecorder . RegisterUsage ( retainedComponent , isExplicitReferencedDependency : true ) ;
752+ singleFileRecorder . RegisterUsage ( baseImageComponent , parentComponentId : retainedComponent . Component . Id ) ;
753+
754+ var containerDetailsMap = new Dictionary < int , ContainerDetails >
755+ {
756+ [ 1 ] = new ContainerDetails
757+ {
758+ Id = 1 ,
759+ Layers = [ new DockerLayer { LayerIndex = 0 , IsBaseImage = true } , new DockerLayer { LayerIndex = 1 , IsBaseImage = false } ] ,
760+ } ,
761+ } ;
762+
763+ var processingResult = new DetectorProcessingResult
764+ {
765+ ResultCode = ProcessingResultCode . Success ,
766+ ContainersDetailsMap = containerDetailsMap ,
767+ ComponentRecorders = [ ( this . componentDetectorMock . Object , this . componentRecorder ) ] ,
768+ } ;
769+
770+ var result = ( DefaultGraphScanResult ) this . serviceUnderTest . GenerateScanResultFromProcessingResult (
771+ processingResult , new ScanSettings { SourceDirectory = this . sourceDirectory , FilterBaseImageComponents = false } ) ;
772+
773+ // Both components should be present.
774+ result . ComponentsFound . Should ( ) . HaveCount ( 2 ) ;
775+
776+ // The dependency graph should still contain both component IDs.
777+ var graph = result . DependencyGraphs . Single ( ) . Value . Graph ;
778+ graph . Should ( ) . ContainKey ( retainedComponent . Component . Id ) ;
779+ graph . Should ( ) . ContainKey ( baseImageComponent . Component . Id ) ;
780+ }
691781}
0 commit comments