v0.21.8
What's Changed
Fixed
-
A password typed at a prompt could be written to the session log in the clear (issue #321) — session-log redaction matched patterns line by line, so it caught
password: <value>on one line but not the real interactive case, where thePassword:prompt and the answer are separate records and the answer (INPUT: hunter2) carries no marker of its own. A password answered by hand therefore reached disk unredacted, while a vault password never did because it is sent straight to the PTY and not echoed. The logger now tracks that a sensitive prompt was just written and redacts the nextINPUT:record whole, regardless of its content. The transcript only arms this state — its own following lines are non-echoed legitimate output — and substantive transcript output disarms it, so a vault-answered prompt does not cause a later command to be blanked. A pending prompt is also flushed to the logger immediately rather than after the 500 ms partial-line grace, closing the window in which the typed answer could arrive first. -
An Expect-rule response could not resolve a connection-local variable (issue #317) —
${var}references in an automation response were substituted only against global variables and the built-in${username}/${host}/${port}/${password}, so a variable defined under Edit Connection → Data → Local Variables resolved against nothing, the rule was dropped as undefined, and the script stalled at the prompt waiting for manual input. Connection-local variables are now included in the set an Expect response resolves against, shadowing a global of the same name (matching the command path's precedence) while the synthetic fields and${password}still shadow a same-named local. -
Embedded RDP clipboard file transfer ("Save N Files") now works — copying files from the remote desktop through the RDP clipboard channel had never functioned. Three faults were untangled: the download and upload directions were crossed (the fetch command read a local file instead of sending a request, and the server's real upload request was dropped); size replies and 8-byte data chunks were told apart by length, which corrupted any 8-byte file; and large files were requested all at once, letting the server truncate the reply. Files are now pulled in 1 MiB ranges until the known size is reached, replies are classified by request type, and a server refusal drops just that file while the rest continue.
-
A clipboard filename from the server could be written outside the folder the user chose — the name came straight from
cFileNamein the server's file descriptor and was only checked for being non-empty.Path::joinreplaces its base when handed an absolute path, so a server sending/home/user/.bashrcwrote there rather than into the selected folder, and../../.ssh/authorized_keystraversed out of it for the same reason. A malicious or compromised RDP server could therefore write arbitrary files with the user's privileges the moment a target folder was picked. The name is now reduced to a single path component — split on both separators, since a Windows server sends Windows paths — and.,.., empty and NUL-bearing names are refused outright. The write also stopped clobbering: it usescreate_new, so an existing file is never truncated and a collision gets a(1)suffix instead. Reachable only since this release, because the download direction had never actually worked before it. -
RDP clipboard "Save N Files" edge cases that could lose a file, stick the UI, or corrupt a download — a failed disk write (full disk, read-only target) was logged but still counted as saved; the summary now reports how many files did not make it. A batch containing a file the server refused could never report a result at all: settlement was measured in completed files, so one refusal held the batch open forever and neither the summary nor the completion callback arrived even when every other file had saved — the common case rather than a rare one, since a server refuses any directory in the list. Directories are now dropped before the list is offered, because transferring one would mean walking the descriptor hierarchy, which this code does not do. Completion is decided from the server's own numbers, so a peer that answered with full chunks past the end of a file would have looped forever while the in-memory buffer grew without bound; a 512 MiB per-file ceiling is the backstop that does not depend on the peer behaving. A chunk arriving after its download finished is now ignored rather than appended and counted twice. Stream ids stay monotonic across batches instead of restarting at 1, so a late reply from a superseded batch lands on no download at all rather than on a fresh one holding the same id. A second press on the same file list resets the previous run's failure tally instead of adding to it. And a request that failed to encode or reach the wire now unwinds the download rather than leaving the batch waiting on a reply that is not coming.
-
Dragging a file onto an embedded RDP session never reached the remote — three faults stacked on the client→server path. With clipboard sharing off there is no CLIPRDR channel at all, yet the drop was accepted and briefly claimed "ready to paste"; the drop is now refused up front with a status line telling the user to enable clipboard sharing. When it was on, the file offer (
FileGroupDescriptorW+FileContents) was sent correctly — and then clobbered: the local-clipboard monitor announces text on any owner-change, and because CLIPRDR keeps a single current offer, that text announce replaced the file offer a couple of seconds later, so the server requested text and never asked for the file descriptor. The monitor now stays quiet on two counts: it does not announce when the clipboard advertises no text (reading the format list is metadata only, so it does not risk the #261 text-converter crash), and it holds off entirely while a file drag-and-drop offer is outstanding — set on the drop and cleared once the server requests the file or after a safety timeout — so even a genuine text change cannot race an in-flight file transfer. Together these leave the file offer standing until the server acts on it. And the session loop no longer swallows a failed announce — a missing channel, a rejectedinitiate_copy(handshake not yet complete), or a failed encode/write each logs a distinct reason instead of silence. Finally the offer itself was going out the wrong way: it was hand-built and parked under a private format id, which left IronRDP's own file list empty, so IronRDP rejected the server's File Contents Request before the backend saw it (the file appeared offered, then failed on the Windows side with "Unspecified error"). It now goes throughCliprdr::initiate_file_copy, which populates that list and sends the descriptor itself; the file transfers in full. The drop is clipboard copy, not drag-to-desktop — RDP has no wire form for the latter — so the status line now says the file was copied to the remote clipboard and to press Ctrl+V in the session to place it. -
A server could make the client allocate up to 4 GiB for one clipboard file request — when the server asks for a file the client offered, the request carries a
u32byte count, and the reply buffer was allocated at exactly that size before a single byte was read. A request foru32::MAXtherefore asked for a 4 GiB allocation. The read is now capped at 8 MiB per response, which is legal: a response shorter than requested is how MS-RDPECLIP expresses a partial read, and a peer wanting more asks again from a later offset — the client's own download loop already works that way. This path had been unreachable until the upload direction started working in this release. -
SecretSettingsandCredentialsderivedDebug, which could print stored credentials in the clear — theSecretStringfields redact themselves, butSecretSettingsalso holds machine-key-encrypted copies of each secret as plain strings, which a derivedDebugprinted verbatim into any log or error. Both types now implementDebugby hand, showing a<set>/<none>marker for every secret-bearing field, with tests that fail if a future secret field leaks (M-PUBLIC-DEBUG). -
Several secret-backend CLI calls had no timeout and could hang the caller forever — the 1Password and Passbolt backends bounded none of their calls, and three Bitwarden calls (
login --apikey,lock,logout) were unbounded, so a slow or unreachable server parked the waiting GUI task indefinitely. All are now wrapped in a 30-second timeout that surfaces asSecretError::ConnectionFailednaming the tool, includingop whoamiandop signout, which reach the service and were the two left over. Local--versionprobes stay unbounded — they never touch the network. A timeout also had to be made to actually end the call:tokio::time::timeoutonly drops the future, andtokio::processdoes not signal a child on drop, so a hungbw,oporpassboltoutlived the timeout as an orphan — still holding a vault session, and in Passbolt's case still showing its passphrase in/proc/PID/cmdline. Every bounded invocation now setskill_on_drop.
Improved
-
A file dropped on an RDP session now confirms with a toast, not just a status line — a dropped file goes onto the remote clipboard (RDP has no drag-to-desktop), and the inline status line saying so was easy to miss, so the drop looked like it did nothing. A dropped file now raises a toast — "N file(s) copied to the remote clipboard — press Ctrl+V in the session to paste" — through the same overlay the "File drag disabled" notice uses; the status line remains as a fallback.
-
The auto-login timeout can now be set on a group, not just a connection — the group editor reused the connection editor's Automatic Login section but ignored the Login Timeout control, so a per-group timeout was TOML-only. It now reads and writes
login_timeout_secsalongside the prompt fields:0leaves it unset so a parent group or the built-in 10-second default applies. No new translatable strings. -
The macOS window header was taller than a native app, even in compact mode — the header-bar icon buttons carried a fixed 44×44px size request (the GNOME HIG tap-target minimum), which set the whole header height and no CSS could shrink. The size request is now 28px on macOS (matching native AppKit toolbar buttons, where there is no touch input) and 44px everywhere else, with matching
.macosheader styling. The result sits much closer to a native macOS titlebar.
Documentation
-
Stale RDP code comments corrected — comments in
rustconn-core/src/rdp_client/pinned behaviour to old IronRDP versions (0.13–0.16) against a workspace now on 0.17. The version tags that only dated the note are dropped; the two real upstream gaps (a missing notify-response PDU and limited lock-control fields in ironrdp-rdpdr 0.7) are restated against the current version. The dead file-contents proxy arm now says plainly it is an unused path rather than "not yet implemented". -
Docs corrected to match the code — the User Guide's template count (21, not 20; WinBox was missing);
rdp_client/gateway.rsclaiming RD Gateway was "not yet implemented" when it has worked behind therd-gatewayfeature for some time; the undocumented fwknop Single Packet Authorization sender now has a User Guide subsection; the README's export coverage (split from a combined Import/Export row that overstated it, and Azure spelled out as two providers);CLI_REFERENCE.md's missingman-pagecommand and misdescribedsftpgating; and Quick Connect history documented as discarded on exit when it is in fact persisted (host, port, protocol and username only, never a password). -
Version and structure drift in docs fixed —
CLI_REFERENCE.mdandZERO_TRUST.mdhad drifted to old versions because the bump tooling did not list them; both are now tracked, and three docs with an old release quoted in example output are refreshed by hand.ARCHITECTURE.mdhad three factual drifts (a "six-crate" opener against a seven-crate table, a documented error-display helper that does not exist, and three moved file paths), all corrected, with three omitted managers added to the Manager Pattern table.
Dependencies
- Updated: rustls 0.23.43→0.23.44.
Installation
Flatpak (Recommended)
flatpak install flathub io.github.totoshko88.RustConnSnap
sudo snap install rustconnDebian/Ubuntu (.deb from this release)
sudo dpkg -i rustconn_0.21.8_amd64.deb
sudo apt-get install -f # Install dependencies if neededFedora (.rpm from this release)
sudo dnf install rustconn-0.21.8-1.fc44.x86_64.rpmAppImage
chmod +x RustConn-0.21.8-x86_64.AppImage
./RustConn-0.21.8-x86_64.AppImagemacOS (Homebrew)
brew tap totoshko88/rustconn
brew install rustconn
open $(brew --prefix)/opt/rustconn/RustConn.appAll dependencies (GTK4, libadwaita, VTE, Adwaita icons) are installed automatically.
Requires macOS 13 (Ventura) or later.
OBS Repositories
Packages available at: https://build.opensuse.org/package/show/home:totoshko88:rustconn/rustconn
# Debian 13 (Trixie)
echo 'deb http://download.opensuse.org/repositories/home:/totoshko88:/rustconn/Debian_13/ /' \
| sudo tee /etc/apt/sources.list.d/rustconn.list
curl -fsSL https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/Debian_13/Release.key \
| gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/rustconn.gpg > /dev/null
sudo apt update && sudo apt install rustconn
# Ubuntu 24.04 LTS (Noble)
echo 'deb http://download.opensuse.org/repositories/home:/totoshko88:/rustconn/xUbuntu_24.04/ /' \
| sudo tee /etc/apt/sources.list.d/rustconn.list
curl -fsSL https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/xUbuntu_24.04/Release.key \
| gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/rustconn.gpg > /dev/null
sudo apt update && sudo apt install rustconn
# Ubuntu 26.04 LTS (Resolute)
echo 'deb http://download.opensuse.org/repositories/home:/totoshko88:/rustconn/xUbuntu_26.04/ /' \
| sudo tee /etc/apt/sources.list.d/rustconn.list
curl -fsSL https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/xUbuntu_26.04/Release.key \
| gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/rustconn.gpg > /dev/null
sudo apt update && sudo apt install rustconn
# Fedora 44
sudo dnf config-manager addrepo --from-repofile=https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/Fedora_44/home:totoshko88:rustconn.repo
sudo dnf install rustconn
# Fedora 43
sudo dnf config-manager addrepo --from-repofile=https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/Fedora_43/home:totoshko88:rustconn.repo
sudo dnf install rustconn
# openSUSE Tumbleweed
sudo zypper ar https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/openSUSE_Tumbleweed/ rustconn
sudo zypper ref && sudo zypper in rustconn
# openSUSE Leap 16.0
sudo zypper ar https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/openSUSE_Leap_16.0/ rustconn
sudo zypper ref && sudo zypper in rustconnArch Linux (AUR)
yay -S rustconnFreeBSD (Ports)
pkg install rustconnFull installation guide: https://github.com/totoshko88/RustConn/blob/main/docs/INSTALL.md