dependabot-nuget 0.293.0 → 0.295.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/helpers/lib/NuGetUpdater/Directory.Packages.props +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +16 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +2 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Advisory.cs +2 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +190 -130
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +3 -6
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +36 -18
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +55 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +61 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +338 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateAllowedTests.cs +286 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdatedDependencyListTests.cs +9 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +88 -2
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ac3a95f5211adff79f8e7954308a4d3656b63c8be20b70a7b6f129f04402881
|
4
|
+
data.tar.gz: a79771c0cc6c0a08bfaa2a7cef8253902c7e8b92b868568c231064098ba77a1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 800097bc4856927e983da508d16b4e6d0fd75cf40b5ca5ed25901fbb467f0f99e8f8dc724341c0da6cc6126303c6437a5bd8c7521132c3363ed752181402ee1b
|
7
|
+
data.tar.gz: c0a1675e7cdf06ec0314d58092de09d633fa46892ae8f01c20ec74553d756265fda431434babb6eb140184b3380b78576f6309c1ec010f41e0e09e6ad6b28adb
|
@@ -36,7 +36,7 @@
|
|
36
36
|
<PackageVersion Include="System.Text.Json" Version="8.0.4" />
|
37
37
|
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
|
38
38
|
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="9.0.0" />
|
39
|
-
<PackageVersion Include="xunit" Version="2.9.
|
39
|
+
<PackageVersion Include="xunit" Version="2.9.3" />
|
40
40
|
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.0" />
|
41
41
|
</ItemGroup>
|
42
42
|
|
@@ -113,11 +113,22 @@ internal static class VersionFinder
|
|
113
113
|
? versionRange.MinVersion
|
114
114
|
: null;
|
115
115
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
116
|
+
var safeVersions = dependencyInfo.Vulnerabilities.SelectMany(v => v.SafeVersions).ToList();
|
117
|
+
return version =>
|
118
|
+
{
|
119
|
+
var versionGreaterThanCurrent = currentVersion is null || version > currentVersion;
|
120
|
+
var rangeSatisfies = versionRange.Satisfies(version);
|
121
|
+
var prereleaseTypeMatches = currentVersion is null || !currentVersion.IsPrerelease || !version.IsPrerelease || version.Version == currentVersion.Version;
|
122
|
+
var isIgnoredVersion = dependencyInfo.IgnoredVersions.Any(i => i.IsSatisfiedBy(version));
|
123
|
+
var isVulnerableVersion = dependencyInfo.Vulnerabilities.Any(v => v.IsVulnerable(version));
|
124
|
+
var isSafeVersion = !safeVersions.Any() || safeVersions.Any(s => s.IsSatisfiedBy(version));
|
125
|
+
return versionGreaterThanCurrent
|
126
|
+
&& rangeSatisfies
|
127
|
+
&& prereleaseTypeMatches
|
128
|
+
&& !isIgnoredVersion
|
129
|
+
&& !isVulnerableVersion
|
130
|
+
&& isSafeVersion;
|
131
|
+
};
|
121
132
|
}
|
122
133
|
|
123
134
|
internal static Func<NuGetVersion, bool> CreateVersionFilter(NuGetVersion currentVersion)
|
@@ -262,7 +262,8 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
262
262
|
}
|
263
263
|
}
|
264
264
|
|
265
|
-
|
265
|
+
var result = expandedProjects.OrderBy(p => p).ToImmutableArray();
|
266
|
+
return result;
|
266
267
|
}
|
267
268
|
|
268
269
|
private static IEnumerable<string> ExpandItemGroupFilesFromProject(string projectPath, params string[] itemTypes)
|
@@ -114,7 +114,7 @@ internal static class SdkProjectDiscovery
|
|
114
114
|
var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(args, startingProjectDirectory, experimentsManager);
|
115
115
|
return (exitCode, stdOut, stdErr);
|
116
116
|
}, logger, retainMSBuildSdks: true);
|
117
|
-
MSBuildHelper.
|
117
|
+
MSBuildHelper.ThrowOnError(stdOut);
|
118
118
|
if (stdOut.Contains("""error MSB4057: The target "GenerateBuildDependencyFile" does not exist in the project."""))
|
119
119
|
{
|
120
120
|
// this can happen if it's a non-SDK-style project; totally normal, not worth examining the binlog
|
@@ -10,4 +10,6 @@ public record Advisory
|
|
10
10
|
public ImmutableArray<Requirement>? AffectedVersions { get; init; } = null;
|
11
11
|
public ImmutableArray<Requirement>? PatchedVersions { get; init; } = null;
|
12
12
|
public ImmutableArray<Requirement>? UnaffectedVersions { get; init; } = null;
|
13
|
+
|
14
|
+
public IEnumerable<Requirement> SafeVersions => (PatchedVersions ?? []).Concat(UnaffectedVersions ?? []);
|
13
15
|
}
|
@@ -4,6 +4,10 @@ using System.Text;
|
|
4
4
|
using System.Text.Json;
|
5
5
|
using System.Text.Json.Serialization;
|
6
6
|
|
7
|
+
using Microsoft.Extensions.FileSystemGlobbing;
|
8
|
+
|
9
|
+
using NuGet.Versioning;
|
10
|
+
|
7
11
|
using NuGetUpdater.Core.Analyze;
|
8
12
|
using NuGetUpdater.Core.Discover;
|
9
13
|
using NuGetUpdater.Core.Run.ApiModel;
|
@@ -113,169 +117,145 @@ public class RunWorker
|
|
113
117
|
await _apiHandler.UpdateDependencyList(discoveredUpdatedDependencies);
|
114
118
|
|
115
119
|
// TODO: pull out relevant dependencies, then check each for updates and track the changes
|
116
|
-
// TODO: for each top-level dependency, _or_ specific dependency (if security, use transitive)
|
117
120
|
var originalDependencyFileContents = new Dictionary<string, string>();
|
118
121
|
var actualUpdatedDependencies = new List<ReportedDependency>();
|
119
|
-
|
122
|
+
await _apiHandler.IncrementMetric(new()
|
120
123
|
{
|
121
|
-
|
122
|
-
{
|
123
|
-
|
124
|
-
|
125
|
-
|
124
|
+
Metric = "updater.started",
|
125
|
+
Tags = { ["operation"] = "group_update_all_versions" },
|
126
|
+
});
|
127
|
+
|
128
|
+
// track original contents for later handling
|
129
|
+
async Task TrackOriginalContentsAsync(string directory, string fileName)
|
130
|
+
{
|
131
|
+
var repoFullPath = Path.Join(directory, fileName).FullyNormalizedRootedPath();
|
132
|
+
var localFullPath = Path.Join(repoContentsPath.FullName, repoFullPath);
|
133
|
+
var content = await File.ReadAllTextAsync(localFullPath);
|
134
|
+
originalDependencyFileContents[repoFullPath] = content;
|
135
|
+
}
|
126
136
|
|
127
|
-
|
128
|
-
|
137
|
+
foreach (var project in discoveryResult.Projects)
|
138
|
+
{
|
139
|
+
var projectDirectory = Path.GetDirectoryName(project.FilePath);
|
140
|
+
await TrackOriginalContentsAsync(discoveryResult.Path, project.FilePath);
|
141
|
+
foreach (var extraFile in project.ImportedFiles.Concat(project.AdditionalFiles))
|
129
142
|
{
|
130
|
-
var
|
131
|
-
|
132
|
-
var content = await File.ReadAllTextAsync(localFullPath);
|
133
|
-
originalDependencyFileContents[repoFullPath] = content;
|
143
|
+
var extraFilePath = Path.Join(projectDirectory, extraFile);
|
144
|
+
await TrackOriginalContentsAsync(discoveryResult.Path, extraFilePath);
|
134
145
|
}
|
146
|
+
// TODO: include global.json, etc.
|
147
|
+
}
|
135
148
|
|
136
|
-
|
149
|
+
// do update
|
150
|
+
_logger.Info($"Running update in directory {repoDirectory}");
|
151
|
+
foreach (var project in discoveryResult.Projects)
|
152
|
+
{
|
153
|
+
foreach (var dependency in project.Dependencies)
|
137
154
|
{
|
138
|
-
|
139
|
-
await TrackOriginalContentsAsync(discoveryResult.Path, project.FilePath);
|
140
|
-
foreach (var extraFile in project.ImportedFiles.Concat(project.AdditionalFiles))
|
155
|
+
if (!IsUpdateAllowed(job, dependency))
|
141
156
|
{
|
142
|
-
|
143
|
-
await TrackOriginalContentsAsync(discoveryResult.Path, extraFilePath);
|
157
|
+
continue;
|
144
158
|
}
|
145
|
-
// TODO: include global.json, etc.
|
146
|
-
}
|
147
159
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
foreach (var dependency in project.Dependencies.Where(d => !d.IsTransitive))
|
160
|
+
var dependencyInfo = GetDependencyInfo(job, dependency);
|
161
|
+
var analysisResult = await _analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
|
162
|
+
// TODO: log analysisResult
|
163
|
+
if (analysisResult.CanUpdate)
|
153
164
|
{
|
154
|
-
|
155
|
-
{
|
156
|
-
// this can't be updated
|
157
|
-
// TODO: pull this out of discovery?
|
158
|
-
continue;
|
159
|
-
}
|
165
|
+
var dependencyLocation = Path.Join(discoveryResult.Path, project.FilePath).FullyNormalizedRootedPath();
|
160
166
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
}
|
166
|
-
|
167
|
-
var ignoredVersions = GetIgnoredRequirementsForDependency(job, dependency.Name);
|
168
|
-
var dependencyInfo = new DependencyInfo()
|
167
|
+
// TODO: this is inefficient, but not likely causing a bottleneck
|
168
|
+
var previousDependency = discoveredUpdatedDependencies.Dependencies
|
169
|
+
.Single(d => d.Name == dependency.Name && d.Requirements.Single().File == dependencyLocation);
|
170
|
+
var updatedDependency = new ReportedDependency()
|
169
171
|
{
|
170
172
|
Name = dependency.Name,
|
171
|
-
Version =
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
};
|
176
|
-
var analysisResult = await _analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
|
177
|
-
// TODO: log analysisResult
|
178
|
-
if (analysisResult.CanUpdate)
|
179
|
-
{
|
180
|
-
var dependencyLocation = Path.Join(discoveryResult.Path, project.FilePath).FullyNormalizedRootedPath();
|
181
|
-
|
182
|
-
// TODO: this is inefficient, but not likely causing a bottleneck
|
183
|
-
var previousDependency = discoveredUpdatedDependencies.Dependencies
|
184
|
-
.Single(d => d.Name == dependency.Name && d.Requirements.Single().File == dependencyLocation);
|
185
|
-
var updatedDependency = new ReportedDependency()
|
186
|
-
{
|
187
|
-
Name = dependency.Name,
|
188
|
-
Version = analysisResult.UpdatedVersion,
|
189
|
-
Requirements =
|
190
|
-
[
|
191
|
-
new ReportedRequirement()
|
192
|
-
{
|
193
|
-
File = dependencyLocation,
|
194
|
-
Requirement = analysisResult.UpdatedVersion,
|
195
|
-
Groups = previousDependency.Requirements.Single().Groups,
|
196
|
-
Source = new RequirementSource()
|
197
|
-
{
|
198
|
-
SourceUrl = analysisResult.UpdatedDependencies.FirstOrDefault(d => d.Name == dependency.Name)?.InfoUrl,
|
199
|
-
},
|
200
|
-
}
|
201
|
-
],
|
202
|
-
PreviousVersion = dependency.Version,
|
203
|
-
PreviousRequirements = previousDependency.Requirements,
|
204
|
-
};
|
205
|
-
|
206
|
-
var dependencyFilePath = Path.Join(discoveryResult.Path, project.FilePath).FullyNormalizedRootedPath();
|
207
|
-
var updateResult = await _updaterWorker.RunAsync(repoContentsPath.FullName, dependencyFilePath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, isTransitive: false);
|
208
|
-
// TODO: need to report if anything was actually updated
|
209
|
-
if (updateResult.Error is null)
|
210
|
-
{
|
211
|
-
if (dependencyLocation != dependencyFilePath)
|
173
|
+
Version = analysisResult.UpdatedVersion,
|
174
|
+
Requirements =
|
175
|
+
[
|
176
|
+
new ReportedRequirement()
|
212
177
|
{
|
213
|
-
|
178
|
+
File = dependencyLocation,
|
179
|
+
Requirement = analysisResult.UpdatedVersion,
|
180
|
+
Groups = previousDependency.Requirements.Single().Groups,
|
181
|
+
Source = new RequirementSource()
|
182
|
+
{
|
183
|
+
SourceUrl = analysisResult.UpdatedDependencies.FirstOrDefault(d => d.Name == dependency.Name)?.InfoUrl,
|
184
|
+
},
|
214
185
|
}
|
186
|
+
],
|
187
|
+
PreviousVersion = dependency.Version,
|
188
|
+
PreviousRequirements = previousDependency.Requirements,
|
189
|
+
};
|
215
190
|
|
216
|
-
|
191
|
+
var dependencyFilePath = Path.Join(discoveryResult.Path, project.FilePath).FullyNormalizedRootedPath();
|
192
|
+
var updateResult = await _updaterWorker.RunAsync(repoContentsPath.FullName, dependencyFilePath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, isTransitive: dependency.IsTransitive);
|
193
|
+
// TODO: need to report if anything was actually updated
|
194
|
+
if (updateResult.Error is null)
|
195
|
+
{
|
196
|
+
if (dependencyLocation != dependencyFilePath)
|
197
|
+
{
|
198
|
+
updatedDependency.Requirements.All(r => r.File == dependencyFilePath);
|
217
199
|
}
|
200
|
+
|
201
|
+
actualUpdatedDependencies.Add(updatedDependency);
|
218
202
|
}
|
219
203
|
}
|
220
204
|
}
|
205
|
+
}
|
221
206
|
|
222
|
-
|
223
|
-
|
224
|
-
|
207
|
+
// create PR - we need to manually check file contents; we can't easily use `git status` in tests
|
208
|
+
var updatedDependencyFiles = new Dictionary<string, DependencyFile>();
|
209
|
+
async Task AddUpdatedFileIfDifferentAsync(string directory, string fileName)
|
210
|
+
{
|
211
|
+
var repoFullPath = Path.Join(directory, fileName).FullyNormalizedRootedPath();
|
212
|
+
var localFullPath = Path.GetFullPath(Path.Join(repoContentsPath.FullName, repoFullPath));
|
213
|
+
var originalContent = originalDependencyFileContents[repoFullPath];
|
214
|
+
var updatedContent = await File.ReadAllTextAsync(localFullPath);
|
215
|
+
if (updatedContent != originalContent)
|
225
216
|
{
|
226
|
-
|
227
|
-
var localFullPath = Path.GetFullPath(Path.Join(repoContentsPath.FullName, repoFullPath));
|
228
|
-
var originalContent = originalDependencyFileContents[repoFullPath];
|
229
|
-
var updatedContent = await File.ReadAllTextAsync(localFullPath);
|
230
|
-
if (updatedContent != originalContent)
|
217
|
+
updatedDependencyFiles[localFullPath] = new DependencyFile()
|
231
218
|
{
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
Content = updatedContent,
|
237
|
-
};
|
238
|
-
}
|
219
|
+
Name = Path.GetFileName(repoFullPath),
|
220
|
+
Directory = Path.GetDirectoryName(repoFullPath)!.NormalizePathToUnix(),
|
221
|
+
Content = updatedContent,
|
222
|
+
};
|
239
223
|
}
|
224
|
+
}
|
240
225
|
|
241
|
-
|
226
|
+
foreach (var project in discoveryResult.Projects)
|
227
|
+
{
|
228
|
+
await AddUpdatedFileIfDifferentAsync(discoveryResult.Path, project.FilePath);
|
229
|
+
var projectDirectory = Path.GetDirectoryName(project.FilePath);
|
230
|
+
foreach (var extraFile in project.ImportedFiles.Concat(project.AdditionalFiles))
|
242
231
|
{
|
243
|
-
|
244
|
-
|
245
|
-
foreach (var extraFile in project.ImportedFiles.Concat(project.AdditionalFiles))
|
246
|
-
{
|
247
|
-
var extraFilePath = Path.Join(projectDirectory, extraFile);
|
248
|
-
await AddUpdatedFileIfDifferentAsync(discoveryResult.Path, extraFilePath);
|
249
|
-
}
|
250
|
-
// TODO: handle global.json, etc.
|
232
|
+
var extraFilePath = Path.Join(projectDirectory, extraFile);
|
233
|
+
await AddUpdatedFileIfDifferentAsync(discoveryResult.Path, extraFilePath);
|
251
234
|
}
|
235
|
+
// TODO: handle global.json, etc.
|
236
|
+
}
|
252
237
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
{
|
261
|
-
Dependencies = actualUpdatedDependencies.ToArray(),
|
262
|
-
UpdatedDependencyFiles = updatedDependencyFileList,
|
263
|
-
BaseCommitSha = baseCommitSha,
|
264
|
-
CommitMessage = "TODO: message",
|
265
|
-
PrTitle = "TODO: title",
|
266
|
-
PrBody = "TODO: body",
|
267
|
-
};
|
268
|
-
await _apiHandler.CreatePullRequest(createPullRequest);
|
269
|
-
// TODO: log updated dependencies to console
|
270
|
-
}
|
271
|
-
else
|
238
|
+
if (updatedDependencyFiles.Count > 0)
|
239
|
+
{
|
240
|
+
var updatedDependencyFileList = updatedDependencyFiles
|
241
|
+
.OrderBy(kvp => kvp.Key)
|
242
|
+
.Select(kvp => kvp.Value)
|
243
|
+
.ToArray();
|
244
|
+
var createPullRequest = new CreatePullRequest()
|
272
245
|
{
|
273
|
-
|
274
|
-
|
246
|
+
Dependencies = actualUpdatedDependencies.ToArray(),
|
247
|
+
UpdatedDependencyFiles = updatedDependencyFileList,
|
248
|
+
BaseCommitSha = baseCommitSha,
|
249
|
+
CommitMessage = "TODO: message",
|
250
|
+
PrTitle = "TODO: title",
|
251
|
+
PrBody = "TODO: body",
|
252
|
+
};
|
253
|
+
await _apiHandler.CreatePullRequest(createPullRequest);
|
254
|
+
// TODO: log updated dependencies to console
|
275
255
|
}
|
276
256
|
else
|
277
257
|
{
|
278
|
-
// TODO: throw if
|
258
|
+
// TODO: log or throw if nothing was updated, but was expected to be
|
279
259
|
}
|
280
260
|
|
281
261
|
var result = new RunResult()
|
@@ -295,6 +275,62 @@ public class RunWorker
|
|
295
275
|
return result;
|
296
276
|
}
|
297
277
|
|
278
|
+
internal static bool IsUpdateAllowed(Job job, Dependency dependency)
|
279
|
+
{
|
280
|
+
if (dependency.Name.Equals("Microsoft.NET.Sdk", StringComparison.OrdinalIgnoreCase))
|
281
|
+
{
|
282
|
+
// this can't be updated
|
283
|
+
// TODO: pull this out of discovery?
|
284
|
+
return false;
|
285
|
+
}
|
286
|
+
|
287
|
+
if (dependency.Version is null)
|
288
|
+
{
|
289
|
+
// if we don't know the version, there's nothing we can do
|
290
|
+
// TODO: pull this out of discovery?
|
291
|
+
return false;
|
292
|
+
}
|
293
|
+
|
294
|
+
var version = NuGetVersion.Parse(dependency.Version);
|
295
|
+
var dependencyInfo = GetDependencyInfo(job, dependency);
|
296
|
+
var isVulnerable = dependencyInfo.Vulnerabilities.Any(v => v.IsVulnerable(version));
|
297
|
+
var allowed = job.AllowedUpdates.Any(allowedUpdate =>
|
298
|
+
{
|
299
|
+
// check name restriction, if any
|
300
|
+
if (allowedUpdate.DependencyName is not null)
|
301
|
+
{
|
302
|
+
var matcher = new Matcher(StringComparison.OrdinalIgnoreCase)
|
303
|
+
.AddInclude(allowedUpdate.DependencyName);
|
304
|
+
var result = matcher.Match(dependency.Name);
|
305
|
+
if (!result.HasMatches)
|
306
|
+
{
|
307
|
+
return false;
|
308
|
+
}
|
309
|
+
}
|
310
|
+
|
311
|
+
var isSecurityUpdate = allowedUpdate.UpdateType == UpdateType.Security || job.SecurityUpdatesOnly;
|
312
|
+
if (isSecurityUpdate)
|
313
|
+
{
|
314
|
+
// only update if it's vulnerable
|
315
|
+
return isVulnerable;
|
316
|
+
}
|
317
|
+
else
|
318
|
+
{
|
319
|
+
// not a security update, so only update if...
|
320
|
+
// ...we've been explicitly asked to update this
|
321
|
+
if ((job.Dependencies ?? []).Any(d => d.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase)))
|
322
|
+
{
|
323
|
+
return true;
|
324
|
+
}
|
325
|
+
|
326
|
+
// ...no specific update being performed, do it if it's not transitive
|
327
|
+
return !dependency.IsTransitive;
|
328
|
+
}
|
329
|
+
});
|
330
|
+
|
331
|
+
return allowed;
|
332
|
+
}
|
333
|
+
|
298
334
|
internal static ImmutableArray<Requirement> GetIgnoredRequirementsForDependency(Job job, string dependencyName)
|
299
335
|
{
|
300
336
|
var ignoreConditions = job.IgnoreConditions
|
@@ -314,6 +350,30 @@ public class RunWorker
|
|
314
350
|
return ignoredVersions;
|
315
351
|
}
|
316
352
|
|
353
|
+
internal static DependencyInfo GetDependencyInfo(Job job, Dependency dependency)
|
354
|
+
{
|
355
|
+
var dependencyVersion = NuGetVersion.Parse(dependency.Version!);
|
356
|
+
var securityAdvisories = job.SecurityAdvisories.Where(s => s.DependencyName.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase)).ToArray();
|
357
|
+
var isVulnerable = securityAdvisories.Any(s => (s.AffectedVersions ?? []).Any(v => v.IsSatisfiedBy(dependencyVersion)));
|
358
|
+
var ignoredVersions = GetIgnoredRequirementsForDependency(job, dependency.Name);
|
359
|
+
var vulnerabilities = securityAdvisories.Select(s => new SecurityVulnerability()
|
360
|
+
{
|
361
|
+
DependencyName = dependency.Name,
|
362
|
+
PackageManager = "nuget",
|
363
|
+
VulnerableVersions = s.AffectedVersions ?? [],
|
364
|
+
SafeVersions = s.SafeVersions.ToImmutableArray(),
|
365
|
+
}).ToImmutableArray();
|
366
|
+
var dependencyInfo = new DependencyInfo()
|
367
|
+
{
|
368
|
+
Name = dependency.Name,
|
369
|
+
Version = dependencyVersion.ToString(),
|
370
|
+
IsVulnerable = isVulnerable,
|
371
|
+
IgnoredVersions = ignoredVersions,
|
372
|
+
Vulnerabilities = vulnerabilities,
|
373
|
+
};
|
374
|
+
return dependencyInfo;
|
375
|
+
}
|
376
|
+
|
317
377
|
internal static UpdatedDependencyList GetUpdatedDependencyListFromDiscovery(WorkspaceDiscoveryResult discoveryResult, string pathToContents)
|
318
378
|
{
|
319
379
|
string GetFullRepoPath(string path)
|
@@ -357,7 +417,7 @@ public class RunWorker
|
|
357
417
|
new ReportedDependency()
|
358
418
|
{
|
359
419
|
Name = d.Name,
|
360
|
-
Requirements =
|
420
|
+
Requirements = [new ReportedRequirement()
|
361
421
|
{
|
362
422
|
File = GetFullRepoPath(p.FilePath),
|
363
423
|
Requirement = d.Version!,
|
@@ -347,7 +347,7 @@ internal static class PackageReferenceUpdater
|
|
347
347
|
projectDirectory,
|
348
348
|
experimentsManager
|
349
349
|
);
|
350
|
-
MSBuildHelper.
|
350
|
+
MSBuildHelper.ThrowOnError(stdout);
|
351
351
|
if (exitCode != 0)
|
352
352
|
{
|
353
353
|
logger.Warn($" Transitive dependency [{dependencyName}/{newDependencyVersion}] was not added.\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}");
|
@@ -148,18 +148,15 @@ internal static partial class PackagesConfigUpdater
|
|
148
148
|
|
149
149
|
if (exitCodeAgain != 0)
|
150
150
|
{
|
151
|
-
MSBuildHelper.
|
152
|
-
MSBuildHelper.
|
153
|
-
MSBuildHelper.ThrowOnMissingPackages(restoreOutput);
|
151
|
+
MSBuildHelper.ThrowOnError(fullOutput);
|
152
|
+
MSBuildHelper.ThrowOnError(restoreOutput);
|
154
153
|
throw new Exception($"Unable to restore.\nOutput:\n${restoreOutput}\n");
|
155
154
|
}
|
156
155
|
|
157
156
|
goto doRestore;
|
158
157
|
}
|
159
158
|
|
160
|
-
MSBuildHelper.
|
161
|
-
MSBuildHelper.ThrowOnMissingFile(fullOutput);
|
162
|
-
MSBuildHelper.ThrowOnMissingPackages(fullOutput);
|
159
|
+
MSBuildHelper.ThrowOnError(fullOutput);
|
163
160
|
throw new Exception(fullOutput);
|
164
161
|
}
|
165
162
|
}
|
@@ -903,21 +903,6 @@ internal static partial class MSBuildHelper
|
|
903
903
|
}
|
904
904
|
}
|
905
905
|
|
906
|
-
internal static void ThrowOnUnauthenticatedFeed(string stdout)
|
907
|
-
{
|
908
|
-
var unauthorizedMessageSnippets = new string[]
|
909
|
-
{
|
910
|
-
"The plugin credential provider could not acquire credentials",
|
911
|
-
"401 (Unauthorized)",
|
912
|
-
"error NU1301: Unable to load the service index for source",
|
913
|
-
"Response status code does not indicate success: 403",
|
914
|
-
};
|
915
|
-
if (unauthorizedMessageSnippets.Any(stdout.Contains))
|
916
|
-
{
|
917
|
-
throw new HttpRequestException(message: stdout, inner: null, statusCode: System.Net.HttpStatusCode.Unauthorized);
|
918
|
-
}
|
919
|
-
}
|
920
|
-
|
921
906
|
internal static string? GetMissingFile(string output)
|
922
907
|
{
|
923
908
|
var missingFilePatterns = new[]
|
@@ -934,7 +919,30 @@ internal static partial class MSBuildHelper
|
|
934
919
|
return null;
|
935
920
|
}
|
936
921
|
|
937
|
-
internal static void
|
922
|
+
internal static void ThrowOnError(string output)
|
923
|
+
{
|
924
|
+
ThrowOnUnauthenticatedFeed(output);
|
925
|
+
ThrowOnMissingFile(output);
|
926
|
+
ThrowOnMissingPackages(output);
|
927
|
+
ThrowOnUnresolvableDependencies(output);
|
928
|
+
}
|
929
|
+
|
930
|
+
private static void ThrowOnUnauthenticatedFeed(string stdout)
|
931
|
+
{
|
932
|
+
var unauthorizedMessageSnippets = new string[]
|
933
|
+
{
|
934
|
+
"The plugin credential provider could not acquire credentials",
|
935
|
+
"401 (Unauthorized)",
|
936
|
+
"error NU1301: Unable to load the service index for source",
|
937
|
+
"Response status code does not indicate success: 403",
|
938
|
+
};
|
939
|
+
if (unauthorizedMessageSnippets.Any(stdout.Contains))
|
940
|
+
{
|
941
|
+
throw new HttpRequestException(message: stdout, inner: null, statusCode: System.Net.HttpStatusCode.Unauthorized);
|
942
|
+
}
|
943
|
+
}
|
944
|
+
|
945
|
+
private static void ThrowOnMissingFile(string output)
|
938
946
|
{
|
939
947
|
var missingFile = GetMissingFile(output);
|
940
948
|
if (missingFile is not null)
|
@@ -943,9 +951,9 @@ internal static partial class MSBuildHelper
|
|
943
951
|
}
|
944
952
|
}
|
945
953
|
|
946
|
-
|
954
|
+
private static void ThrowOnMissingPackages(string output)
|
947
955
|
{
|
948
|
-
var missingPackagesPattern = new Regex(@"Package '(?<PackageName>[^']
|
956
|
+
var missingPackagesPattern = new Regex(@"Package '(?<PackageName>[^']*)' is not found on source");
|
949
957
|
var matchCollection = missingPackagesPattern.Matches(output);
|
950
958
|
var missingPackages = matchCollection.Select(m => m.Groups["PackageName"].Value).Distinct().ToArray();
|
951
959
|
if (missingPackages.Length > 0)
|
@@ -954,6 +962,16 @@ internal static partial class MSBuildHelper
|
|
954
962
|
}
|
955
963
|
}
|
956
964
|
|
965
|
+
private static void ThrowOnUnresolvableDependencies(string output)
|
966
|
+
{
|
967
|
+
var unresolvablePackagePattern = new Regex(@"Unable to resolve dependencies\. '(?<PackageName>[^ ]+) (?<PackageVersion>[^']+)'");
|
968
|
+
var match = unresolvablePackagePattern.Match(output);
|
969
|
+
if (match.Success)
|
970
|
+
{
|
971
|
+
throw new UpdateNotPossibleException([$"{match.Groups["PackageName"].Value}.{match.Groups["PackageVersion"].Value}"]);
|
972
|
+
}
|
973
|
+
}
|
974
|
+
|
957
975
|
internal static bool TryGetGlobalJsonPath(string repoRootPath, string workspacePath, [NotNullWhen(returnValue: true)] out string? globalJsonPath)
|
958
976
|
{
|
959
977
|
globalJsonPath = PathHelper.GetFileInDirectoryOrParent(workspacePath, repoRootPath, "global.json", caseSensitive: false);
|
@@ -478,6 +478,61 @@ public partial class AnalyzeWorkerTests : AnalyzeWorkerTestBase
|
|
478
478
|
);
|
479
479
|
}
|
480
480
|
|
481
|
+
[Fact]
|
482
|
+
public async Task SafeVersionsPropertyIsHonored()
|
483
|
+
{
|
484
|
+
await TestAnalyzeAsync(
|
485
|
+
packages:
|
486
|
+
[
|
487
|
+
MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0"), // initially this
|
488
|
+
MockNuGetPackage.CreateSimplePackage("Some.Package", "1.1.0", "net8.0"), // should update to this due to `SafeVersions`
|
489
|
+
MockNuGetPackage.CreateSimplePackage("Some.Package", "1.2.0", "net8.0"), // this should not be considered
|
490
|
+
],
|
491
|
+
discovery: new()
|
492
|
+
{
|
493
|
+
Path = "/",
|
494
|
+
Projects = [
|
495
|
+
new()
|
496
|
+
{
|
497
|
+
FilePath = "./project.csproj",
|
498
|
+
TargetFrameworks = ["net8.0"],
|
499
|
+
Dependencies = [
|
500
|
+
new("Some.Package", "1.0.0", DependencyType.PackageReference),
|
501
|
+
],
|
502
|
+
ReferencedProjectPaths = [],
|
503
|
+
ImportedFiles = [],
|
504
|
+
AdditionalFiles = [],
|
505
|
+
},
|
506
|
+
],
|
507
|
+
},
|
508
|
+
dependencyInfo: new()
|
509
|
+
{
|
510
|
+
Name = "Some.Package",
|
511
|
+
Version = "1.0.0",
|
512
|
+
IgnoredVersions = [],
|
513
|
+
IsVulnerable = false,
|
514
|
+
Vulnerabilities = [
|
515
|
+
new()
|
516
|
+
{
|
517
|
+
DependencyName = "Some.Package",
|
518
|
+
PackageManager = "nuget",
|
519
|
+
VulnerableVersions = [Requirement.Parse(">= 1.0.0, < 1.1.0")],
|
520
|
+
SafeVersions = [Requirement.Parse("= 1.1.0")]
|
521
|
+
}
|
522
|
+
],
|
523
|
+
},
|
524
|
+
expectedResult: new()
|
525
|
+
{
|
526
|
+
UpdatedVersion = "1.1.0",
|
527
|
+
CanUpdate = true,
|
528
|
+
VersionComesFromMultiDependencyProperty = false,
|
529
|
+
UpdatedDependencies = [
|
530
|
+
new("Some.Package", "1.1.0", DependencyType.Unknown, TargetFrameworks: ["net8.0"]),
|
531
|
+
],
|
532
|
+
}
|
533
|
+
);
|
534
|
+
}
|
535
|
+
|
481
536
|
[Fact]
|
482
537
|
public async Task VersionFinderCanHandle404FromPackageSource_V2()
|
483
538
|
{
|