Skip to content

Drop-path control-character filtering: Swift URL branch unfiltered, and the Rust/Swift predicates diverge #282

Description

@charliek

Surfaced while fixing the bracketed-paste injection (#280). Security-adjacent — read item 1 first.

1. Swift's dragged-URL branch has no control-character rejection

TerminalView.dropContentString's web-URL branch passes the URL through ShellEscape.escape(url) and nothing else — unlike the file-path branch, it never rejects control characters. A dragged URL carrying escape bytes therefore reaches the PTY.

Current mitigation: wrapBracketedPaste (added in #280) strips bracketed-paste markers at the paste boundary, so the specific early-terminate injection that motivated #280 is blocked. What is not blocked is other escape sequences when DEC mode 2004 is off — comparable to the baseline risk of pasting arbitrary text into a terminal, which is why this is defense-in-depth rather than an open hole.

Wanted: reject (don't strip — see #280's discussion) control characters on the URL branch, matching the file-path branch, and give Rust an equivalent since it has no dragged-URL path at all today.

2. The filter predicates differ between the UIs

Rust drop_content::resolve rejects ['\n', '\r', '\u{1b}']; Swift dropContentString rejects Character.isNewline || "\u{1b}". isNewline is a superset (VT U+000B, FF U+000C, NEL U+0085, LS U+2028, PS U+2029), so Swift rejects paths Rust accepts. Not an integrity bug — Swift rejects, never mutates — but the two UIs disagree about what is droppable.

Converge on one predicate with shared test vectors, the way shell_escape.rs / ShellEscape.swift already do (see #280 for that pattern).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions