@@ -783,6 +783,74 @@ describe('dependency-analysis', () => {
783783 expect ( result . vulnerablePackages [ 0 ] ?. vulnerabilities [ 0 ] ?. fixedIn ) . toBe ( '1.6.0' )
784784 } )
785785
786+ it ( 'suggests closest fixedIn when multiple ranges match (backport fix preferred)' , async ( ) => {
787+ const mockResolved = new Map ( [
788+ [
789+ 'example@3.4.6' ,
790+ {
791+ name : 'example' ,
792+ version : '3.4.6' ,
793+ size : 1000 ,
794+ optional : false ,
795+ depth : 'root' as const ,
796+ path : [ 'example@3.4.6' ] ,
797+ } ,
798+ ] ,
799+ ] )
800+ vi . mocked ( resolveDependencyTree ) . mockResolvedValue ( mockResolved )
801+
802+ // Two affected ranges:
803+ // Range 1 (broad): >= 3.4.5-foo.2, < 3.5.9-foo.15 (fix: 3.5.9-foo.15)
804+ // Range 2 (narrow backport): >= 3.4.5-foo.2, < 3.4.8 (fix: 3.4.8)
805+ //
806+ // Version 3.4.6 falls in BOTH ranges.
807+ // The broad range is listed first, so the current early-return picks 3.5.9-foo.15.
808+ // But 3.4.8 is the closer/more appropriate fix for someone on 3.4.x.
809+ mockOsvApi (
810+ [ { vulns : [ { id : 'GHSA-backport' , modified : '2024-01-01' } ] } ] ,
811+ new Map ( [
812+ [
813+ 'example@3.4.6' ,
814+ {
815+ vulns : [
816+ {
817+ id : 'GHSA-backport' ,
818+ summary : 'Vulnerability with backported fix' ,
819+ database_specific : { severity : 'HIGH' } ,
820+ affected : [
821+ {
822+ package : { ecosystem : 'npm' , name : 'example' } ,
823+ ranges : [
824+ {
825+ type : 'SEMVER' ,
826+ events : [ { introduced : '3.4.5-foo.2' } , { fixed : '3.5.9-foo.15' } ] ,
827+ } ,
828+ ] ,
829+ } ,
830+ {
831+ package : { ecosystem : 'npm' , name : 'example' } ,
832+ ranges : [
833+ {
834+ type : 'SEMVER' ,
835+ events : [ { introduced : '3.4.5-foo.2' } , { fixed : '3.4.8' } ] ,
836+ } ,
837+ ] ,
838+ } ,
839+ ] ,
840+ } ,
841+ ] ,
842+ } ,
843+ ] ,
844+ ] ) ,
845+ )
846+
847+ const result = await analyzeDependencyTree ( 'example' , '3.4.6' )
848+
849+ expect ( result . vulnerablePackages ) . toHaveLength ( 1 )
850+ // Should suggest 3.4.8 (the closest fix), not 3.5.9-foo.15
851+ expect ( result . vulnerablePackages [ 0 ] ?. vulnerabilities [ 0 ] ?. fixedIn ) . toBe ( '3.4.8' )
852+ } )
853+
786854 it ( 'returns undefined fixedIn when no matching range has a fixed version' , async ( ) => {
787855 const mockResolved = new Map ( [
788856 [
0 commit comments