--answer puts credentials in the process list and shell history.
NEXUS_ANSWERS (one answer per line) is read at startup and removed from
the environment so local shells don't inherit it, and either source now
prints a warning when used outside --screenshot (SEC-20261012-018).
- ci.yml declares permissions: contents: read instead of the default
token scope.
- Every action is pinned to the commit its current tag points at
(checkout v4.4.0, upload-artifact v4.6.2, action-gh-release v2.6.2),
so a moved tag can't change what runs with release write access.
- A cargo audit job runs on every push; the single ignored advisory
(RUSTSEC-2023-0071, rsa via russh, no fix available) is recorded with
its rationale in rust/.cargo/audit.toml (SEC-20261012-019, -012).
Release signing and macOS notarisation are not part of this change.
- Quick Connect runs the same validation as saved profiles, so an SSH
connection without a username fails up front with a clear message (F5).
- ~/.ssh/config imports fill a missing User (and jump-host user) with the
local user name, as ssh does, and skip blocks that still don't validate,
reporting how many (F6).
- An unrecognised hostKeyPolicy/lineEnding/parity/flowControl/direction in
a stored config falls back to that field's default instead of failing
the whole JSON column and silently resetting every other setting; parse
failures that remain are logged (F7).
- A JSON export without secrets now blanks login-script 'send' steps that
answer a password-like 'wait' prompt (SSH and Telnet), not just the
proxy password and inline TOTP seed (SEC-20261012-011, partial).
- Credential kinds were string literals in the session manager, profile
editor and secret store; they are now SecretKind with one ALL list, so
the profile-delete purge can't miss a kind (F19).
Moving proxyPassword out of the profile record is left open: it changes
.link/.nexus compatibility with the Go app and needs an owner decision.
An empty vault accepted any password, so a typo at the first unlock
encrypted later secrets under the wrong key. New vaults store a verifier
(a known value sealed under the key) that every unlock must open. Vaults
from Go or older builds keep today's behaviour and gain a verifier on
their next write; Go ignores the extra field (SEC-20261012-016, F8).
- IRC: channel state is kept only for rooms the client itself joined, so
JOIN/PART/KICK/NAMES for other rooms can't grow it; users per channel
are capped at 10 000 and the view keeps at most 200 conversations,
dropping the oldest idle query (SEC-20261012-014, F14).
- Recordings: the list reads the header line and the last 64 KiB for the
duration instead of whole files of up to 250 MiB, falling back to a
full scan only when the final event is longer than the window
(SEC-20261012-020, F9).
- Audit log: keeps the newest 10 000 entries (F17).
Every shared lock was std::sync::Mutex with lock().unwrap() (~150 sites),
so a panic in any session's reader or callback poisoned the lock and the
next UI frame panicked too, dropping every open session. Locks are now
parking_lot::Mutex (no poisoning; already in the dependency tree), and a
panic hook reports worker panics as an error toast instead of the session
silently stopping (F3, F4).
A password typed at a 'saved' prompt was stored before the server
accepted it, so a typo was persisted and silently retried on every
reconnect. Typed values are now held per attempt and written once the
session connects; when the server refuses a saved credential the next
attempt asks again and the new value replaces it on success. A store
failure (keychain refused, vault locked) now shows a toast instead of
being swallowed, and reconnect prompts wait 5 minutes like the first
connect (SEC-20261012-015, F1, F2, F20).
Non-ASCII cells were centred vertically in the cell while ASCII runs were
top-anchored; the cell height is the row height rounded up, so é, ñ, €,
box-drawing and similar glyphs sat lower than their neighbours (more
visibly at larger sizes and on HiDPI). Combining marks on an ASCII base
(e + U+0301, as macOS filenames use) were also dropped by the ASCII run
path; such cells are now drawn individually with their marks.
OSC 52 and VNC ServerCutText now go through one handler that honours
'allow remote clipboard' and always toasts the first line, so a silent
clipboard swap can't set up a paste into another shell (SEC-20261012-010).
Multi-line pastes into a terminal without bracketed paste ask first, with a
preview (SEC-20261012-017). The launch-time update check can be turned off
and the updater docs no longer claim it is opt-in; the unused GitHub
fallback is removed (SEC-20261012-013, F15).
Under the 'ask' policy a changed host key used to be a hard error. The
prompt now shows the stored and received fingerprints and offers Reject,
Accept Once (connect, keep the record) and Accept & Update (replace the
record; audited). Unknown keys gain an Accept Once option too. Strict and
accept-new still refuse changed keys without prompting. Known-host rows
written by the old app are confirmed removable by id (test).
The NickServ password (and any other "remember for this session" secret) was
resolved through a fresh per-connect cache, so every auto-reconnect prompted
again with a 60 s timeout and an unanswered prompt failed the reconnect.
Session-only secrets now live on the session, survive auto and explicit
reconnects, and IDENTIFY is sent on every registration as before.
- The VNC client refuses desktop sizes over 64 Mpx (ServerInit and
DesktopSize/ExtendedDesktopSize) and ZRLE rectangles over 64 MiB before
allocating, so a hostile server can't force a multi-gigabyte allocation.
- Release URLs from the update manifest and repository URLs from the GitHub
API are handed to the system opener only when they are http(s) links.
- SSH key pairs are generated with ssh-key (Ed25519/ECDSA P-256/RSA) and
encrypted in-process, so the passphrase no longer appears as an ssh-keygen
command-line argument visible in the process list.
- open_tunnel's loopback bridge checks the accepted peer is our own client
socket, so a local process racing for the ephemeral port can't be spliced
into the IRC/VNC SSH tunnel.
- run_command caps buffered output at 1 MiB; local-forward abort handles are
pruned as connections finish; SOCKS5 host/credential lengths over 255 bytes
are rejected instead of being truncated into a malformed request.
- Secrets::delete_profile removes every profile credential kind from the OS
keychain as well as the vault (previously only the vault was purged, so
keychain passwords/tokens outlived the profile).
- write_private writes to a sibling temp file and renames it into place, so a
crash mid-write cannot truncate the vault or an export, and a pre-existing
world-readable file is replaced rather than reused.
- Argon2-derived vault/export keys are Zeroizing and wiped on drop.
- Recordings (which capture typed input), JSON exports and --data-dir
directories are created owner-only.