dependabot-nuget 0.265.0 → 0.266.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +6 -6
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalysisResult.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +73 -77
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +21 -8
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +24 -8
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +33 -16
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +44 -25
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +8 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/ProjectBuildFile.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NativeResult.cs +8 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +18 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +2 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationResult.cs +5 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +62 -22
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +19 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +6 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +448 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/CompatibilityCheckerTests.cs +23 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +2 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +148 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestHttpServer.cs +81 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +27 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +32 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +87 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +88 -0
- data/lib/dependabot/nuget/analysis/dependency_analysis.rb +3 -0
- data/lib/dependabot/nuget/file_fetcher.rb +1 -1
- data/lib/dependabot/nuget/metadata_finder.rb +160 -2
- data/lib/dependabot/nuget/native_discovery/native_workspace_discovery.rb +3 -0
- data/lib/dependabot/nuget/native_helpers.rb +34 -3
- data/lib/dependabot/nuget/native_update_checker/native_update_checker.rb +1 -0
- data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +3 -0
- metadata +11 -7
@@ -1,3 +1,10 @@
|
|
1
|
+
using System.Net;
|
2
|
+
using System.Text.Json;
|
3
|
+
using System.Text.Json.Serialization;
|
4
|
+
|
5
|
+
using NuGetUpdater.Core.Analyze;
|
6
|
+
using NuGetUpdater.Core.Updater;
|
7
|
+
|
1
8
|
namespace NuGetUpdater.Core;
|
2
9
|
|
3
10
|
public class UpdaterWorker
|
@@ -5,48 +12,81 @@ public class UpdaterWorker
|
|
5
12
|
private readonly Logger _logger;
|
6
13
|
private readonly HashSet<string> _processedProjectPaths = new(StringComparer.OrdinalIgnoreCase);
|
7
14
|
|
15
|
+
internal static readonly JsonSerializerOptions SerializerOptions = new()
|
16
|
+
{
|
17
|
+
WriteIndented = true,
|
18
|
+
Converters = { new JsonStringEnumConverter() },
|
19
|
+
};
|
20
|
+
|
8
21
|
public UpdaterWorker(Logger logger)
|
9
22
|
{
|
10
23
|
_logger = logger;
|
11
24
|
}
|
12
25
|
|
13
|
-
public async Task RunAsync(string repoRootPath, string workspacePath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive)
|
26
|
+
public async Task RunAsync(string repoRootPath, string workspacePath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive, string? resultOutputPath = null)
|
14
27
|
{
|
15
28
|
MSBuildHelper.RegisterMSBuild(Environment.CurrentDirectory, repoRootPath);
|
29
|
+
UpdateOperationResult result;
|
16
30
|
|
17
31
|
if (!Path.IsPathRooted(workspacePath) || !File.Exists(workspacePath))
|
18
32
|
{
|
19
33
|
workspacePath = Path.GetFullPath(Path.Join(repoRootPath, workspacePath));
|
20
34
|
}
|
21
35
|
|
22
|
-
|
36
|
+
try
|
23
37
|
{
|
24
|
-
|
25
|
-
|
38
|
+
if (!isTransitive)
|
39
|
+
{
|
40
|
+
await DotNetToolsJsonUpdater.UpdateDependencyAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, _logger);
|
41
|
+
await GlobalJsonUpdater.UpdateDependencyAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, _logger);
|
42
|
+
}
|
43
|
+
|
44
|
+
var extension = Path.GetExtension(workspacePath).ToLowerInvariant();
|
45
|
+
switch (extension)
|
46
|
+
{
|
47
|
+
case ".sln":
|
48
|
+
await RunForSolutionAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
49
|
+
break;
|
50
|
+
case ".proj":
|
51
|
+
await RunForProjFileAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
52
|
+
break;
|
53
|
+
case ".csproj":
|
54
|
+
case ".fsproj":
|
55
|
+
case ".vbproj":
|
56
|
+
await RunForProjectAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
57
|
+
break;
|
58
|
+
default:
|
59
|
+
_logger.Log($"File extension [{extension}] is not supported.");
|
60
|
+
break;
|
61
|
+
}
|
62
|
+
|
63
|
+
result = new(); // all ok
|
64
|
+
_logger.Log("Update complete.");
|
65
|
+
}
|
66
|
+
catch (HttpRequestException ex)
|
67
|
+
when (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
|
68
|
+
{
|
69
|
+
// TODO: consolidate this error handling between AnalyzeWorker, DiscoveryWorker, and UpdateWorker
|
70
|
+
result = new()
|
71
|
+
{
|
72
|
+
ErrorType = ErrorType.AuthenticationFailure,
|
73
|
+
ErrorDetails = "(" + string.Join("|", NuGetContext.GetPackageSourceUrls(workspacePath)) + ")",
|
74
|
+
};
|
26
75
|
}
|
27
76
|
|
28
|
-
|
29
|
-
|
77
|
+
_processedProjectPaths.Clear();
|
78
|
+
if (resultOutputPath is { })
|
30
79
|
{
|
31
|
-
|
32
|
-
await RunForSolutionAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
33
|
-
break;
|
34
|
-
case ".proj":
|
35
|
-
await RunForProjFileAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
36
|
-
break;
|
37
|
-
case ".csproj":
|
38
|
-
case ".fsproj":
|
39
|
-
case ".vbproj":
|
40
|
-
await RunForProjectAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
41
|
-
break;
|
42
|
-
default:
|
43
|
-
_logger.Log($"File extension [{extension}] is not supported.");
|
44
|
-
break;
|
80
|
+
await WriteResultFile(result, resultOutputPath, _logger);
|
45
81
|
}
|
82
|
+
}
|
46
83
|
|
47
|
-
|
84
|
+
internal static async Task WriteResultFile(UpdateOperationResult result, string resultOutputPath, Logger logger)
|
85
|
+
{
|
86
|
+
logger.Log($" Writing update result to [{resultOutputPath}].");
|
48
87
|
|
49
|
-
|
88
|
+
var resultJson = JsonSerializer.Serialize(result, SerializerOptions);
|
89
|
+
await File.WriteAllTextAsync(resultOutputPath, resultJson);
|
50
90
|
}
|
51
91
|
|
52
92
|
private async Task RunForSolutionAsync(
|
@@ -185,8 +185,9 @@ internal static partial class MSBuildHelper
|
|
185
185
|
var versionSpecification = packageItem.Metadata.FirstOrDefault(m => m.Name.Equals("Version", StringComparison.OrdinalIgnoreCase))?.Value
|
186
186
|
?? packageItem.Metadata.FirstOrDefault(m => m.Name.Equals("VersionOverride", StringComparison.OrdinalIgnoreCase))?.Value
|
187
187
|
?? string.Empty;
|
188
|
-
foreach (var
|
188
|
+
foreach (var rawAttributeValue in new[] { packageItem.Include, packageItem.Update })
|
189
189
|
{
|
190
|
+
var attributeValue = rawAttributeValue?.Trim();
|
190
191
|
if (!string.IsNullOrWhiteSpace(attributeValue))
|
191
192
|
{
|
192
193
|
if (packageInfo.TryGetValue(attributeValue, out var existingInfo))
|
@@ -312,6 +313,7 @@ internal static partial class MSBuildHelper
|
|
312
313
|
{
|
313
314
|
var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages);
|
314
315
|
var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"restore \"{tempProjectPath}\"", workingDirectory: tempDirectory.FullName);
|
316
|
+
ThrowOnUnauthenticatedFeed(stdOut);
|
315
317
|
|
316
318
|
// simple cases first
|
317
319
|
// if restore failed, nothing we can do
|
@@ -506,6 +508,7 @@ internal static partial class MSBuildHelper
|
|
506
508
|
<TargetFramework>{targetFramework}</TargetFramework>
|
507
509
|
<GenerateDependencyFile>true</GenerateDependencyFile>
|
508
510
|
<RunAnalyzers>false</RunAnalyzers>
|
511
|
+
<NuGetInteractive>false</NuGetInteractive>
|
509
512
|
</PropertyGroup>
|
510
513
|
<ItemGroup>
|
511
514
|
{packageReferences}
|
@@ -565,6 +568,7 @@ internal static partial class MSBuildHelper
|
|
565
568
|
var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages);
|
566
569
|
|
567
570
|
var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", $"build \"{tempProjectPath}\" /t:_ReportDependencies", workingDirectory: tempDirectory.FullName);
|
571
|
+
ThrowOnUnauthenticatedFeed(stdout);
|
568
572
|
|
569
573
|
if (exitCode == 0)
|
570
574
|
{
|
@@ -602,6 +606,20 @@ internal static partial class MSBuildHelper
|
|
602
606
|
}
|
603
607
|
}
|
604
608
|
|
609
|
+
internal static void ThrowOnUnauthenticatedFeed(string stdout)
|
610
|
+
{
|
611
|
+
var unauthorizedMessageSnippets = new string[]
|
612
|
+
{
|
613
|
+
"The plugin credential provider could not acquire credentials",
|
614
|
+
"401 (Unauthorized)",
|
615
|
+
"error NU1301: Unable to load the service index for source",
|
616
|
+
};
|
617
|
+
if (unauthorizedMessageSnippets.Any(stdout.Contains))
|
618
|
+
{
|
619
|
+
throw new HttpRequestException(message: stdout, inner: null, statusCode: System.Net.HttpStatusCode.Unauthorized);
|
620
|
+
}
|
621
|
+
}
|
622
|
+
|
605
623
|
internal static bool TryGetGlobalJsonPath(string repoRootPath, string workspacePath, [NotNullWhen(returnValue: true)] out string? globalJsonPath)
|
606
624
|
{
|
607
625
|
globalJsonPath = PathHelper.GetFileInDirectoryOrParent(workspacePath, repoRootPath, "global.json", caseSensitive: false);
|
@@ -18,7 +18,8 @@ public class AnalyzeWorkerTestBase
|
|
18
18
|
WorkspaceDiscoveryResult discovery,
|
19
19
|
DependencyInfo dependencyInfo,
|
20
20
|
ExpectedAnalysisResult expectedResult,
|
21
|
-
MockNuGetPackage[]? packages = null
|
21
|
+
MockNuGetPackage[]? packages = null,
|
22
|
+
TestFile[]? extraFiles = null)
|
22
23
|
{
|
23
24
|
var relativeDependencyPath = $"./dependabot/dependency/{dependencyInfo.Name}.json";
|
24
25
|
|
@@ -27,7 +28,8 @@ public class AnalyzeWorkerTestBase
|
|
27
28
|
(relativeDependencyPath, JsonSerializer.Serialize(dependencyInfo, AnalyzeWorker.SerializerOptions)),
|
28
29
|
];
|
29
30
|
|
30
|
-
var
|
31
|
+
var allFiles = files.Concat(extraFiles ?? []).ToArray();
|
32
|
+
var actualResult = await RunAnalyzerAsync(dependencyInfo.Name, allFiles, async directoryPath =>
|
31
33
|
{
|
32
34
|
await UpdateWorkerTestBase.MockNuGetPackagesInDirectory(packages, directoryPath);
|
33
35
|
|
@@ -50,6 +52,8 @@ public class AnalyzeWorkerTestBase
|
|
50
52
|
Assert.Equal(expectedResult.VersionComesFromMultiDependencyProperty, actualResult.VersionComesFromMultiDependencyProperty);
|
51
53
|
ValidateDependencies(expectedResult.UpdatedDependencies, actualResult.UpdatedDependencies);
|
52
54
|
Assert.Equal(expectedResult.ExpectedUpdatedDependenciesCount ?? expectedResult.UpdatedDependencies.Length, actualResult.UpdatedDependencies.Length);
|
55
|
+
Assert.Equal(expectedResult.ErrorType, actualResult.ErrorType);
|
56
|
+
Assert.Equal(expectedResult.ErrorDetails, actualResult.ErrorDetails);
|
53
57
|
|
54
58
|
return;
|
55
59
|
|
@@ -1,3 +1,8 @@
|
|
1
|
+
using System.Text;
|
2
|
+
using System.Text.Json;
|
3
|
+
|
4
|
+
using NuGet;
|
5
|
+
|
1
6
|
using NuGetUpdater.Core.Analyze;
|
2
7
|
|
3
8
|
using Xunit;
|
@@ -301,4 +306,447 @@ public partial class AnalyzeWorkerTests : AnalyzeWorkerTestBase
|
|
301
306
|
}
|
302
307
|
);
|
303
308
|
}
|
309
|
+
|
310
|
+
[Fact]
|
311
|
+
public async Task VersionFinderCanHandle404FromPackageSource_V2()
|
312
|
+
{
|
313
|
+
static (int, byte[]) TestHttpHandler1(string uriString)
|
314
|
+
{
|
315
|
+
// this is a valid nuget package source, but doesn't contain anything
|
316
|
+
var uri = new Uri(uriString, UriKind.Absolute);
|
317
|
+
var baseUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}";
|
318
|
+
return uri.PathAndQuery switch
|
319
|
+
{
|
320
|
+
"/api/v2/" => (200, Encoding.UTF8.GetBytes($"""
|
321
|
+
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom" xml:base="{baseUrl}/api/v2">
|
322
|
+
<workspace>
|
323
|
+
<atom:title type="text">Default</atom:title>
|
324
|
+
<collection href="Packages">
|
325
|
+
<atom:title type="text">Packages</atom:title>
|
326
|
+
</collection>
|
327
|
+
</workspace>
|
328
|
+
</service>
|
329
|
+
""")),
|
330
|
+
_ => (404, Encoding.UTF8.GetBytes("{}")), // nothing else is found
|
331
|
+
};
|
332
|
+
}
|
333
|
+
var desktopAppRefPackage = MockNuGetPackage.WellKnownReferencePackage("Microsoft.WindowsDesktop.App", "net8.0");
|
334
|
+
(int, byte[]) TestHttpHandler2(string uriString)
|
335
|
+
{
|
336
|
+
// this contains the actual package
|
337
|
+
var uri = new Uri(uriString, UriKind.Absolute);
|
338
|
+
var baseUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}";
|
339
|
+
switch (uri.PathAndQuery)
|
340
|
+
{
|
341
|
+
case "/api/v2/":
|
342
|
+
return (200, Encoding.UTF8.GetBytes($"""
|
343
|
+
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom" xml:base="{baseUrl}/api/v2">
|
344
|
+
<workspace>
|
345
|
+
<atom:title type="text">Default</atom:title>
|
346
|
+
<collection href="Packages">
|
347
|
+
<atom:title type="text">Packages</atom:title>
|
348
|
+
</collection>
|
349
|
+
</workspace>
|
350
|
+
</service>
|
351
|
+
"""));
|
352
|
+
case "/api/v2/FindPackagesById()?id='Some.Package'&semVerLevel=2.0.0":
|
353
|
+
return (200, Encoding.UTF8.GetBytes($"""
|
354
|
+
<feed xml:base="{baseUrl}/api/v2" xmlns="http://www.w3.org/2005/Atom"
|
355
|
+
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
|
356
|
+
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
|
357
|
+
xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml">
|
358
|
+
<m:count>2</m:count>
|
359
|
+
<id>http://schemas.datacontract.org/2004/07/</id>
|
360
|
+
<title />
|
361
|
+
<updated>{DateTime.UtcNow:O}</updated>
|
362
|
+
<link rel="self" href="{baseUrl}/api/v2/Packages" />
|
363
|
+
<entry>
|
364
|
+
<id>{baseUrl}/api/v2/Packages(Id='Some.Package',Version='1.0.0')</id>
|
365
|
+
<content type="application/zip" src="{baseUrl}/api/v2/package/Some.Package/1.0.0" />
|
366
|
+
<m:properties>
|
367
|
+
<d:Version>1.0.0</d:Version>
|
368
|
+
</m:properties>
|
369
|
+
</entry>
|
370
|
+
<entry>
|
371
|
+
<id>{baseUrl}/api/v2/Packages(Id='Some.Package',Version='1.2.3')</id>
|
372
|
+
<content type="application/zip" src="{baseUrl}/api/v2/package/Some.Package/1.2.3" />
|
373
|
+
<m:properties>
|
374
|
+
<d:Version>1.2.3</d:Version>
|
375
|
+
</m:properties>
|
376
|
+
</entry>
|
377
|
+
</feed>
|
378
|
+
"""));
|
379
|
+
case "/api/v2/Packages(Id='Some.Package',Version='1.2.3')":
|
380
|
+
return (200, Encoding.UTF8.GetBytes($"""
|
381
|
+
<entry xml:base="{baseUrl}/api/v2" xmlns="http://www.w3.org/2005/Atom"
|
382
|
+
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
|
383
|
+
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
|
384
|
+
xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml">
|
385
|
+
<id>{baseUrl}/api/v2/Packages(Id='Some.Package',Version='1.2.3')</id>
|
386
|
+
<updated>{DateTime.UtcNow:O}</updated>
|
387
|
+
<content type="application/zip" src="{baseUrl}/api/v2/package/Some.Package/1.2.3" />
|
388
|
+
<m:properties>
|
389
|
+
<d:Version>1.2.3</d:Version>
|
390
|
+
</m:properties>
|
391
|
+
</entry>
|
392
|
+
"""));
|
393
|
+
case "/api/v2/package/Some.Package/1.2.3":
|
394
|
+
return (200, MockNuGetPackage.CreateSimplePackage("Some.Package", "1.2.3", "net8.0").GetZipStream().ReadAllBytes());
|
395
|
+
case "/api/v2/FindPackagesById()?id='Microsoft.WindowsDesktop.App.Ref'&semVerLevel=2.0.0":
|
396
|
+
return (200, Encoding.UTF8.GetBytes($"""
|
397
|
+
<feed xml:base="{baseUrl}/api/v2" xmlns="http://www.w3.org/2005/Atom"
|
398
|
+
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
|
399
|
+
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
|
400
|
+
xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml">
|
401
|
+
<m:count>1</m:count>
|
402
|
+
<id>http://schemas.datacontract.org/2004/07/</id>
|
403
|
+
<title />
|
404
|
+
<updated>{DateTime.UtcNow:O}</updated>
|
405
|
+
<link rel="self" href="{baseUrl}/api/v2/Packages" />
|
406
|
+
<entry>
|
407
|
+
<id>{baseUrl}/api/v2/Packages(Id='Microsoft.WindowsDesktop.App.Ref',Version='{desktopAppRefPackage.Version}')</id>
|
408
|
+
<content type="application/zip" src="{baseUrl}/api/v2/package/Microsoft.WindowsDesktop.App.Ref/{desktopAppRefPackage.Version}" />
|
409
|
+
<m:properties>
|
410
|
+
<d:Version>{desktopAppRefPackage.Version}</d:Version>
|
411
|
+
</m:properties>
|
412
|
+
</entry>
|
413
|
+
</feed>
|
414
|
+
"""));
|
415
|
+
default:
|
416
|
+
if (uri.PathAndQuery == $"/api/v2/package/Microsoft.WindowsDesktop.App.Ref/{desktopAppRefPackage.Version}")
|
417
|
+
{
|
418
|
+
return (200, desktopAppRefPackage.GetZipStream().ReadAllBytes());
|
419
|
+
}
|
420
|
+
|
421
|
+
// nothing else is found
|
422
|
+
return (404, Encoding.UTF8.GetBytes("{}"));
|
423
|
+
};
|
424
|
+
}
|
425
|
+
using var http1 = TestHttpServer.CreateTestServer(TestHttpHandler1);
|
426
|
+
using var http2 = TestHttpServer.CreateTestServer(TestHttpHandler2);
|
427
|
+
await TestAnalyzeAsync(
|
428
|
+
extraFiles:
|
429
|
+
[
|
430
|
+
("NuGet.Config", $"""
|
431
|
+
<configuration>
|
432
|
+
<packageSources>
|
433
|
+
<clear />
|
434
|
+
<add key="package_feed_1" value="{http1.BaseUrl.TrimEnd('/')}/api/v2/" allowInsecureConnections="true" />
|
435
|
+
<add key="package_feed_2" value="{http2.BaseUrl.TrimEnd('/')}/api/v2/" allowInsecureConnections="true" />
|
436
|
+
</packageSources>
|
437
|
+
</configuration>
|
438
|
+
""")
|
439
|
+
],
|
440
|
+
discovery: new()
|
441
|
+
{
|
442
|
+
Path = "/",
|
443
|
+
Projects =
|
444
|
+
[
|
445
|
+
new()
|
446
|
+
{
|
447
|
+
FilePath = "./project.csproj",
|
448
|
+
TargetFrameworks = ["net8.0"],
|
449
|
+
Dependencies =
|
450
|
+
[
|
451
|
+
new("Some.Package", "1.0.0", DependencyType.PackageReference),
|
452
|
+
]
|
453
|
+
}
|
454
|
+
]
|
455
|
+
},
|
456
|
+
dependencyInfo: new()
|
457
|
+
{
|
458
|
+
Name = "Some.Package",
|
459
|
+
Version = "1.0.0",
|
460
|
+
IgnoredVersions = [],
|
461
|
+
IsVulnerable = false,
|
462
|
+
Vulnerabilities = [],
|
463
|
+
},
|
464
|
+
expectedResult: new()
|
465
|
+
{
|
466
|
+
UpdatedVersion = "1.2.3",
|
467
|
+
CanUpdate = true,
|
468
|
+
VersionComesFromMultiDependencyProperty = false,
|
469
|
+
UpdatedDependencies =
|
470
|
+
[
|
471
|
+
new("Some.Package", "1.2.3", DependencyType.Unknown, TargetFrameworks: ["net8.0"]),
|
472
|
+
],
|
473
|
+
}
|
474
|
+
);
|
475
|
+
}
|
476
|
+
|
477
|
+
[Fact]
|
478
|
+
public async Task VersionFinderCanHandle404FromPackageSource_V3()
|
479
|
+
{
|
480
|
+
static (int, byte[]) TestHttpHandler1(string uriString)
|
481
|
+
{
|
482
|
+
// this is a valid nuget package source, but doesn't contain anything
|
483
|
+
var uri = new Uri(uriString, UriKind.Absolute);
|
484
|
+
var baseUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}";
|
485
|
+
return uri.PathAndQuery switch
|
486
|
+
{
|
487
|
+
"/index.json" => (200, Encoding.UTF8.GetBytes($$"""
|
488
|
+
{
|
489
|
+
"version": "3.0.0",
|
490
|
+
"resources": [
|
491
|
+
{
|
492
|
+
"@id": "{{baseUrl}}/download",
|
493
|
+
"@type": "PackageBaseAddress/3.0.0"
|
494
|
+
},
|
495
|
+
{
|
496
|
+
"@id": "{{baseUrl}}/query",
|
497
|
+
"@type": "SearchQueryService"
|
498
|
+
},
|
499
|
+
{
|
500
|
+
"@id": "{{baseUrl}}/registrations",
|
501
|
+
"@type": "RegistrationsBaseUrl"
|
502
|
+
}
|
503
|
+
]
|
504
|
+
}
|
505
|
+
""")),
|
506
|
+
_ => (404, Encoding.UTF8.GetBytes("{}")), // nothing else is found
|
507
|
+
};
|
508
|
+
}
|
509
|
+
var desktopAppRefPackage = MockNuGetPackage.WellKnownReferencePackage("Microsoft.WindowsDesktop.App", "net8.0");
|
510
|
+
(int, byte[]) TestHttpHandler2(string uriString)
|
511
|
+
{
|
512
|
+
// this contains the actual package
|
513
|
+
var uri = new Uri(uriString, UriKind.Absolute);
|
514
|
+
var baseUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}";
|
515
|
+
switch (uri.PathAndQuery)
|
516
|
+
{
|
517
|
+
case "/index.json":
|
518
|
+
return (200, Encoding.UTF8.GetBytes($$"""
|
519
|
+
{
|
520
|
+
"version": "3.0.0",
|
521
|
+
"resources": [
|
522
|
+
{
|
523
|
+
"@id": "{{baseUrl}}/download",
|
524
|
+
"@type": "PackageBaseAddress/3.0.0"
|
525
|
+
},
|
526
|
+
{
|
527
|
+
"@id": "{{baseUrl}}/query",
|
528
|
+
"@type": "SearchQueryService"
|
529
|
+
},
|
530
|
+
{
|
531
|
+
"@id": "{{baseUrl}}/registrations",
|
532
|
+
"@type": "RegistrationsBaseUrl"
|
533
|
+
}
|
534
|
+
]
|
535
|
+
}
|
536
|
+
"""));
|
537
|
+
case "/registrations/some.package/index.json":
|
538
|
+
return (200, Encoding.UTF8.GetBytes("""
|
539
|
+
{
|
540
|
+
"count": 1,
|
541
|
+
"items": [
|
542
|
+
{
|
543
|
+
"lower": "1.0.0",
|
544
|
+
"upper": "1.2.3",
|
545
|
+
"items": [
|
546
|
+
{
|
547
|
+
"catalogEntry": {
|
548
|
+
"listed": true,
|
549
|
+
"version": "1.0.0"
|
550
|
+
}
|
551
|
+
},
|
552
|
+
{
|
553
|
+
"catalogEntry": {
|
554
|
+
"listed": true,
|
555
|
+
"version": "1.2.3"
|
556
|
+
}
|
557
|
+
}
|
558
|
+
]
|
559
|
+
}
|
560
|
+
]
|
561
|
+
}
|
562
|
+
"""));
|
563
|
+
case "/download/some.package/index.json":
|
564
|
+
return (200, Encoding.UTF8.GetBytes("""
|
565
|
+
{
|
566
|
+
"versions": [
|
567
|
+
"1.0.0",
|
568
|
+
"1.2.3"
|
569
|
+
]
|
570
|
+
}
|
571
|
+
"""));
|
572
|
+
case "/download/microsoft.windowsdesktop.app.ref/index.json":
|
573
|
+
return (200, Encoding.UTF8.GetBytes($$"""
|
574
|
+
{
|
575
|
+
"versions": [
|
576
|
+
"{{desktopAppRefPackage.Version}}"
|
577
|
+
]
|
578
|
+
}
|
579
|
+
"""));
|
580
|
+
case "/download/some.package/1.0.0/some.package.1.0.0.nupkg":
|
581
|
+
return (200, MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0").GetZipStream().ReadAllBytes());
|
582
|
+
case "/download/some.package/1.2.3/some.package.1.2.3.nupkg":
|
583
|
+
return (200, MockNuGetPackage.CreateSimplePackage("Some.Package", "1.2.3", "net8.0").GetZipStream().ReadAllBytes());
|
584
|
+
default:
|
585
|
+
if (uri.PathAndQuery == $"/download/microsoft.windowsdesktop.app.ref/{desktopAppRefPackage.Version}/microsoft.windowsdesktop.app.ref.{desktopAppRefPackage.Version}.nupkg")
|
586
|
+
{
|
587
|
+
return (200, desktopAppRefPackage.GetZipStream().ReadAllBytes());
|
588
|
+
}
|
589
|
+
|
590
|
+
// nothing else is found
|
591
|
+
return (404, Encoding.UTF8.GetBytes("{}"));
|
592
|
+
};
|
593
|
+
}
|
594
|
+
using var http1 = TestHttpServer.CreateTestServer(TestHttpHandler1);
|
595
|
+
using var http2 = TestHttpServer.CreateTestServer(TestHttpHandler2);
|
596
|
+
await TestAnalyzeAsync(
|
597
|
+
extraFiles:
|
598
|
+
[
|
599
|
+
("NuGet.Config", $"""
|
600
|
+
<configuration>
|
601
|
+
<packageSources>
|
602
|
+
<clear />
|
603
|
+
<add key="package_feed_1" value="{http1.BaseUrl.TrimEnd('/')}/index.json" allowInsecureConnections="true" />
|
604
|
+
<add key="package_feed_2" value="{http2.BaseUrl.TrimEnd('/')}/index.json" allowInsecureConnections="true" />
|
605
|
+
</packageSources>
|
606
|
+
</configuration>
|
607
|
+
""")
|
608
|
+
],
|
609
|
+
discovery: new()
|
610
|
+
{
|
611
|
+
Path = "/",
|
612
|
+
Projects =
|
613
|
+
[
|
614
|
+
new()
|
615
|
+
{
|
616
|
+
FilePath = "./project.csproj",
|
617
|
+
TargetFrameworks = ["net8.0"],
|
618
|
+
Dependencies =
|
619
|
+
[
|
620
|
+
new("Some.Package", "1.0.0", DependencyType.PackageReference),
|
621
|
+
]
|
622
|
+
}
|
623
|
+
]
|
624
|
+
},
|
625
|
+
dependencyInfo: new()
|
626
|
+
{
|
627
|
+
Name = "Some.Package",
|
628
|
+
Version = "1.0.0",
|
629
|
+
IgnoredVersions = [],
|
630
|
+
IsVulnerable = false,
|
631
|
+
Vulnerabilities = [],
|
632
|
+
},
|
633
|
+
expectedResult: new()
|
634
|
+
{
|
635
|
+
UpdatedVersion = "1.2.3",
|
636
|
+
CanUpdate = true,
|
637
|
+
VersionComesFromMultiDependencyProperty = false,
|
638
|
+
UpdatedDependencies =
|
639
|
+
[
|
640
|
+
new("Some.Package", "1.2.3", DependencyType.Unknown, TargetFrameworks: ["net8.0"]),
|
641
|
+
],
|
642
|
+
}
|
643
|
+
);
|
644
|
+
}
|
645
|
+
|
646
|
+
[Fact]
|
647
|
+
public async Task ResultFileHasCorrectShapeForAuthenticationFailure()
|
648
|
+
{
|
649
|
+
using var temporaryDirectory = await TemporaryDirectory.CreateWithContentsAsync([]);
|
650
|
+
await AnalyzeWorker.WriteResultsAsync(temporaryDirectory.DirectoryPath, "Some.Dependency", new()
|
651
|
+
{
|
652
|
+
ErrorType = ErrorType.AuthenticationFailure,
|
653
|
+
ErrorDetails = "<some package feed>",
|
654
|
+
UpdatedVersion = "",
|
655
|
+
UpdatedDependencies = [],
|
656
|
+
}, new Logger(false));
|
657
|
+
var discoveryContents = await File.ReadAllTextAsync(Path.Combine(temporaryDirectory.DirectoryPath, "Some.Dependency.json"));
|
658
|
+
|
659
|
+
// raw result file should look like this:
|
660
|
+
// {
|
661
|
+
// ...
|
662
|
+
// "ErrorType": "AuthenticationFailure",
|
663
|
+
// "ErrorDetails": "<some package feed>",
|
664
|
+
// ...
|
665
|
+
// }
|
666
|
+
var jsonDocument = JsonDocument.Parse(discoveryContents);
|
667
|
+
var errorType = jsonDocument.RootElement.GetProperty("ErrorType");
|
668
|
+
var errorDetails = jsonDocument.RootElement.GetProperty("ErrorDetails");
|
669
|
+
|
670
|
+
Assert.Equal("AuthenticationFailure", errorType.GetString());
|
671
|
+
Assert.Equal("<some package feed>", errorDetails.GetString());
|
672
|
+
}
|
673
|
+
|
674
|
+
[Fact]
|
675
|
+
public async Task ReportsPrivateSourceAuthenticationFailure()
|
676
|
+
{
|
677
|
+
static (int, string) TestHttpHandler(string uriString)
|
678
|
+
{
|
679
|
+
var uri = new Uri(uriString, UriKind.Absolute);
|
680
|
+
var baseUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}";
|
681
|
+
return uri.PathAndQuery switch
|
682
|
+
{
|
683
|
+
// initial request is good
|
684
|
+
"/index.json" => (200, $$"""
|
685
|
+
{
|
686
|
+
"version": "3.0.0",
|
687
|
+
"resources": [
|
688
|
+
{
|
689
|
+
"@id": "{{baseUrl}}/download",
|
690
|
+
"@type": "PackageBaseAddress/3.0.0"
|
691
|
+
},
|
692
|
+
{
|
693
|
+
"@id": "{{baseUrl}}/query",
|
694
|
+
"@type": "SearchQueryService"
|
695
|
+
},
|
696
|
+
{
|
697
|
+
"@id": "{{baseUrl}}/registrations",
|
698
|
+
"@type": "RegistrationsBaseUrl"
|
699
|
+
}
|
700
|
+
]
|
701
|
+
}
|
702
|
+
"""),
|
703
|
+
// all other requests are unauthorized
|
704
|
+
_ => (401, "{}"),
|
705
|
+
};
|
706
|
+
}
|
707
|
+
using var http = TestHttpServer.CreateTestStringServer(TestHttpHandler);
|
708
|
+
await TestAnalyzeAsync(
|
709
|
+
extraFiles:
|
710
|
+
[
|
711
|
+
("NuGet.Config", $"""
|
712
|
+
<configuration>
|
713
|
+
<packageSources>
|
714
|
+
<clear />
|
715
|
+
<add key="private_feed" value="{http.BaseUrl.TrimEnd('/')}/index.json" allowInsecureConnections="true" />
|
716
|
+
</packageSources>
|
717
|
+
</configuration>
|
718
|
+
""")
|
719
|
+
],
|
720
|
+
discovery: new()
|
721
|
+
{
|
722
|
+
Path = "/",
|
723
|
+
Projects = [
|
724
|
+
new()
|
725
|
+
{
|
726
|
+
FilePath = "./project.csproj",
|
727
|
+
TargetFrameworks = ["net8.0"],
|
728
|
+
Dependencies = [
|
729
|
+
new("Some.Package", "1.2.3", DependencyType.PackageReference),
|
730
|
+
],
|
731
|
+
}
|
732
|
+
]
|
733
|
+
},
|
734
|
+
dependencyInfo: new()
|
735
|
+
{
|
736
|
+
Name = "Some.Package",
|
737
|
+
Version = "1.2.3",
|
738
|
+
IgnoredVersions = [],
|
739
|
+
IsVulnerable = false,
|
740
|
+
Vulnerabilities = [],
|
741
|
+
},
|
742
|
+
expectedResult: new()
|
743
|
+
{
|
744
|
+
ErrorType = ErrorType.AuthenticationFailure,
|
745
|
+
ErrorDetails = $"({http.BaseUrl.TrimEnd('/')}/index.json)",
|
746
|
+
UpdatedVersion = string.Empty,
|
747
|
+
CanUpdate = false,
|
748
|
+
UpdatedDependencies = [],
|
749
|
+
}
|
750
|
+
);
|
751
|
+
}
|
304
752
|
}
|