diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependabotProxy.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependabotProxy.cs new file mode 100644 index 000000000000..96ba3452cefe --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependabotProxy.cs @@ -0,0 +1,67 @@ +using System; +using System.Diagnostics; +using System.IO; +using Semmle.Util; +using Semmle.Util.Logging; + +namespace Semmle.Extraction.CSharp.DependencyFetching +{ + internal class DependabotProxy + { + private readonly string? host; + private readonly string? port; + private readonly FileInfo? certFile; + + /// + /// The full address of the Dependabot proxy, if available. + /// + internal readonly string? Address; + + /// + /// Gets a value indicating whether a Dependabot proxy is configured. + /// + internal bool IsConfigured => !string.IsNullOrEmpty(this.Address); + + internal DependabotProxy(TemporaryDirectory tempWorkingDirectory) + { + // Obtain and store the address of the Dependabot proxy, if available. + this.host = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyHost); + this.port = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyPort); + + if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(port)) + { + return; + } + + this.Address = $"http://{this.host}:{this.port}"; + + // Obtain and store the proxy's certificate, if available. + var cert = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyCertificate); + + if (string.IsNullOrWhiteSpace(cert)) + { + return; + } + + var certDirPath = new DirectoryInfo(Path.Join(tempWorkingDirectory.DirInfo.FullName, ".dependabot-proxy")); + Directory.CreateDirectory(certDirPath.FullName); + + this.certFile = new FileInfo(Path.Join(certDirPath.FullName, "proxy.crt")); + + using var writer = this.certFile.CreateText(); + writer.Write(cert); + } + + internal void ApplyProxy(ILogger logger, ProcessStartInfo startInfo) + { + // If the proxy isn't configured, we have nothing to do. + if (!this.IsConfigured) return; + + logger.LogInfo($"Setting up Dependabot proxy at {this.Address}"); + + startInfo.EnvironmentVariables["HTTP_PROXY"] = this.Address; + startInfo.EnvironmentVariables["HTTPS_PROXY"] = this.Address; + startInfo.EnvironmentVariables["SSL_CERT_FILE"] = this.certFile?.FullName; + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs index d0646ea921cf..860f60cd5390 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs @@ -27,7 +27,7 @@ private DotNet(IDotNetCliInvoker dotnetCliInvoker, ILogger logger, TemporaryDire Info(); } - private DotNet(ILogger logger, string? dotNetPath, TemporaryDirectory tempWorkingDirectory) : this(new DotNetCliInvoker(logger, Path.Combine(dotNetPath ?? string.Empty, "dotnet")), logger, tempWorkingDirectory) { } + private DotNet(ILogger logger, string? dotNetPath, TemporaryDirectory tempWorkingDirectory) : this(new DotNetCliInvoker(logger, Path.Combine(dotNetPath ?? string.Empty, "dotnet"), tempWorkingDirectory), logger, tempWorkingDirectory) { } internal static IDotNet Make(IDotNetCliInvoker dotnetCliInvoker, ILogger logger) => new DotNet(dotnetCliInvoker, logger); diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetCliInvoker.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetCliInvoker.cs index 4295cce67167..522d3e9ffd45 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetCliInvoker.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetCliInvoker.cs @@ -12,12 +12,14 @@ namespace Semmle.Extraction.CSharp.DependencyFetching internal sealed class DotNetCliInvoker : IDotNetCliInvoker { private readonly ILogger logger; + private readonly DependabotProxy proxy; public string Exec { get; } - public DotNetCliInvoker(ILogger logger, string exec) + public DotNetCliInvoker(ILogger logger, string exec, TemporaryDirectory tempWorkingDirectory) { this.logger = logger; + this.proxy = new DependabotProxy(tempWorkingDirectory); this.Exec = exec; logger.LogInfo($"Using .NET CLI executable: '{Exec}'"); } @@ -38,6 +40,10 @@ private ProcessStartInfo MakeDotnetStartInfo(string args, string? workingDirecto startInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] = "en"; startInfo.EnvironmentVariables["MSBUILDDISABLENODEREUSE"] = "1"; startInfo.EnvironmentVariables["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "true"; + + // Configure the proxy settings, if applicable. + this.proxy.ApplyProxy(this.logger, startInfo); + return startInfo; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/EnvironmentVariableNames.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/EnvironmentVariableNames.cs index 345cb43453fc..d825e5daeb03 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/EnvironmentVariableNames.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/EnvironmentVariableNames.cs @@ -74,5 +74,20 @@ internal static class EnvironmentVariableNames /// Specifies the location of the diagnostic directory. /// public const string DiagnosticDir = "CODEQL_EXTRACTOR_CSHARP_DIAGNOSTIC_DIR"; + + /// + /// Specifies the hostname of the Dependabot proxy. + /// + public const string ProxyHost = "CODEQL_PROXY_HOST"; + + /// + /// Specifies the hostname of the Dependabot proxy. + /// + public const string ProxyPort = "CODEQL_PROXY_PORT"; + + /// + /// Contains the certificate used by the Dependabot proxy. + /// + public const string ProxyCertificate = "CODEQL_PROXY_CA_CERTIFICATE"; } }