Skip to content

Triage: external vulnerability report against 0.1.2-alpha.6 (13 findings) #157

Open

Description

@Sadykhzadeh

Triage of an external report of 13 findings, written against 0.1.2-alpha.6 (russh 0.46, tauri 2.11.5, russh-sftp 3.0.0). Each finding was re-checked against current main (894ea12) and the dependency versions in Cargo.lock: russh 0.63.3, russh-cryptovec 0.62.0, russh-sftp 3.0.1, tauri 2.12.1.

Summary

Key Finding Report Verdict Where
C1-A1-H1 read_key_file reads any local path high fixed on main #107: the command is gone; pick_key_file opens the native dialog from Rust
C1-A1-H2 read_key_file size check bypassed by procfs files low fixed on main #107 (no page-supplied path) + #120 (cap enforced on the read itself)
C1-A2-H1 sftp_download_to_local writes any local path high fixed on main #107: the backend opens the save dialog itself
C1-A2-H2 sftp_upload_from_local reads any local path medium fixed on main #107: single-use tokens from a native pick
C4-A2-H1 server-chosen name steers the save dialog's path high fixed on main, hardened further #107 + #120 (remote_file_name); bidi and device-name hardening in #155
C3-A2-H2 dialog:default lets the page fake host-key prompts high fixed on main #107 removed every dialog grant; #123 adds a test that keeps them out
C3-A1-H1 curve25519 f length ≠ 32 panics under panic = "abort" high fixed by upgrade russh 0.63.3 checks the length (kex/curve25519.rs:115)
C3-A1-H2 all-zero X25519 secret panics in the mpint encoder high fixed by upgrade russh 0.63.3 rejects a zero secret and fixed encode_mpint
C4-A3-H1 russh-sftp read_packet(stream, u32::MAX) allocation medium fixed by upgrade russh-sftp 3.0.1 caps packets at 256 KiB
C3-A2-H1 held host-key change outlives the pinned key verifying again high valid (medium) #137 → #138
C4-A3-H2 endless read_dir under the manager-wide SFTP mutex medium valid (medium) #139 → #140 (narrower lock, 30 s deadline, vault-reset drain restored)
C4-A1-H1 unbounded terminal-output queue in the Tauri channel high partially valid (medium) tauri 2.12.1 made the queue per-webview and clears it on close, but it is still unbounded while the webview lives. #158 → #159 (credit-based flow control, stacked on #140)
C1-A3-H1 host and identity writes persist without confirmation high valid (low on main) #122 (open) adds the native confirmation
C1-A2-H3 state files have no integrity check high won't fix see below

Notes

Severity. Most renderer-side findings assume script running in the webview. No XSS path was found: there is no v-html / innerHTML / eval, the CSP has no unsafe-eval and no remote scripts, and xterm has no link addon. Script in the page can also already open a local shell through create_local_terminal + session_write (#30), which outranks every file-access finding. So the "high" ratings above are mostly overstated for current main. The dependency DoS findings require the user to connect to the hostile server (UI:R, not UI:N).

State file integrity (won't fix).

Release. 0.1.2-alpha.6 is still the latest release, and it predates #107 and #120. Everything marked "fixed on main" reaches users only with the next release.

Found during the same pass (not in the report)

Activity

  1. Sadykhzadeh commented on Oct 17, 2026

    @Sadykhzadeh
    MemberAuthor

    @computerbox124, here is the merge order for the open security and hardening PRs.

    I trial-merged all of them on top of main in this order, together with #122 and #123, and ran cargo test --workspace on the result. Everything passes except the expected ACL check between #123 and #159, described below.

    Order

    1. Consolidate the four open dependency bumps and clear the npm production audit #128: dependency bumps, plus the rustls/quinn-proto fix for this week's RUSTSEC advisories. It is the only PR with a green security-audit; every other PR shows it red only because of the advisories this one fixes. Merge it first.
    2. Withdraw a held host key change once the pinned key answers again #138: host-key change fix. Independent.
    3. Keep the session maps unlocked across network I/O #140: SSH/SFTP session locking. The base for three stacked PRs.
    4. Leave the original file alone when a download fails, and widen upload writes #142, Refuse channels the server opens back, and type the startup command into the shell #148, Stop reading SSH output the webview has not caught up with #159: their base is fix/session-lock-scope (Keep the session maps unlocked across network I/O #140). After Keep the session maps unlocked across network I/O #140 merges, switch each base to main (Edit → base); GitHub may also retarget them automatically when the branch is deleted. Merge in any order. CI only runs once the base is main.
    5. Bind update signatures to their version and take manifests only from our releases #144: updater signed version. Independent, but see the release note below.
    6. Build against the committed Cargo.lock, bound CI jobs, and keep the updater key out of local release builds #146: CI/release hardening and CODEOWNERS (lists you and @Sadykhzadeh). Independent.
    7. Harden terminal input against hostile pastes, links and reordering #150, Wipe every buffer a vault save serializes private keys into #152, End and reap a local shell when its tab closes, and drop the unused os and process plugins #154, Stop a server-chosen file name from disguising itself in the save dialog #155: independent, any order.
    8. Ask before a host or identity write lets a vault key sign in somewhere new #122, Put the app's commands behind the capability ACL, trim plugin grants, keep the window on the app's pages #123: these go last, for the two reasons below.

    Conflicts to expect

    Everything else merges cleanly in this order.

    Before the next release

  2. Sadykhzadeh commented on Oct 17, 2026

    @Sadykhzadeh
    MemberAuthor

    @computerbox124, an update to the merge order above, now covering everything opened since. I trial-merged all open PRs onto main in this order, then checked every conflict pairwise to see which PR it actually collides with.

    Order

    1. Consolidate the four open dependency bumps and clear the npm production audit #128 + Suppress the updater's zip extraction advisories with a review date #186. Together they turn security-audit green. Consolidate the four open dependency bumps and clear the npm production audit #128 fixes the rustls/quinn-proto advisories. Suppress the updater's zip extraction advisories with a review date #186 suppresses three new zip advisories that tauri-plugin-updater pins us to: no upstream fix exists, and the vulnerable path is only reachable with a validly signed update. Any order between the two.
    2. Security/core batch from the earlier comment: Withdraw a held host key change once the pinned key answers again #138, Keep the session maps unlocked across network I/O #140 → Leave the original file alone when a download fails, and widen upload writes #142/Refuse channels the server opens back, and type the startup command into the shell #148/Stop reading SSH output the webview has not caught up with #159, Bind update signatures to their version and take manifests only from our releases #144, Build against the committed Cargo.lock, bound CI jobs, and keep the updater key out of local release builds #146, Harden terminal input against hostile pastes, links and reordering #150, Wipe every buffer a vault save serializes private keys into #152, End and reap a local shell when its tab closes, and drop the unused os and process plugins #154, Stop a server-chosen file name from disguising itself in the save dialog #155. Unchanged.
    3. Core, new:
    4. Frontend, in this order:
      1. Ask for destructive confirmations in the page, since window.confirm never blocks in the app #172: in-app confirmations. This is the important one: window.confirm never blocks in the packaged app (Confirmations never block: the dialog plugin turns window.confirm into a Promise #168).
      2. Keep keyboard focus inside dialogs, close them on Escape, and keep forms open on a backdrop click #181: dialog keyboard handling. Stacked on Ask for destructive confirmations in the page, since window.confirm never blocks in the app #172; retarget to main after Ask for destructive confirmations in the page, since window.confirm never blocks in the app #172 merges.
      3. Guard form saves against double clicks, surface failures, and drop imported keys #165: form saves. Conflicts with Ask for destructive confirmations in the page, since window.confirm never blocks in the app #172/Keep keyboard focus inside dialogs, close them on Escape, and keep forms open on a backdrop click #181.
      4. Count terminal typing for auto-lock, mask hosts in errors, and keep UI state honest after failures #167: auto-lock, masking, vault state. Still has one open thread: its Workspace Open confirm moves to confirmAction after Ask for destructive confirmations in the page, since window.confirm never blocks in the app #172.
      5. Keep SFTP entry actions on the directory they were picked from #163: SFTP stale targets. Conflicts with Ask for destructive confirmations in the page, since window.confirm never blocks in the app #172/Keep keyboard focus inside dialogs, close them on Escape, and keep forms open on a backdrop click #181/Count terminal typing for auto-lock, mask hosts in errors, and keep UI state honest after failures #167.
      6. Tell a failed or unfinished load apart from an empty host, identity or workspace list #179: list load/error states.
      7. Show row actions on keyboard focus and touch, name unlabeled controls, and allow selecting fingerprints #182: a11y quick wins.
      8. Make the empty terminal readable and startable, and stop presenting reset as passphrase recovery #183: empty terminal + vault copy.
      9. Leave Ctrl+F, Ctrl+K and Ctrl+arrows to the shell on Linux and Windows #180: platform terminal shortcuts. Conflicts with Ask for destructive confirmations in the page, since window.confirm never blocks in the app #172/Keep keyboard focus inside dialogs, close them on Escape, and keep forms open on a backdrop click #181 in App.vue.
      10. Make a terminal pane one warm surface with inset output and a tuned palette #184: terminal surface redesign. Conflicts with Show row actions on keyboard focus and touch, name unlabeled controls, and allow selecting fingerprints #182 in TerminalPane.vue.
      11. Add an appearance setting (System / Light / Dark) and fix the dark palette's switches and red text #187: appearance setting (System/Light/Dark).
      12. Let the command palette open hosts, Files and the vault lock, as a real listbox #188: command palette. Conflicts with Count terminal typing for auto-lock, mask hosts in errors, and keep UI state honest after failures #167.
    5. Tooling, any time: Wait for the GPU renderer in e2e, and fix the harness's dev-server probe and snapshot #171.
    6. Ask before a host or identity write lets a vault key sign in somewhere new #122 and Put the app's commands behind the capability ACL, trim plugin grants, keep the window on the app's pages #123 last, as before. Ask before a host or identity write lets a vault key sign in somewhere new #122 conflicts with Test the real terminal geometry path, and drop dead code and stale docs #161, Fail closed when a state file cannot be checked, and never create a vault over an existing one #174, Sign in through keyboard-interactive, explain rejections, and dial each address with its own timeout #177, Ask for destructive confirmations in the page, since window.confirm never blocks in the app #172, Keep keyboard focus inside dialogs, close them on Escape, and keep forms open on a backdrop click #181 and Guard form saves against double clicks, surface failures, and drop imported keys #165. Put the app's commands behind the capability ACL, trim plugin grants, keep the window on the app's pages #123 conflicts with End and reap a local shell when its tab closes, and drop the unused os and process plugins #154, the capabilities/default.json case already noted above.

    Conflicts

    Every conflict above is in a file both PRs touch for unrelated reasons; none of them changes behaviour. Merge in the order above and leave the rest to me. After each merge I'll rebase whichever open PR then conflicts, rerun its tests, and push, so the next one is always mergeable.

    Bot review threads

    Every Qodo thread on these PRs has been answered: fixed with a test, or replied to with the reasoning. The one exception is #167's Workspace Open thread, which waits for #172.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions