Skip to content

Commit 6046d76

Browse files
authored
Merge pull request #22752 from michaelnebel/csharp/nugetexeproxyconfig
C#: NuGet CLI proxy environment.
2 parents 36994cd + 2e0f0c4 commit 6046d76

8 files changed

Lines changed: 88 additions & 13 deletions

File tree

‎csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetCliInvoker.cs‎

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,7 @@ private ProcessStartInfo MakeDotnetStartInfo(List<string> args, string? workingD
4444
}
4545

4646
// Configure the proxy settings, if applicable.
47-
if (this.proxy != null)
48-
{
49-
logger.LogDebug($"Configuring environment variables for the registry proxy at {this.proxy.Address}");
50-
51-
startInfo.EnvironmentVariables["HTTP_PROXY"] = this.proxy.Address;
52-
startInfo.EnvironmentVariables["HTTPS_PROXY"] = this.proxy.Address;
53-
startInfo.EnvironmentVariables["SSL_CERT_FILE"] = this.proxy.CertificatePath;
54-
}
47+
proxy?.SetProcessEnvironment(startInfo);
5548

5649
return startInfo;
5750
}

‎csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IRegistryProxy.cs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Immutable;
3+
using System.Diagnostics;
34
using System.Security.Cryptography.X509Certificates;
45

56
namespace Semmle.Extraction.CSharp.DependencyFetching
@@ -30,5 +31,11 @@ public interface IRegistryProxy : IDisposable
3031
/// The certificate used for the registry proxy.
3132
/// </summary>
3233
X509Certificate2? Certificate { get; }
34+
35+
/// <summary>
36+
/// Configures the environment variables for a process to use the registry proxy.
37+
/// </summary>
38+
/// <param name="pi">The process start info to configure.</param>
39+
void SetProcessEnvironment(ProcessStartInfo pi);
3340
}
3441
}

‎csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ internal sealed partial class NugetPackageRestorer : IDisposable
1818
private readonly IFileProvider fileProvider;
1919
private readonly FileContent fileContent;
2020
private readonly IDotNet dotnet;
21+
private readonly IRegistryProxy? registryProxy;
2122
private readonly IDiagnosticsWriter diagnosticsWriter;
2223
private readonly DependencyDirectory legacyPackageDirectory;
2324
private readonly DependencyDirectory missingPackageDirectory;
@@ -40,6 +41,7 @@ public NugetPackageRestorer(
4041
this.fileProvider = fileProvider;
4142
this.fileContent = fileContent;
4243
this.dotnet = dotnet;
44+
this.registryProxy = registryProxy;
4345
this.diagnosticsWriter = diagnosticsWriter;
4446
this.logger = logger;
4547
this.compilationInfoContainer = compilationInfoContainer;
@@ -133,7 +135,7 @@ public HashSet<AssemblyLookupLocation> Restore()
133135

134136
try
135137
{
136-
var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager);
138+
var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager, registryProxy);
137139
var count = packagesConfigRestore.InstallPackages();
138140
if (packagesConfigRestore.PackageCount > 0)
139141
{

‎csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ internal interface IPackagesConfigRestore
3333
/// </summary>
3434
internal class PackagesConfigRestoreFactory
3535
{
36-
public static IPackagesConfigRestore Create(IFileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager)
36+
public static IPackagesConfigRestore Create(IFileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager, IRegistryProxy? registryProxy)
3737
{
3838
if (SystemBuildActions.Instance.IsWindows() || SystemBuildActions.Instance.IsMonoInstalled())
3939
{
40-
return new NugetExeWrapper(fileProvider, packageDirectory, logger, feedManager);
40+
return new NugetExeWrapper(fileProvider, packageDirectory, logger, feedManager, registryProxy);
4141
}
4242

4343
return new NoOpPackagesConfig(fileProvider.PackagesConfigs, logger);
@@ -52,6 +52,7 @@ private class NugetExeWrapper : IPackagesConfigRestore
5252
{
5353
private readonly string? nugetExe;
5454
private readonly Semmle.Util.Logging.ILogger logger;
55+
private readonly IRegistryProxy? registryProxy;
5556

5657
public int PackageCount => fileProvider.PackagesConfigs.Count;
5758

@@ -70,12 +71,13 @@ private class NugetExeWrapper : IPackagesConfigRestore
7071
/// <summary>
7172
/// Create the package manager for a specified source tree.
7273
/// </summary>
73-
public NugetExeWrapper(IFileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager)
74+
public NugetExeWrapper(IFileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager, IRegistryProxy? registryProxy)
7475
{
7576
this.fileProvider = fileProvider;
7677
this.packageDirectory = packageDirectory;
7778
this.logger = logger;
7879
this.feedManager = feedManager;
80+
this.registryProxy = registryProxy;
7981

8082
if (fileProvider.PackagesConfigs.Count > 0)
8183
{
@@ -209,9 +211,13 @@ private bool TryRestoreNugetPackage(string packagesConfig)
209211
UseShellExecute = false
210212
};
211213

214+
// Configure the proxy settings, if applicable.
215+
registryProxy?.SetProcessEnvironment(pi);
216+
212217
var threadId = Environment.CurrentManagedThreadId;
213218
void onOut(string s) => logger.LogDebug(s, threadId);
214219
void onError(string s) => logger.LogError(s, threadId);
220+
logger.LogInfo($"Running '{pi.FileName} {string.Join(" ", pi.ArgumentList)}'");
215221
var exitCode = pi.ReadOutput(out _, onOut, onError);
216222
if (exitCode != 0)
217223
{

‎csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/RegistryProxy.cs‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Immutable;
33
using System.Collections.Generic;
4+
using System.Diagnostics;
45
using System.IO;
56
using System.Security.Cryptography.X509Certificates;
67
using Semmle.Util;
@@ -36,6 +37,8 @@ public class RegistryConfig
3637

3738
public string Address { get; }
3839

40+
private readonly ILogger logger;
41+
3942
/// <summary>
4043
/// A dictionary mapping registry URLs to a boolean indicating whether they replace the base registry.
4144
/// </summary>
@@ -65,8 +68,9 @@ public class RegistryConfig
6568

6669
public X509Certificate2? Certificate { get; private set; }
6770

68-
private RegistryProxy(IRegistryProxyConfiguration config, ILogger logger, TemporaryDirectory tempWorkingDirectory)
71+
private RegistryProxy(IRegistryProxyConfiguration config, ILogger l, TemporaryDirectory tempWorkingDirectory)
6972
{
73+
logger = l;
7074
Address = $"http://{config.Host}:{config.Port}";
7175

7276
if (!string.IsNullOrWhiteSpace(config.Certificate))
@@ -179,6 +183,25 @@ private RegistryProxy(IRegistryProxyConfiguration config, ILogger logger, Tempor
179183
return result;
180184
}
181185

186+
public void SetProcessEnvironment(ProcessStartInfo pi)
187+
{
188+
logger.LogDebug($"Configuring environment variables for the registry proxy at {Address}");
189+
190+
pi.EnvironmentVariables["HTTP_PROXY"] = Address;
191+
pi.EnvironmentVariables["HTTPS_PROXY"] = Address;
192+
193+
// Also set the lower case variants of the environment variables
194+
// This might be needed on Linux systems.
195+
pi.EnvironmentVariables["http_proxy"] = Address;
196+
pi.EnvironmentVariables["https_proxy"] = Address;
197+
198+
if (CertificatePath != null)
199+
{
200+
logger.LogDebug("Setting the SSL certificate path for the registry proxy.");
201+
pi.EnvironmentVariables["SSL_CERT_FILE"] = CertificatePath;
202+
}
203+
}
204+
182205
public void Dispose()
183206
{
184207
Certificate?.Dispose();

‎csharp/extractor/Semmle.Extraction.Tests/FeedManager.cs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System;
33
using System.Collections.Generic;
44
using System.Collections.Immutable;
5+
using System.Diagnostics;
56
using System.IO;
67
using System.Linq;
78
using System.Security.Cryptography.X509Certificates;
@@ -17,6 +18,8 @@ public class RegistryProxyStub : IRegistryProxy
1718
public string? CertificatePath { get; } = null;
1819
public X509Certificate2? Certificate { get; } = null;
1920

21+
public void SetProcessEnvironment(ProcessStartInfo pi) { }
22+
2023
public void Dispose() { }
2124
}
2225

@@ -28,6 +31,7 @@ public class RegistryProxyStubWithBaseUrls : IRegistryProxy
2831
public string? CertificatePath { get; } = null;
2932
public X509Certificate2? Certificate { get; } = null;
3033

34+
public void SetProcessEnvironment(ProcessStartInfo pi) { }
3135
public void Dispose() { }
3236
}
3337

‎csharp/extractor/Semmle.Extraction.Tests/RegistryProxy.cs‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Xunit;
22
using System;
3+
using System.Diagnostics;
34
using System.IO;
45
using Semmle.Extraction.CSharp.DependencyFetching;
56
using Semmle.Util;
@@ -245,5 +246,40 @@ public void TestRegistryProxyUrlsReplacesBase()
245246
"https://example.com/org/index.json",
246247
], proxy.RegistryBaseURLs);
247248
}
249+
250+
/// <summary>
251+
/// Verifies that the registry proxy correctly sets the environment variables needed for a .NET process to use the proxy.
252+
/// In this case, the environment variables for the HTTP and HTTPS proxies, as well as the SSL certificate file, should be correctly set.
253+
/// The http proxies should be set to the proxy address, and the SSL certificate file should point to the certificate path.
254+
/// The latter is tested by verifying that the SSL_CERT_FILE environment variable ends with "proxy.crt" as we can't check the absolute path
255+
/// due to temporary directories.
256+
/// </summary>
257+
[Fact]
258+
public void TestRegistryProxyProcessEnvironment()
259+
{
260+
// Setup
261+
var config = new RegistryConfigurationStub
262+
{
263+
Port = "8080",
264+
Host = "localhost",
265+
Certificate = ExampleCertificate
266+
};
267+
268+
// Execute
269+
using var tempWorkingDirectory = MakeTemporaryDirectory();
270+
using var proxy = RegistryProxy.Make(config, new LoggerStub(), new DiagnosticsWriterStub(), tempWorkingDirectory);
271+
272+
var pi = new ProcessStartInfo("nuget");
273+
proxy?.SetProcessEnvironment(pi);
274+
275+
// Verify
276+
Assert.NotNull(proxy);
277+
Assert.Equal("http://localhost:8080", proxy.Address);
278+
Assert.Equal("http://localhost:8080", pi.EnvironmentVariables["HTTP_PROXY"]);
279+
Assert.Equal("http://localhost:8080", pi.EnvironmentVariables["HTTPS_PROXY"]);
280+
Assert.Equal("http://localhost:8080", pi.EnvironmentVariables["http_proxy"]);
281+
Assert.Equal("http://localhost:8080", pi.EnvironmentVariables["https_proxy"]);
282+
Assert.EndsWith("proxy.crt", pi.EnvironmentVariables["SSL_CERT_FILE"]);
283+
}
248284
}
249285
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The subprocess for the NuGet CLI is now provided with the proxy and certificate environment variables needed to access private registries if any are configured.

0 commit comments

Comments
 (0)