-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathDependabotProxy.cs
More file actions
150 lines (130 loc) · 6.48 KB
/
DependabotProxy.cs
File metadata and controls
150 lines (130 loc) · 6.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using Semmle.Util;
using Semmle.Util.Logging;
using Newtonsoft.Json;
using System.Linq;
namespace Semmle.Extraction.CSharp.DependencyFetching
{
public class DependabotProxy : IDisposable
{
/// <summary>
/// Represents configurations for package registries.
/// </summary>
/// <param name="Type">The type of package registry.</param>
/// <param name="URL">The URL of the package registry.</param>
public record class RegistryConfig(string Type, string URL);
private readonly string host;
private readonly string port;
/// <summary>
/// The full address of the Dependabot proxy, if available.
/// </summary>
internal string Address { get; }
/// <summary>
/// The URLs of package registries that are configured for the proxy.
/// </summary>
internal HashSet<string> RegistryURLs { get; }
/// <summary>
/// The path to the temporary file where the certificate is stored.
/// </summary>
internal string? CertificatePath { get; private set; }
/// <summary>
/// The certificate used for the Dependabot proxy.
/// </summary>
internal X509Certificate2? Certificate { get; private set; }
internal static DependabotProxy? GetDependabotProxy(
ILogger logger, IDiagnosticsWriter diagnosticsWriter, TemporaryDirectory tempWorkingDirectory)
{
// Setting HTTP(S)_PROXY and SSL_CERT_FILE have no effect on Windows or macOS,
// but we would still end up using the Dependabot proxy to check for feed reachability.
// This would result in us discovering that the feeds are reachable, but `dotnet` would
// fail to connect to them. To prevent this from happening, we do not initialise an
// instance of `DependabotProxy` on those platforms.
if (SystemBuildActions.Instance.IsWindows() || SystemBuildActions.Instance.IsMacOs()) return null;
// Obtain and store the address of the Dependabot proxy, if available.
var host = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyHost);
var port = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyPort);
if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(port))
{
logger.LogInfo("No Dependabot proxy credentials are configured.");
return null;
}
var result = new DependabotProxy(host, port);
logger.LogInfo($"Dependabot proxy configured at {result.Address}");
// Obtain and store the proxy's certificate, if available.
var cert = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyCertificate);
if (!string.IsNullOrWhiteSpace(cert))
{
var certDirPath = new DirectoryInfo(Path.Join(tempWorkingDirectory.DirInfo.FullName, ".dependabot-proxy"));
Directory.CreateDirectory(certDirPath.FullName);
result.CertificatePath = Path.Join(certDirPath.FullName, "proxy.crt");
var certFile = new FileInfo(result.CertificatePath);
using var writer = certFile.CreateText();
writer.Write(cert);
writer.Close();
logger.LogInfo($"Stored Dependabot proxy certificate at {result.CertificatePath}");
result.Certificate = X509Certificate2.CreateFromPem(cert);
}
// Try to obtain the list of private registry URLs.
var registryURLs = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyURLs);
if (!string.IsNullOrWhiteSpace(registryURLs))
{
try
{
// The value of the environment variable should be a JSON array of objects, such as:
// [ { "type": "nuget_feed", "url": "https://nuget.pkg.github.com/org/index.json" } ]
var array = JsonConvert.DeserializeObject<List<RegistryConfig>>(registryURLs);
if (array is not null)
{
foreach (RegistryConfig config in array)
{
// The array contains all configured private registries, not just ones for C#.
// We ignore the non-C# ones here.
if (!config.Type.Equals("nuget_feed"))
{
logger.LogDebug($"Ignoring registry at '{config.URL}' since it is not of type 'nuget_feed'.");
continue;
}
logger.LogInfo($"Found private registry at '{config.URL}'");
result.RegistryURLs.Add(config.URL);
}
}
}
catch (JsonException ex)
{
logger.LogError($"Unable to parse '{EnvironmentVariableNames.ProxyURLs}': {ex.Message}");
}
}
// Emit a diagnostic for the discovered private registries, so that it is easy
// for users to see that they were picked up.
if (result.RegistryURLs.Count > 0)
{
diagnosticsWriter.AddEntry(new DiagnosticMessage(
Language.CSharp,
"buildless/analysis-using-private-registries",
severity: DiagnosticMessage.TspSeverity.Note,
visibility: new DiagnosticMessage.TspVisibility(true, true, true),
name: "C# extraction used private package registries",
markdownMessage: string.Format(
"C# was extracted using the following private package registries:\n\n{0}\n",
string.Join("\n", result.RegistryURLs.Select(url => string.Format("- `{0}`", url)))
)
));
}
return result;
}
private DependabotProxy(string host, string port)
{
this.host = host;
this.port = port;
this.Address = $"http://{this.host}:{this.port}";
this.RegistryURLs = new HashSet<string>();
}
public void Dispose()
{
this.Certificate?.Dispose();
}
}
}