@@ -37,7 +37,7 @@ internal class LinuxScanner : ILinuxScanner
3737 /// When multiple detectors scan the same image concurrently, the second
3838 /// caller awaits the already-running task instead of launching a new container.
3939 /// </summary>
40- private static readonly ConcurrentDictionary < ( string Source , LinuxScannerScope Scope ) , Task < string > > SyftRunCache = new ( ) ;
40+ private static readonly ConcurrentDictionary < ( string Source , LinuxScannerScope Scope , string Binds ) , Task < string > > SyftRunCache = new ( ) ;
4141
4242 private static readonly int SemaphoreTimeout = Convert . ToInt32 (
4343 TimeSpan . FromHours ( 1 ) . TotalMilliseconds
@@ -261,8 +261,8 @@ private IEnumerable<LayerMappedLinuxComponents> ProcessSyftOutputWithTelemetry(
261261
262262 /// <summary>
263263 /// Runs the Syft scanner container and returns the stdout output.
264- /// For Docker image scans (no additional binds), results are cached so that
265- /// concurrent callers scanning the same image+scope share a single container run.
264+ /// Results are cached by (source, scope, binds) so that concurrent callers
265+ /// with identical parameters share a single container run.
266266 /// </summary>
267267 private async Task < string > RunSyftAsync (
268268 string syftSource ,
@@ -272,14 +272,8 @@ private async Task<string> RunSyftAsync(
272272 LinuxScannerSyftTelemetryRecord syftTelemetryRecord ,
273273 CancellationToken cancellationToken )
274274 {
275- // Local image scans use additional binds specific to each call, so they
276- // cannot be deduplicated safely — run them directly.
277- if ( additionalBinds is { Count : > 0 } )
278- {
279- return await this . RunSyftCoreAsync ( syftSource , scope , additionalBinds , record , syftTelemetryRecord , cancellationToken ) ;
280- }
281-
282- var cacheKey = ( syftSource , scope ) ;
275+ var bindsKey = string . Join ( ";" , ( additionalBinds ?? [ ] ) . OrderBy ( b => b , StringComparer . Ordinal ) ) ;
276+ var cacheKey = ( syftSource , scope , bindsKey ) ;
283277 var tcs = new TaskCompletionSource < string > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
284278 var existingTask = SyftRunCache . GetOrAdd ( cacheKey , tcs . Task ) ;
285279
@@ -292,7 +286,7 @@ private async Task<string> RunSyftAsync(
292286 // We own this cache entry — run syft and propagate the result.
293287 try
294288 {
295- var result = await this . RunSyftCoreAsync ( syftSource , scope , additionalBinds , record , syftTelemetryRecord , cancellationToken ) ;
289+ var result = await this . RunSyftCoreAsync ( syftSource , scope , additionalBinds ?? [ ] , record , syftTelemetryRecord , cancellationToken ) ;
296290 tcs . SetResult ( result ) ;
297291 return result ;
298292 }
0 commit comments