Skip to content

Commit d4a488b

Browse files
Upgraded parsing (#10564)
1 parent 997f9b4 commit d4a488b

2 files changed

Lines changed: 98 additions & 66 deletions

File tree

src/NuGetGallery/Infrastructure/CookieTempDataProvider.cs

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
55
using System.Collections.Generic;
66
using System.Globalization;
77
using System.Net;
8+
using System.Text;
89
using System.Web;
910
using System.Web.Mvc;
11+
using System.Web.Security;
12+
using Newtonsoft.Json;
1013

1114
namespace NuGetGallery
1215
{
@@ -71,52 +74,49 @@ protected virtual IDictionary<string, object> LoadTempData(ControllerContext con
7174
return dictionary;
7275
}
7376

74-
foreach (var key in cookie.Values.AllKeys)
77+
try
7578
{
76-
// As the index setter on HttpCookie does not guard against null keys,
77-
// we should guard against ArgumentNullException on Dictionary.Insert
78-
// when key == null.
79-
if (key == null)
79+
// Unprotect (decrypt/verify) the cookie value
80+
byte[] unprotectedBytes = MachineKey.Unprotect(
81+
Convert.FromBase64String(cookie.Value), TempDataCookieKey);
82+
string json = Encoding.UTF8.GetString(unprotectedBytes);
83+
84+
// Deserialize back to dictionary
85+
var deserialized = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
86+
if (deserialized != null)
8087
{
81-
continue;
88+
foreach (var kvp in deserialized)
89+
dictionary[kvp.Key] = kvp.Value;
8290
}
83-
84-
dictionary[key] = HttpUtility.UrlDecode(cookie[key]);
91+
}
92+
catch
93+
{
94+
//silently ignore incorrect cookie values
8595
}
8696

8797
cookie.Expires = DateTime.MinValue;
8898
cookie.Value = String.Empty;
89-
if (CookieHasTempData)
90-
{
91-
cookie.Expires = DateTime.MinValue;
92-
cookie.Value = String.Empty;
93-
}
9499
return dictionary;
95100
}
96101

97102
protected virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
98103
{
99104
if (values.Count > 0)
100105
{
101-
var cookie = new HttpCookie(TempDataCookieKey);
102-
cookie.HttpOnly = true;
103-
cookie.Secure = true;
104-
foreach (var item in values)
106+
// Serialize dictionary to JSON
107+
string json = JsonConvert.SerializeObject(values);
108+
109+
// Protect (encrypt/sign) the JSON
110+
string protectedJson = Convert.ToBase64String(
111+
MachineKey.Protect(Encoding.UTF8.GetBytes(json), TempDataCookieKey));
112+
113+
var cookie = new HttpCookie(TempDataCookieKey, protectedJson)
105114
{
106-
// As the index setter on HttpCookie does not guard against null keys,
107-
// we should guard against ArgumentNullException on Dictionary.Insert
108-
// when key == null.
109-
if (item.Key == null)
110-
{
111-
continue;
112-
}
113-
114-
cookie[item.Key] = HttpUtility.UrlEncode(Convert.ToString(item.Value, CultureInfo.InvariantCulture));
115-
}
115+
HttpOnly = true,
116+
Secure = true
117+
};
116118
_httpContext.Response.Cookies.Add(cookie);
117119
}
118120
}
119-
120-
// Properties
121121
}
122-
}
122+
}

tests/NuGetGallery.Facts/Infrastructure/CookieTempDataProviderFacts.cs

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33
using System;
44
using System.Collections.Generic;
5+
using System.Text;
56
using System.Web;
67
using System.Web.Mvc;
8+
using System.Web.Security;
79
using Moq;
10+
using Newtonsoft.Json;
811
using Xunit;
912

1013
namespace NuGetGallery.Infrastructure
@@ -17,46 +20,69 @@ public class TheLoadTempDataMethod
1720
public void RetrievesValuesFromCookie()
1821
{
1922
var cookies = new HttpCookieCollection();
20-
var cookie = new HttpCookie("__Controller::TempData");
21-
cookie.HttpOnly = true;
23+
var tempData = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
24+
{
25+
{ "message", "Say hello to my little friend" },
26+
{ "question", "How am I funny?" }
27+
};
28+
29+
// Serialize and protect the tempData dictionary
30+
var json = JsonConvert.SerializeObject(tempData);
31+
var protectedBytes = MachineKey.Protect(Encoding.UTF8.GetBytes(json), "__Controller::TempData");
32+
var cookie = new HttpCookie("__Controller::TempData")
33+
{
34+
HttpOnly = true,
35+
Secure = true,
36+
Value = Convert.ToBase64String(protectedBytes)
37+
};
2238
cookies.Add(cookie);
23-
cookie["message"] = "Say hello to my little friend";
24-
cookie["question"] = "How am I funny?";
39+
2540
var httpContext = new Mock<HttpContextBase>();
2641
httpContext.Setup(c => c.Request.Cookies).Returns(cookies);
2742
ITempDataProvider provider = new CookieTempDataProvider(httpContext.Object);
2843
var controllerContext = new ControllerContext();
2944

30-
var tempData = provider.LoadTempData(controllerContext);
45+
var loadedTempData = provider.LoadTempData(controllerContext);
3146

32-
Assert.Equal(2, tempData.Count);
33-
Assert.Equal("Say hello to my little friend", tempData["message"]);
34-
Assert.Equal("How am I funny?", tempData["question"]);
35-
Assert.Equal("How am I funny?", tempData["QUESTION"]);
47+
Assert.Equal(2, loadedTempData.Count);
48+
Assert.Equal("Say hello to my little friend", loadedTempData["message"]);
49+
Assert.Equal("How am I funny?", loadedTempData["question"]);
50+
Assert.Equal("How am I funny?", loadedTempData["QUESTION"]);
3651
}
3752

3853

3954
[Fact]
4055
public void DoesNotThrowWhenKeyValuesFromCookieContainsNullKey()
4156
{
4257
var cookies = new HttpCookieCollection();
43-
var cookie = new HttpCookie("__Controller::TempData");
44-
cookie.HttpOnly = true;
58+
var tempData = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
59+
{
60+
{ "message", "Say hello to my little friend" },
61+
{ "question", "How am I funny?" }
62+
};
63+
64+
// Serialize and protect the tempData dictionary
65+
var json = JsonConvert.SerializeObject(tempData);
66+
var protectedBytes = MachineKey.Protect(Encoding.UTF8.GetBytes(json), "__Controller::TempData");
67+
var cookie = new HttpCookie("__Controller::TempData")
68+
{
69+
HttpOnly = true,
70+
Secure = true,
71+
Value = Convert.ToBase64String(protectedBytes)
72+
};
4573
cookies.Add(cookie);
46-
cookie["message"] = "Say hello to my little friend";
47-
cookie["question"] = "How am I funny?";
48-
cookie[null] = "This should be ignored.";
74+
4975
var httpContext = new Mock<HttpContextBase>();
5076
httpContext.Setup(c => c.Request.Cookies).Returns(cookies);
5177
ITempDataProvider provider = new CookieTempDataProvider(httpContext.Object);
5278
var controllerContext = new ControllerContext();
5379

54-
var tempData = provider.LoadTempData(controllerContext);
80+
var loadedTempData = provider.LoadTempData(controllerContext);
5581

56-
Assert.Equal(2, tempData.Count);
57-
Assert.Equal("Say hello to my little friend", tempData["message"]);
58-
Assert.Equal("How am I funny?", tempData["question"]);
59-
Assert.Equal("How am I funny?", tempData["QUESTION"]);
82+
Assert.Equal(2, loadedTempData.Count);
83+
Assert.Equal("Say hello to my little friend", loadedTempData["message"]);
84+
Assert.Equal("How am I funny?", loadedTempData["question"]);
85+
Assert.Equal("How am I funny?", loadedTempData["QUESTION"]);
6086
}
6187

6288
[Fact]
@@ -102,22 +128,28 @@ public void StoresValuesInCookieInEncodedFormat()
102128
ITempDataProvider provider = new CookieTempDataProvider(httpContext.Object);
103129
var controllerContext = new ControllerContext();
104130

105-
provider.SaveTempData(
106-
controllerContext,
107-
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
108-
{
109-
{ "message", "Say hello to my little friend" },
110-
{ "key2", 123 },
111-
{ "key3", "dumb&dumber?:;,isit" }
112-
});
131+
var tempData = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
132+
{
133+
{ "message", "Say hello to my little friend" },
134+
{ "key2", 123 },
135+
{ "key3", "dumb&dumber?:;,isit" }
136+
};
137+
provider.SaveTempData(controllerContext, tempData);
113138

114139
Assert.Single(cookies);
115-
Assert.True(cookies[0].HttpOnly);
116-
Assert.True(cookies[0].Secure);
117-
Assert.Equal(3, cookies[0].Values.Count);
118-
Assert.Equal(HttpUtility.UrlEncode("Say hello to my little friend"), cookies[0]["message"]);
119-
Assert.Equal(HttpUtility.UrlEncode("123"), cookies[0]["key2"]);
120-
Assert.Equal(HttpUtility.UrlEncode("dumb&dumber?:;,isit"), cookies[0]["key3"]);
140+
var cookie = cookies["__Controller::TempData"];
141+
Assert.True(cookie.HttpOnly);
142+
Assert.True(cookie.Secure);
143+
144+
// Decrypt and deserialize the cookie value
145+
var unprotectedBytes = MachineKey.Unprotect(Convert.FromBase64String(cookie.Value), "__Controller::TempData");
146+
var json = Encoding.UTF8.GetString(unprotectedBytes);
147+
var deserialized = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
148+
149+
Assert.Equal(3, deserialized.Count);
150+
Assert.Equal("Say hello to my little friend", deserialized["message"]);
151+
Assert.Equal(123, Convert.ToInt32(deserialized["key2"]));
152+
Assert.Equal("dumb&dumber?:;,isit", deserialized["key3"]);
121153
}
122154

123155
[Fact]
@@ -160,4 +192,4 @@ public void WithInitialStateAndNoValuesClearsCookie()
160192
}
161193
}
162194
}
163-
}
195+
}

0 commit comments

Comments
 (0)