Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit fe2b673

Browse files
author
N. Taylor Mullen
committed
Update JsonHelper to escape HTML.
- This functionality can be disabled by setting the `Switch.Microsoft.AspNetCore.Mvc.AllowJsonHtml` switch in an app.config. - Updated tests to react to this new behavior. - Exposed a new `PublicSerializerSettings` property on `JsonOutputFormatter` so we can accurately copy settings from the used output formatter in `JsonHelper`.
1 parent eec16ff commit fe2b673

File tree

5 files changed

+150
-8
lines changed

5 files changed

+150
-8
lines changed

src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonOutputFormatter.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Buffers;
6+
using System.ComponentModel;
67
using System.IO;
78
using System.Text;
89
using System.Threading.Tasks;
@@ -61,6 +62,16 @@ public JsonOutputFormatter(JsonSerializerSettings serializerSettings, ArrayPool<
6162
/// </remarks>
6263
protected JsonSerializerSettings SerializerSettings { get; }
6364

65+
/// <summary>
66+
/// Gets the <see cref="JsonSerializerSettings"/> used to configure the <see cref="JsonSerializer"/>.
67+
/// </summary>
68+
/// <remarks>
69+
/// Any modifications to the <see cref="JsonSerializerSettings"/> object after this
70+
/// <see cref="JsonOutputFormatter"/> has been used will have no effect.
71+
/// </remarks>
72+
[EditorBrowsable(EditorBrowsableState.Never)]
73+
public JsonSerializerSettings PublicSerializerSettings => SerializerSettings;
74+
6475
/// <summary>
6576
/// Writes the given <paramref name="value"/> as JSON using the given
6677
/// <paramref name="writer"/>.

src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/JsonHelper.cs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@
33

44
using System;
55
using System.Buffers;
6+
#if NET451
7+
using System.Configuration;
8+
#endif
69
using System.Globalization;
710
using System.IO;
11+
#if NET451
12+
using System.Linq;
13+
#endif
814
using Microsoft.AspNetCore.Html;
915
using Microsoft.AspNetCore.Mvc.Formatters;
1016
using Microsoft.AspNetCore.Mvc.Rendering;
@@ -17,6 +23,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
1723
/// </summary>
1824
public class JsonHelper : IJsonHelper
1925
{
26+
private const string AllowJsonHtml = "Switch.Microsoft.AspNetCore.Mvc.AllowJsonHtml";
2027
private readonly JsonOutputFormatter _jsonOutputFormatter;
2128
private readonly ArrayPool<char> _charPool;
2229

@@ -46,7 +53,23 @@ public JsonHelper(JsonOutputFormatter jsonOutputFormatter, ArrayPool<char> charP
4653
/// <inheritdoc />
4754
public IHtmlContent Serialize(object value)
4855
{
49-
return SerializeInternal(_jsonOutputFormatter, value);
56+
var allowJsonHtml = false;
57+
#if NET451
58+
var switchValue = ConfigurationManager.AppSettings.GetValues(AllowJsonHtml)?.FirstOrDefault();
59+
bool.TryParse(switchValue, out allowJsonHtml);
60+
#else
61+
AppContext.TryGetSwitch(AllowJsonHtml, out allowJsonHtml);
62+
#endif
63+
64+
if (allowJsonHtml)
65+
{
66+
return SerializeInternal(_jsonOutputFormatter, value);
67+
}
68+
69+
var settings = ShallowCopy(_jsonOutputFormatter.PublicSerializerSettings);
70+
settings.StringEscapeHandling = StringEscapeHandling.EscapeHtml;
71+
72+
return Serialize(value, settings);
5073
}
5174

5275
/// <inheritdoc />
@@ -69,5 +92,43 @@ private IHtmlContent SerializeInternal(JsonOutputFormatter jsonOutputFormatter,
6992

7093
return new HtmlString(stringWriter.ToString());
7194
}
95+
96+
private static JsonSerializerSettings ShallowCopy(JsonSerializerSettings settings)
97+
{
98+
var copiedSettings = new JsonSerializerSettings
99+
{
100+
Binder = settings.Binder,
101+
CheckAdditionalContent = settings.CheckAdditionalContent,
102+
ConstructorHandling = settings.ConstructorHandling,
103+
Context = settings.Context,
104+
ContractResolver = settings.ContractResolver,
105+
Converters = settings.Converters,
106+
Culture = settings.Culture,
107+
DateFormatHandling = settings.DateFormatHandling,
108+
DateFormatString = settings.DateFormatString,
109+
DateParseHandling = settings.DateParseHandling,
110+
DateTimeZoneHandling = settings.DateTimeZoneHandling,
111+
DefaultValueHandling = settings.DefaultValueHandling,
112+
EqualityComparer = settings.EqualityComparer,
113+
Error = settings.Error,
114+
FloatFormatHandling = settings.FloatFormatHandling,
115+
FloatParseHandling = settings.FloatParseHandling,
116+
Formatting = settings.Formatting,
117+
MaxDepth = settings.MaxDepth,
118+
MetadataPropertyHandling = settings.MetadataPropertyHandling,
119+
MissingMemberHandling = settings.MissingMemberHandling,
120+
NullValueHandling = settings.NullValueHandling,
121+
ObjectCreationHandling = settings.ObjectCreationHandling,
122+
PreserveReferencesHandling = settings.PreserveReferencesHandling,
123+
ReferenceLoopHandling = settings.ReferenceLoopHandling,
124+
ReferenceResolverProvider = settings.ReferenceResolverProvider,
125+
StringEscapeHandling = settings.StringEscapeHandling,
126+
TraceWriter = settings.TraceWriter,
127+
TypeNameAssemblyFormat = settings.TypeNameAssemblyFormat,
128+
TypeNameHandling = settings.TypeNameHandling,
129+
};
130+
131+
return copiedSettings;
132+
}
72133
}
73134
}

src/Microsoft.AspNetCore.Mvc.ViewFeatures/project.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@
5757
"System.Buffers": "4.3.0"
5858
},
5959
"frameworks": {
60-
"net451": {},
60+
"net451": {
61+
"frameworkAssemblies": {
62+
"System.Configuration": ""
63+
}
64+
},
6165
"netstandard1.6": {
6266
"dependencies": {
6367
"System.Runtime.Serialization.Primitives": "4.3.0"

test/Microsoft.AspNetCore.Mvc.FunctionalTests/BasicTests.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -221,12 +221,10 @@ public async Task ActionWithRequireHttps_AllowsHttpsRequests(string method)
221221
public async Task JsonHelper_RendersJson_WithCamelCaseNames()
222222
{
223223
// Arrange
224-
var json = "{\"id\":9000,\"fullName\":\"John <b>Smith</b>\"}";
225-
var expectedBody = string.Format(
226-
@"<script type=""text/javascript"">
227-
var json = {0};
228-
</script>",
229-
json);
224+
var expectedBody =
225+
@"<script type=""text/javascript"">
226+
var json = {""id"":9000,""fullName"":""John \u003cb\u003eSmith\u003c/b\u003e""};
227+
</script>";
230228

231229
// Act
232230
var response = await Client.GetAsync("Home/JsonHelperInView");
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Buffers;
5+
using Microsoft.AspNetCore.Html;
6+
using Microsoft.AspNetCore.Mvc.Formatters;
7+
using Newtonsoft.Json;
8+
using Newtonsoft.Json.Serialization;
9+
using Xunit;
10+
11+
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
12+
{
13+
public class JsonHelperTest
14+
{
15+
[Fact]
16+
public void Serialize_EscapesHtmlByDefault()
17+
{
18+
// Arrange
19+
var settings = new JsonSerializerSettings()
20+
{
21+
StringEscapeHandling = StringEscapeHandling.EscapeNonAscii,
22+
};
23+
var helper = new JsonHelper(
24+
new JsonOutputFormatter(settings, ArrayPool<char>.Shared),
25+
ArrayPool<char>.Shared);
26+
var obj = new
27+
{
28+
HTML = "<b>John Doe</b>"
29+
};
30+
var expectedOutput = "{\"HTML\":\"\\u003cb\\u003eJohn Doe\\u003c/b\\u003e\"}";
31+
32+
// Act
33+
var result = helper.Serialize(obj);
34+
35+
// Assert
36+
var htmlString = Assert.IsType<HtmlString>(result);
37+
Assert.Equal(expectedOutput, htmlString.ToString());
38+
}
39+
40+
[Fact]
41+
public void Serialize_MaintainsSettingsAndEscapesHtml()
42+
{
43+
// Arrange
44+
var settings = new JsonSerializerSettings()
45+
{
46+
ContractResolver = new DefaultContractResolver
47+
{
48+
NamingStrategy = new CamelCaseNamingStrategy(),
49+
},
50+
};
51+
var helper = new JsonHelper(
52+
new JsonOutputFormatter(settings, ArrayPool<char>.Shared),
53+
ArrayPool<char>.Shared);
54+
var obj = new
55+
{
56+
FullHtml = "<b>John Doe</b>"
57+
};
58+
var expectedOutput = "{\"fullHtml\":\"\\u003cb\\u003eJohn Doe\\u003c/b\\u003e\"}";
59+
60+
// Act
61+
var result = helper.Serialize(obj);
62+
63+
// Assert
64+
var htmlString = Assert.IsType<HtmlString>(result);
65+
Assert.Equal(expectedOutput, htmlString.ToString());
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)