Skip to content

Commit 9aba862

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. (cherry picked from commit ba9c5eb) # Conflicts: # dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs # dotnet/test/DotNetCoreUnitTest/HttpUtils/HttpUtils.cs
1 parent 2e77072 commit 9aba862

File tree

3 files changed

+106
-2
lines changed

3 files changed

+106
-2
lines changed

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

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
using System.Runtime.Serialization;
2929
using GeneXus.Mime;
3030
using System.Text.RegularExpressions;
31+
using System.Globalization;
3132

3233
namespace GeneXus.Http
3334
{
@@ -82,6 +83,8 @@ public static void SetResponseStatus(HttpContext httpContext, string statusCode,
8283
HttpStatusCode httpStatusCode = MapStatusCode(statusCode);
8384
SetResponseStatus(httpContext, httpStatusCode, statusDescription);
8485
}
86+
87+
8588
public static void SetResponseStatus(HttpContext httpContext, HttpStatusCode httpStatusCode, string statusDescription)
8689
{
8790
#if !NETCORE
@@ -339,6 +342,82 @@ public static string[] GetParameterValues(string query)
339342
return query.Split(',');
340343
}
341344
}
345+
internal static string HtmlEncodeJsonValue(string value)
346+
{
347+
return GXUtil.HtmlEncodeInputValue(JsonQuote(value));
348+
}
349+
350+
static void AppendCharAsUnicodeJavaScript(StringBuilder builder, char c)
351+
{
352+
builder.Append("\\u");
353+
int num = c;
354+
builder.Append(num.ToString("x4", CultureInfo.InvariantCulture));
355+
}
356+
/**
357+
* Produce a string in double quotes with backslash sequences in all the
358+
* right places. A backslash will be inserted within </, allowing JSON
359+
* text to be delivered in HTML. In JSON text, a string cannot contain a
360+
* control character or an unescaped quote or backslash.
361+
* */
362+
internal static string JsonQuote(string value, bool addDoubleQuotes=false)
363+
{
364+
string text = string.Empty;
365+
if (!string.IsNullOrEmpty(value))
366+
{
367+
int i;
368+
int len = value.Length;
369+
StringBuilder sb = new StringBuilder(len + 4);
370+
371+
for (i = 0; i < len; i += 1)
372+
{
373+
char c = value[i];
374+
switch (c)
375+
{
376+
case '\\':
377+
case '"':
378+
sb.Append('\\');
379+
sb.Append(c);
380+
break;
381+
case '\b':
382+
sb.Append("\\b");
383+
break;
384+
case '\t':
385+
sb.Append("\\t");
386+
break;
387+
case '\n':
388+
sb.Append("\\n");
389+
break;
390+
case '\f':
391+
sb.Append("\\f");
392+
break;
393+
case '\r':
394+
sb.Append("\\r");
395+
break;
396+
default:
397+
{
398+
if (c < ' ')
399+
{
400+
AppendCharAsUnicodeJavaScript(sb, c);
401+
}
402+
else
403+
{
404+
sb.Append(c);
405+
}
406+
}
407+
break;
408+
}
409+
}
410+
text = sb.ToString();
411+
}
412+
if (!addDoubleQuotes)
413+
{
414+
return text;
415+
}
416+
else
417+
{
418+
return "\"" + text + "\"";
419+
}
420+
}
342421

343422
}
344423
#if NETCORE
@@ -467,7 +546,7 @@ public static void AddHeader(this HttpResponse response, string name, string val
467546

468547
public static void Write(this HttpResponse response, string value)
469548
{
470-
//response.WriteAsync(value).Wait();
549+
//response.WriteAsync(value).Wait();//Unsupported by GxHttpAzureResponse
471550
response.Body.Write(Encoding.UTF8.GetBytes(value));
472551
}
473552
public static void WriteFile(this HttpResponse response, string fileName)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1522,7 +1522,7 @@ protected void SendState()
15221522
context.httpAjaxContext.AddStylesHidden();
15231523
if (IsSpaRequest())
15241524
{
1525-
context.WriteHtmlTextNl("<script>gx.ajax.saveJsonResponse('" + GXUtil.HtmlEncodeInputValue(HttpUtility.JavaScriptStringEncode(context.getJSONResponse())) + "');</script>");
1525+
context.WriteHtmlTextNl("<script>gx.ajax.saveJsonResponse('" + HttpHelper.HtmlEncodeJsonValue(context.getJSONResponse()) + "');</script>");
15261526
}
15271527
else
15281528
{
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Web;
3+
using GeneXus.Application;
4+
using GeneXus.Http;
5+
using GeneXus.Utils;
6+
using Xunit;
7+
8+
namespace DotNetCoreUnitTest.HttpUtils
9+
{
10+
public class TestHttpUtils
11+
{
12+
13+
[Fact]
14+
public void TestDoNotDoubleEncodeAmpersand()
15+
{
16+
string state = "{\"gxProps\":[\"FORM\":{\"Class\":\"form-horizontal Form\"}}], \"gxHiddens\":{\"gxhash_vA\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\"," +
17+
"\"hsh\":\"C/CAcgMV0JZC/+o3ikT+R2Hhb1LcQ==\",\"Z3c\":\"&#039;\"}]}}";
18+
19+
string jsonEncoded = HttpHelper.HtmlEncodeJsonValue(state);
20+
Assert.Contains("&amp;", jsonEncoded, StringComparison.OrdinalIgnoreCase);
21+
Assert.StartsWith("{", jsonEncoded, StringComparison.OrdinalIgnoreCase);
22+
}
23+
24+
}
25+
}

0 commit comments

Comments
 (0)