From 77c700c5659740a3f8c0f0d2b13ea6c673919da2 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 2 Jun 2026 11:25:48 -0600 Subject: [PATCH 1/3] fix: add escape handling for byte arrays and strings in DeviceJsonApi --- .../Devices/DeviceJsonApi.cs | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs b/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs index c4ec2ca99..c01d5e815 100644 --- a/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs +++ b/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Text.RegularExpressions; using System.Threading.Tasks; namespace PepperDash.Essentials.Core @@ -176,7 +177,20 @@ private static object ConvertType(object value, Type conversionType) { if (!conversionType.IsEnum) { - return Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture); + if (conversionType == typeof(byte[]) && value is string byteString) + { + var unescaped = UnescapeString(byteString); + return System.Text.Encoding.GetEncoding(28591).GetBytes(unescaped); + } + + var converted = Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture); + + if (conversionType == typeof(string) && converted is string s) + { + return UnescapeString(s); + } + + return converted; } var stringValue = Convert.ToString(value); @@ -189,6 +203,32 @@ private static object ConvertType(object value, Type conversionType) return Enum.Parse(conversionType, stringValue, true); } + /// + /// Processes escape sequences in a string, converting sequences like \r, \n, \t, \xHH + /// to their corresponding non-printable ASCII characters. + /// + private static string UnescapeString(string input) + { + if (string.IsNullOrEmpty(input)) + return input; + + return Regex.Replace(input, @"\\(r|n|t|\\|x[0-9A-Fa-f]{2})", match => + { + var seq = match.Groups[1].Value; + switch (seq) + { + case "r": return "\r"; + case "n": return "\n"; + case "t": return "\t"; + case "\\": return "\\"; + default: + // \xHH hex escape + var hex = seq.Substring(1); + return ((char)Convert.ToInt32(hex, 16)).ToString(); + } + }); + } + /// /// Gets the properties on a device /// From 1ce86dab291814938b18d524fd41e6c47b28e4b0 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 2 Jun 2026 14:10:33 -0600 Subject: [PATCH 2/3] fix: add escape handling for port forwarding in DebugSessionRequestHandler --- .../DebugSessionRequestHandler.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs index 01e388344..56c983ae1 100644 --- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs +++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs @@ -57,6 +57,36 @@ protected override void HandleGet(Crestron.SimplSharp.WebScripting.HttpCwsContex // Start the WS Server Debug.WebsocketSink.StartServerAndSetPort(port); Debug.SetWebSocketMinimumDebugLevel(Serilog.Events.LogEventLevel.Verbose); + + // Attempt to forward the port to the CS LAN + try + { + var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType( + EthernetAdapterType.EthernetCSAdapter); + var csIp = CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId); + + var result = CrestronEthernetHelper.AddPortForwarding( + (ushort)port, (ushort)port, csIp, + CrestronEthernetHelper.ePortMapTransport.TCP); + + if (result != CrestronEthernetHelper.PortForwardingUserPatRetCodes.NoErr) + { + Debug.LogMessage(LogEventLevel.Warning, "Error adding port forwarding for debug websocket: {0}", result); + } + else + { + Debug.LogMessage(LogEventLevel.Information, "Port {0} forwarded to CS LAN for debug websocket", port); + } + } + catch (ArgumentException) + { + Debug.LogMessage(LogEventLevel.Debug, "This processor does not have a CS LAN adapter; skipping port forwarding"); + } + catch (Exception ex) + { + Debug.LogMessage(LogEventLevel.Warning, "Error automatically forwarding debug websocket port to CS LAN: {0}", ex.Message); + } } var url = Debug.WebsocketSink.Url; @@ -90,8 +120,40 @@ protected override void HandleGet(Crestron.SimplSharp.WebScripting.HttpCwsContex /// protected override void HandlePost(HttpCwsContext context) { + var port = Debug.WebsocketSink.Port; + Debug.WebsocketSink.StopServer(); + // Remove the port forwarding entry + try + { + var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType( + EthernetAdapterType.EthernetCSAdapter); + var csIp = CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId); + + var result = CrestronEthernetHelper.RemovePortForwarding( + (ushort)port, (ushort)port, csIp, + CrestronEthernetHelper.ePortMapTransport.TCP); + + if (result != CrestronEthernetHelper.PortForwardingUserPatRetCodes.NoErr) + { + Debug.LogMessage(LogEventLevel.Warning, "Error removing port forwarding for debug websocket: {0}", result); + } + else + { + Debug.LogMessage(LogEventLevel.Information, "Port forwarding for port {0} removed", port); + } + } + catch (ArgumentException) + { + Debug.LogMessage(LogEventLevel.Debug, "This processor does not have a CS LAN adapter; skipping port forwarding removal"); + } + catch (Exception ex) + { + Debug.LogMessage(LogEventLevel.Warning, "Error removing debug websocket port forwarding: {0}", ex.Message); + } + context.Response.StatusCode = 200; context.Response.StatusDescription = "OK"; context.Response.End(); From 5f26cb98fd4def73cafba659d908f60c4bc729cb Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Fri, 12 Jun 2026 11:28:39 -0600 Subject: [PATCH 3/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../DebugSessionRequestHandler.cs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs index 56c983ae1..1eeb27809 100644 --- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs +++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs @@ -132,17 +132,24 @@ protected override void HandlePost(HttpCwsContext context) var csIp = CrestronEthernetHelper.GetEthernetParameter( CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId); - var result = CrestronEthernetHelper.RemovePortForwarding( - (ushort)port, (ushort)port, csIp, - CrestronEthernetHelper.ePortMapTransport.TCP); - - if (result != CrestronEthernetHelper.PortForwardingUserPatRetCodes.NoErr) + if (port <= 0) { - Debug.LogMessage(LogEventLevel.Warning, "Error removing port forwarding for debug websocket: {0}", result); + Debug.LogMessage(LogEventLevel.Debug, "Debug websocket port is not set; skipping port forwarding removal"); } else { - Debug.LogMessage(LogEventLevel.Information, "Port forwarding for port {0} removed", port); + var result = CrestronEthernetHelper.RemovePortForwarding( + (ushort)port, (ushort)port, csIp, + CrestronEthernetHelper.ePortMapTransport.TCP); + + if (result != CrestronEthernetHelper.PortForwardingUserPatRetCodes.NoErr) + { + Debug.LogMessage(LogEventLevel.Warning, "Error removing port forwarding for debug websocket: {0}", result); + } + else + { + Debug.LogMessage(LogEventLevel.Information, "Port forwarding for port {0} removed", port); + } } } catch (ArgumentException)