Skip to content

update: bump workspace deps and sync russh forks to upstream stable#203

Merged
inureyes merged 1 commit into
mainfrom
update/upgrade-deps-sync-russh-forks
May 18, 2026
Merged

update: bump workspace deps and sync russh forks to upstream stable#203
inureyes merged 1 commit into
mainfrom
update/upgrade-deps-sync-russh-forks

Conversation

@inureyes
Copy link
Copy Markdown
Member

Summary

Workspace-wide dependency upgrade pass plus a controlled sync of both internal russh forks to upstream stable, picking up CVE-2026-46673 in the process.

Main bssh crate

  • lru 0.17.0 → 0.18.0 (lifetime fix in get_or_insert_mut_ref; no API surface change for our LRU usage in src/ssh/config_cache/manager.rs).
  • signal-hook 0.3 → 0.4 (the only breaking change is low_level::pipe which we don't use; consts::SIGWINCH and iterator::Signals are stable across the bump).
  • opentelemetry / opentelemetry_sdk / opentelemetry-otlp 0.31 → 0.32 (the removed ExportConfig / HasExportConfig trio isn't called from our src/server/audit/otel.rs; WithExportConfig::with_endpoint, LogExporter::builder().with_tonic(), SdkLoggerProvider::builder(), Resource::builder(), and the LogRecord API all remain source-compatible). Note: 0.32 now errors when an https:// endpoint is configured without a TLS feature; our current features stay at [grpc-tonic, logs] because all uses today are http:// — a follow-up will be needed if anyone runs the audit exporter against TLS in production.
  • nix 0.31.2 → 0.31.3 plus transitive bumps in pin-project, tower-http, and zerofrom via cargo update.

bssh-russh fork: 0.60.1 → 0.60.3

  • Bumps russh-cryptovec 0.59.0 → 0.60.3 and aws-lc-rs 1.16.3 → 1.17.0. The cryptovec bump carries the CryptoVec hardening half of CVE-2026-46673 (compression ZIP-bomb DoS).
  • Forward-ports the SSH-agent half of CVE-2026-46673 (upstream commit a2d48a7) into src/keys/agent/{client,server}.rs since stable v0.60.3 includes it but our fork was based on 0.60.1 and did not. Adds MAX_AGENT_FRAME_LEN = 256 * 1024, factors out read_frame(), and rejects oversized peer frame lengths with Error::AgentProtocolError before resizing the receive buffer. The same change is recorded as crates/bssh-russh/patches/agent-frame-length-cap.patch so sync-upstream.sh's reverse-apply dry-run will auto-skip it on the next sync.
  • Confirms the two unreleased upstream fixes our previous forward-port (PR update: upgrade deps and forward-port unreleased upstream russh fixes #193) brought in are now in upstream v0.60.2 (#690 SHA-1 MAC exclusion, #693 channel write ordering), so no double-apply.

bssh-russh-sftp fork: 2.1.1 → 2.1.2

  • Full source sync to upstream russh-sftp 2.1.2 (src/ replaced by upstream tree). Upstream 2.1.2 absorbed the original serde_bytes perf fix on protocol::Write.data and protocol::Data.data plus the matching serialize_bytes implementation, so patches/sftp-serde-bytes-perf.patch is moved to patches/historical/ for provenance.
  • The fork's remaining custom value-add is two pipelined File I/O helpers, write_all_pipelined(&mut reader, max_inflight) and read_to_writer_pipelined(&mut writer, max_inflight). These drive reader → file and file → writer transfers with bounded in-flight chunks to hide per-request RTT, mirroring how OpenSSH's sftp client keeps ~64 outstanding requests by default. They were undocumented in the previous Cargo.toml description (only the perf patch was mentioned), which almost caused them to be lost when this PR initially tried to drop the fork. They are now re-ported on top of upstream 2.1.2's new Features API: chunk size derives from features.limits.{write,read}_len or, when unadvertised, from features.max_packet_len.saturating_sub({WRITE,READ}_OVERHEAD_LENGTH) instead of the removed MAX_*_LENGTH constants.
  • crates/bssh-russh-sftp/Cargo.toml replaces flurry with dashmap 6.1.0, adds serde_bytes as a direct dep, keeps futures (used by our pipelined helpers' FuturesUnordered), pins versions to upstream's set, and updates the package description to call out both the absorbed perf fix and the still-custom pipelined helpers — so the next maintainer doesn't repeat the near-miss of deleting the fork without realizing the pipelined methods aren't in upstream.

Security

  • CVE-2026-46673 is closed for the SSH-agent peer path by the forward-ported frame-length cap, and for the cryptovec path by the russh-cryptovec 0.60.3 bump.
  • cargo audit against the latest 1090-advisory RustSec database reports 0 vulnerabilities and 0 unmaintained/yanked warnings after the upgrade.

What is not bumped

The remaining "outdated" direct deps in bssh-russh (aes 0.9, hmac 0.13, sha1/sha2 0.11, digest 0.11, cbc/ctr/des new majors, pkcs5/pkcs8 stable, and the RC family ecdsa rc.18 / rsa rc.18 / elliptic-curve rc.32 / ed25519-dalek pre.7) are blocked by RustCrypto's coordinated release model — they share trait crates, and the pre-release crypto crates we depend on still require the old trait versions. Upstream russh stable v0.60.3 carries the same constraints, so we deliberately stay aligned with it. Transitive lru@0.16.4 and signal-hook@0.3.18 are pulled in by ratatui-core / crossterm respectively and will resolve themselves whenever those upstream crates bump.

Test plan

  • cargo build — clean
  • cargo check --all-targets — clean
  • cargo clippy -p bssh --bins -p bssh-russh-sftp --lib -- -D warnings — clean
  • cargo fmt — applied
  • cargo test --lib server::audit::otel — 5 pass / 2 ignored (require a live OTLP collector)
  • cargo test --lib ssh::config_cache — 14 pass
  • cargo test --lib pty:: — 195 pass
  • cargo test -p bssh --lib ssh:: — 280 pass
  • cargo audit — 0 vulnerabilities, 0 warnings against 1090-advisory DB

…table

bssh deps: lru 0.17→0.18 (lifetime fix), signal-hook 0.3→0.4 (only the unused low_level::pipe API changed), opentelemetry / opentelemetry_sdk / opentelemetry-otlp 0.31→0.32 (source-compatible at our LogExporter / SdkLoggerProvider / Resource builders + LogRecord call sites; the removed ExportConfig / HasExportConfig trio isn't used here), plus nix 0.31.2→0.31.3 and transitive pin-project / tower-http / zerofrom patches via cargo update.

bssh-russh 0.60.1→0.60.3: track upstream stable. Bump russh-cryptovec 0.59.0→0.60.3 (carries the CryptoVec hardening half of CVE-2026-46673) and aws-lc-rs 1.16.3→1.17.0. Manually forward-port the agent-frame-length-cap half of CVE-2026-46673 (commit a2d48a7) into src/keys/agent/{client,server}.rs — adds MAX_AGENT_FRAME_LEN = 256 KiB, factors out read_frame(), and rejects oversized frames with Error::AgentProtocolError before allocating. Recorded as patches/agent-frame-length-cap.patch so the next sync-upstream.sh run will auto-skip it via the reverse-apply dry-run check.

bssh-russh-sftp 2.1.1→2.1.2: full source sync to upstream 2.1.2. Upstream absorbed the original serde_bytes perf fix in protocol/{write,data}.rs and the matching serialize_bytes implementation; the patch is moved to patches/historical/ for provenance. The fork's remaining value-add is two pipelined File I/O helpers (write_all_pipelined / read_to_writer_pipelined) that drive reader→file and file→writer transfers with bounded in-flight chunks to hide per-request RTT; they were undocumented before and have now been re-ported on top of the new Features API — chunk size derives from features.limits.{write,read}_len or features.max_packet_len.saturating_sub({WRITE,READ}_OVERHEAD_LENGTH) instead of the removed MAX_*_LENGTH constants. Cargo.toml replaces flurry with dashmap, adds serde_bytes as a direct dep, keeps futures (used by our pipelined helpers' FuturesUnordered), and pins versions to upstream's set. The fork description now records both the absorbed perf fix and the still-custom pipelined helpers so future maintainers won't repeat the mistake of dropping the fork without realising the pipelined methods aren't in upstream.

Verification: cargo build, cargo check --all-targets, and cargo clippy -p bssh --bins -p bssh-russh-sftp --lib -- -D warnings all clean. Affected-module tests pass — server::audit::otel 5, ssh::config_cache 14, pty:: 195, ssh:: 280. cargo audit reports 0 vulnerabilities and 0 unmaintained/yanked warnings against the 1090-advisory database after the bumps.
@inureyes inureyes added status:review Under review priority:high High priority issue type:security Security vulnerability or fix type:dependency Dependency updates labels May 18, 2026
@inureyes inureyes merged commit 39cff4a into main May 18, 2026
1 of 2 checks passed
@inureyes inureyes deleted the update/upgrade-deps-sync-russh-forks branch May 18, 2026 15:53
inureyes added a commit that referenced this pull request May 18, 2026
The fork's inline test modules (src/client/test.rs, src/keys/mod.rs, src/tests.rs) were imported from upstream russh during the initial sync (commit 508aa3f, "Sync bssh-russh fork with upstream russh 0.60.0") but the matching [dev-dependencies] block was never copied across, so every `cargo test -p bssh-russh` since the fork's inception has failed with E0433 on `env_logger`, `tempfile`, and cascading E0282 type-inference errors where helper functions return `tempfile::TempDir`. The test target compiled exactly zero times under our manifest until now.

Adds a minimal [dev-dependencies] block covering only what the imported tests actually reference: `env_logger = "0.11"` for `env_logger::try_init()` in `src/client/test.rs`, `tempfile = "3"` for `tempfile::tempdir()` / `tempfile::TempDir` in `src/keys/mod.rs` and `src/tests.rs`, and `tokio = { version = "1.52.1", features = ["process", "macros"] }` as an additive entry that merges with the main tokio dep to add the `process` feature (needed by the spawn-ssh-agent helpers that call `tokio::process::Command::new("ssh-agent").spawn()`) and the `macros` feature (needed by `#[tokio::test]`). Other tokio surfaces the tests use — `tokio::net::{TcpStream, UnixListener, UnixStream}`, `tokio::sync::oneshot`, `tokio::time::sleep` — were already covered by the main dep's `net`/`sync`/`time` features.

Verification: `cargo test -p bssh-russh --lib` now runs 75 tests (0 failed, 0 ignored) covering the agent client/server roundtrip (test_agent, test_client_agent_{ed25519,rsa,openssh_rsa}, test_request_identities_full_with_keys_and_certs, test_sign_request_{,cert,cert_rsa,cert_rsa_sha512,cert_missing_key_returns_agent_failure,missing_key_returns_agent_failure}), PKCS#8 / OpenSSH key decoding (test_decode_pkcs8_p{256,384,521}_secret_key, test_decode_ed25519_{,aesctr}_secret_key, test_pkcs8_encrypted, format::test_ec_private_key), channel lifecycle (channels::test_{channel_objects,channel_streams,server_channels,channel_window_size,server_receives_close_on_client_close}), and protocol-level paths (gex::peer_request_accepts_rfc4419_minimum_when_server_can_choose_stronger_group, gex::local_client_config_still_rejects_minimum_below_2048, compress::compress_local_test, future_certificate::test_future_certificate_auth_full_flow, server_kex_junk::server_kex_junk_test). Full workspace aggregate is now 1871 passed / 0 failed / 10 ignored, up from 1796 on PR #203 because the 75 newly-runnable bssh-russh tests are now contributing.

Note: `cargo clippy --workspace --tests -- -D warnings` does report ~250 warnings on the newly-reachable test code (mostly `clippy::panic` on `panic!("Unexpected message ...")` arms and similar idioms that upstream russh writes routinely in their test scaffolding). They are inherited verbatim from upstream and don't affect runtime behavior; cleaning them up is a separate follow-up so this PR can stay focused on unblocking the test target.
inureyes added a commit that referenced this pull request May 18, 2026
)

CI on rustc 1.95 fails `cargo clippy -- -D warnings` with `clippy::useless_conversion` on `client/session.rs:194` because `Iterator::chain` already accepts any `IntoIterator`, so the explicit `.chain(files.into_iter())` is redundant. The lint did not exist (or was less aggressive) on rustc 1.93 where PR #203 was developed, so the issue only surfaced after upstream toolchain bump.

The line was imported verbatim from upstream russh-sftp 2.1.2 when we full-source-synced the fork in PR #203. Upstream has the same lint waiting to fire whenever they bump their MSRV / clippy. Fix is the obvious one — `.chain(files)` instead of `.chain(files.into_iter())`. Functionally identical, just lets the trait do its job.

Verified locally on rustc 1.95.0 (matching CI): `cargo clippy -- -D warnings` clean, `cargo clippy --workspace --lib -- -D warnings` clean, `cargo test --lib` 1233 passed / 0 failed / 9 ignored (matches CI's `cargo test --lib --verbose` step exactly). No other `useless_conversion` hits in the workspace's lib targets under 1.95.

The same change is worth filing upstream against AspectUnk/russh-sftp so future syncs don't have to re-port this; tracking that as a follow-up rather than blocking this CI fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority:high High priority issue status:review Under review type:dependency Dependency updates type:security Security vulnerability or fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant