Skip to content

Commit

Permalink
[dotnet] Tolerate invalid UTF-16 strings in DevTools JSON response (#…
Browse files Browse the repository at this point in the history
…14972)

Co-authored-by: Nikolay Borisenko <[email protected]>
  • Loading branch information
RenderMichael and nvborisenko authored Jan 10, 2025
1 parent 8740f13 commit c1bb977
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 3 deletions.
35 changes: 35 additions & 0 deletions dotnet/src/webdriver/DevTools/Json/DevToolsJsonOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// <copyright file="DevToolsJsonOptions.cs" company="Selenium Committers">
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// </copyright>

using System.Text.Json;

#nullable enable

namespace OpenQA.Selenium.DevTools.Json;

internal static class DevToolsJsonOptions
{
public static JsonSerializerOptions Default { get; } = new JsonSerializerOptions()
{
Converters =
{
new StringConverter(),
}
};
}
61 changes: 61 additions & 0 deletions dotnet/src/webdriver/DevTools/Json/StringConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// <copyright file="StringConverter.cs" company="Selenium Committers">
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// </copyright>

using System;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

#nullable enable

namespace OpenQA.Selenium.DevTools.Json;

internal sealed class StringConverter : JsonConverter<string>
{
public override bool HandleNull => true;

public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
try
{
return reader.GetString();
}
catch (InvalidOperationException)
{
// Fallback to read the value as bytes instead of string.
// System.Text.Json library throws exception when CDP remote end sends non-encoded string as binary data.
// Using JavaScriptEncoder.UnsafeRelaxedJsonEscaping doesn't help because the string actually is byte[].
// https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-Request - here "postData" property
// is a string, which we cannot deserialize properly. This property is marked as deprecated, and new "postDataEntries"
// is suggested for using, where most likely it is base64 encoded.

var bytes = reader.ValueSpan;
var sb = new StringBuilder(bytes.Length);
foreach (byte b in bytes)
{
sb.Append(Convert.ToChar(b));
}

return sb.ToString();
}
}

public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) =>
writer.WriteStringValue(value);
}
4 changes: 2 additions & 2 deletions dotnet/src/webdriver/cdp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ add an entry for version `<N>` to the `SupportedDevToolsVersions` dictionary ini
6. In [`//dotnet/src/webdriver:WebDriver.csproj.prebuild.cmd`](https://github.com/SeleniumHQ/selenium/blob/trunk/dotnet/src/webdriver/WebDriver.csproj.prebuild.cmd),
add the following block (substituting the proper value for `<N>`):

```
```bash
if not exist "%1..\..\..\bazel-bin\dotnet\src\webdriver\cdp\v<N>\DevToolsSessionDomains.cs" (
echo Generating CDP code for version <N>
pushd "%1..\..\.."
Expand All @@ -29,7 +29,7 @@ if not exist "%1..\..\..\bazel-bin\dotnet\src\webdriver\cdp\v<N>\DevToolsSessio
7. In [`//dotnet/src/webdriver:WebDriver.csproj.prebuild.sh`](https://github.com/SeleniumHQ/selenium/blob/trunk/dotnet/src/webdriver/WebDriver.csproj.prebuild.sh),
add the following block (substituting the proper value for `<N>`):
```
```bash
if [[ ! -f "$1../../../bazel-bin/dotnet/src/webdriver/cdp/v<N>/DevToolsSessionDomains.cs" ]]
then
echo "Generating CDP code for version <N>"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ namespace {{rootNamespace}}.{{domain.Name}}
if (m_eventMap.ContainsKey(e.EventName))
{
var eventData = m_eventMap[e.EventName];
var eventArgs = e.EventData.Deserialize(eventData.EventArgsType);
var eventArgs = e.EventData.Deserialize(eventData.EventArgsType, global::OpenQA.Selenium.DevTools.Json.DevToolsJsonOptions.Default);
eventData.EventInvoker(eventArgs);
}
}
Expand Down

0 comments on commit c1bb977

Please sign in to comment.