Skip to content

fix(clipboard): use base64 encoding for PowerShell read path to prevent ANSI codepage corruption - #37212

Closed
annguyenNous wants to merge 1 commit into
NousResearch:mainfrom
annguyenNous:fix/clipboard-powershell-encoding
Closed

fix(clipboard): use base64 encoding for PowerShell read path to prevent ANSI codepage corruption#37212
annguyenNous wants to merge 1 commit into
NousResearch:mainfrom
annguyenNous:fix/clipboard-powershell-encoding

Conversation

@annguyenNous

Copy link
Copy Markdown
Contributor

Problem

PowerShell's Get-Clipboard -Raw outputs text in the system's ANSI codepage (e.g. CP1252 for Western, CP936 for Chinese), not UTF-8. When Node.js reads this with encoding: 'utf8', non-ASCII characters are corrupted:

  • CJK characters become mojibake
  • Emoji are garbled
  • Accented characters (é, ü, etc.) are mangled

The write path already solved this exact problem by base64-encoding UTF-8 bytes and passing them via -Command argument (see the detailed comment at lines 94-98). The read path has no equivalent mitigation — it relies on encoding: 'utf8' which only works for ASCII clipboard content.

Fix

Apply the same base64 approach to the read path, symmetric with the write path:

  1. Change PowerShell read command to [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((Get-Clipboard -Raw))) — this converts clipboard text to UTF-8 bytes, then base64-encodes them
  2. Add base64 flag to the read command type for PowerShell entries
  3. Decode base64 result in readClipboardText when flag is set: Buffer.from(result.stdout.trim(), 'base64').toString('utf8')

Also adds child.unref() to spawned clipboard child processes in the write path to prevent process.exit() from being blocked by slow powershell.exe startup on WSL.

Before vs After

Scenario Before After
CJK text on WSL Corrupted (ANSI→UTF8 misparse) Correct (base64 roundtrip)
Emoji on Windows Garbled Correct
Accented chars on WSL Mangled Correct
ASCII text Works Works (base64 is transparent)
App exit during clipboard write May hang until child completes Exits immediately (unref)

Tests

  • Updated existing PowerShell read tests to verify base64 encoding/decoding
  • Added CJK text preservation test for PowerShell read path on WSL
  • All 20 tests pass

…nt ANSI codepage corruption

PowerShell's Get-Clipboard -Raw outputs text in the system's ANSI codepage
(e.g. CP1252, CP936), not UTF-8. When Node.js reads this with encoding: 'utf8',
non-ASCII characters (CJK, emoji, accented chars) are corrupted.

The write path already solved this by base64-encoding UTF-8 bytes and passing
them via -Command argument (see comment at line 94-98). This fix applies the
same approach to the read path:

- Change PowerShell read command to base64-encode the clipboard content
  using [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(...))
- Add base64 flag to read command type for PowerShell entries
- Decode base64 result in readClipboardText when flag is set

Also adds child.unref() to spawned clipboard child processes in the write
path to prevent delaying process.exit() when the app exits mid-clipboard-write.
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for addressing the PowerShell read path. The premise remains on current main: ui-tui/src/lib/clipboard.ts:6, :42, and :48 run raw Get-Clipboard -Raw, while readClipboardText requests UTF-8 decoding and returns stdout directly at :77-84. This is asymmetric with the explicit UTF-8/base64 PowerShell write path at :94-104.

Problems

  • The extra child.unref() behavior is not verified. The diff adds unref: vi.fn() to the child mocks but does not assert it was called, so the tests do not protect the new exit-liveness behavior.

Suggested changes

  • Add focused assertions for unref() in one piped-stdin and one PowerShell write test, or keep the encoding fix scoped and move the lifecycle change into a separately tested follow-up.

GitHub currently reports this PR as conflicting, but the target functions remain recognizable on current main, so this looks mechanically salvageable. Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 13, 2026
@teknium1

teknium1 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Merged in #81963 — your base64 clipboard strategy is on main with authorship preserved. It was the strongest of three competing approaches: encoding the clipboard bytes as base64 inside PowerShell sidesteps the console codepage entirely, where chcp/OutputEncoding forcing is fragile under -NoProfile. Covers TUI + desktop composer paste. Thanks!

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

Labels

comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants