From e252d337d1c19b0a2b8ea13de245f669d5499e05 Mon Sep 17 00:00:00 2001 From: Jaap-Jan de Wit | DodoTech Date: Fri, 14 Aug 2026 09:36:24 +0200 Subject: [PATCH] Take the SSH.NET release that fixes the SCP path traversal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every project in the repository stopped building, on a warning about a package none of them had changed: error NU1903: Warning As Error: Package 'SSH.NET' 2025.1.0 has a known high severity vulnerability GHSA-q939-rpr3-3284 was published after the pin was written. ScpClient.Download in recursive mode takes the filenames the server sends and joins them to the local destination without validating them, so a malicious or compromised SCP server can name "../" its way out of the download directory and write anywhere the client process can — CWE-22, 7.1, and the same shape as OpenSSH's CVE-2019-6111 with the traversal left in. Everything up to and including 2025.1.0 is affected; 2026.0.0 is the fix. ◆ THIS APPLICATION WAS NEVER EXPOSED, AND THE UPGRADE IS STILL THE RIGHT ANSWER. Nothing here constructs a ScpClient. File transfer is SftpClient throughout — SshNetSftpSession is the only path to a remote file — so the vulnerable method has no caller to reach it from. That makes this a hygiene bump rather than an incident, and it is worth saying plainly because the alternative on offer was a scoped NuGetAuditSuppress with that reasoning written beside it. A suppression is what you reach for when there is nothing to upgrade TO. There is: the maintainers shipped the fix, taking it costs two nullable annotations, and a suppression would have left this repository carrying a known-vulnerable library and a comment explaining why that is fine — which stays true only until somebody adds the first ScpClient call and has no reason to look here. BouncyCastle.Cryptography moves 2.6.2 -> 2.7.0 as a consequence, not a preference. CentralPackageTransitivePinningEnabled makes the PackageVersion here the resolved version for a transitive dependency, and SSH.NET 2026.0.0 asks for 2.7.0, so a pin left at 2.6.2 is a downgrade error rather than a version this repository gets to choose. Its comment records the constraint so the next person does not try to walk it back. ◆ THE RELEASE NOTES SAY NO BREAKING CHANGES. THE NULLABLE ANNOTATIONS DISAGREE. ConnectionInfo.CurrentServerEncryption is now string? — correct of them, since it really is null until the key exchange completes — and that is CS8601 at both places this codebase reads a cipher off a live connection. Neither can observe the null: SshNetConnection and SshNetSftpSession are only ever constructed after ConnectAsync has been awaited, which is what their existing remarks already say and why the reads are at construction rather than lazy. Coalesced to string.Empty rather than silenced with !, because empty is already this codebase's word for a cipher that could not be read. MainWindowViewModel's NullIfEmpty exists for exactly that, TerminalWorkspace.GetSessionFacts already hands over string.Empty on the same grounds, and the status bar collapses on it. So the boundary keeps the interface's non-nullable string, no consumer changes, and an unreachable null would degrade the way a real one already does. SshNetConnection's remark asserted that SSH.NET types the property non-nullable and that reading straight through was therefore honest rather than lazy. That sentence is now false, and a comment contradicting the line beneath it is worse than no comment, so it says what is actually true: the coalesce is there for the annotation and the pre-key-exchange window this call site cannot be in. The Android head needed its own restore. It is outside DodoSSH.slnx, so the solution-wide --force-evaluate does not reach it, and left alone it would have kept the vulnerable pin and gone stale under locked mode — the hazard platform-flags.md records, arriving on schedule. Heeding the warning left by the ILLink commit: --force-evaluate on Windows rewrote all 39 lock files with CRLF, and 21 of them had no content change. Only the 18 that really moved are here. Verified with `dotnet build` at the root, no audit-disabling flags: 0 errors, NU1903 gone, and the 21 remaining warnings are the pre-existing Meziantou/CA and Avalonia ones that were present in the failing build too. A library upgrade is not a version string, so the suites that exercise it were run against live containers rather than trusted: 87 Ssh, 80 Terminal, 27 ObjectStore, 10 Transfer, none failing. --- Directory.Packages.props | 16 +++++++++++++--- src/DodoSSH.Client.Android/packages.lock.json | 16 ++++++++-------- src/DodoSSH.Client.App/packages.lock.json | 16 ++++++++-------- .../packages.lock.json | 16 ++++++++-------- src/DodoSSH.Client.Session/packages.lock.json | 16 ++++++++-------- src/DodoSSH.Client.Shell/packages.lock.json | 16 ++++++++-------- .../SshNetConnectionFactory.cs | 7 ++++--- src/DodoSSH.Client.Ssh/SshNetSftpSession.cs | 5 +++-- src/DodoSSH.Client.Ssh/packages.lock.json | 14 +++++++------- src/DodoSSH.Client.Terminal/packages.lock.json | 16 ++++++++-------- src/DodoSSH.Client.Transfer/packages.lock.json | 16 ++++++++-------- tests/DodoSSH.Api.Tests/packages.lock.json | 14 +++++++------- .../packages.lock.json | 16 ++++++++-------- .../DodoSSH.Client.App.Tests/packages.lock.json | 16 ++++++++-------- .../packages.lock.json | 16 ++++++++-------- .../packages.lock.json | 16 ++++++++-------- .../DodoSSH.Client.Ssh.Tests/packages.lock.json | 16 ++++++++-------- .../packages.lock.json | 16 ++++++++-------- .../packages.lock.json | 16 ++++++++-------- .../packages.lock.json | 14 +++++++------- tests/DodoSSH.SystemTests/packages.lock.json | 16 ++++++++-------- 21 files changed, 161 insertions(+), 149 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 0a150ed..5354c42 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -88,8 +88,12 @@ --> - - + + - +