Skip to content

v0.21.11

Latest

Choose a tag to compare

@github-actions github-actions released this 11 Sep 16:00
· 1 commit to main since this release

What's Changed

Fixed

  • Session recordings stored secrets in clear text despite the promised redaction — the User Guide states that recordings redact passwords, API keys, tokens, AWS credentials and PEM blocks. Nothing did: recording is driven by an external script process that writes raw terminal bytes straight to disk, so RustConn never saw the stream and the recorder's own sanitiser could not run. Anything typed or echoed during a recorded session was stored verbatim. Finished recordings are now scrubbed in every stop path (local, remote-after-SCP, application shutdown), rewritten atomically and kept at 0600. The .timing file is rewritten alongside the .data file so scriptreplay stays in sync, line terminators are preserved so a replay is not a staircase, and script's own header and footer — which no timing entry accounts for — are kept rather than mistaken for session output. Matching spans the whole recording instead of each flush, so a secret typed a few bytes at a time is caught, and binary output no longer shields a secret that follows it. Whole-line blanking is deliberately not used here: in a raw terminal stream a single "line" holds the prompt, escape sequences, the echoed command and its output together, so blanking it destroyed benign output without protecting anything.
  • Session log files and the session-restore file were world-readable — both were created under the process umask, commonly 0644. A transcript can hold sensitive output even after redaction, and the log path is often outside the 0700 config directory — the guide itself suggests ~/Downloads for Flatpak — where other local users could read it. Both are now created 0600 on unix, with the mode set in the open itself so the file never exists wider. Appending to a pre-existing log leaves its mode alone, so a path deliberately widened is not clobbered.
  • A log rotation could delete the user's own files — retention is documented as applying only inside the log directory RustConn manages, and the GUI honours that. The logger, however, also pruned the parent of its own log path, which comes from a user-supplied template and can be anywhere. A log in ~/Downloads crossing its size limit therefore deleted every *.log in that directory older than the retention window, including files RustConn had never written. The logger no longer prunes at all: age-based deletion happens in one place, against a directory the caller knows RustConn owns, which the GUI already did at every session start.
  • Dangerous VNC viewer arguments could be smuggled in from an imported connection — the GUI's viewer launch filtered only NUL and newline while the CLI path blocked a set of dangerous options, so -via attacker (rerouting the session through a host of the attacker's choice) or -passwordfile reached the GUI where the CLI would have refused them. The block list was bypassable on both paths anyway: a viewer accepts two leading dashes as readily as one — verified on TigerVNC 1.15.0 — so --via=… passed untouched. And blocking a flag left its value behind as a bare positional argument, which is how a VNC viewer is told which server to connect to. Both builders now share one filter that compares the option name whatever its spelling, and drops a blocked option's value with it. Benign arguments are unaffected.
  • A Script password source logged its full command line — the resolver logged the whole command string at debug level, and again on timeout. The password was never logged, but the command itself can carry a secret in its arguments, such as a token passed to a vault- or pass-style helper. Only the program name is logged now, which is enough to diagnose a misconfiguration.
  • RDP gateway connections failed on FreeRDP 3.x when launched through the CLI — that argument builder still emitted the 2.x aliases /g: and /gu:, which FreeRDP 3.x rejects as "Unexpected keyword" before connecting. The same defect was fixed for the embedded builder in 0.21 (issue #187) and this second one had been missed. It now emits the unified /gateway:g:HOST:PORT[,u:USER], reusing the session credentials and adding a gateway user only when it differs from the session one.
  • External FreeRDP would not start on macOS — the single-use file carrying the RDP session arguments, including the password, was written only to $XDG_RUNTIME_DIR, which macOS does not have, so the launch aborted before the viewer opened. This is the same class of defect fixed for the SPICE .vv file in 0.21.10. The runtime directory now comes from one shared resolver used by the RDP arguments file, the SPICE connection file, the SSH askpass script and secret file, and the ssh-agent key cache: $XDG_RUNTIME_DIR on Linux, the per-user temp directory on macOS. Files are still created 0600 on both.
  • SSH keep-alive defaults reached only GUI terminal sessions — the guide states that ServerAliveInterval=15 and ServerAliveCountMax=3 apply to all SSH sessions, so a dead peer is noticed in about 45 s and reconnect fires promptly (#217), but they were injected only in the GUI spawn path. CLI sessions and standalone tunnels got none unless the connection set them explicitly. They now come from the shared argument builder, so every SSH command RustConn constructs carries them.
  • An empty Custom Option produced an SSH command that would not start — a blank value was emitted as -o Name=, which OpenSSH rejects, so leaving a field empty broke the connection instead of doing nothing. An empty value now means "do not send this option at all". That also supplies the only way to keep a value from your own ~/.ssh/config: a -o on the command line always overrides the config file, so an option RustConn supplies a default for could not otherwise be deferred to it. It works for any option, not only keep-alive.
  • Dynamic Folder entries ignored their documented group sub-group path — the field was parsed but then dropped, so every generated connection landed flat under the base folder. A refresh now walks each entry's /-separated path, creating the nested sub-groups with stable ids so a repeat refresh reuses them instead of duplicating, and places the connection in the leaf. Stale dynamic connections are cleaned up across the folder's whole subtree, and sub-groups a refresh created that no entry uses any more are swept away — folders the user made by hand are never touched. The GUI and CLI share one planner.
  • macOS packaging: H.264 RDP degraded silently, and the icon step failed opaquely — the Homebrew formula neither bundled libopenh264 nor declared it, so the runtime dlopen behind the gfx-h264 feature often found nothing and RDP EGFX/AVC quietly fell back to a slower path; openh264 is a dependency now. Separately, the .app icon was rendered without checking the result, so a bad render made iconutil report only Invalid Iconset twice and abort the install after a full compile — a shared script now verifies every PNG's size before packaging and names the offending file, and both the DMG producer and the formula call it. Also hardened: translations compile with msgfmt --check so a broken catalog fails the build instead of shipping, the post_install schema and icon-cache refresh no longer abort on a cosmetic failure, and the build logs a hint when a bundled GNOME library is newer than the formula's highest known feature rung.

Improved

  • The feature-ladder gate now covers every packaging channel — the GNOME feature ladder (libadwaita/GTK/VTE version → feature flag) is hand-maintained in seven places and the gate compared three of them. The Flatpak and Flathub manifests, which hardcode their feature list because the SDK version is pinned, went unchecked entirely — and that is where a rung was last forgotten, leaving the channel most users install from on an older baseline in silence. The four version-detecting sources must now agree on their thresholds, the three manifests must each name the ladder's highest rung, and thresholds sort by version so a future 1.10 outranks 1.6. Both halves were verified to fail when a rung is missing rather than passing vacuously.

Documentation

  • User Guide corrected against the code — export: the "Include passwords" option and the "Passwords: Encrypted" column were replaced with an explicit note that no export format ever writes a password, the native .rcn included, because a connection stores only a password source. SSH Custom Options: dangerous directives are dropped rather than "passed through if explicitly set" — only the dedicated ProxyJump/ProxyCommand field is honoured. Session logging: the timestamp example now matches the real default. The Smart Folder "Host Pattern" filter matches the host or the connection name, case-insensitively, and a pattern without glob metacharacters also matches as a substring. SPICE clipboard sharing and image compression are documented as having no effect on the external viewer, which has an option for neither: compression is negotiated with the server and clipboard policy belongs to the host. On macOS, SFTP does not open through xdg-open/Dolphin/Nautilus — the guide now points at open(1) and the built-in Midnight Commander mode, and the desktop-handler section is marked Linux-only. Newly documented though long present in code: the Activity Monitor's Command finished mode, automatic reconnect after sleep or a network change with its skip rules, and the built-in dynamic variables.
  • Code comments corrected where they described something else — the PortableEncryptedFile backend does not use the machine-bound backend's per-entry format; it derives one key from the passphrase that unwraps a single shared data key, which is what lets a passphrase change rewrap 32 bytes instead of re-encrypting every credential. The built-in CRITICAL and FATAL highlight rules are deliberately two rules, each independently overridable, not one. The SPICE builders now record why two connection fields never become viewer flags. No behaviour change.
  • Version strings in the docs no longer go stale — copy-paste build commands in docs/MACOS_BUILD.md and docs/INSTALL.md carried a hardcoded 0.21.7 and now use placeholders, the CI-flow diagrams were normalised, and a brittle "relocates 58 Homebrew dylibs" count was replaced with a version-independent description.

Dependencies

  • Updated: bitflags 2.13.1→2.13.2, open 5.4.3→5.4.4, smallvec 1.16.0→1.16.1, toml 1.1.5→1.1.6, toml_edit 0.25.13→0.25.15, uuid 1.26.0→1.26.1. All patch-level; cargo deny check advisories reports no advisories.
  • Unchanged, checked: the bundled Flatpak sources are all current — FreeRDP 3.31.1 (the newest release), cJSON 1.7.19, openh264 2.6.0, VTE 0.80.5, waypipe 0.11.2 — and the local and Flathub manifests carry identical pins. The CLI download catalogue is current on every entry. Snap stage-packages are unpinned by design, so each build takes the current Ubuntu 24.04 archive; the python3.12 security update in USN-8744-1 is picked up by rebuilding and needs no manifest change.

Installation

Flatpak (Recommended)

flatpak install flathub io.github.totoshko88.RustConn

Snap

sudo snap install rustconn

Debian/Ubuntu (.deb from this release)

sudo dpkg -i rustconn_0.21.11_amd64.deb
sudo apt-get install -f  # Install dependencies if needed

Fedora (.rpm from this release)

sudo dnf install rustconn-0.21.11-1.fc44.x86_64.rpm

AppImage

chmod +x RustConn-0.21.11-x86_64.AppImage
./RustConn-0.21.11-x86_64.AppImage

macOS (Homebrew)

brew tap totoshko88/rustconn
brew install rustconn
open $(brew --prefix)/opt/rustconn/RustConn.app

All 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 rustconn

Arch Linux (AUR)

yay -S rustconn

FreeBSD (Ports)

pkg install rustconn

Full installation guide: https://github.com/totoshko88/RustConn/blob/main/docs/INSTALL.md