Skip to content

Commit 6d81efd

Browse files
authored
[HotFix]: Fix base address and blob uri decoding mismatch (#10429)
1 parent 1e51341 commit 6d81efd

2 files changed

Lines changed: 132 additions & 13 deletions

File tree

src/Catalog/Persistence/Storage.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ public virtual Task<bool> AreSynchronized(Uri firstResourceUri, Uri secondResour
180180
return Task.FromResult(false);
181181
}
182182

183-
protected string GetName(Uri uri)
183+
public string GetName(Uri uri)
184184
{
185185
if (uri is null)
186186
{
@@ -192,22 +192,22 @@ protected string GetName(Uri uri)
192192
throw new InvalidOperationException("BaseAddress must be set.");
193193
}
194194

195-
string address = Uri.UnescapeDataString(BaseAddress.GetLeftPart(UriPartial.Path));
195+
// The GetLeftPart method performs encoding under the hood, which could be problematic if it contains Unicode characters.
196+
// It doesn't perform double encoding; the Uri object knows if it's already encoded and skips encoding it again.
197+
// Decoding the base address to remove any encoded characters.
198+
string address = Uri.UnescapeDataString(BaseAddress.GetLeftPart(UriPartial.Path)); // Remove potential query or SAS from the URI
196199
if (!address.EndsWith("/"))
197200
{
198201
address += "/";
199202
}
200203

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
204+
// Do the same with the above to get it decoded.
205+
string fullPath = Uri.UnescapeDataString(uri.GetLeftPart(UriPartial.Path)); // Remove potential query or SAS from the URI
203206

204207
// handle mismatched scheme (http vs https)
205208
int schemeLengthDifference = uri.Scheme.Length - BaseAddress.Scheme.Length;
206209

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);
210+
string name = fullPath.Substring(address.Length + schemeLengthDifference);
211211

212212
if (name.Contains("#"))
213213
{

tests/CatalogTests/Persistence/StorageTests.cs

Lines changed: 124 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,25 @@ namespace CatalogTests.Persistence
1010
{
1111
public class StorageTests
1212
{
13+
[Theory]
14+
[InlineData("package.1.0.0.nupkg", "http://contoso.blob.core.windows.net/packages/package.1.0.0.nupkg")]
15+
[InlineData("123.8.0.0-preview.7.23368.14.nupkg", "http://contoso.blob.core.windows.net/packages/123.8.0.0-preview.7.23368.14.nupkg")]
16+
public void GetUri_ReturnsCorrectUri(string name, string expectedUri)
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+
22+
// Act
23+
string uri = storage.Object.GetUri(name).AbsoluteUri;
24+
25+
// Assert
26+
Assert.Equal(expectedUri, uri);
27+
}
28+
1329
[Theory]
1430
[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")]
31+
[InlineData("http://contoso.blob.core.windows.net/packages/123.8.0.0-preview.7.23368.14.nupkg", "123.8.0.0-preview.7.23368.14.nupkg")]
1632
public void GetName_NonUnicodeUri_ReturnsCorrectName(string uriString, string expectedName)
1733
{
1834
// Arrange
@@ -21,10 +37,10 @@ public void GetName_NonUnicodeUri_ReturnsCorrectName(string uriString, string ex
2137
var uri = new Uri(uriString);
2238

2339
// Act
24-
var name = storage.Object.GetUri(expectedName).ToString();
40+
var name = storage.Object.GetName(uri).ToString();
2541

2642
// Assert
27-
Assert.EndsWith(expectedName, name);
43+
Assert.Equal(expectedName, name);
2844
}
2945

3046
[Theory]
@@ -39,10 +55,113 @@ public void GetName_UnicodeUri_ReturnsCorrectName(string uriString, string expec
3955
var uri = new Uri(uriString);
4056

4157
// Act
42-
var name = storage.Object.GetUri(expectedName).ToString();
58+
var name = storage.Object.GetName(uri).ToString();
59+
60+
// Assert
61+
Assert.Equal(expectedName, name);
62+
}
63+
64+
[Theory]
65+
[InlineData(
66+
"https://contoso.blob.core.windows.net/v3-flatcontainer/package123/",
67+
"https://contoso.blob.core.windows.net/v3-flatcontainer/package123/2.1.0/package123.nuspec",
68+
"2.1.0/package123.nuspec")]
69+
[InlineData(
70+
"https://contoso.blob.core.windows.net/v3-flatcontainer/пакет123/",
71+
"https://contoso.blob.core.windows.net/v3-flatcontainer/пакет123/2.1.0/пакет123.nuspec",
72+
"2.1.0/пакет123.nuspec")]
73+
[InlineData(
74+
"https://contoso.blob.core.windows.net/v3-flatcontainer/%D0%BF%D0%B0%D0%BA%D0%B5%D1%82123/",
75+
"https://contoso.blob.core.windows.net/v3-flatcontainer/%D0%BF%D0%B0%D0%BA%D0%B5%D1%82123/2.1.0/%D0%BF%D0%B0%D0%BA%D0%B5%D1%82123.nuspec",
76+
"2.1.0/пакет123.nuspec")]
77+
[InlineData(
78+
"https://contoso.blob.core.windows.net/v3-flatcontainer/пакет123/",
79+
"https://contoso.blob.core.windows.net/v3-flatcontainer/%D0%BF%D0%B0%D0%BA%D0%B5%D1%82123/2.1.0/%D0%BF%D0%B0%D0%BA%D0%B5%D1%82123.nuspec",
80+
"2.1.0/пакет123.nuspec")]
81+
[InlineData(
82+
"https://contoso.blob.core.windows.net/v3-flatcontainer/%D0%BF%D0%B0%D0%BA%D0%B5%D1%82123/",
83+
"https://contoso.blob.core.windows.net/v3-flatcontainer/пакет123/2.1.0/%D0%BF%D0%B0%D0%BA%D0%B5%D1%82123.nuspec",
84+
"2.1.0/пакет123.nuspec")]
85+
[InlineData(
86+
"https://contoso.blob.core.windows.net/v3-flatcontainer/%D0%BF%D0%B0%D0%BA%D0%B5%D1%82123/",
87+
"https://contoso.blob.core.windows.net/v3-flatcontainer/пакет123/2.1.0/пакет123.nuspec",
88+
"2.1.0/пакет123.nuspec")]
89+
[InlineData(
90+
"https://contoso.blob.core.windows.net/v3-flatcontainer/пакет123/",
91+
"https://contoso.blob.core.windows.net/v3-flatcontainer/%D0%BF%D0%B0%D0%BA%D0%B5%D1%82123/2.1.0/пакет123.nuspec",
92+
"2.1.0/пакет123.nuspec")]
93+
public void GetName_EncodedAndDecodedUnicodeUris_ReturnsCorrectName(string baseAddressString, string uriString, string expectedName)
94+
{
95+
// Arrange
96+
Uri baseAddress = new Uri(baseAddressString);
97+
var storage = new Mock<Storage>(baseAddress) { CallBase = true };
98+
var uri = new Uri(uriString);
99+
100+
// Act
101+
var name = storage.Object.GetName(uri).ToString();
102+
103+
// Assert
104+
Assert.Equal(expectedName, name);
105+
}
106+
107+
108+
[Theory]
109+
[InlineData(
110+
"https://contoso.blob.core.windows.net/v3-flatcontainer/package123/",
111+
"https://contoso.blob.core.windows.net/v3-flatcontainer/package123/2.1.0/package123.nuspec?sv=2019-12-12&ss=b&srt=co&sp=rlx&se=2023-01-01T00:00:00Z&st=2022-01-01T00:00:00Z&spr=https&sig=abcdef123456",
112+
"2.1.0/package123.nuspec")]
113+
[InlineData(
114+
"https://contoso.blob.core.windows.net/v3-flatcontainer/пакет123/",
115+
"https://contoso.blob.core.windows.net/v3-flatcontainer/пакет123/2.1.0/пакет123.nuspec?sv=2019-12-12&ss=b&srt=co&sp=rlx&se=2023-01-01T00:00:00Z&st=2022-01-01T00:00:00Z&spr=https&sig=abcdef123456",
116+
"2.1.0/пакет123.nuspec")]
117+
public void GetName_UriWithSasToken_RemovesSasTokenAndReturnsCorrectName(string baseAddressString, string uriString, string expectedName)
118+
{
119+
// Arrange
120+
Uri baseAddress = new Uri(baseAddressString);
121+
var storage = new Mock<Storage>(baseAddress) { CallBase = true };
122+
var uri = new Uri(uriString);
123+
124+
// Act
125+
var name = storage.Object.GetName(uri).ToString();
126+
127+
// Assert
128+
Assert.Equal(expectedName, name);
129+
}
130+
131+
[Theory]
132+
[InlineData("http://contoso.blob.core.windows.net/packages/", "https://contoso.blob.core.windows.net/packages/package.1.0.0.nupkg", "package.1.0.0.nupkg")]
133+
[InlineData("https://contoso.blob.core.windows.net/packages/", "http://contoso.blob.core.windows.net/packages/package.1.0.0.nupkg", "package.1.0.0.nupkg")]
134+
public void GetName_DifferentSchemes_HandlesSchemeDifferenceCorrectly(string baseAddressString, string uriString, string expectedName)
135+
{
136+
// Arrange
137+
Uri baseAddress = new Uri(baseAddressString);
138+
var storage = new Mock<Storage>(baseAddress) { CallBase = true };
139+
var uri = new Uri(uriString);
140+
141+
// Act
142+
var name = storage.Object.GetName(uri).ToString();
143+
144+
// Assert
145+
Assert.Equal(expectedName, name);
146+
}
147+
148+
[Theory]
149+
[InlineData("https://contoso.blob.core.windows.net/v3-flatcontainer/пакет.2.0.0.nupkg#/metadata/core-properties", "пакет.2.0.0.nupkg")]
150+
[InlineData("https://contoso.blob.core.windows.net/v3-flatcontainer/пакет.2.0.0.nupkg#/metadata/core-properties/", "пакет.2.0.0.nupkg")]
151+
[InlineData("https://contoso.blob.core.windows.net/v3-flatcontainer/package123.2.0.0.nupkg#/metadata/core-properties", "package123.2.0.0.nupkg")]
152+
[InlineData("https://contoso.blob.core.windows.net/v3-flatcontainer/package123.2.0.0.nupkg#/metadata/core-properties/", "package123.2.0.0.nupkg")]
153+
public void GetName_UriWithFragment_RemovesFragmentAndReturnsCorrectName(string uriString, string expectedName)
154+
{
155+
// Arrange
156+
Uri baseAddress = new Uri("https://contoso.blob.core.windows.net/v3-flatcontainer/");
157+
var storage = new Mock<Storage>(baseAddress) { CallBase = true };
158+
var uri = new Uri(uriString);
159+
160+
// Act
161+
var name = storage.Object.GetName(uri).ToString();
43162

44163
// Assert
45-
Assert.EndsWith(expectedName, name);
164+
Assert.Equal(expectedName, name);
46165
}
47166
}
48167
}

0 commit comments

Comments
 (0)