Skip to content

Conversation

@dytsou
Copy link

@dytsou dytsou commented Jan 12, 2026

Description

This PR introduces a comprehensive Raycast extension for file transfer using rsync with SSH config integration. The extension provides three main commands for seamless file management between local and remote servers.

Key Features

  1. Upload Files via Rsync - Transfer files from local system to remote servers
  2. Download Files via Rsync - Transfer files from remote servers to local system
  3. Browse Remote Files - Browse and navigate remote server directories with file listing

Technical Highlights

  • Rsync Integration: Uses rsync instead of SCP for better performance, delta transfers, and progress reporting
  • SSH Config Integration: Automatically parses ~/.ssh/config for seamless host selection
  • Advanced Rsync Options:
    • -h (human-readable): Display file sizes in KB, MB, GB format
    • -P (progress): Real-time progress updates with transfer speed and remaining time
    • --delete: Synchronize deletions (optional, use with caution)
  • Real-time Progress: Shows live transfer progress when -P flag is enabled
  • Comprehensive Error Handling: User-friendly error messages for common SSH/rsync issues
  • Path Navigation: Browse remote directories with copy-to-clipboard functionality

Testing

  • 181 tests passing (unit, component, and E2E)
  • Comprehensive E2E test coverage for all workflows
  • All linting and TypeScript checks passing
  • Build verified and tested in Raycast

Screencast

ScreenRecording.mov

Checklist

- test: Add end-to-end tests for Rsync command options, validating flag inclusion and command structure for various transfer scenarios.
- feat: Add Rsync options for file transfer configuration, including human-readable sizes, progress display, and deletion of extraneous files. Update transfer execution to support real-time progress updates and enhance user feedback with formatted output messages.
- refactor: Migrate from SCP to Rsync for file transfer extension, enhancing performance and features. Update documentation and commands accordingly.
- test: Add end-to-end tests for browsing functionality, validating remote paths and host configurations
- feat: Add browsing functionality for remote files via SSH, including UI components and file listing logic
- chore: Update @types/react to version 19.0.10 and add overrides in package.json
- chore: Add initial CHANGELOG.md documenting the SCP File Transfer extension release and features
- chore: Update package version to 1.0.0 and switch from pnpm to npm in README installation instructions
- fix: Update file selection instructions and clean up whitespace in upload component and tests
- fix: Improve error handling in file selection process, adding specific handling for Finder not being frontmost
- feat: Implement popToRoot functionality to close extension after successful download and upload
- chore: Add LICENSE file and update README with testing instructions and license details
- feat: Add ESLint configuration and refactor code for improved readability and consistency
- fix: Update icon path and standardize command titles in package.json
- test: Add end-to-end tests for download and upload workflows, including validation and error handling
- feat: Improve logging and error messages in download process and path validation
- feat: Enhance logging and error messages in upload process and path validation
- feat: Improve error handling and logging in SSH config parser
- feat: Enhance error handling in SCP execution with user-friendly messages
- test: Add unit tests for FileList component logic
- feat: Add FileList component for displaying files
- feat: Add download and upload command components
- test: Add unit tests for SCP command builder functionality
- feat: Implement SCP command builder and executor for file transfers
- test: Add comprehensive unit tests for validation utilities
- feat: Add validation utility functions for paths and SSH config
- test: Add comprehensive unit tests for SSH config parser
- test: Add Vitest testing framework configuration
- docs: Update package manager from npm to pnpm
- feat: Add SSH config parser utility
- feat: Add server configuration and transfer types
- Initialize Raycast SCP extension project
@raycastbot raycastbot added the new extension Label for PRs with new extensions label Jan 12, 2026
@raycastbot
Copy link
Collaborator

Congratulations on your new Raycast extension! 🚀

Due to our current reduced availability, the initial review may take up to 10-15 business days.

Once the PR is approved and merged, the extension will be available on our Store.

- feat: Update package name to raycast-rsync-extension, add React dependency, and enhance SSH config parsing with caching for improved performance and error handling.
- feat: Refactor copy actions in RemoteFileList to use a unified Action component, enhancing code consistency and user feedback with clipboard integration.
- feat: Enhance copy actions in RemoteFileList with success toasts and navigation, improving user feedback when copying file paths and names.
@dytsou dytsou marked this pull request as ready for review January 12, 2026 20:08
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 12, 2026

Greptile Overview

Greptile Summary

This PR introduces a Raycast extension for rsync-based file transfers with SSH config integration. While the extension demonstrates good structure with comprehensive testing (117 tests) and solid error handling patterns, it has critical security vulnerabilities that must be addressed before merging.

Critical Issues

🚨 Shell Command Injection Vulnerabilities

  • rsync.ts and ssh.ts directly interpolate user-provided paths into shell commands without proper escaping
  • The validation only checks for control characters (0x00-0x1F) but allows shell metacharacters like ;, |, `, $, etc.
  • Attack vector: A path like /tmp/test; rm -rf / would execute arbitrary commands
  • This affects both upload/download commands and browse functionality

❌ Missing Browse Command Export

  • The browse command is declared in package.json but not exported in index.tsx
  • This will cause a runtime error when users try to access "Browse Remote Files"

Store Requirements Issues

📸 Missing Metadata Folder

  • View-type commands require a metadata/ folder with Raycast-styled screenshots
  • Per extension guidelines, this is mandatory for store submission

⚙️ Duplicated Preferences

  • Rsync options (humanReadable, progress, delete) are duplicated across upload/download commands
  • Should use global preferences for settings that apply to multiple commands

Positive Aspects

  • Well-structured codebase with clear separation of concerns
  • Comprehensive test coverage (unit, component, E2E tests)
  • Good error handling with user-friendly error messages
  • Proper SSH config parsing with caching
  • Clean UI implementation with Raycast best practices

Recommendation

This PR cannot be merged in its current state due to critical security vulnerabilities. The shell injection issues pose a serious security risk to users and must be fixed. Additionally, the missing browse command export will cause immediate runtime failures.

Confidence Score: 0/5

  • This PR has critical security vulnerabilities and runtime errors that make it unsafe to merge
  • Score of 0 reflects multiple critical issues: (1) Shell command injection vulnerabilities in rsync.ts and ssh.ts that allow arbitrary code execution, (2) Missing browse command export causing guaranteed runtime failures, (3) Missing required metadata folder for store submission. While the code shows good structure and testing, these critical issues must be resolved before any consideration for merge.
  • src/utils/rsync.ts and src/utils/ssh.ts require immediate attention for shell injection fixes. src/index.tsx needs browse command export. src/utils/validation.ts needs enhanced validation to block shell metacharacters.

Important Files Changed

File Analysis

Filename Score Overview
extensions/raycast-rsync-extension/src/index.tsx 1/5 Missing browse command export - will cause runtime error
extensions/raycast-rsync-extension/src/utils/rsync.ts 0/5 Critical shell injection vulnerability in command construction
extensions/raycast-rsync-extension/src/utils/ssh.ts 0/5 Critical shell injection vulnerability in SSH command
extensions/raycast-rsync-extension/src/utils/validation.ts 2/5 Insufficient validation - only checks control chars, not shell metacharacters
extensions/raycast-rsync-extension/src/upload.tsx 3/5 Rsync options duplicated per-command instead of global preferences
extensions/raycast-rsync-extension/package.json 3/5 Missing metadata folder requirement, declares browse command

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 files reviewed, 5 comments

Edit Code Review Agent Settings | Greptile

export { default as upload } from "./upload";

// Export download command
export { default as download } from "./download";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the browse command is not exported from index.tsx, but it's declared in package.json at line 27-32. This will cause a runtime error when users try to use the "Browse Remote Files" command.

Suggested change
export { default as download } from "./download";
// Export download command
export { default as download } from "./download";
// Export browse command
export { default as browse } from "./browse";

Copy link
Author

@dytsou dytsou Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Browse Remote Files" is added.

Comment on lines 61 to 67
if (direction === TransferDirection.UPLOAD) {
// Upload: rsync -e "ssh -F ~/.ssh/config" [flags] {localPath} {hostAlias}:{remotePath}
return `${baseCommand} ${localPath} ${hostAlias}:${remotePath}`;
} else {
// Download: rsync -e "ssh -F ~/.ssh/config" [flags] {hostAlias}:{remotePath} {localPath}
return `${baseCommand} ${hostAlias}:${remotePath} ${localPath}`;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shell command injection vulnerability - localPath, remotePath, and hostAlias are directly interpolated into shell commands without proper escaping. A malicious path like /tmp/test; rm -rf / would execute arbitrary commands.

The validation in validation.ts only checks for control characters (0x00-0x1F), but does NOT prevent shell metacharacters like semicolons, backticks, pipes, dollar signs, etc.

You need to properly escape or quote these values before passing them to the shell. Consider using a library like shell-escape or manually escaping shell metacharacters.

Copy link
Author

@dytsou dytsou Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been solved.


// Use ls -lAh for detailed listing with human-readable sizes
// -l: long format, -A: all files except . and .., -h: human-readable sizes
const command = `ssh -F ${configPath} ${hostAlias} "ls -lAh ${remotePath}"`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shell command injection vulnerability - remotePath is directly interpolated into the SSH command without proper escaping. A path like ~; malicious_command would execute arbitrary commands on the local system before attempting SSH connection.

The same security issue exists here as in rsync.ts - paths must be properly shell-escaped before being used in command strings.

Copy link
Author

@dytsou dytsou Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been solved.

Comment on lines 207 to 209
const [humanReadable, setHumanReadable] = useState<boolean>(true);
const [showProgress, setShowProgress] = useState<boolean>(true);
const [deleteExtraneous, setDeleteExtraneous] = useState<boolean>(false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the rsync options (humanReadable, showProgress, deleteExtraneous) are duplicated across upload.tsx and download.tsx commands. Per Raycast extension guidelines, when preferences apply to multiple related commands, prefer creating unified global settings in package.json rather than duplicating the same preference across individual commands.

Consider adding these as global preferences in package.json that all commands can access via getPreferenceValues().

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Author

@dytsou dytsou Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A global rsync preference in introduced.

Comment on lines +14 to +33
"commands": [
{
"name": "upload",
"title": "Upload Files Via Rsync",
"description": "Upload local files to a remote server using rsync",
"mode": "view"
},
{
"name": "download",
"title": "Download Files Via Rsync",
"description": "Download files from a remote server using rsync",
"mode": "view"
},
{
"name": "browse",
"title": "Browse Remote Files",
"description": "Browse and list files on a remote server",
"mode": "view"
}
],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this extension has view-type commands but is missing the required metadata/ folder with Raycast-styled screenshots. According to the extension guidelines, extensions with view-type commands must include a metadata folder containing professional screenshots.

You need to:

  1. Create a metadata/ folder in the extension root
  2. Add Raycast-styled screenshots showing the extension's UI
  3. Follow the screenshot guidelines at https://developers.raycast.com/basics/prepare-an-extension-for-store#screenshots

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metadata added.

- feat: Add metadata folder with Raycast extension screenshots
- feat: Enhance Rsync options in file transfer forms by adding user-configurable settings for human-readable sizes, progress display, and deletion of extraneous files. Update form handling to utilize these options during transfer execution.
- feat: Implement tilde expansion handling in SSH commands to enhance path usability and security. Add tests to validate correct command generation for paths starting with ~ and standalone ~.
- feat: Implement tilde expansion for local paths in Rsync command builder, enhancing usability for both upload and download scenarios. Add corresponding tests to validate the new functionality.
- fix: Ensure local destination paths for downloads have a trailing slash to create directories correctly. Update tests to reflect this normalization for both download and upload scenarios.
- feat: Introduce global Rsync preferences for file transfer options, allowing users to configure human-readable sizes, progress display, and deletion of extraneous files through the extension preferences. Update transfer execution to utilize these global settings.
- feat: Add browsing command to Rsync extension, update documentation to reflect three main commands, and introduce test setup and mock for improved testing capabilities.
- feat: Implement shell escaping for Rsync command inputs to prevent command injection vulnerabilities and enhance security. Update tests to validate escaping behavior for various input scenarios.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new extension Label for PRs with new extensions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants