C#: Support replaces-base via the DependabotProxy. - #22494
Conversation
e4e0207 to
9d030bd
Compare
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Feed-check opt-out behavior is regressed, and one fallback path can still retain nuget.org.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Balanced
Findings: 1
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs — This now probes the default feeds even when `CODEQL_EXTRACTOR_CSHARP_BUILDLESS_NUGET_FEEDS_CHECK=fal… |
What changed in this PR
Adds replaces-base support for private NuGet registries used during buildless C# dependency restoration.
Changes:
- Parses and exposes replacement-base registry URLs.
- Uses replacement registries for default and fallback feeds.
- Adds unit coverage and a change note.
| File | Description |
|---|---|
csharp/ql/lib/change-notes/2026-09-03-replaces-base.md |
Documents the new behavior. |
csharp/extractor/Semmle.Extraction.Tests/FeedManager.cs |
Tests default and fallback feed selection. |
csharp/extractor/Semmle.Extraction.Tests/DependabotProxy.cs |
Tests replaces-base parsing. |
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs |
Uses reachable default feeds during restoration. |
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDependabotProxy.cs |
Exposes replacement-base registry URLs. |
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs |
Selects replacement registries as default feeds. |
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependabotProxy.cs |
Parses and stores registry replacement metadata. |
Suppressed comments (1)
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs:315
- The replacement is only applied through
GetReachableFallbackNugetFeeds, butNugetPackageRestorer.DownloadMissingPackagescalls that path only when feed responsiveness checks are enabled; its disabled branch usesAllFeeds, which can still contain inheritednuget.org. Thusreplaces-base: truedoes not actually replace the public base feed in fallback downloads when the supported responsiveness opt-out is used. Please separate construction of the configured fallback set from reachability filtering and use that set in both branches.
fallbackFeeds.UnionWith(defaultFeeds);
logger.LogInfo($"No fallback NuGet feeds specified. Adding default feeds: {string.Join(", ", defaultFeeds.OrderBy(f => f))}");
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…Ls to replace the default NuGet feed.
9d030bd to
4e3476f
Compare
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The responsiveness-check opt-out path can still use public NuGet.org despite a configured replacement registry.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Balanced
Findings: 2
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs — DefaultFeeds is substituted only on the responsiveness-checking path. When… |
Pre-existing issues (1)
| Severity | Finding |
|---|---|
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs — This now probes the default feeds even when `CODEQL_EXTRACTOR_CSHARP_BUILDLESS_NUGET_FEEDS_CHECK=fal… View comment |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Public NuGet endpoint variants such as the supported v2 URL are not replaced.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Balanced
Findings: 2
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs — This only recognizes the canonical v3 URL, so a configured public NuGet source such as… |
Pre-existing issues (1)
| Severity | Finding |
|---|---|
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs — DefaultFeeds is substituted only on the responsiveness-checking path. When… View comment |
Issues resolved since last review (1)
| Severity | Finding |
|---|---|
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs — This now probes the default feeds even when `CODEQL_EXTRACTOR_CSHARP_BUILDLESS_NUGET_FEEDS_CHECK=fal… View resolved comment |
d34b10b to
20e2e56
Compare
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The release note inaccurately describes the replacement behavior as fallback-only.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Balanced
Findings: 1
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
csharp/ql/lib/change-notes/2026-09-03-replaces-base.md — The release note describes the replacement as fallback-only, but GetFeeds now replaces NuGet.org… |
Issues resolved since last review (2)
| Severity | Finding |
|---|---|
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs — This only recognizes the canonical v3 URL, so a configured public NuGet source such as… View resolved comment |
|
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs — DefaultFeeds is substituted only on the responsiveness-checking path. When… View resolved comment |
20e2e56 to
e4b277b
Compare
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The implementation is coherent and the remaining documentation and regression-test gaps are minor.
Review tier: Balanced
Findings: 1
New issues introduced by this change (2)
| Severity | Finding |
|---|---|
csharp/extractor/Semmle.Extraction.Tests/FeedManager.cs — The replacement test still supplies the canonical v3 endpoint in both inputs, so the newly… |
|
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs — This makes the fallback default conditional on replaces-base, but the public XML documentation… |
Issues resolved since last review (1)
| Severity | Finding |
|---|---|
csharp/ql/lib/change-notes/2026-09-03-replaces-base.md — The release note describes the replacement as fallback-only, but GetFeeds now replaces NuGet.org… View resolved comment |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
A trailing semicolon after the nested class declaration causes a compilation error.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Balanced
Findings: 1
Pre-existing issues (1)
| Severity | Finding |
|---|---|
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs — This makes the fallback default conditional on replaces-base, but the public XML documentation… View comment |
Issues resolved since last review (1)
| Severity | Finding |
|---|---|
csharp/extractor/Semmle.Extraction.Tests/FeedManager.cs — The replacement test still supplies the canonical v3 endpoint in both inputs, so the newly… View resolved comment |
…s replaces-base is set, otherwise use nuget.org.
… private registries with replaces-base: true is set.
…vate registries are configured (to make implementation consistent).
7b5b63e to
983dbbc
Compare
mbg
left a comment
There was a problem hiding this comment.
Thank you for your work on this!
I have had a look over the changes here with a wider systems point of view, but I haven't looked at how the changes may affect other parts of the extractor that haven't directly changed here.
I have left a few detailed comments on the code. As an additional, high-level point, I think it would be good to add more comments in a few places to annotate the implementation with the intended behaviour / reasoning for various checks.
| private ImmutableHashSet<string>? registryURLs; | ||
| public ImmutableHashSet<string> RegistryURLs => | ||
| registryURLs ??= registryMapping.Keys.ToImmutableHashSet(); | ||
|
|
||
| private ImmutableHashSet<string>? registryBaseURLs; | ||
| public ImmutableHashSet<string> RegistryBaseURLs => | ||
| registryBaseURLs ??= registryMapping.Where(kvp => kvp.Value).Select(kvp => kvp.Key).ToImmutableHashSet(); |
There was a problem hiding this comment.
It might be good to add docs comments for RegistryURLs and RegistryBaseURLs so that it is easier to understand what they represent without needing to read the implementation.
| /// <summary> | ||
| /// The type of the package registry. | ||
| /// </summary> | ||
| public string Type { get; init; } = ""; | ||
|
|
||
| /// <summary> | ||
| /// The URL of the package registry. | ||
| /// </summary> | ||
| public string URL { get; init; } = ""; |
There was a problem hiding this comment.
You can treat these fields as required, so if it would help avoid unexpected results elsewhere, it might be better not to initialise them to the empty string.
| var fallbackFeeds = EnvironmentVariables.GetURLs(EnvironmentVariableNames.FallbackNugetFeeds).ToHashSet(); | ||
| if (fallbackFeeds.Count == 0) |
There was a problem hiding this comment.
I don't think it's possible to set the environment variable for the fallback feeds in Default Setup, but I would still treat both those and the ones from the private registry configurations as explicit, user-provided configuration. Therefore, it might make sense to compute fallbackFeeds.UnionWith(defaultFeeds); even if there are fallback feeds configured in the environment variable. Alternatively, it might be good to at least visibly log that we are ignoring anything that doesn't come from the environment variable, when it is configured.
| } | ||
|
|
||
| [Fact] | ||
| public void TestDefaultFeeds1() |
There was a problem hiding this comment.
Minor: Here and elsewhere, the method names don't communicate what the corresponding tests are testing very well. It would be good to rename them to communicate the objectives more clearly, or add suitable comments to describe the goals of each test.
| --- | ||
| category: minorAnalysis | ||
| --- | ||
| * In `build-mode: none`, private NuGet registries configured with `replaces-base: true` in the organization-level private registry configuration now replace `nuget.org` sources whenever dependencies are downloaded, including sources discovered from NuGet configuration. |
There was a problem hiding this comment.
It would be better to write "In Default Setup" rather than "In build-mode: none", since the private registry functionality is only available in Default Setup.
Also, while "replaces-base": true is the relevant property in the JSON, it might be better to talk about the UI instead. E.g. [..] private NuGet registries that have the "Replaces base" option in the organization-level private registry configuration enabled now replace [..]


In this PR we add support using the
replace-baseflag for private registries. If any private registries are configured to replace base, then we use these registries as NuGet feed sources instead of the default publicnuget.orginfallback scenariosall scenarios - even if the public NuGet feed is mentioned innuget.configfiles (unless it is explicitly configured as a fallback feed as well).As an add on for this PR, we also prevent the fallback that doesn't provide feeds via the command line when restoring packages manually, if private registries are configured (to make the logic consistent with other similar paths).
DCA looks good.