@@ -12,6 +12,10 @@ export function createAdvisorySource(options?: { osvUrl?: string }): AdvisorySou
1212 return new OsvAdvisorySource ( options ?. osvUrl ) ;
1313}
1414
15+ function getPackageCacheKey ( pkg : PackageRef ) : string {
16+ return `${ pkg . ecosystem } :${ pkg . name } @${ pkg . version } ` ;
17+ }
18+
1519export async function scanPackages (
1620 packages : PackageRef [ ] ,
1721 batchSize : number ,
@@ -22,12 +26,27 @@ export async function scanPackages(
2226
2327 const spinner = createSpinner ( "Scanning dependencies against OSV..." , options ) ;
2428 const advisorySource = createAdvisorySource ( { osvUrl : options . osvUrl } ) ;
29+ const cache = loadCache ( cacheDirOverride ) ;
2530
2631 try {
27- const chunks = chunk ( packages , batchSize ) ;
2832 const results : Array < { pkg : PackageRef ; vulnIds : string [ ] } > = [ ] ;
33+ const uncachedPackages : PackageRef [ ] = [ ] ;
2934
3035 if ( ! offline ) {
36+ for ( const pkg of packages ) {
37+ const cacheKey = getPackageCacheKey ( pkg ) ;
38+ if ( cacheKey in cache . queryEntries ) {
39+ const vulnIds = cache . queryEntries [ cacheKey ] ?? [ ] ;
40+ if ( vulnIds . length > 0 ) {
41+ results . push ( { pkg, vulnIds } ) ;
42+ }
43+ continue ;
44+ }
45+
46+ uncachedPackages . push ( pkg ) ;
47+ }
48+
49+ const chunks = chunk ( uncachedPackages , batchSize ) ;
3150 for ( let i = 0 ; i < chunks . length ; i ++ ) {
3251 spinner . update ( `Scanning OSV batch ${ i + 1 } /${ chunks . length } ...` ) ;
3352 const chunkItems = chunks [ i ] ;
@@ -38,18 +57,22 @@ export async function scanPackages(
3857 const pkg = chunkItems [ j ] ;
3958 const row = rows [ j ] ;
4059 const vulnIds = ( row ?. vulnerabilities ?? [ ] ) . map ( v => v . id ) . filter ( Boolean ) ;
60+ cache . queryEntries [ getPackageCacheKey ( pkg ) ] = vulnIds ;
4161 if ( vulnIds . length > 0 ) {
4262 results . push ( { pkg, vulnIds } ) ;
4363 }
4464 }
4565 }
4666
47- spinner . succeed ( `Queried OSV in ${ chunks . length } batch${ chunks . length === 1 ? "" : "es" } ` ) ;
67+ if ( chunks . length === 0 ) {
68+ spinner . succeed ( "Loaded package matches from cache" ) ;
69+ } else {
70+ spinner . succeed ( `Queried OSV in ${ chunks . length } batch${ chunks . length === 1 ? "" : "es" } ` ) ;
71+ }
4872 } else {
4973 spinner . succeed ( "Offline mode enabled; package matching was skipped" ) ;
5074 }
5175
52- const cache = loadCache ( cacheDirOverride ) ;
5376 const idSet = new Set ( results . flatMap ( r => r . vulnIds ) ) ;
5477 const vulnMap = new Map < string , OsvVuln > ( ) ;
5578
@@ -60,9 +83,11 @@ export async function scanPackages(
6083 for ( let i = 0 ; i < ids . length ; i ++ ) {
6184 const id = ids [ i ] ;
6285 detailSpinner . update ( `Fetching vulnerability details ${ i + 1 } /${ ids . length } ...` ) ;
63- const cached = cache . entries [ id ] ;
64- if ( cached ) {
65- vulnMap . set ( id , cached ) ;
86+ if ( id in cache . entries ) {
87+ const cached = cache . entries [ id ] ;
88+ if ( cached ) {
89+ vulnMap . set ( id , cached ) ;
90+ }
6691 continue ;
6792 }
6893
0 commit comments