Skip to content

fix: Handle non-UTF-8 inputs without crashing#3268

Open
mvadari wants to merge 4 commits intomainfrom
fix-non-utf8-crash
Open

fix: Handle non-UTF-8 inputs without crashing#3268
mvadari wants to merge 4 commits intomainfrom
fix-non-utf8-crash

Conversation

@mvadari
Copy link
Copy Markdown
Collaborator

@mvadari mvadari commented Apr 6, 2026

High Level Overview of Change

This PR adds better handling for non-UTF-8 inputs from rippled. Previously, xrpl.js would crash when it encountered these inputs.

Context of Change

Transia-RnD#2

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

Did you update HISTORY.md?

  • Yes

Test Plan

Added tests. It no longer crashes.

Copilot AI review requested due to automatic review settings April 6, 2026 15:26
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dee9feaa-ecb3-4177-b306-2444424875d7

📥 Commits

Reviewing files that changed from the base of the PR and between 21a4aaa and 7adc62c.

📒 Files selected for processing (2)
  • packages/xrpl/src/client/connection.ts
  • packages/xrpl/test/client/subscribe.test.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/xrpl/test/client/subscribe.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/xrpl/src/client/connection.ts

Walkthrough

WebSocket handling now tolerates malformed UTF-8 by enabling skipUTF8Validation and converting Buffer message payloads into UTF-8 strings; tests and mock server utilities were extended to exercise binary/raw frames and verify replacement-character behavior.

Changes

Cohort / File(s) Summary
Connection runtime & changelog
packages/xrpl/src/client/connection.ts, packages/xrpl/HISTORY.md
Set skipUTF8Validation: true for WebSocket clients, updated message handler signature to accept `string
Mock server & test helpers
packages/xrpl/test/createMockRippled.ts
Added RawMockFrame and MockResponse union, isRawMockFrame guard, sendResponse branch to emit raw frames (`string
Tests (new cases)
packages/xrpl/test/client/submitAndWait.test.ts, packages/xrpl/test/client/subscribe.test.ts, packages/xrpl/test/connection.test.ts
Added tests that inject malformed UTF-8 bytes (0xff) in WebSocket payloads (binary/text) to verify polling, subscription events, and request handling produce Unicode replacement characters and still return expected parsed results.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

bug

Suggested reviewers

  • ckeshava
  • achowdhry-ripple
  • khancode

Poem

🐰 A whisper of bytes that slipped and fell,
0xff hiding where JSON dwell,
I skip the strictness, stitch the string fine,
replace with a glyph, let parsing shine,
hop — tests pass, all lines align. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: Handle non-UTF-8 inputs without crashing' directly and clearly describes the primary change: fixing a bug where non-UTF-8 inputs caused crashes.
Description check ✅ Passed The description covers all major template sections: high-level overview of the change, context with a reference link, bug fix type selected, and HISTORY.md updated confirmed. The test plan is brief but present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-non-utf8-crash

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR prevents xrpl’s WebSocket connection layer from crashing when rippled sends malformed UTF-8 in text frames by disabling UTF-8 validation at the WebSocket layer and decoding any Buffer message payloads as UTF-8 (allowing replacement characters). It also updates the test mock to emit raw WebSocket frames and adds regression tests.

Changes:

  • Enable skipUTF8Validation for WebSocket connections and decode Buffer message payloads to UTF-8 strings before JSON parsing.
  • Extend the mock rippled server to optionally send raw WebSocket frames (including malformed UTF-8) and route responses through a unified sender.
  • Add tests covering malformed UTF-8 handling for requests and (attempted) subscription events; update HISTORY.md.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/xrpl/src/client/connection.ts Enables skip UTF-8 validation and decodes Buffer messages to avoid crashes on malformed UTF-8.
packages/xrpl/test/createMockRippled.ts Adds support for sending raw frames (Buffer/string) from the mock server for malformed UTF-8 tests.
packages/xrpl/test/connection.test.ts Adds a regression test for malformed UTF-8 in a text WebSocket response.
packages/xrpl/test/client/subscribe.test.ts Adds a subscription-event test intended to cover non-Unicode payloads.
packages/xrpl/test/client/submitAndWait.test.ts Adds a regression test for malformed UTF-8 while polling tx during submitAndWait.
packages/xrpl/HISTORY.md Documents the fix in Unreleased notes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

...Buffer.from('{"type":"path_find","message":"'),
0xff,
...Buffer.from('"}'),
]),
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this test, socket.send(Buffer.from(...)) will default to sending a binary WebSocket frame in ws (since the payload is a Buffer). That means it doesn't exercise malformed UTF-8 handling for text frames (the UTF-8 validation logic only applies to text messages). Consider sending with { binary: false } (or using the new RawMockFrame support in the mock server) so this test actually covers malformed UTF-8 text messages.

Suggested change
]),
]),
{ binary: false },

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/xrpl/test/client/subscribe.test.ts`:
- Around line 132-138: The test is sending the malformed payload as a binary
WebSocket frame (testContext.mockRippled!.socket.send(Buffer...)) but real
rippled sends JSON as text frames; update the send call to send a text frame by
passing the options object { binary: false } (or send the JSON string directly)
to testContext.mockRippled!.socket.send so the mocked response is delivered as a
text frame matching real rippled behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1526bdbf-f3b9-47df-ad87-7da6fe1939b4

📥 Commits

Reviewing files that changed from the base of the PR and between 29108b3 and 21a4aaa.

📒 Files selected for processing (6)
  • packages/xrpl/HISTORY.md
  • packages/xrpl/src/client/connection.ts
  • packages/xrpl/test/client/submitAndWait.test.ts
  • packages/xrpl/test/client/subscribe.test.ts
  • packages/xrpl/test/connection.test.ts
  • packages/xrpl/test/createMockRippled.ts

Comment on lines +132 to +138
testContext.mockRippled!.socket.send(
Buffer.from([
...Buffer.from('{"type":"path_find","message":"'),
0xff,
...Buffer.from('"}'),
]),
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read-only verification: locate Buffer-based websocket sends in tests.
rg -n "mockRippled!?\\.socket\\.send\\(|\\.send\\(\\s*Buffer\\.from\\(" packages/xrpl/test -C2

Repository: XRPLF/xrpl.js

Length of output: 433


🏁 Script executed:

# Get the full test case to understand what it's testing
sed -n '121,140p' packages/xrpl/test/client/subscribe.test.ts

Repository: XRPLF/xrpl.js

Length of output: 628


🏁 Script executed:

# Check ws library version and documentation about send options
cat packages/xrpl/package.json | grep -A2 -B2 '"ws"'

Repository: XRPLF/xrpl.js

Length of output: 236


🌐 Web query:

ws npm library socket.send Buffer binary frame default behavior

💡 Result:

In the Node.js ws npm library, socket.send(data[, options][, cb]) decides whether to send a text frame (opcode 0x1) or a binary frame (opcode 0x2) via the options.binary flag, whose default is “autodetected” based on the type of data you pass. (github.com)

Practically:

  • socket.send(Buffer.from(...)) (or other binary-like types such as ArrayBuffer, TypedArray, DataView) will be sent as binary by default (autodetect). (github.com)
  • socket.send("...") will be sent as text by default (autodetect). (github.com)
  • You can override the autodetect with socket.send(data, { binary: true/false }). (github.com)

On the receiving side, the 'message' event provides isBinary so you can tell which kind of message arrived. (github.com)

Citations:


🏁 Script executed:

# Check for other socket.send calls in the test file to see patterns
grep -n "socket.send" packages/xrpl/test/client/subscribe.test.ts

Repository: XRPLF/xrpl.js

Length of output: 104


🏁 Script executed:

# Check how the client handles the 'message' event - does it care about frame type?
rg -n "on\(['\"]message['\"]|isBinary" packages/xrpl/src -C3

Repository: XRPLF/xrpl.js

Length of output: 681


🏁 Script executed:

# Check what onMessage does with the message - does it parse differently based on type?
sed -n '395,450p' packages/xrpl/src/client/connection.ts

Repository: XRPLF/xrpl.js

Length of output: 2215


🏁 Script executed:

# Search for the onMessage method implementation
rg -n "onMessage\s*\(" packages/xrpl/src/client/connection.ts -A15

Repository: XRPLF/xrpl.js

Length of output: 1377


Send this malformed payload explicitly as a text frame.

At line 132, socket.send(Buffer) in the ws library defaults to a binary frame. Real rippled sends JSON responses as text frames, so pass { binary: false } to match that behavior.

💡 Suggested patch
-      testContext.mockRippled!.socket.send(
-        Buffer.from([
-          ...Buffer.from('{"type":"path_find","message":"'),
-          0xff,
-          ...Buffer.from('"}'),
-        ]),
-      )
+      testContext.mockRippled!.socket.send(
+        Buffer.from([
+          ...Buffer.from('{"type":"path_find","message":"'),
+          0xff,
+          ...Buffer.from('"}'),
+        ]),
+        { binary: false },
+      )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
testContext.mockRippled!.socket.send(
Buffer.from([
...Buffer.from('{"type":"path_find","message":"'),
0xff,
...Buffer.from('"}'),
]),
)
testContext.mockRippled!.socket.send(
Buffer.from([
...Buffer.from('{"type":"path_find","message":"'),
0xff,
...Buffer.from('"}'),
]),
{ binary: false },
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/xrpl/test/client/subscribe.test.ts` around lines 132 - 138, The test
is sending the malformed payload as a binary WebSocket frame
(testContext.mockRippled!.socket.send(Buffer...)) but real rippled sends JSON as
text frames; update the send call to send a text frame by passing the options
object { binary: false } (or send the JSON string directly) to
testContext.mockRippled!.socket.send so the mocked response is delivered as a
text frame matching real rippled behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants