Skip to content

feat(ssh): add ssh/scp/sftp builtins with russh transport#945

Open
chaliy wants to merge 8 commits intomainfrom
claude/ssh-supabase-bashkit-9YgaZ
Open

feat(ssh): add ssh/scp/sftp builtins with russh transport#945
chaliy wants to merge 8 commits intomainfrom
claude/ssh-supabase-bashkit-9YgaZ

Conversation

@chaliy
Copy link
Copy Markdown
Contributor

@chaliy chaliy commented Apr 1, 2026

Summary

  • Add SSH support as opt-in feature (ssh), following the same pattern as git and http_client
  • SshHandler trait for pluggable transport, default RusshHandler backed by russh 0.52
  • SshAllowlist with glob patterns (*.supabase.co) and port restrictions (default-deny)
  • SshConfig builder: timeouts, response limits, session limits, default user/key/password
  • ssh builtin: remote command exec, heredoc, shell sessions (ssh supabase.sh)
  • scp builtin: upload/download between VFS and remote hosts
  • sftp builtin: non-interactive put/get/ls via heredoc/pipe
  • Auth: none (public services), pubkey, or password — tried in that order
  • Example: ssh supabase.sh — no credentials needed
  • 33 integration tests via mock handler + 2 real connection tests
  • Shell injection fix (TM-SSH-008): all remote paths are shell-escaped

Test plan

  • 33 builtin integration tests (ssh_builtin_tests.rs) covering: basic exec, user@host, shell sessions, heredoc, blocked hosts, wildcards, port flags, identity flags, pipe output, variable capture, SCP upload/download/errors, SFTP put/get/ls/errors, not-configured errors
  • 2 real connection tests (ssh_supabase_tests.rs): ssh supabase.sh connect + allowlist rejection
  • 13 allowlist unit tests, 3 config tests, 6 client tests
  • All 1979+ existing lib tests pass with --features ssh
  • cargo fmt --check clean
  • cargo clippy --features ssh -- -D warnings clean
  • CI: example + integration tests run in Examples job

@chaliy chaliy force-pushed the claude/ssh-supabase-bashkit-9YgaZ branch 7 times, most recently from b58d953 to 2a60791 Compare April 3, 2026 03:01
chaliy added 8 commits April 3, 2026 03:11
…wlist

Adds SSH support as an opt-in feature (`ssh`), following the same
pattern as `git` and `http_client`. Includes:

- SshHandler trait for pluggable transport (mock, proxy, russh)
- SshAllowlist with glob patterns (*.supabase.co) and port control
- SshConfig builder with timeouts, response limits, session limits
- SshClient enforcing allowlist + resource limits before delegation
- ssh builtin: remote command execution with -p, -i, heredoc support
- scp builtin: file upload/download between VFS and remote hosts
- sftp builtin: non-interactive put/get/ls via heredoc/pipe
- Spec 015-ssh-support.md with threat model (TM-SSH-001..008)
Adds an example demonstrating SSH/SCP/SFTP with a mock Supabase
handler (psql queries, pg_dump, file transfers, security blocking).
Runs in CI alongside git_workflow.
Replace mock handler with default RusshHandler backed by russh crate.
SSH now works out of the box — no custom handler needed.

- Add russh 0.52 + russh-keys 0.49 as optional deps (behind ssh feature)
- RusshHandler: password auth, pubkey auth, exec/upload/download
- Upload/download via base64-piped remote commands
- SshClient uses RusshHandler as default when no custom handler set
- SshConfig.default_password() for env-based credential injection
- Example connects to real SSH host via SSH_HOST/SSH_PASSWORD env vars
- CI: real SSH step with continue-on-error (needs GitHub secrets)
… test

- Add SshHandler::shell() for no-command sessions (TUI services)
- RusshHandler::shell() requests PTY + shell, captures output
- ssh builtin calls shell() when no command given (instead of error)
- SshConfig::default_private_key() for key injection without -i flag
- Builtins fall back to config key when no -i specified
- Example: literally runs `ssh supabase.sh` with DEPLOY_SSH_KEY
- Integration test: ssh_supabase_tests.rs (connects + verifies allowlist)
- CI: runs example + test with DEPLOY_SSH_KEY secret (no skip, no continue-on-error)
- RusshHandler tries "none" auth when no key/password given
- Works for public SSH services like supabase.sh
- Example simplified: just SshConfig::new().allow("supabase.sh")
- No env vars, no secrets, no DEPLOY_SSH_KEY
- CI: no secrets needed for example or integration test
- Create crates/bashkit/docs/ssh.md covering ssh/scp/sftp usage
- Embed as ssh_guide module via include_str! (feature-gated)
- Covers: remote exec, heredoc, shell sessions, SCP, SFTP, allowlist,
  auth methods, resource limits, custom handlers, flag reference
- Add .cargo/audit.toml with RUSTSEC-2023-0071 ignore for cargo-audit
- Mark ssh_supabase_connects test as #[ignore] (needs network)
- CI: run mock tests normally, real SSH with continue-on-error
- Fix curl.rs missing ssh_client field from rebase
@chaliy chaliy force-pushed the claude/ssh-supabase-bashkit-9YgaZ branch from d111489 to e1169ff Compare April 3, 2026 03:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant