Skip to content

Commit 1e51341

Browse files
authored
[HotFix]: Fix double encoding (#10417)
1 parent e363891 commit 1e51341

2 files changed

Lines changed: 67 additions & 5 deletions

File tree

src/Catalog/Persistence/Storage.cs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,19 +182,33 @@ public virtual Task<bool> AreSynchronized(Uri firstResourceUri, Uri secondResour
182182

183183
protected string GetName(Uri uri)
184184
{
185-
var address = Uri.UnescapeDataString(BaseAddress.GetLeftPart(UriPartial.Path));
185+
if (uri is null)
186+
{
187+
throw new ArgumentNullException(nameof(uri));
188+
}
189+
190+
if (BaseAddress is null)
191+
{
192+
throw new InvalidOperationException("BaseAddress must be set.");
193+
}
194+
195+
string address = Uri.UnescapeDataString(BaseAddress.GetLeftPart(UriPartial.Path));
186196
if (!address.EndsWith("/"))
187197
{
188198
address += "/";
189199
}
190-
var uriString = uri.GetLeftPart(UriPartial.Path);
191200

192-
int baseAddressLength = address.Length;
201+
// This method does encoding under the hood, could be a problem if it contains Unicode characters.
202+
string fullPath = uri.GetLeftPart(UriPartial.Path); // Remove potential SAS token from the URI
193203

194204
// handle mismatched scheme (http vs https)
195-
var schemeLengthDifference = uri.Scheme.Length - BaseAddress.Scheme.Length;
205+
int schemeLengthDifference = uri.Scheme.Length - BaseAddress.Scheme.Length;
206+
207+
string encodedName = fullPath.Substring(address.Length + schemeLengthDifference);
208+
209+
// decode back previous encoding in case of Unicode characters, otherwise it will be double encoded later
210+
string name = Uri.UnescapeDataString(encodedName);
196211

197-
var name = uriString.Substring(baseAddressLength + schemeLengthDifference);
198212
if (name.Contains("#"))
199213
{
200214
name = name.Substring(0, name.IndexOf("#"));
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Moq;
6+
using NuGet.Services.Metadata.Catalog.Persistence;
7+
using Xunit;
8+
9+
namespace CatalogTests.Persistence
10+
{
11+
public class StorageTests
12+
{
13+
[Theory]
14+
[InlineData("http://contoso.blob.core.windows.net/packages/package.1.0.0.nupkg", "package.1.0.0.nupkg")]
15+
[InlineData("http://contoso.blob.core.windows.net/packages/another-package123.2.0.0.nupkg", "another-package123.2.0.0.nupkg")]
16+
public void GetName_NonUnicodeUri_ReturnsCorrectName(string uriString, string expectedName)
17+
{
18+
// Arrange
19+
Uri baseAddress = new Uri("http://contoso.blob.core.windows.net/packages/");
20+
var storage = new Mock<Storage>(baseAddress) { CallBase = true };
21+
var uri = new Uri(uriString);
22+
23+
// Act
24+
var name = storage.Object.GetUri(expectedName).ToString();
25+
26+
// Assert
27+
Assert.EndsWith(expectedName, name);
28+
}
29+
30+
[Theory]
31+
[InlineData("http://contoso.blob.core.windows.net/packages/邮件.1.0.0.nupkg", "邮件.1.0.0.nupkg")]
32+
[InlineData("http://contoso.blob.core.windows.net/packages/пакет.2.0.0.nupkg", "пакет.2.0.0.nupkg")]
33+
[InlineData("http://contoso.blob.core.windows.net/packages/パッケージ.3.0.0.nupkg", "パッケージ.3.0.0.nupkg")]
34+
public void GetName_UnicodeUri_ReturnsCorrectName(string uriString, string expectedName)
35+
{
36+
// Arrange
37+
Uri baseAddress = new Uri("http://contoso.blob.core.windows.net/packages/");
38+
var storage = new Mock<Storage>(baseAddress) { CallBase = true };
39+
var uri = new Uri(uriString);
40+
41+
// Act
42+
var name = storage.Object.GetUri(expectedName).ToString();
43+
44+
// Assert
45+
Assert.EndsWith(expectedName, name);
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)