mirror of
https://github.com/2dust/v2rayN.git
synced 2026-11-06 04:24:59 +03:00
fix(fmt): keep a ':' in a Naive password through export and import (#10526)
NaiveFmt.ToUri percent-encodes the username and password, so a password-only profile with the password a:b is exported as naive+https://a%3Ab@host. Resolve decoded the user info before looking for the ':' separator and imported it as username "a", password "b". Split the raw user info first and decode each part. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
namespace ServiceLib.Tests.Fmt;
|
||||
|
||||
public class NaiveFmtUserInfoTests
|
||||
{
|
||||
[Test]
|
||||
[Arguments("pa:ss")]
|
||||
[Arguments("a:b:c")]
|
||||
[Arguments(":leading")]
|
||||
[Arguments("trailing:")]
|
||||
public async Task GetShareUriAndResolveConfig_PasswordOnlyWithColon_ShouldKeepPassword(string password)
|
||||
{
|
||||
var source = CreateNaiveProfile(string.Empty, password);
|
||||
|
||||
var uri = FmtHandler.GetShareUri(source);
|
||||
await uri.Should().NotBeNull();
|
||||
|
||||
var resolved = FmtHandler.ResolveConfig(uri!, out var msg);
|
||||
|
||||
await resolved.Should().NotBeNull().Because($"uri: {uri}, msg: {msg}");
|
||||
await resolved!.Username.Should().BeEqualTo(string.Empty);
|
||||
await resolved.Password.Should().BeEqualTo(password);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetShareUriAndResolveConfig_UsernameAndPasswordWithColons_ShouldKeepBoth()
|
||||
{
|
||||
var source = CreateNaiveProfile("us:er", "pa:ss");
|
||||
|
||||
var uri = FmtHandler.GetShareUri(source);
|
||||
await uri.Should().NotBeNull();
|
||||
|
||||
var resolved = FmtHandler.ResolveConfig(uri!, out var msg);
|
||||
|
||||
await resolved.Should().NotBeNull().Because($"uri: {uri}, msg: {msg}");
|
||||
await resolved!.Username.Should().BeEqualTo("us:er");
|
||||
await resolved.Password.Should().BeEqualTo("pa:ss");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Arguments("naive+https://what:happened@test.someone.cf:443", "what", "happened")]
|
||||
[Arguments("naive+https://what:hap:pened@test.someone.cf:443", "what", "hap:pened")]
|
||||
[Arguments("naive+https://wh%40t:hap%3Apened@test.someone.cf:443", "wh@t", "hap:pened")]
|
||||
[Arguments("naive+https://pa%3Ass@test.someone.cf:443", "", "pa:ss")]
|
||||
[Arguments("naive+quic://secret@test.someone.cf:443", "", "secret")]
|
||||
public async Task ResolveConfig_ShouldSplitUserInfoBeforeDecoding(string shareUri, string username, string password)
|
||||
{
|
||||
var resolved = FmtHandler.ResolveConfig(shareUri, out var msg);
|
||||
|
||||
await resolved.Should().NotBeNull().Because($"uri: {shareUri}, msg: {msg}");
|
||||
await resolved!.Username.Should().BeEqualTo(username);
|
||||
await resolved.Password.Should().BeEqualTo(password);
|
||||
}
|
||||
|
||||
private static ProfileItem CreateNaiveProfile(string username, string password)
|
||||
{
|
||||
var item = new ProfileItem
|
||||
{
|
||||
ConfigType = EConfigType.Naive,
|
||||
Remarks = "naive demo",
|
||||
Address = "naive.example",
|
||||
Port = 443,
|
||||
Username = username,
|
||||
Password = password,
|
||||
Network = nameof(ETransport.raw),
|
||||
StreamSecurity = Global.None,
|
||||
};
|
||||
item.SetTransportExtra(new TransportExtraItem { RawHeaderType = Global.None, });
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -30,16 +30,17 @@ public class NaiveFmt : BaseFmt
|
||||
NaiveQuic = true,
|
||||
};
|
||||
}
|
||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||
// Split before decoding: ToUri percent-encodes a ':' inside the username or password.
|
||||
var rawUserInfo = parsedUrl.UserInfo;
|
||||
if (rawUserInfo.Contains(':'))
|
||||
{
|
||||
var split = rawUserInfo.Split(':', 2);
|
||||
item.Username = split[0];
|
||||
item.Password = split[1];
|
||||
item.Username = Utils.UrlDecode(split[0]);
|
||||
item.Password = Utils.UrlDecode(split[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Password = rawUserInfo;
|
||||
item.Password = Utils.UrlDecode(rawUserInfo);
|
||||
}
|
||||
|
||||
var query = Utils.ParseQueryString(parsedUrl.Query);
|
||||
|
||||
Reference in New Issue
Block a user