Skip to content

Commit ea380fc

Browse files
JSON to be delivered in HTML must not escape ampersand, it is escaped later in HtmlEncodeInputValue.
1 parent ec80c83 commit ea380fc

File tree

3 files changed

+91
-2
lines changed

3 files changed

+91
-2
lines changed

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

Lines changed: 77 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,82 @@ 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)
472+
{
473+
474+
if (value == null || value.Length == 0)
475+
{
476+
return "\"\"";
477+
}
478+
479+
char b;
480+
char c = (char)0;
481+
int i;
482+
int len = value.Length;
483+
StringBuilder sb = new StringBuilder(len + 4);
484+
485+
sb.Append('"');
486+
for (i = 0; i < len; i += 1)
487+
{
488+
b = c;
489+
c = value[i];
490+
switch (c)
491+
{
492+
case '\\':
493+
case '"':
494+
sb.Append('\\');
495+
sb.Append(c);
496+
break;
497+
case '\b':
498+
sb.Append("\\b");
499+
break;
500+
case '\t':
501+
sb.Append("\\t");
502+
break;
503+
case '\n':
504+
sb.Append("\\n");
505+
break;
506+
case '\f':
507+
sb.Append("\\f");
508+
break;
509+
case '\r':
510+
sb.Append("\\r");
511+
break;
512+
default:
513+
{
514+
if (c < ' ')
515+
{
516+
AppendCharAsUnicodeJavaScript(sb, c);
517+
}
518+
else
519+
{
520+
sb.Append(c);
521+
}
522+
}
523+
break;
524+
}
525+
}
526+
sb.Append('"');
527+
return sb.ToString();
528+
}
529+
454530
}
455531
#if NETCORE
456532
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: 13 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,16 @@ 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+
}
77+
6578
}
6679
}

0 commit comments

Comments
 (0)