Skip to content

Commit 0230a47

Browse files
committed
(#3600) Handle non-ASCII passwords with legacy encoding fallback
Add a method to convert passwords to NetworkCredential using codepage 1252 encoding to support non-ASCII characters in credentials. This addresses issues with encoding mismatches in .NET 4.8.1 that cause incorrect password handling. Update credential retrieval to use this conversion method, falling back to the original password on retry attempts to maintain compatibility. Add tests verifying correct username and legacy-encoded password handling for non-ASCII characters in explicit and implicit credential scenarios.
1 parent 62e69d2 commit 0230a47

File tree

2 files changed

+112
-7
lines changed

2 files changed

+112
-7
lines changed

src/chocolatey.tests/infrastructure.app/nuget/ChocolateyNugetCredentialProviderSpecs.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,69 @@ public void Should_Provide_The_Correct_Password()
182182
}
183183
}
184184

185+
public class When_A_Password_Using_Non_ASCII_Characters_With_Explicit_Username_and_Password : ChocolateyNugetCredentialProviderSpecsBase
186+
{
187+
public override void Context()
188+
{
189+
base.Context();
190+
Configuration.Sources = Configuration.ExplicitSources = TargetSourceUrl;
191+
Configuration.SourceCommand.Username = "user";
192+
Configuration.SourceCommand.Password = "tøtally_sæcure_påssword!!!";
193+
}
194+
195+
[Fact]
196+
public void Should_Find_The_Saved_Source_And_Return_The_Credential()
197+
{
198+
Result.Should().NotBeNull();
199+
}
200+
201+
[Fact]
202+
public void Should_Provide_The_Correct_Username()
203+
{
204+
Result.UserName.Should().Be(Username);
205+
}
206+
207+
[Fact]
208+
public void Should_Provide_Password_Using_1252_Codepage()
209+
{
210+
// The following looks odd, but this is a workaround to
211+
// allow passwords using non-ascii characters to be used
212+
// against sources.
213+
Result.Password.Should().Be("tøtally_sæcure_påssword!!!");
214+
}
215+
}
216+
217+
public class When_A_Password_Using_Non_ASCII_Characters_With_Implicit_Usernam_And_Password : ChocolateyNugetCredentialProviderSpecsBase
218+
{
219+
public override void Context()
220+
{
221+
base.Context();
222+
Configuration.Sources = Configuration.ExplicitSources = TargetSourceName;
223+
Configuration.MachineSources[1].EncryptedPassword = NugetEncryptionUtility.EncryptString("tøtally_sæcure_påssword!!!");
224+
}
225+
226+
[Fact]
227+
public void Should_Find_The_Saved_Source_And_Return_The_Credential()
228+
{
229+
Result.Should().NotBeNull();
230+
}
231+
232+
[Fact]
233+
public void Should_Provide_The_Correct_Username()
234+
{
235+
Result.UserName.Should().Be(Username);
236+
}
237+
238+
[Fact]
239+
public void Should_Provide_Password_Using_1252_Codepage()
240+
{
241+
// The following looks odd, but this is a workaround to
242+
// allow passwords using non-ascii characters to be used
243+
// against sources.
244+
Result.Password.Should().Be("tøtally_sæcure_påssword!!!");
245+
}
246+
}
247+
185248
public class Looks_Up_Source_Url_When_Name_And_Credentials_Is_Provided : ChocolateyNugetCredentialProviderSpecsBase
186249
{
187250
public override void Context()

src/chocolatey/infrastructure.app/nuget/ChocolateyNugetCredentialProvider.cs

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using System;
1818
using System.Linq;
1919
using System.Net;
20+
using System.Text;
2021
using System.Text.RegularExpressions;
2122
using chocolatey.infrastructure.commandline;
2223
using chocolatey.infrastructure.app.configuration;
@@ -81,7 +82,7 @@ public Task<CredentialResponse> GetAsync(Uri uri, IWebProxy proxy, CredentialReq
8182
{
8283
this.Log().Debug("Using passed in credentials");
8384

84-
return Task.FromResult(new CredentialResponse(new NetworkCredential(_config.SourceCommand.Username, _config.SourceCommand.Password)));
85+
return Task.FromResult(new CredentialResponse(ToNetworkCredentials(_config.SourceCommand.Username, _config.SourceCommand.Password, isRetry)));
8586
}
8687
}
8788

@@ -156,7 +157,9 @@ public Task<CredentialResponse> GetAsync(Uri uri, IWebProxy proxy, CredentialReq
156157
this.Log().Debug("Using saved credentials");
157158
}
158159

159-
return Task.FromResult(new CredentialResponse(new NetworkCredential(source.Username, NugetEncryptionUtility.DecryptString(source.EncryptedPassword))));
160+
var credentials = ToNetworkCredentials(source.Username, NugetEncryptionUtility.DecryptString(source.EncryptedPassword), isRetry);
161+
162+
return Task.FromResult(new CredentialResponse(credentials));
160163
}
161164

162165
#pragma warning disable IDE0060 // unused method parameter
@@ -186,15 +189,54 @@ public ICredentials GetUserCredentials(Uri uri, IWebProxy proxy, CredentialReque
186189
return CredentialCache.DefaultNetworkCredentials;
187190
}
188191

189-
var credentials = new NetworkCredential
190-
{
191-
UserName = username,
192-
Password = password
193-
};
192+
var credentials = ToNetworkCredentials(username, password, isRetry: false);
194193

195194
return credentials;
196195
}
197196

197+
private static NetworkCredential ToNetworkCredentials(string username, string password, bool isRetry)
198+
{
199+
if (isRetry)
200+
{
201+
// If we have already attempted using the fallback legacy
202+
// way to set the string, then we will just return the network
203+
// credentials as is, and assume that the server expects the
204+
// encoding used to be the same as the bug in .NET 4.8.1 that
205+
// causes incorrect encodings to be used.
206+
207+
return new NetworkCredential(username, password);
208+
}
209+
210+
var utf8Bytes = Encoding.UTF8.GetBytes(password);
211+
try
212+
{
213+
var legacyEncoding = GetEncoding(1252);
214+
215+
var legacyPassword = legacyEncoding.GetString(utf8Bytes);
216+
217+
return new NetworkCredential(username, legacyPassword);
218+
}
219+
catch
220+
{
221+
return new NetworkCredential(username, password);
222+
}
223+
}
224+
225+
private static Encoding GetEncoding(int codepage)
226+
{
227+
try
228+
{
229+
return Encoding.GetEncoding(codepage);
230+
}
231+
catch
232+
{
233+
// If the code page specified isn't available,
234+
// then let us fall back to the default encoding
235+
// used on the system.
236+
return Encoding.Default;
237+
}
238+
}
239+
198240
#pragma warning disable IDE0022, IDE1006
199241
[Obsolete("This overload is deprecated and will be removed in v3.")]
200242
public ICredentials get_credentials_from_user(Uri uri, IWebProxy proxy, CredentialRequestType credentialType)

0 commit comments

Comments
 (0)