Skip to content

Commit ba9c5eb

Browse files
JSON to be delivered in HTML must not escape ampersand (#812)
* JSON to be delivered in HTML must not escape ampersand, it is escaped later in HtmlEncodeInputValue. * Do not delimit encoded json at HtmlEncodeJsonValue. It is already delimited outside HtmlEncodeJsonValue use.
1 parent be1d271 commit ba9c5eb

File tree

3 files changed

+93
-2
lines changed

3 files changed

+93
-2
lines changed

dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
using System.Text.RegularExpressions;
2828
using Microsoft.Net.Http.Headers;
2929
using System.Net.Http;
30-
using System.Linq;
30+
using System.Globalization;
3131

3232
namespace GeneXus.Http
3333
{
@@ -451,6 +451,83 @@ internal static void AllowHeader(HttpContext httpContext, List<string> methods)
451451
{
452452
httpContext.Response.AppendHeader(HeaderNames.Allow, string.Join(",", methods));
453453
}
454+
internal static string HtmlEncodeJsonValue(string value)
455+
{
456+
return GXUtil.HtmlEncodeInputValue(JsonQuote(value));
457+
}
458+
459+
static void AppendCharAsUnicodeJavaScript(StringBuilder builder, char c)
460+
{
461+
builder.Append("\\u");
462+
int num = c;
463+
builder.Append(num.ToString("x4", CultureInfo.InvariantCulture));
464+
}
465+
/**
466+
* Produce a string in double quotes with backslash sequences in all the
467+
* right places. A backslash will be inserted within </, allowing JSON
468+
* text to be delivered in HTML. In JSON text, a string cannot contain a
469+
* control character or an unescaped quote or backslash.
470+
* */
471+
internal static string JsonQuote(string value, bool addDoubleQuotes=false)
472+
{
473+
string text = string.Empty;
474+
if (!string.IsNullOrEmpty(value))
475+
{
476+
int i;
477+
int len = value.Length;
478+
StringBuilder sb = new StringBuilder(len + 4);
479+
480+
for (i = 0; i < len; i += 1)
481+
{
482+
char c = value[i];
483+
switch (c)
484+
{
485+
case '\\':
486+
case '"':
487+
sb.Append('\\');
488+
sb.Append(c);
489+
break;
490+
case '\b':
491+
sb.Append("\\b");
492+
break;
493+
case '\t':
494+
sb.Append("\\t");
495+
break;
496+
case '\n':
497+
sb.Append("\\n");
498+
break;
499+
case '\f':
500+
sb.Append("\\f");
501+
break;
502+
case '\r':
503+
sb.Append("\\r");
504+
break;
505+
default:
506+
{
507+
if (c < ' ')
508+
{
509+
AppendCharAsUnicodeJavaScript(sb, c);
510+
}
511+
else
512+
{
513+
sb.Append(c);
514+
}
515+
}
516+
break;
517+
}
518+
}
519+
text = sb.ToString();
520+
}
521+
if (!addDoubleQuotes)
522+
{
523+
return text;
524+
}
525+
else
526+
{
527+
return "\"" + text + "\"";
528+
}
529+
}
530+
454531
}
455532
#if NETCORE
456533
public class HttpCookieCollection : Dictionary<string, HttpCookie>

dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1585,7 +1585,7 @@ protected void SendState()
15851585
context.httpAjaxContext.AddStylesHidden();
15861586
if (IsSpaRequest())
15871587
{
1588-
context.WriteHtmlTextNl("<script>gx.ajax.saveJsonResponse('" + GXUtil.HtmlEncodeInputValue(HttpUtility.JavaScriptStringEncode(context.getJSONResponse())) + "');</script>");
1588+
context.WriteHtmlTextNl("<script>gx.ajax.saveJsonResponse('" + HttpHelper.HtmlEncodeJsonValue(context.getJSONResponse()) + "');</script>");
15891589
}
15901590
else
15911591
{

dotnet/test/DotNetCoreUnitTest/HttpUtils/HttpUtils.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
2+
using System.Web;
23
using GeneXus.Application;
4+
using GeneXus.Http;
35
using GeneXus.Utils;
46
using Xunit;
57

@@ -62,5 +64,17 @@ private static void DoTest(string contentDisposition, string expectedContentDisp
6264

6365
Assert.Equal(expectedContentDisposition, encodedValue);
6466
}
67+
68+
[Fact]
69+
public void TestDoNotDoubleEncodeAmpersand()
70+
{
71+
string state = "{\"gxProps\":[\"FORM\":{\"Class\":\"form-horizontal Form\"}}], \"gxHiddens\":{\"gxhash_vA\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\"," +
72+
"\"hsh\":\"C/CAcgMV0JZC/+o3ikT+R2Hhb1LcQ==\",\"Z3c\":\"&#039;\"}]}}";
73+
74+
string jsonEncoded = HttpHelper.HtmlEncodeJsonValue(state);
75+
Assert.Contains("&amp;", jsonEncoded, StringComparison.OrdinalIgnoreCase);
76+
Assert.StartsWith("{", jsonEncoded, StringComparison.OrdinalIgnoreCase);
77+
}
78+
6579
}
6680
}

0 commit comments

Comments
 (0)