Skip to content

Neutralise escape sequences in filenames and peer messages before printing - #68

Open
op-q wants to merge 3 commits into
ci/three-os-testsfrom
fix/sanitize-peer-text
Open

op-q wants to merge 3 commits into
ci/three-os-testsfrom
fix/sanitize-peer-text

Conversation

@op-q

@op-q op-q commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Receiver consent plan, phase 1. This fixes a bug in shipped releases and does not change the protocol. It is a behaviour change, so it waits for your review. Stacked on #67.

The bug

drop recv printed the sender's filename verbatim (Receiving <name>), before the receiver had agreed to anything. A filename can carry:

  • escape sequences that clear the line, move the cursor, retitle the window or plant an OSC 8 hyperlink;
  • a right-to-left override, so invoice_<U+202E>fdp.exe reads as invoice_exe.pdf.

Error messages from the peer or the relay reached the terminal unfiltered in the same way. The relay is untrusted in Drop's threat model, so its messages count as peer text too.

The fix

New cli/src/display.rs:

  • for_terminal replaces control characters (C0, DEL, C1) and bidi/invisible formatting characters with U+FFFD. It replaces rather than deletes, so a doctored name looks doctored. It also collapses whitespace padding.
  • name does the same, then shortens anything over 80 columns in the middle so the extension survives. Wide characters count as two columns.
  • peer_message does the same, capped at 200 columns.

It is applied to: the receiver's Receiving, collision, Saved and extraction lines; archive warnings; every error frame's message (recv, send, relay transport); the relay's HTTP error body; the sender's own summary and warnings; and the interface's file browser rows.

Only what gets printed changes. The received file keeps the name its bytes arrived with.

Tests

  • 12 unit tests: CSI, OSC 8, OSC title, C1, newlines and tabs, each bidi control, the RTL extension trick, padding, middle ellipsis, wide characters, and honest names (Łódź 東京 🎉.txt, Arabic, an emoji ZWJ sequence, 10:30 standup.md) left unchanged.
  • One end-to-end test through the real binary over a real relay: a file named with ESC[2K ESC[1A U+202E crosses, and neither side's stderr contains ESC or U+202E. #[cfg(unix)] because Windows can't create that filename.
  • Negative control: with the Receiving line reverted, the end-to-end test fails with "an escape sequence from the name reached the receiver's terminal".
  • cargo fmt --check, clippy -D warnings, and cargo test --workspace --all-targets all pass: 193 tests, up from 180. check-secrets.sh passes.

unicode-width becomes a direct dependency. It was already in the tree through ratatui, so no new crate is built.

🤖 Generated with Claude Code

https://claude.ai/code/session_01G7Fy45hUvna94cd79WKG8S

The receiver printed the sender's filename verbatim in its "Receiving" line,
before the receiver had agreed to anything. A name carrying an escape sequence
could clear the line, move the cursor, retitle the window or plant a hyperlink,
and a right-to-left override could make `exe.pdf` read as `fdp.exe`. Error
messages from a peer, or from the relay, which is equally untrusted, reached
the terminal the same way.

`display.rs` replaces control characters and bidirectional and invisible
formatting characters with U+FFFD rather than deleting them, so a doctored name
looks doctored. It collapses whitespace padding and shortens long names in the
middle, keeping the extension. Honest names in any script, emoji included, pass
through unchanged. Applied to every place where someone else's text is printed.

Pinned end to end through the real binary over a real relay. With the fix
reverted, the test fails on the escape sequence reaching the receiver.

Receiver consent plan, phase 1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G7Fy45hUvna94cd79WKG8S
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

This branch has not been deployed

No deployments
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.

1 participant