Files
rabby/rabby-core/src/transfer/mod.rs
T
2026-07-08 10:43:56 -05:00

86 lines
2.3 KiB
Rust

use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TransferProtocol {
Sftp,
Zmodem,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TransferDirection {
Upload,
Download,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TransferRequest {
pub protocol: TransferProtocol,
pub direction: TransferDirection,
pub local_path: String,
pub remote_path: String,
}
impl TransferRequest {
pub fn validate(&self) -> Result<(), String> {
if self.local_path.trim().is_empty() {
return Err("local path is required".to_string());
}
if self.remote_path.trim().is_empty() {
return Err("remote path is required".to_string());
}
if self.local_path.contains("..") {
return Err("local path traversal is not allowed".to_string());
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TransferEvent {
Queued,
Progress { bytes_done: u64, bytes_total: u64 },
Completed,
Failed(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn validates_sftp_download_request() {
let request = TransferRequest {
protocol: TransferProtocol::Sftp,
direction: TransferDirection::Download,
local_path: "downloads/file.txt".to_string(),
remote_path: "/home/tom/file.txt".to_string(),
};
assert!(request.validate().is_ok());
}
#[test]
fn validates_zmodem_upload_request() {
let request = TransferRequest {
protocol: TransferProtocol::Zmodem,
direction: TransferDirection::Upload,
local_path: "uploads/firmware.bin".to_string(),
remote_path: "firmware.bin".to_string(),
};
assert!(request.validate().is_ok());
}
#[test]
fn rejects_local_path_traversal() {
let request = TransferRequest {
protocol: TransferProtocol::Sftp,
direction: TransferDirection::Download,
local_path: "../secret".to_string(),
remote_path: "/tmp/secret".to_string(),
};
assert!(request.validate().unwrap_err().contains("traversal"));
}
}