Replace SSH/SCP/rsync with OpenShell native CLI commands - #761
Conversation
|
fullsend review is working on this — view logs |
Site previewPreview: https://f1805264-site.fullsend-ai.workers.dev Commit: |
Review: #761Head SHA: 79b476b SummaryThis PR replaces all SSH/SCP/rsync FindingsMedium
Low
Info
FooterOutcome: comment-only Previous runReview: #761Head SHA: 70d79fd SummaryClean transport-layer migration from SSH/SCP/rsync exec.Command wrappers to OpenShell native CLI commands. The change is well-structured: security posture improves with FindingsMedium
Low
Info
FooterOutcome: approve Previous run (2)Review: #761Head SHA: 36aa646 SummaryThis PR cleanly replaces SSH/SCP/rsync subprocess wrappers with OpenShell native CLI commands and upgrades path traversal containment from manual FindingsMedium
Low
Info
FooterOutcome: comment-only |
|
Tested locally and LGTM |
|
fullsend review is working on this — view logs |
|
fullsend review is working on this — view logs |
Replace SSH/SCP/rsync exec.Command wrappers with OpenShell's native CLI commands (sandbox exec/upload/download) and os.Root containment for local writes. Addresses fullsend-ai#261. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8 tasks covering sanitizeDownload, Exec/Upload/Download replacements, os.Root containment, and run.go caller migration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all SSH-based transport functions with openshell sandbox exec, upload, and download commands, eliminating the SSH config dependency. - SSH -> Exec: uses `openshell sandbox exec --no-tty --timeout` - SSHStream -> ExecStream: same pattern, streaming to *os.File - SSHStreamReader -> ExecStreamReader: same pattern, returns stdout pipe - SCP -> Upload: uses `openshell sandbox upload` - SCPFrom -> Download: uses `openshell sandbox download` - RsyncFrom -> SafeDownload: Download + sanitizeDownload - Remove GetSSHConfig (no longer needed) - ExtractTranscripts/ExtractOutputFiles: use Exec/Download, replace filepath.Clean+HasPrefix path containment with os.Root - Replace TestPathTraversalContainment with TestOsRootContainment - Add TestExec_OpenshellNotInPath The sshConfigPath parameter is removed from all function signatures. Callers in run.go still reference old functions and will be migrated in a follow-up task. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ownload Remove sshConfigPath plumbing from all internal functions. Update all call sites to use the new sandbox.Exec/Upload/Download/SafeDownload API. SSH config temp file creation and cleanup are no longer needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename sshErr variables to execErr and update stale comments referencing SSH. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
openshell sandbox exec runs commands via sh -c (POSIX sh), where `source` is undefined. Replace with `.` in buildClaudeCommand and buildScanContextCommand. openshell sandbox download fails if the destination file already exists (unlike scp which overwrites). Add os.Remove after os.Root validation in ExtractTranscripts and ExtractOutputFiles so the validation file is cleaned up before download. Restore root.MkdirAll for nested output file parent directories using kernel-enforced containment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
openshell sandbox download always treats the local destination as a directory, creating it and placing the file inside. For single-file downloads this produces path/file.md/file.md instead of path/file.md. Add DownloadFile which downloads to the parent directory and renames when the desired local name differs from the remote basename. Use it in ExtractTranscripts and ExtractOutputFiles. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ExecStream (formerly SSHStream) has had no callers since progress tracking replaced it with ExecStreamReader in 9b4aa18. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename leftover scpErr variable names to dlErr now that SCP is replaced by openshell download. Update architecture diagram to reflect openshell upload/exec instead of SCP/SSH. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add client-side context timeout to Exec() as defense-in-depth against openshell process hangs (matches Upload/Download/ExecStreamReader) - Fix sanitizeDownload to match .git/hooks/ at any depth (submodules), not just at the repo root; return error on RemoveAll failure - Add comments explaining the os.Root probe-and-remove validation pattern - Add test for submodule .git/hooks/ removal Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New upstream OIDC refresh code used sshConfigPath and sandbox.SCP. Remove sshConfigPath parameter from runOIDCRefresh and refreshOIDCToken, replace sandbox.SCP with sandbox.Upload. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
70d79fd to
79b476b
Compare
|
fullsend review is working on this — view logs |
ralphbean
left a comment
There was a problem hiding this comment.
Clean, thorough transport migration. Security properties are preserved or improved (os.Root kernel-enforced containment is strictly stronger than filepath.Clean+HasPrefix). One minor inconsistency noted inline — not blocking.
| // given writer. The caller must read stdout to completion, then call cmd.Wait(). | ||
| func SSHStreamReader(sshConfigPath, sandboxName, command string, timeout time.Duration, stderrW io.Writer) (io.ReadCloser, *exec.Cmd, context.CancelFunc, error) { | ||
| func ExecStreamReader(sandboxName, command string, timeout time.Duration, stderrW io.Writer) (io.ReadCloser, *exec.Cmd, context.CancelFunc, error) { | ||
| ctx, cancel := context.WithTimeout(context.Background(), timeout) |
There was a problem hiding this comment.
[moderate] Exec() adds +10*time.Second to the Go context timeout as defense-in-depth against openshell process hangs, but ExecStreamReader() uses the raw timeout with no buffer. Since ExecStreamReader is used for the main long-running agent session, the same defense-in-depth rationale applies — if openshell's --timeout fails to kill the child, the Go context fires at the exact same moment.
Non-blocking, but worth aligning in a follow-up:
ctx, cancel := context.WithTimeout(context.Background(), timeout+10*time.Second)PR fullsend-ai#761 replaced ssh/scp/rsync with OpenShell native transport (openshell sandbox exec/upload/download), but the Containerfile was not updated. Remove the stale rsync dependency to reduce image size and attack surface. Changes: - Remove rsync from apt-get install in images/sandbox/Containerfile - Remove rsync mention from the Containerfile header comment - Update images/README.md tool list to drop rsync - Update images/code/Containerfile base image comment to list jq instead of rsync Note: make lint could not run due to Go module cache permission errors in the sandbox (infrastructure issue, not related to this change). No Go code was modified. Closes fullsend-ai#1150
Summary
exec.CommandSSH/SCP/rsync wrappers ininternal/sandbox/with OpenShell native CLI commands (sandbox exec,sandbox upload,sandbox download) that use gRPC internallyos.Rootkernel-enforced path containment for local writes inExtractTranscriptsandExtractOutputFiles, replacing manualfilepath.Clean+HasPrefixcheckssanitizeDownloadpost-download cleanup (symlink removal +.git/hooks/deletion) to replace rsync--no-linksand--excludeflagssshConfigPathparameter from all function signatures — no more SSH config temp file creation/cleanupDownloadFilehelper for single-file downloads (openshell always treats destination as directory).instead of bashsourcesince openshell exec runs viash -cExecStreamfunction (unused since progress tracking replaced it)Closes #261
Test plan
make go-test)go vetclean (make go-vet)make lint)fullsend run hello-worldagainst live sandbox — bootstrap, agent execution with streaming, output extraction, transcript extraction, and repo extraction all verified working🤖 Generated with Claude Code