fix(tools): keep SSH ControlMaster socket path under macOS 104-byte limit

On macOS, Unix domain socket paths are capped at 104 bytes (sun_path).
SSH appends a 16-byte random suffix to the ControlPath when operating
in ControlMaster mode. With an IPv6 host embedded literally in the
filename and a deeply-nested macOS $TMPDIR like
/var/folders/XX/YYYYYYYYYYYY/T/, the full path reliably exceeds the
limit — every terminal/file-op tool call then fails immediately with
``unix_listener: path "…" too long for Unix domain socket``.

Swap the ``user@host:port.sock`` filename for a sha256-derived 16-char
hex digest. The digest is deterministic for a given (user, host, port)
triple, so ControlMaster reuse across reconnects is preserved, and the
full path fits comfortably under the limit even after SSH's random
suffix. Collision space is 2^64 — effectively unreachable for the
handful of concurrent connections any single Hermes process holds.

Regression tests cover: path length under realistic macOS $TMPDIR with
the IPv6 host from the issue report, determinism for reconnects, and
distinctness across different (user, host, port) triples.

Closes #11840
This commit is contained in:
Alexazhu 2026-04-18 13:54:12 +08:00 committed by Teknium
parent 649ef5c8f1
commit 64a1368210
2 changed files with 81 additions and 1 deletions

View file

@ -1,5 +1,6 @@
"""SSH remote execution environment with ControlMaster connection persistence."""
import hashlib
import logging
import os
import shlex
@ -47,7 +48,18 @@ class SSHEnvironment(BaseEnvironment):
self.control_dir = Path(tempfile.gettempdir()) / "hermes-ssh"
self.control_dir.mkdir(parents=True, exist_ok=True)
self.control_socket = self.control_dir / f"{user}@{host}:{port}.sock"
# Keep the socket filename short and deterministic so the full path
# stays under the 104-byte sun_path limit that macOS enforces on
# Unix domain sockets. A raw ``user@host:port`` — especially with an
# IPv6 host — plus the 16-byte random suffix SSH appends in
# ControlMaster mode easily exceeds the limit under macOS's
# deeply-nested $TMPDIR (e.g. /var/folders/xx/yy/T/). Hashing the
# triple keeps the path stable across reconnects so ControlMaster
# reuse still works.
_socket_id = hashlib.sha256(
f"{user}@{host}:{port}".encode()
).hexdigest()[:16]
self.control_socket = self.control_dir / f"{_socket_id}.sock"
_ensure_ssh_available()
self._establish_connection()
self._remote_home = self._detect_remote_home()