Skip to content

socket: panic-safe utf-8 truncation in socket event log (#1814) - #1826

Merged
senamakel merged 2 commits into
tinyhumansai:mainfrom
CodeGhost21:cursor/a01-1814-socket-log-utf8-boundary
May 15, 2026
Merged

senamakel merged 2 commits into
tinyhumansai:mainfrom
CodeGhost21:cursor/a01-1814-socket-log-utf8-boundary

Conversation

@CodeGhost21

@CodeGhost21 CodeGhost21 commented May 15, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Fix panic-prone debug-log truncation in handle_sio_event when Socket.IO payload JSON contains a multi-byte UTF-8 codepoint straddling byte 500 (OPENHUMAN-TAURI-KC).
  • Bind data.to_string() once and slice via crate::openhuman::util::floor_char_boundary (same helper as fix(memory_tree, e2e tests ): deterministic query_topic ordering + robust CEF cleanup #1751 memory-tree body preview).
  • Add regression test handle_sio_event_debug_truncation_respects_utf8_boundary with a Cyrillic fixture at byte 500.

Problem

handle_sio_event logged incoming payloads with a naked byte slice:

&data.to_string()[..data.to_string().len().min(500)]

When the JSON serialization is ≥500 bytes and a multi-byte character (e.g. Cyrillic 'н') straddles byte 500, the core thread panicked (byte index 500 is not a char boundary). Real Sentry repro: WhatsApp ingest payload on Windows (OPENHUMAN-TAURI-KC).

Solution

  • let payload_json = data.to_string();
  • let preview_end = floor_char_boundary(&payload_json, 500);
  • Log &payload_json[..preview_end] — never slices inside a codepoint.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — changed lines covered by new regression test plus existing handle_sio_event_* tests (local: cargo test --lib openhuman::socket::event_handlers::tests)
  • Coverage matrix updated — N/A: behaviour-preserving panic fix, no feature rows added/removed/renamed
  • All affected feature IDs from the matrix are listed in the PR description under ## Related — N/A: no matrix feature IDs changed
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • Manual smoke checklist updated if this touches release-cut surfaces (docs/RELEASE-MANUAL-SMOKE.md) — N/A: log truncation only
  • Linked issue closed via Closes #NNN in the ## Related section

Impact

  • Runtime: desktop core sidecar — every incoming Socket.IO event hits this dispatcher; no routing/dispatch logic changed.
  • Performance: one fewer data.to_string() per event on the debug path.
  • Security: debug logs still truncate payload previews; no secrets/PII policy change beyond fixing the panic.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: cursor/a01-1814-socket-log-utf8-boundary
  • Commit SHA: 7ce2d5c8eed06a502a622a5a791a6d2d743121c4

Validation Run

  • pnpm --filter openhuman-app format:check
  • pnpm typecheck
  • Focused tests: CARGO_TARGET_DIR=target cargo test --lib openhuman::socket::event_handlers::tests (18 passed including new regression)
  • Rust fmt/check (if changed): cargo fmt --manifest-path Cargo.toml --all --check
  • Tauri fmt/check (if changed): N/A — no Tauri shell files changed

Validation Blocked

  • command: pnpm test:coverage / pnpm test:rust (full diff-cover gate)
  • error: Not run locally in this session (pre-push hook ran format, lint, typecheck, rust:check successfully)
  • impact: CI coverage gate on PR should be authoritative; changed surface is small and fully unit-tested

Behavior Changes

  • Intended behavior change: handle_sio_event no longer panics on multi-byte UTF-8 at the 500-byte log preview boundary.
  • User-visible effect: Core sidecar stays alive when receiving large non-ASCII Socket.IO payloads.

Parity Contract

  • Legacy behavior preserved: ASCII and sub-500-byte payloads truncate identically; match event_name dispatch unchanged.
  • Guard/fallback/dispatch parity checks: only debug log binding/slice and new test added.

Duplicate / Superseded PR Handling

Made with Cursor

Summary by CodeRabbit

  • Chores
    • Added a unit test ensuring debug/logging truncation is UTF‑8 safe.
    • Improved internal test coverage for event handling; no public API or behavior changes.

Review Change Stack

…inyhumansai#1814)

Bind payload JSON once and slice via floor_char_boundary so multi-byte
characters straddling byte 500 no longer abort the core thread (OPENHUMAN-TAURI-KC).

Co-authored-by: Cursor <cursoragent@cursor.com>
@CodeGhost21
CodeGhost21 requested a review from a team May 15, 2026 18:12
@coderabbitai

coderabbitai Bot commented May 15, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ed28f8ec-48c1-43e7-9112-d136ea97ef97

📥 Commits

Reviewing files that changed from the base of the PR and between 7ce2d5c and 6640305.

📒 Files selected for processing (1)
  • src/openhuman/socket/event_handlers.rs

📝 Walkthrough

Walkthrough

Adds a unit test that supplies a JSON payload with a multi-byte UTF-8 character straddling byte 500 and verifies handle_sio_event processes it without panicking or changing ConnectionStatus.

Changes

UTF-8 Truncation Test

Layer / File(s) Summary
Regression test for UTF-8-safe truncation
src/openhuman/socket/event_handlers.rs
Adds handle_sio_event_debug_truncation_respects_utf8_boundary, which builds a JSON string with a multi-byte char crossing byte 500, calls handle_sio_event with an unrelated event name, and asserts the socket ConnectionStatus remains Disconnected.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • graycyrus

Poem

🐇 I nibbled bytes until I found
A Cyrillic hop halfway around—
At five-hundred the code can rest,
No panic now, the test is blessed.
Safe slices make my burrow sound.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: fixing panic-safe UTF-8 truncation in socket event logging, directly addressing the core issue in the changeset.
Linked Issues check ✅ Passed The PR comprehensively addresses all coding requirements from issue #1814: uses char-boundary-safe truncation, binds payload once, adds regression test with UTF-8 fixture at byte 500, and achieves diff coverage compliance.
Out of Scope Changes check ✅ Passed All changes are strictly scoped to fixing the UTF-8 truncation panic: only a regression test was added with no modifications to exported APIs or handler logic.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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


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

@CodeGhost21 CodeGhost21 added rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. sentry-traced-bug Bug identified via Sentry triage labels May 15, 2026

@coderabbitai coderabbitai Bot left a comment

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/openhuman/socket/event_handlers.rs (1)

30-40: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Stop logging raw payload previews and serialize once.

On Line 33 and Line 35, data.to_string() is still executed twice. Also, Line 40 logs raw payload content, which can expose sensitive fields.

Suggested patch
-    log::info!(
-        "[socket] event received: name={} data_bytes={}",
-        event_name,
-        data.to_string().len()
-    );
-    let payload_json = data.to_string();
+    let payload_json = data.to_string();
+    log::info!(
+        "[socket] event received: name={} data_bytes={}",
+        event_name,
+        payload_json.len()
+    );
     let preview_end = crate::openhuman::util::floor_char_boundary(&payload_json, 500);
     log::debug!(
-        "[socket] event payload: name={} data={}",
+        "[socket] event payload preview: name={} preview_bytes={} total_bytes={}",
         event_name,
-        &payload_json[..preview_end]
+        preview_end,
+        payload_json.len()
     );

As per coding guidelines: “Never log secrets, raw JWTs, API keys, credentials, or full PII in debug logs; redact or omit sensitive fields.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/socket/event_handlers.rs` around lines 30 - 40, The code
currently calls data.to_string() twice and logs a raw payload preview which may
expose sensitive fields; fix by converting data to a single String once into
payload_json (use the existing payload_json variable), compute preview_end via
crate::openhuman::util::floor_char_boundary(&payload_json, 500), log only the
payload length with log::info (using event_name and payload_json.len()) and
replace the raw debug payload log (log::debug in this block) with a redacted or
safe preview derived from payload_json (e.g., redact known sensitive keys or
show a fixed placeholder or sanitized substring using floor_char_boundary) so no
raw JWTs/API keys/PII are emitted; ensure you reference event_name, data,
payload_json, floor_char_boundary, log::info and log::debug when making changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/openhuman/socket/event_handlers.rs`:
- Around line 30-40: The code currently calls data.to_string() twice and logs a
raw payload preview which may expose sensitive fields; fix by converting data to
a single String once into payload_json (use the existing payload_json variable),
compute preview_end via
crate::openhuman::util::floor_char_boundary(&payload_json, 500), log only the
payload length with log::info (using event_name and payload_json.len()) and
replace the raw debug payload log (log::debug in this block) with a redacted or
safe preview derived from payload_json (e.g., redact known sensitive keys or
show a fixed placeholder or sanitized substring using floor_char_boundary) so no
raw JWTs/API keys/PII are emitted; ensure you reference event_name, data,
payload_json, floor_char_boundary, log::info and log::debug when making changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1a9392c0-aa88-4fe7-acbc-fcfb893f09fa

📥 Commits

Reviewing files that changed from the base of the PR and between 21fd2cd and 7ce2d5c.

📒 Files selected for processing (1)
  • src/openhuman/socket/event_handlers.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 15, 2026
# Conflicts:
#	src/openhuman/socket/event_handlers.rs
@senamakel

Copy link
Copy Markdown
Member

Merged main into this branch (commit 6640305) and resolved the conflict in src/openhuman/socket/event_handlers.rs by adopting main's approach from #1818 (now merged), which removes the payload preview log entirely on PII/secrets grounds rather than truncating at a UTF-8 boundary. The PR's regression test (handle_sio_event_debug_truncation_respects_utf8_boundary) was preserved and still passes alongside main's handle_sio_event_payload_redacted_no_panic_on_multibyte_boundary — both validate no-panic on the same Cyrillic-at-byte-500 fixture.

Net code-change diff vs main is now empty for the affected file; the PR effectively contributes one extra regression test. Maintainers may want to close this PR as superseded by #1818 (per the duplicate note already in the PR body).

Tests run locally: cargo test --lib openhuman::socket::event_handlers::tests → 19/19 passing.

Pre-push hook bypassed with --no-verify: the Rust build step failed with ENOSPC (disk full on the local machine, environmental, unrelated) and Prettier/ESLint flagged pre-existing issues in two unrelated test files (RecoveryPhrasePanel.test.tsx, VoicePanel.test.tsx) that this PR doesn't touch — those weren't bundled in. CI is authoritative.

@senamakel
senamakel merged commit 781b1c3 into tinyhumansai:main May 15, 2026
24 checks passed
AusAgentSmith pushed a commit to AusAgentSmith/openhuman that referenced this pull request May 23, 2026
…#1814) (tinyhumansai#1826)

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
senamakel pushed a commit to nocstah/openhuman that referenced this pull request Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. sentry-traced-bug Bug identified via Sentry triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Panic on multi-byte socket payload in event handler log truncation

2 participants