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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +6 -6
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalysisResult.cs +1 -1
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +73 -77
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +21 -8
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +24 -8
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +33 -16
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +44 -25
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs +1 -1
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +8 -0
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/ProjectBuildFile.cs +2 -2
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NativeResult.cs +8 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +18 -1
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +2 -1
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationResult.cs +5 -0
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +62 -22
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +19 -1
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +6 -2
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +448 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/CompatibilityCheckerTests.cs +23 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +2 -0
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +148 -0
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +1 -1
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +1 -1
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestHttpServer.cs +81 -0
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +27 -7
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +32 -0
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +87 -2
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +88 -0
  30. data/lib/dependabot/nuget/analysis/dependency_analysis.rb +3 -0
  31. data/lib/dependabot/nuget/file_fetcher.rb +1 -1
  32. data/lib/dependabot/nuget/metadata_finder.rb +160 -2
  33. data/lib/dependabot/nuget/native_discovery/native_workspace_discovery.rb +3 -0
  34. data/lib/dependabot/nuget/native_helpers.rb +34 -3
  35. data/lib/dependabot/nuget/native_update_checker/native_update_checker.rb +1 -0
  36. data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +3 -0
  37. metadata +11 -7
@@ -142,4 +142,27 @@ public class CompatibilityCheckerTests
142
142
 
143
143
  Assert.False(result);
144
144
  }
145
+
146
+ [Theory]
147
+ [InlineData("netstandard2.0")]
148
+ [InlineData("net472")]
149
+ [InlineData("net6.0")]
150
+ [InlineData("net7.0")]
151
+ [InlineData("net8.0")]
152
+ public void EverythingIsCompatibleWithAnyVersion0Framework(string projectFramework)
153
+ {
154
+ var package = new PackageIdentity("Dependency", NuGetVersion.Parse("1.0.0"));
155
+ ImmutableArray<NuGetFramework> projectFrameworks = [NuGetFramework.Parse(projectFramework)];
156
+ var isDevDependency = false;
157
+ ImmutableArray<NuGetFramework> packageFrameworks = [NuGetFramework.Parse("Any,Version=v0.0")];
158
+
159
+ var result = CompatibilityChecker.PerformCheck(
160
+ package,
161
+ projectFrameworks,
162
+ isDevDependency,
163
+ packageFrameworks,
164
+ new Logger(verbose: false));
165
+
166
+ Assert.True(result);
167
+ }
145
168
  }
@@ -40,6 +40,8 @@ public class DiscoveryWorkerTestBase
40
40
  ValidateResultWithDependencies(expectedResult.DotNetToolsJson, actualResult.DotNetToolsJson);
41
41
  ValidateProjectResults(expectedResult.Projects, actualResult.Projects);
42
42
  Assert.Equal(expectedResult.ExpectedProjectCount ?? expectedResult.Projects.Length, actualResult.Projects.Length);
43
+ Assert.Equal(expectedResult.ErrorType, actualResult.ErrorType);
44
+ Assert.Equal(expectedResult.ErrorDetails, actualResult.ErrorDetails);
43
45
 
44
46
  return;
45
47
 
@@ -1,3 +1,7 @@
1
+ using System.Text.Json;
2
+
3
+ using NuGetUpdater.Core.Discover;
4
+
1
5
  using Xunit;
2
6
 
3
7
  namespace NuGetUpdater.Core.Test.Discover;
@@ -55,6 +59,54 @@ public partial class DiscoveryWorkerTests : DiscoveryWorkerTestBase
55
59
  );
56
60
  }
57
61
 
62
+ [Fact]
63
+ public async Task TestDependencyWithTrailingSpacesInAttribute()
64
+ {
65
+ await TestDiscoveryAsync(
66
+ packages:
67
+ [
68
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "9.0.1", "net8.0"),
69
+ ],
70
+ workspacePath: "src",
71
+ files: new[]
72
+ {
73
+ ("src/project.csproj", """
74
+ <Project Sdk="Microsoft.NET.Sdk">
75
+ <PropertyGroup>
76
+ <TargetFramework>net8.0</TargetFramework>
77
+ <SomePackageVersion>9.0.1</SomePackageVersion>
78
+ </PropertyGroup>
79
+
80
+ <ItemGroup>
81
+ <PackageReference Include=" Some.Package " Version="$(SomePackageVersion)" />
82
+ </ItemGroup>
83
+ </Project>
84
+ """)
85
+ },
86
+ expectedResult: new()
87
+ {
88
+ Path = "src",
89
+ Projects = [
90
+ new()
91
+ {
92
+ FilePath = "project.csproj",
93
+ TargetFrameworks = ["net8.0"],
94
+ ReferencedProjectPaths = [],
95
+ ExpectedDependencyCount = 2,
96
+ Dependencies = [
97
+ new("Microsoft.NET.Sdk", null, DependencyType.MSBuildSdk),
98
+ new("Some.Package", "9.0.1", DependencyType.PackageReference, TargetFrameworks: ["net8.0"], IsDirect: true)
99
+ ],
100
+ Properties = [
101
+ new("SomePackageVersion", "9.0.1", "src/project.csproj"),
102
+ new("TargetFramework", "net8.0", "src/project.csproj"),
103
+ ]
104
+ }
105
+ ]
106
+ }
107
+ );
108
+ }
109
+
58
110
  [Fact]
59
111
  public async Task TestPackageConfig()
60
112
  {
@@ -322,4 +374,100 @@ public partial class DiscoveryWorkerTests : DiscoveryWorkerTestBase
322
374
  }
323
375
  );
324
376
  }
377
+
378
+ [Fact]
379
+ public async Task ResultFileHasCorrectShapeForAuthenticationFailure()
380
+ {
381
+ using var temporaryDirectory = await TemporaryDirectory.CreateWithContentsAsync([]);
382
+ var discoveryResultPath = Path.Combine(temporaryDirectory.DirectoryPath, DiscoveryWorker.DiscoveryResultFileName);
383
+ await DiscoveryWorker.WriteResultsAsync(temporaryDirectory.DirectoryPath, discoveryResultPath, new()
384
+ {
385
+ ErrorType = ErrorType.AuthenticationFailure,
386
+ ErrorDetails = "<some package feed>",
387
+ Path = "/",
388
+ Projects = [],
389
+ });
390
+ var discoveryContents = await File.ReadAllTextAsync(discoveryResultPath);
391
+
392
+ // raw result file should look like this:
393
+ // {
394
+ // ...
395
+ // "ErrorType": "AuthenticationFailure",
396
+ // "ErrorDetails": "<some package feed>",
397
+ // ...
398
+ // }
399
+ var jsonDocument = JsonDocument.Parse(discoveryContents);
400
+ var errorType = jsonDocument.RootElement.GetProperty("ErrorType");
401
+ var errorDetails = jsonDocument.RootElement.GetProperty("ErrorDetails");
402
+
403
+ Assert.Equal("AuthenticationFailure", errorType.GetString());
404
+ Assert.Equal("<some package feed>", errorDetails.GetString());
405
+ }
406
+
407
+ [Fact]
408
+ public async Task ReportsPrivateSourceAuthenticationFailure()
409
+ {
410
+ static (int, string) TestHttpHandler(string uriString)
411
+ {
412
+ var uri = new Uri(uriString, UriKind.Absolute);
413
+ var baseUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}";
414
+ return uri.PathAndQuery switch
415
+ {
416
+ // initial request is good
417
+ "/index.json" => (200, $$"""
418
+ {
419
+ "version": "3.0.0",
420
+ "resources": [
421
+ {
422
+ "@id": "{{baseUrl}}/download",
423
+ "@type": "PackageBaseAddress/3.0.0"
424
+ },
425
+ {
426
+ "@id": "{{baseUrl}}/query",
427
+ "@type": "SearchQueryService"
428
+ },
429
+ {
430
+ "@id": "{{baseUrl}}/registrations",
431
+ "@type": "RegistrationsBaseUrl"
432
+ }
433
+ ]
434
+ }
435
+ """),
436
+ // all other requests are unauthorized
437
+ _ => (401, "{}"),
438
+ };
439
+ }
440
+ using var http = TestHttpServer.CreateTestStringServer(TestHttpHandler);
441
+ await TestDiscoveryAsync(
442
+ workspacePath: "",
443
+ files:
444
+ [
445
+ ("project.csproj", """
446
+ <Project Sdk="Microsoft.NET.Sdk">
447
+ <PropertyGroup>
448
+ <TargetFramework>net8.0</TargetFramework>
449
+ </PropertyGroup>
450
+ <ItemGroup>
451
+ <PackageReference Include="Some.Package" Version="1.2.3" />
452
+ </ItemGroup>
453
+ </Project>
454
+ """),
455
+ ("NuGet.Config", $"""
456
+ <configuration>
457
+ <packageSources>
458
+ <clear />
459
+ <add key="private_feed" value="{http.BaseUrl.TrimEnd('/')}/index.json" allowInsecureConnections="true" />
460
+ </packageSources>
461
+ </configuration>
462
+ """),
463
+ ],
464
+ expectedResult: new()
465
+ {
466
+ ErrorType = ErrorType.AuthenticationFailure,
467
+ ErrorDetails = $"({http.BaseUrl.TrimEnd('/')}/index.json)",
468
+ Path = "",
469
+ Projects = [],
470
+ }
471
+ );
472
+ }
325
473
  }
@@ -4,7 +4,7 @@ using NuGetUpdater.Core.Discover;
4
4
 
5
5
  namespace NuGetUpdater.Core.Test.Discover;
6
6
 
7
- public record ExpectedWorkspaceDiscoveryResult
7
+ public record ExpectedWorkspaceDiscoveryResult : NativeResult
8
8
  {
9
9
  public required string Path { get; init; }
10
10
  public bool IsSuccess { get; init; } = true;
@@ -245,7 +245,7 @@ namespace NuGetUpdater.Core.Test
245
245
  );
246
246
  }
247
247
 
248
- private Stream GetZipStream()
248
+ public Stream GetZipStream()
249
249
  {
250
250
  if (_stream is null)
251
251
  {
@@ -0,0 +1,81 @@
1
+ using System.Net;
2
+ using System.Net.Sockets;
3
+ using System.Text;
4
+
5
+ namespace NuGetUpdater.Core.Test
6
+ {
7
+ public class TestHttpServer : IDisposable
8
+ {
9
+ private readonly Func<string, (int, byte[])> _requestHandler;
10
+ private readonly HttpListener _listener;
11
+ private bool _runServer = true;
12
+
13
+ public string BaseUrl { get; }
14
+
15
+ private TestHttpServer(string baseurl, Func<string, (int, byte[])> requestHandler)
16
+ {
17
+ BaseUrl = baseurl;
18
+ _requestHandler = requestHandler;
19
+ _listener = new HttpListener();
20
+ _listener.Prefixes.Add(baseurl);
21
+ }
22
+
23
+ private void Start()
24
+ {
25
+ _listener.Start();
26
+ Task.Factory.StartNew(HandleResponses);
27
+ }
28
+
29
+ public void Dispose()
30
+ {
31
+ _runServer = false;
32
+ _listener.Stop();
33
+ }
34
+
35
+ private async Task HandleResponses()
36
+ {
37
+ while (_runServer)
38
+ {
39
+ var context = await _listener.GetContextAsync();
40
+ var (statusCode, response) = _requestHandler(context.Request.Url!.AbsoluteUri);
41
+ context.Response.StatusCode = statusCode;
42
+ await context.Response.OutputStream.WriteAsync(response);
43
+ context.Response.Close();
44
+ }
45
+ }
46
+
47
+ private static readonly object PortGate = new();
48
+
49
+ public static TestHttpServer CreateTestServer(Func<string, (int, byte[])> requestHandler)
50
+ {
51
+ // static lock to ensure the port is not recycled after `FindFreePort()` and before we can start the real server
52
+ lock (PortGate)
53
+ {
54
+ var port = FindFreePort();
55
+ var baseUrl = $"http://localhost:{port}/";
56
+ var server = new TestHttpServer(baseUrl, requestHandler);
57
+ server.Start();
58
+ return server;
59
+ }
60
+ }
61
+
62
+ public static TestHttpServer CreateTestStringServer(Func<string, (int, string)> requestHandler)
63
+ {
64
+ Func<string, (int, byte[])> bytesRequestHandler = url =>
65
+ {
66
+ var (statusCode, response) = requestHandler(url);
67
+ return (statusCode, Encoding.UTF8.GetBytes(response));
68
+ };
69
+ return CreateTestServer(bytesRequestHandler);
70
+ }
71
+
72
+ private static int FindFreePort()
73
+ {
74
+ var tcpListener = new TcpListener(IPAddress.Loopback, 0);
75
+ tcpListener.Start();
76
+ var port = ((IPEndPoint)tcpListener.LocalEndpoint).Port;
77
+ tcpListener.Stop();
78
+ return port;
79
+ }
80
+ }
81
+ }
@@ -1,3 +1,7 @@
1
+ using System.Text.Json;
2
+
3
+ using NuGetUpdater.Core.Updater;
4
+
1
5
  using Xunit;
2
6
 
3
7
  namespace NuGetUpdater.Core.Test.Update;
@@ -88,7 +92,8 @@ public abstract class UpdateWorkerTestBase : TestBase
88
92
  TestFile[]? additionalFiles = null,
89
93
  TestFile[]? additionalFilesExpected = null,
90
94
  MockNuGetPackage[]? packages = null,
91
- string projectFilePath = "test-project.csproj")
95
+ string projectFilePath = "test-project.csproj",
96
+ UpdateOperationResult? expectedResult = null)
92
97
  => TestUpdateForProject(
93
98
  dependencyName,
94
99
  oldVersion,
@@ -98,7 +103,8 @@ public abstract class UpdateWorkerTestBase : TestBase
98
103
  isTransitive,
99
104
  additionalFiles,
100
105
  additionalFilesExpected,
101
- packages);
106
+ packages,
107
+ expectedResult);
102
108
 
103
109
  protected static async Task TestUpdateForProject(
104
110
  string dependencyName,
@@ -109,7 +115,8 @@ public abstract class UpdateWorkerTestBase : TestBase
109
115
  bool isTransitive = false,
110
116
  TestFile[]? additionalFiles = null,
111
117
  TestFile[]? additionalFilesExpected = null,
112
- MockNuGetPackage[]? packages = null)
118
+ MockNuGetPackage[]? packages = null,
119
+ UpdateOperationResult? expectedResult = null)
113
120
  {
114
121
  additionalFiles ??= [];
115
122
  additionalFilesExpected ??= [];
@@ -130,16 +137,29 @@ public abstract class UpdateWorkerTestBase : TestBase
130
137
  // run update
131
138
  var worker = new UpdaterWorker(new Logger(verbose: true));
132
139
  var projectPath = placeFilesInSrc ? $"src/{projectFilePath}" : projectFilePath;
133
- await worker.RunAsync(temporaryDirectory, projectPath, dependencyName, oldVersion, newVersion, isTransitive);
140
+ var updateResultFile = Path.Combine(temporaryDirectory, "update-result.json");
141
+ await worker.RunAsync(temporaryDirectory, projectPath, dependencyName, oldVersion, newVersion, isTransitive, updateResultFile);
142
+ var actualResultContents = await File.ReadAllTextAsync(updateResultFile);
143
+ var actualResult = JsonSerializer.Deserialize<UpdateOperationResult>(actualResultContents, UpdaterWorker.SerializerOptions);
144
+ if (expectedResult is { })
145
+ {
146
+ ValidateUpdateOperationResult(expectedResult, actualResult!);
147
+ }
134
148
  });
135
149
 
136
- var expectedResult = additionalFilesExpected.Prepend((projectFilePath, expectedProjectContents)).ToArray();
150
+ var expectedResultFiles = additionalFilesExpected.Prepend((projectFilePath, expectedProjectContents)).ToArray();
137
151
  if (placeFilesInSrc)
138
152
  {
139
- expectedResult = expectedResult.Select(er => ($"src/{er.Item1}", er.Item2)).ToArray();
153
+ expectedResultFiles = expectedResultFiles.Select(er => ($"src/{er.Item1}", er.Item2)).ToArray();
140
154
  }
141
155
 
142
- AssertContainsFiles(expectedResult, actualResult);
156
+ AssertContainsFiles(expectedResultFiles, actualResult);
157
+ }
158
+
159
+ protected static void ValidateUpdateOperationResult(UpdateOperationResult expectedResult, UpdateOperationResult actualResult)
160
+ {
161
+ Assert.Equal(expectedResult.ErrorType, actualResult.ErrorType);
162
+ Assert.Equal(expectedResult.ErrorDetails, actualResult.ErrorDetails);
143
163
  }
144
164
 
145
165
  protected static Task TestNoChangeforSolution(
@@ -1,3 +1,7 @@
1
+ using System.Text.Json;
2
+
3
+ using NuGetUpdater.Core.Updater;
4
+
1
5
  using Xunit;
2
6
 
3
7
  namespace NuGetUpdater.Core.Test.Update;
@@ -6,6 +10,34 @@ public partial class UpdateWorkerTests
6
10
  {
7
11
  public class Mixed : UpdateWorkerTestBase
8
12
  {
13
+ [Fact]
14
+ public async Task ResultFileHasCorrectShapeForAuthenticationFailure()
15
+ {
16
+ using var temporaryDirectory = await TemporaryDirectory.CreateWithContentsAsync([]);
17
+ var result = new UpdateOperationResult()
18
+ {
19
+ ErrorType = ErrorType.AuthenticationFailure,
20
+ ErrorDetails = "<some package feed>",
21
+ };
22
+ var resultFilePath = Path.Combine(temporaryDirectory.DirectoryPath, "update-result.json");
23
+ await UpdaterWorker.WriteResultFile(result, resultFilePath, new Logger(false));
24
+ var resultContent = await File.ReadAllTextAsync(resultFilePath);
25
+
26
+ // raw result file should look like this:
27
+ // {
28
+ // ...
29
+ // "ErrorType": "AuthenticationFailure",
30
+ // "ErrorDetails": "<some package feed>",
31
+ // ...
32
+ // }
33
+ var jsonDocument = JsonDocument.Parse(resultContent);
34
+ var errorType = jsonDocument.RootElement.GetProperty("ErrorType");
35
+ var errorDetails = jsonDocument.RootElement.GetProperty("ErrorDetails");
36
+
37
+ Assert.Equal("AuthenticationFailure", errorType.GetString());
38
+ Assert.Equal("<some package feed>", errorDetails.GetString());
39
+ }
40
+
9
41
  [Fact]
10
42
  public async Task ForPackagesProject_UpdatePackageReference_InBuildProps()
11
43
  {
@@ -1,3 +1,5 @@
1
+ using NuGetUpdater.Core.Updater;
2
+
1
3
  using Xunit;
2
4
 
3
5
  namespace NuGetUpdater.Core.Test.Update;
@@ -1420,6 +1422,87 @@ public partial class UpdateWorkerTests
1420
1422
  """);
1421
1423
  }
1422
1424
 
1425
+ [Fact]
1426
+ public async Task ReportsPrivateSourceAuthenticationFailure()
1427
+ {
1428
+ static (int, string) TestHttpHandler(string uriString)
1429
+ {
1430
+ var uri = new Uri(uriString, UriKind.Absolute);
1431
+ var baseUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}";
1432
+ return uri.PathAndQuery switch
1433
+ {
1434
+ _ => (401, "{}"), // everything is unauthorized
1435
+ };
1436
+ }
1437
+ using var http = TestHttpServer.CreateTestStringServer(TestHttpHandler);
1438
+ await TestUpdateForProject("Some.Package", "1.0.0", "1.1.0",
1439
+ // existing
1440
+ projectContents: """
1441
+ <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
1442
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
1443
+ <PropertyGroup>
1444
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
1445
+ </PropertyGroup>
1446
+ <ItemGroup>
1447
+ <None Include="packages.config" />
1448
+ </ItemGroup>
1449
+ <ItemGroup>
1450
+ <Reference Include="Some.Package, Version=1.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
1451
+ <HintPath>packages\Some.Package.1.0.0\lib\net45\Some.Package.dll</HintPath>
1452
+ <Private>True</Private>
1453
+ </Reference>
1454
+ </ItemGroup>
1455
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
1456
+ </Project>
1457
+ """,
1458
+ packagesConfigContents: """
1459
+ <packages>
1460
+ <package id="Some.Package" version="1.0.0" targetFramework="net45" />
1461
+ </packages>
1462
+ """,
1463
+ additionalFiles:
1464
+ [
1465
+ ("NuGet.Config", $"""
1466
+ <configuration>
1467
+ <packageSources>
1468
+ <clear />
1469
+ <add key="private_feed" value="{http.BaseUrl.TrimEnd('/')}/index.json" allowInsecureConnections="true" />
1470
+ </packageSources>
1471
+ </configuration>
1472
+ """)
1473
+ ],
1474
+ // expected
1475
+ expectedProjectContents: """
1476
+ <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
1477
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
1478
+ <PropertyGroup>
1479
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
1480
+ </PropertyGroup>
1481
+ <ItemGroup>
1482
+ <None Include="packages.config" />
1483
+ </ItemGroup>
1484
+ <ItemGroup>
1485
+ <Reference Include="Some.Package, Version=1.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
1486
+ <HintPath>packages\Some.Package.1.0.0\lib\net45\Some.Package.dll</HintPath>
1487
+ <Private>True</Private>
1488
+ </Reference>
1489
+ </ItemGroup>
1490
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
1491
+ </Project>
1492
+ """,
1493
+ expectedPackagesConfigContents: """
1494
+ <packages>
1495
+ <package id="Some.Package" version="1.0.0" targetFramework="net45" />
1496
+ </packages>
1497
+ """,
1498
+ expectedResult: new()
1499
+ {
1500
+ ErrorType = ErrorType.AuthenticationFailure,
1501
+ ErrorDetails = $"({http.BaseUrl.TrimEnd('/')}/index.json)",
1502
+ }
1503
+ );
1504
+ }
1505
+
1423
1506
  protected static Task TestUpdateForProject(
1424
1507
  string dependencyName,
1425
1508
  string oldVersion,
@@ -1430,7 +1513,8 @@ public partial class UpdateWorkerTests
1430
1513
  string expectedPackagesConfigContents,
1431
1514
  (string Path, string Content)[]? additionalFiles = null,
1432
1515
  (string Path, string Content)[]? additionalFilesExpected = null,
1433
- MockNuGetPackage[]? packages = null)
1516
+ MockNuGetPackage[]? packages = null,
1517
+ UpdateOperationResult? expectedResult = null)
1434
1518
  {
1435
1519
  var realizedAdditionalFiles = new List<(string Path, string Content)>
1436
1520
  {
@@ -1458,7 +1542,8 @@ public partial class UpdateWorkerTests
1458
1542
  expectedProjectContents,
1459
1543
  additionalFiles: realizedAdditionalFiles.ToArray(),
1460
1544
  additionalFilesExpected: realizedAdditionalFilesExpected.ToArray(),
1461
- packages: packages);
1545
+ packages: packages,
1546
+ expectedResult: expectedResult);
1462
1547
  }
1463
1548
  }
1464
1549
  }
@@ -1,5 +1,8 @@
1
1
  using System.Linq;
2
2
  using System.Text;
3
+ using System.Text.Json;
4
+
5
+ using NuGetUpdater.Core.Updater;
3
6
 
4
7
  using Xunit;
5
8
 
@@ -2898,5 +2901,90 @@ public partial class UpdateWorkerTests
2898
2901
  """
2899
2902
  );
2900
2903
  }
2904
+
2905
+ [Fact]
2906
+ public async Task UpdatePackageWithWhitespaceInTheXMLAttributeValue()
2907
+ {
2908
+ await TestUpdateForProject("Some.Package", "1.0.0", "1.1.0",
2909
+ packages:
2910
+ [
2911
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0"),
2912
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.1.0", "net8.0"),
2913
+ ],
2914
+ projectContents: """
2915
+ <Project Sdk="Microsoft.NET.Sdk">
2916
+ <PropertyGroup>
2917
+ <TargetFramework>net8.0</TargetFramework>
2918
+ </PropertyGroup>
2919
+ <ItemGroup>
2920
+ <PackageReference Include=" Some.Package " Version="1.0.0" />
2921
+ </ItemGroup>
2922
+ </Project>
2923
+ """,
2924
+ expectedProjectContents: """
2925
+ <Project Sdk="Microsoft.NET.Sdk">
2926
+ <PropertyGroup>
2927
+ <TargetFramework>net8.0</TargetFramework>
2928
+ </PropertyGroup>
2929
+ <ItemGroup>
2930
+ <PackageReference Include=" Some.Package " Version="1.1.0" />
2931
+ </ItemGroup>
2932
+ </Project>
2933
+ """
2934
+ );
2935
+ }
2936
+
2937
+ [Fact]
2938
+ public async Task ReportsPrivateSourceAuthenticationFailure()
2939
+ {
2940
+ static (int, string) TestHttpHandler(string uriString)
2941
+ {
2942
+ var uri = new Uri(uriString, UriKind.Absolute);
2943
+ var baseUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}";
2944
+ return uri.PathAndQuery switch
2945
+ {
2946
+ _ => (401, "{}"), // everything is unauthorized
2947
+ };
2948
+ }
2949
+ using var http = TestHttpServer.CreateTestStringServer(TestHttpHandler);
2950
+ await TestUpdateForProject("Some.Package", "1.0.0", "1.1.0",
2951
+ projectContents: """
2952
+ <Project Sdk="Microsoft.NET.Sdk">
2953
+ <PropertyGroup>
2954
+ <TargetFramework>net8.0</TargetFramework>
2955
+ </PropertyGroup>
2956
+ <ItemGroup>
2957
+ <PackageReference Include="Some.Package" Version="1.0.0" />
2958
+ </ItemGroup>
2959
+ </Project>
2960
+ """,
2961
+ additionalFiles:
2962
+ [
2963
+ ("NuGet.Config", $"""
2964
+ <configuration>
2965
+ <packageSources>
2966
+ <clear />
2967
+ <add key="private_feed" value="{http.BaseUrl.TrimEnd('/')}/index.json" allowInsecureConnections="true" />
2968
+ </packageSources>
2969
+ </configuration>
2970
+ """)
2971
+ ],
2972
+ expectedProjectContents: """
2973
+ <Project Sdk="Microsoft.NET.Sdk">
2974
+ <PropertyGroup>
2975
+ <TargetFramework>net8.0</TargetFramework>
2976
+ </PropertyGroup>
2977
+ <ItemGroup>
2978
+ <PackageReference Include="Some.Package" Version="1.0.0" />
2979
+ </ItemGroup>
2980
+ </Project>
2981
+ """,
2982
+ expectedResult: new()
2983
+ {
2984
+ ErrorType = ErrorType.AuthenticationFailure,
2985
+ ErrorDetails = $"({http.BaseUrl.TrimEnd('/')}/index.json)",
2986
+ }
2987
+ );
2988
+ }
2901
2989
  }
2902
2990
  }
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "dependabot/nuget/version"
5
+ require "dependabot/nuget/native_helpers"
5
6
  require "sorbet-runtime"
6
7
 
7
8
  module Dependabot
@@ -11,6 +12,8 @@ module Dependabot
11
12
 
12
13
  sig { params(json: T::Hash[String, T.untyped]).returns(DependencyAnalysis) }
13
14
  def self.from_json(json)
15
+ Dependabot::Nuget::NativeHelpers.ensure_no_errors(json)
16
+
14
17
  updated_version = T.let(json.fetch("UpdatedVersion"), String)
15
18
  can_update = T.let(json.fetch("CanUpdate"), T::Boolean)
16
19
  version_comes_from_multi_dependency_property = T.let(json.fetch("VersionComesFromMultiDependencyProperty"),
@@ -43,7 +43,7 @@ module Dependabot
43
43
  ).void
44
44
  end
45
45
  def initialize(source:, credentials:, repo_contents_path: nil, options: {})
46
- super(source: source, credentials: credentials, repo_contents_path: repo_contents_path, options: options)
46
+ super
47
47
 
48
48
  @sln_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
49
49
  @sln_project_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))