From 1ceb4208dd8a59a5a2b0a8afd7f911ec50de519d Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 27 Feb 2026 14:17:29 +0000 Subject: [PATCH 1/7] Refactor `GetReachableNuGetFeeds` out of `GetReachableFallbackNugetFeeds` --- .../NugetPackageRestorer.cs | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index 1d01412ee051..33e8db19eab4 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -192,6 +192,34 @@ public HashSet Restore() return assemblyLookupLocations; } + /// + /// Tests which of the feeds given by are reachable. + /// + /// The feeds to check. + /// Whether the feeds are fallback feeds or not. + /// The list of feeds that could be reached. + private List GetReachableNuGetFeeds(HashSet feedsToCheck, bool isFallback) + { + var fallbackStr = isFallback ? "fallback " : ""; + logger.LogInfo($"Checking {fallbackStr}NuGet feed reachability on feeds: {string.Join(", ", feedsToCheck.OrderBy(f => f))}"); + + var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback); + var reachableFeeds = feedsToCheck + .Where(feed => IsFeedReachable(feed, initialTimeout, tryCount, allowExceptions: false)) + .ToList(); + + if (reachableFeeds.Count == 0) + { + logger.LogWarning($"No {fallbackStr}NuGet feeds are reachable."); + } + else + { + logger.LogInfo($"Reachable {fallbackStr}NuGet feeds: {string.Join(", ", reachableFeeds.OrderBy(f => f))}"); + } + + return reachableFeeds; + } + private List GetReachableFallbackNugetFeeds(HashSet? feedsFromNugetConfigs) { var fallbackFeeds = EnvironmentVariables.GetURLs(EnvironmentVariableNames.FallbackNugetFeeds).ToHashSet(); @@ -212,21 +240,7 @@ private List GetReachableFallbackNugetFeeds(HashSet? feedsFromNu } } - logger.LogInfo($"Checking fallback NuGet feed reachability on feeds: {string.Join(", ", fallbackFeeds.OrderBy(f => f))}"); - var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback: true); - var reachableFallbackFeeds = fallbackFeeds.Where(feed => IsFeedReachable(feed, initialTimeout, tryCount, allowExceptions: false)).ToList(); - if (reachableFallbackFeeds.Count == 0) - { - logger.LogWarning("No fallback NuGet feeds are reachable."); - } - else - { - logger.LogInfo($"Reachable fallback NuGet feeds: {string.Join(", ", reachableFallbackFeeds.OrderBy(f => f))}"); - } - - compilationInfoContainer.CompilationInfos.Add(("Reachable fallback NuGet feed count", reachableFallbackFeeds.Count.ToString())); - - return reachableFallbackFeeds; + return GetReachableNuGetFeeds(fallbackFeeds, isFallback: true); } /// From ddcd9d5cedaef64cd41b580e2e84ff38a08128c4 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 27 Feb 2026 14:23:03 +0000 Subject: [PATCH 2/7] Use `GetReachableNuGetFeeds` in `CheckSpecifiedFeeds` --- .../NugetPackageRestorer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index 33e8db19eab4..5d7ad8f6124a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -767,8 +767,7 @@ private bool CheckFeeds(out HashSet explicitFeeds, out HashSet a /// True if all feeds are reachable or false otherwise. private bool CheckSpecifiedFeeds(HashSet feeds) { - logger.LogInfo("Checking that NuGet feeds are reachable..."); - + // Exclude any feeds that are configured by the corresponding environment variable. var excludedFeeds = EnvironmentVariables.GetURLs(EnvironmentVariableNames.ExcludedNugetFeedsFromResponsivenessCheck) .ToHashSet(); @@ -777,9 +776,10 @@ private bool CheckSpecifiedFeeds(HashSet feeds) logger.LogInfo($"Excluded NuGet feeds from responsiveness check: {string.Join(", ", excludedFeeds.OrderBy(f => f))}"); } - var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback: false); + var feedsToCheck = feeds.Where(feed => !excludedFeeds.Contains(feed)).ToHashSet(); + var reachableFeeds = this.GetReachableNuGetFeeds(feedsToCheck, isFallback: false); + var allFeedsReachable = reachableFeeds.Count == feedsToCheck.Count; - var allFeedsReachable = feeds.All(feed => excludedFeeds.Contains(feed) || IsFeedReachable(feed, initialTimeout, tryCount)); if (!allFeedsReachable) { logger.LogWarning("Found unreachable NuGet feed in C# analysis with build-mode 'none'. This may cause missing dependencies in the analysis."); From acba599217d8d032d2189301fa2b02b0ae5c40d4 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 27 Feb 2026 14:24:55 +0000 Subject: [PATCH 3/7] Inline `CheckFeeds` --- .../NugetPackageRestorer.cs | 64 +++++++++---------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index 5d7ad8f6124a..7bdec255ab4f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -119,13 +119,37 @@ public HashSet Restore() try { - if (checkNugetFeedResponsiveness && !CheckFeeds(out explicitFeeds, out allFeeds)) + if (checkNugetFeedResponsiveness) { - // todo: we could also check the reachability of the inherited nuget feeds, but to use those in the fallback we would need to handle authentication too. - var unresponsiveMissingPackageLocation = DownloadMissingPackagesFromSpecificFeeds([], explicitFeeds); - return unresponsiveMissingPackageLocation is null - ? [] - : [unresponsiveMissingPackageLocation]; + // Find feeds that are configured in NuGet.config files and divide them into ones that + // are explicitly configured for the project, and "all feeds" (including inherited ones) + // from other locations on the host outside of the working directory. + (explicitFeeds, allFeeds) = GetAllFeeds(); + var inheritedFeeds = allFeeds.Except(explicitFeeds).ToHashSet(); + + // Check whether the explicit feeds can be reached. + HashSet feedsToCheck = explicitFeeds; + + // If private package registries are configured for C#, then check those + // in addition to the ones that are configured in `nuget.config` files. + this.dependabotProxy?.RegistryURLs.ForEach(url => feedsToCheck.Add(url)); + + var explicitFeedsReachable = this.CheckSpecifiedFeeds(feedsToCheck); + + if (inheritedFeeds.Count > 0) + { + logger.LogInfo($"Inherited NuGet feeds (not checked for reachability): {string.Join(", ", inheritedFeeds.OrderBy(f => f))}"); + compilationInfoContainer.CompilationInfos.Add(("Inherited NuGet feed count", inheritedFeeds.Count.ToString())); + } + + if (!explicitFeedsReachable) + { + // todo: we could also check the reachability of the inherited nuget feeds, but to use those in the fallback we would need to handle authentication too. + var unresponsiveMissingPackageLocation = DownloadMissingPackagesFromSpecificFeeds([], explicitFeeds); + return unresponsiveMissingPackageLocation is null + ? [] + : [unresponsiveMissingPackageLocation]; + } } using (var nuget = new NugetExeWrapper(fileProvider, legacyPackageDirectory, logger)) @@ -732,34 +756,6 @@ private bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount, return (timeoutMilliSeconds, tryCount); } - /// - /// Checks that we can connect to all NuGet feeds that are explicitly configured in configuration files - /// as well as any private package registry feeds that are configured. - /// - /// Outputs the set of explicit feeds. - /// Outputs the set of all feeds (explicit and inherited). - /// True if all feeds are reachable or false otherwise. - private bool CheckFeeds(out HashSet explicitFeeds, out HashSet allFeeds) - { - (explicitFeeds, allFeeds) = GetAllFeeds(); - HashSet feedsToCheck = explicitFeeds; - - // If private package registries are configured for C#, then check those - // in addition to the ones that are configured in `nuget.config` files. - this.dependabotProxy?.RegistryURLs.ForEach(url => feedsToCheck.Add(url)); - - var allFeedsReachable = this.CheckSpecifiedFeeds(feedsToCheck); - - var inheritedFeeds = allFeeds.Except(explicitFeeds).ToHashSet(); - if (inheritedFeeds.Count > 0) - { - logger.LogInfo($"Inherited NuGet feeds (not checked for reachability): {string.Join(", ", inheritedFeeds.OrderBy(f => f))}"); - compilationInfoContainer.CompilationInfos.Add(("Inherited NuGet feed count", inheritedFeeds.Count.ToString())); - } - - return allFeedsReachable; - } - /// /// Checks that we can connect to the specified NuGet feeds. /// From 27ff77e5781f1ee95ef3f36f6a3f3edd3c5d856b Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 27 Feb 2026 14:25:58 +0000 Subject: [PATCH 4/7] Use `explicitFeeds` directly --- .../NugetPackageRestorer.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index 7bdec255ab4f..4a4029507d18 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -127,14 +127,11 @@ public HashSet Restore() (explicitFeeds, allFeeds) = GetAllFeeds(); var inheritedFeeds = allFeeds.Except(explicitFeeds).ToHashSet(); - // Check whether the explicit feeds can be reached. - HashSet feedsToCheck = explicitFeeds; - - // If private package registries are configured for C#, then check those + // If private package registries are configured for C#, then consider those // in addition to the ones that are configured in `nuget.config` files. - this.dependabotProxy?.RegistryURLs.ForEach(url => feedsToCheck.Add(url)); + this.dependabotProxy?.RegistryURLs.ForEach(url => explicitFeeds.Add(url)); - var explicitFeedsReachable = this.CheckSpecifiedFeeds(feedsToCheck); + var explicitFeedsReachable = this.CheckSpecifiedFeeds(explicitFeeds); if (inheritedFeeds.Count > 0) { @@ -191,6 +188,7 @@ public HashSet Restore() logger.LogError($"Failed to restore NuGet packages with nuget.exe: {exc.Message}"); } + // Restore project dependencies with `dotnet restore`. var restoredProjects = RestoreSolutions(out var container); var projects = fileProvider.Projects.Except(restoredProjects); RestoreProjects(projects, allFeeds, out var containers); From 5415bb71193bb4ec6a60b7191204919a479e24da Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 27 Feb 2026 14:30:57 +0000 Subject: [PATCH 5/7] Divide up `CheckSpecifiedFeeds` --- .../NugetPackageRestorer.cs | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index 4a4029507d18..f300865d2aeb 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -755,13 +755,10 @@ private bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount, } /// - /// Checks that we can connect to the specified NuGet feeds. + /// Retrieves a list of excluded NuGet feeds from the corresponding environment variable. /// - /// The set of package feeds to check. - /// True if all feeds are reachable or false otherwise. - private bool CheckSpecifiedFeeds(HashSet feeds) + private HashSet GetExcludedFeeds() { - // Exclude any feeds that are configured by the corresponding environment variable. var excludedFeeds = EnvironmentVariables.GetURLs(EnvironmentVariableNames.ExcludedNugetFeedsFromResponsivenessCheck) .ToHashSet(); @@ -770,10 +767,35 @@ private bool CheckSpecifiedFeeds(HashSet feeds) logger.LogInfo($"Excluded NuGet feeds from responsiveness check: {string.Join(", ", excludedFeeds.OrderBy(f => f))}"); } + return excludedFeeds; + } + + /// + /// Checks that we can connect to the specified NuGet feeds. + /// + /// The set of package feeds to check. + /// True if all feeds are reachable or false otherwise. + private bool CheckSpecifiedFeeds(HashSet feeds) + { + // Exclude any feeds that are configured by the corresponding environment variable. + var excludedFeeds = GetExcludedFeeds(); + var feedsToCheck = feeds.Where(feed => !excludedFeeds.Contains(feed)).ToHashSet(); var reachableFeeds = this.GetReachableNuGetFeeds(feedsToCheck, isFallback: false); var allFeedsReachable = reachableFeeds.Count == feedsToCheck.Count; + this.EmitUnreachableFeedsDiagnostics(allFeedsReachable); + + return allFeedsReachable; + } + + /// + /// If is `false`, logs this and emits a diagnostic. + /// Adds a `CompilationInfos` entry either way. + /// + /// Whether all feeds were reachable or not. + private void EmitUnreachableFeedsDiagnostics(bool allFeedsReachable) + { if (!allFeedsReachable) { logger.LogWarning("Found unreachable NuGet feed in C# analysis with build-mode 'none'. This may cause missing dependencies in the analysis."); @@ -787,8 +809,6 @@ private bool CheckSpecifiedFeeds(HashSet feeds) )); } compilationInfoContainer.CompilationInfos.Add(("All NuGet feeds reachable", allFeedsReachable ? "1" : "0")); - - return allFeedsReachable; } private IEnumerable GetFeeds(Func> getNugetFeeds) From 7a8e10e17e319855fb17f9d2505efe50dfc25409 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 27 Feb 2026 14:32:01 +0000 Subject: [PATCH 6/7] Check reachability of inherited feeds --- .../NugetPackageRestorer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index f300865d2aeb..474b3f6c7f2c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -132,10 +132,10 @@ public HashSet Restore() this.dependabotProxy?.RegistryURLs.ForEach(url => explicitFeeds.Add(url)); var explicitFeedsReachable = this.CheckSpecifiedFeeds(explicitFeeds); + this.GetReachableNuGetFeeds(inheritedFeeds, isFallback: false); if (inheritedFeeds.Count > 0) { - logger.LogInfo($"Inherited NuGet feeds (not checked for reachability): {string.Join(", ", inheritedFeeds.OrderBy(f => f))}"); compilationInfoContainer.CompilationInfos.Add(("Inherited NuGet feed count", inheritedFeeds.Count.ToString())); } From 791c1fa3d8491210285898ee400d48014fb54bf4 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 27 Feb 2026 14:38:01 +0000 Subject: [PATCH 7/7] Only use reachable feeds when private registries are configured --- .../NugetPackageRestorer.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index 474b3f6c7f2c..0e8134bd19ba 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -116,6 +116,7 @@ public HashSet Restore() HashSet? explicitFeeds = null; HashSet? allFeeds = null; + HashSet? reachableFeeds = []; try { @@ -131,8 +132,11 @@ public HashSet Restore() // in addition to the ones that are configured in `nuget.config` files. this.dependabotProxy?.RegistryURLs.ForEach(url => explicitFeeds.Add(url)); - var explicitFeedsReachable = this.CheckSpecifiedFeeds(explicitFeeds); - this.GetReachableNuGetFeeds(inheritedFeeds, isFallback: false); + var (explicitFeedsReachable, reachableExplicitFeeds) = + this.CheckSpecifiedFeeds(explicitFeeds); + reachableFeeds.UnionWith(reachableExplicitFeeds); + + reachableFeeds.UnionWith(this.GetReachableNuGetFeeds(inheritedFeeds, isFallback: false)); if (inheritedFeeds.Count > 0) { @@ -191,7 +195,7 @@ public HashSet Restore() // Restore project dependencies with `dotnet restore`. var restoredProjects = RestoreSolutions(out var container); var projects = fileProvider.Projects.Except(restoredProjects); - RestoreProjects(projects, allFeeds, out var containers); + RestoreProjects(projects, reachableFeeds, out var containers); var dependencies = containers.Flatten(container); @@ -774,8 +778,11 @@ private HashSet GetExcludedFeeds() /// Checks that we can connect to the specified NuGet feeds. /// /// The set of package feeds to check. - /// True if all feeds are reachable or false otherwise. - private bool CheckSpecifiedFeeds(HashSet feeds) + /// + /// True if all feeds are reachable or false otherwise. + /// Also returns the list of reachable feeds. + /// + private (bool, List) CheckSpecifiedFeeds(HashSet feeds) { // Exclude any feeds that are configured by the corresponding environment variable. var excludedFeeds = GetExcludedFeeds(); @@ -786,7 +793,7 @@ private bool CheckSpecifiedFeeds(HashSet feeds) this.EmitUnreachableFeedsDiagnostics(allFeedsReachable); - return allFeedsReachable; + return (allFeedsReachable, reachableFeeds); } ///