Skip to content

[BUG] get_cwd writes a CR-terminated command into the live PTY with no foreground-app guard — submits unsent input in interactive programs #714

@ahbarnum

Description

@ahbarnum

Platform

Platform-independent. This is a backend defect in src/backend/ssh/terminal.ts; the report is based on source inspection, not on a single client platform.

Server Installation Method

N/A — verified by reading source at Termix-SSH/Termix, branch main.

Version

main @ commit b30ed8eabea35107522b14452e7adc8e29b7ec52. (I have not mapped this to specific released versions — please confirm the affected release range from the code refs below.)

Troubleshooting

  • I have reviewed opened and closed issues

(Other boxes intentionally left unchecked because they don't apply — this is a source-level report, not a runtime-log report.)

The Problem

The get_cwd WebSocket handler writes a carriage-return-terminated shell command directly into the live PTY stream, with no check for what application currently holds that PTY. When the File Manager action is used on a terminal tab that has an interactive/foreground program running, that program receives the command text followed by an Enter, so input the user never sent gets submitted.

Verified code path (all links pinned to b30ed8eabea35107522b14452e7adc8e29b7ec52):

  1. Frontend sends get_cwdsrc/ui/desktop/apps/features/terminal/Terminal.tsx#L715-L720:
    openFileManager: () => {
      if (webSocketRef.current?.readyState === WebSocket.OPEN) {
        webSocketRef.current.send(JSON.stringify({ type: "get_cwd" }));
      } else {
  2. What invokes it — the File Manager icon button on a terminal tab, src/ui/desktop/navigation/tabs/Tab.tsx#L276-L288 (onClickonOpenFileManager()), which TopNavbar wires to terminalRef.current.openFileManager() only when the host has File Manager enabled, src/ui/desktop/navigation/TopNavbar.tsx#L505-L509.
  3. Backend writes the command into the PTYsrc/backend/ssh/terminal.ts#L761-L773:
    case "get_cwd": {
      const activeStream =
        sessionManager.getSession(currentSessionId)?.sshStream ?? sshStream;
      ...
      cwdPending = true;
      cwdBuffer = "";
      // Split the sentinel across shell variables so the echoed command
      // itself never contains "TERMIX_CWD:" — only the output line does.
      activeStream.write('a=TERMIX_CWD; echo "$a:$(pwd)"\r');
      break;
    }

The literal bytes written are a=TERMIX_CWD; echo "$a:$(pwd)" followed by a carriage return (\r). This case "get_cwd" block performs the write unconditionally; there is no check in that handler for whether an interactive or full-screen application currently owns the PTY.

This is a problem because the command + \r is delivered as input to whatever program is in the foreground of that PTY. If that program reads a line of input, the carriage return submits it — together with whatever the user had already typed and not yet sent.

How to Reproduce

  1. Add/enable an SSH host with Enable File Manager turned on (Host Manager → File Manager tab) and open a terminal tab to it.
  2. In that terminal, run cat (no arguments) so a foreground program is reading line-buffered stdin.
  3. Type hello but do not press Enter.
  4. Click the File Manager (folder) icon on that terminal's tab.
  5. Actual: terminal.ts:772 writes a=TERMIX_CWD; echo "$a:$(pwd)" + \r into the PTY; cat therefore receives and echoes a completed line containing the user's hello followed by the injected probe text — i.e., a line was submitted that the user never entered.
  6. Expected: obtaining the working directory for the File Manager must not write keystrokes or an Enter into the foreground program's input. (Originally observed with an interactive line-based CLI running in the terminal: the probe text was appended to an unsent draft and submitted.)

Additional Context

  • The injected string is deliberately constructed (comment at terminal.ts:770-771) so the echoed command never contains the TERMIX_CWD: sentinel — only the result line does — and the backend then scrapes that result line (CWD_SENTINEL = "TERMIX_CWD:", terminal.ts:1503). So the design assumes a shell prompt that echoes and runs the line; it does not account for a foreground application that simply consumes the bytes as input.
  • The File Manager icon is rendered on the terminal tab itself (Tab.tsx:276-288), adjacent to the tab's other affordances.
  • Verified workaround: turning off Enable File Manager for the host makes onOpenFileManager undefined (TopNavbar.tsx:505-509), so the tab icon is not rendered and get_cwd can no longer be triggered from it.
  • Suggestion (not verified, offered only as a direction): gate the get_cwd write on the PTY not being in the alternate screen / not in a raw-input mode, or obtain the cwd without writing into the interactive PTY.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions