- ircformat: an unterminated OSC made the stripper advance two bytes and
rescan the rest, so one hostile 1 MiB line of ESC ] froze the UI thread
for minutes. It now stops at the first OSC with no terminator (none can
follow), which is linear.
- Server lines are capped at 64 KiB (512 bytes + 8 KiB of tags is the
real ceiling) instead of 1 MiB.
- Spoofed self-JOINs can't grow channel state past 500 rooms (they were
also persisted and auto-joined next time); CAP LS is ignored after
registration and its gathered list is capped.
- PASS, SASL PLAIN and NickServ IDENTIFY are only sent over TLS, an SSH
tunnel or a loopback server; otherwise the Server tab says why.
- TcpConn: a local close no longer races the reader into reporting a
spurious read error (regression from 00f110f, which shut the socket
down before taking the stream).
Registration now opens with CAP LS 302 (servers without IRCv3 ignore it
and register as before). From the offered capabilities (multi-line LS
lists included) the client requests server-time and, when the profile
enables it, sasl; then AUTHENTICATE PLAIN with the account (defaulting
to the nick) and a stored SASL password (irc-sasl-password), base64 in
400-byte chunks, and CAP END on success or failure — failures are shown
on the Server tab and the connection carries on (NickServ still works).
server-time tags stamp events with the server's clock, so replayed
history shows real times.
Tested against the fake IRC server: multi-line LS, REQ/ACK, the PLAIN
exchange, 903, CAP END, and a tagged message's timestamp.
rust/fuzz (cargo-fuzz, nightly, its own workspace with the same IronRDP
patches) has targets for the Telnet IAC parser, the IRC parser + state
machine + mIRC formatter, VNC framebuffer updates and decoders,
~/.ssh/config parsing and the update manifest. They reach the private
parsers through hidden entry points behind nexus-core's 'fuzzing'
feature. A nightly workflow runs each for two minutes and uploads any
crashing input. A first 45 s run of each locally found nothing.
Every client's reader thread (Telnet/RAW, serial, local shell, IRC, VNC,
RDP) and SSH's channel reader task now run under clients::guarded /
guarded_task. A panic there, such as a decoder bug triggered by a
hostile server, reports 'internal error: …' through the session's
on_error and then closes it, so the tab shows the error (and
auto-reconnect can run) instead of looking connected with nothing
reading. Other sessions were already unaffected since the switch to
parking_lot locks.
Seeded pseudo-random input, biased towards each parser's control bytes,
is fed to the IRC line parser and state machine (plus mIRC formatting),
Telnet IAC negotiation, VNC framebuffer updates (including ZRLE behind a
real zlib stream, with a check that the decoders are actually reached),
~/.ssh/config parsing and the update manifest. None panic, and Telnet's
pending buffer stays bounded.
These run on stable in every CI run; they are not coverage-guided, so a
cargo-fuzz setup (nightly) remains a possible follow-up.
- 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).