diff --git a/src/dependency_downloader.rs b/src/dependency_downloader.rs index 36143da..884068f 100644 --- a/src/dependency_downloader.rs +++ b/src/dependency_downloader.rs @@ -32,7 +32,7 @@ pub async fn download_dependencies( })) .await .into_iter() - .collect::>()?; + .collect::, DownloadError>>()?; Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 1a0bf50..7f6dd5d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,11 @@ use crate::lock::{ lock_check, write_lock, }; -use crate::utils::get_current_working_dir; +use crate::utils::{ + check_dotfiles_recursive, + get_current_working_dir, + prompt_user_for_confirmation, +}; use crate::versioning::push_version; use config::{ add_to_config, @@ -271,6 +275,17 @@ pub async fn run(command: Subcommands) -> Result<(), SoldeerError> { } } Subcommands::Push(push) => { + let path = push + .path + .unwrap_or(get_current_working_dir().to_str().unwrap().to_string()); + let path_buf = PathBuf::from(&path); + + // Check for sensitive files or directories + if check_dotfiles_recursive(&path_buf) && !prompt_user_for_confirmation() { + println!("{}", Paint::yellow("Push operation aborted by the user.")); + return Ok(()); + } + if push.dry_run.is_some() && push.dry_run.unwrap() { println!( "{}", @@ -283,9 +298,6 @@ pub async fn run(command: Subcommands) -> Result<(), SoldeerError> { push.dependency.split('~').collect::>()[0].to_string(); let dependency_version: String = push.dependency.split('~').collect::>()[1].to_string(); - let path = push - .path - .unwrap_or(get_current_working_dir().to_str().unwrap().to_string()); let regex = Regex::new(r"^[@|a-z0-9][a-z0-9-]*[a-z0-9]$").unwrap(); if !regex.is_match(&dependency_name) { @@ -596,7 +608,6 @@ libs = ["dependencies"] let archive = File::open(&path_dependency); let archive = ZipArchive::new(archive.unwrap()); assert_eq!(archive.unwrap().len(), 2); - let _ = remove_file(&path_dependency); clean_test_env(PathBuf::default()); } @@ -640,4 +651,43 @@ libs = ["dependencies"] env::set_var("config_file", path.clone().to_str().unwrap()); path } + + #[test] + #[serial] + fn push_prompts_user_on_sensitive_files() { + let _ = remove_dir_all(DEPENDENCY_DIR.clone()); + let _ = remove_file(LOCK_FILE.clone()); + let test_dir = env::current_dir().unwrap().join("test_push_sensitive"); + + // Create test directory + if !test_dir.exists() { + std::fs::create_dir(&test_dir).unwrap(); + } + + // Create a .env file in the test directory + let env_file_path = test_dir.join(".env"); + let mut env_file = File::create(&env_file_path).unwrap(); + writeln!(env_file, "SENSITIVE_DATA=secret").unwrap(); + + let command = Subcommands::Push(Push { + dependency: "@test~1.1".to_string(), + path: Some(test_dir.to_str().unwrap().to_string()), + dry_run: None, + }); + + match run(command) { + Ok(_) => {} + Err(_) => { + clean_test_env(PathBuf::default()); + assert_eq!("Invalid State", "") + } + } + + // Check if the .env file exists + assert!(env_file_path.exists()); + + // Clean up + let _ = remove_file(&env_file_path); + let _ = remove_dir_all(&test_dir); + } } diff --git a/src/utils.rs b/src/utils.rs index 000ef03..d64f43a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -127,3 +127,54 @@ pub fn get_base_url() -> String { "https://api.soldeer.xyz".to_string() } } + +// Function to check for the presence of sensitive files or directories +pub fn check_dotfiles(path: &Path) -> bool { + if let Ok(entries) = fs::read_dir(path) { + for entry in entries.flatten() { + let file_name = entry.file_name(); + let file_name_str = file_name.to_string_lossy(); + if file_name_str.starts_with('.') { + return true; + } + } + } + false +} + +// Function to recursively check for sensitive files or directories in a given path +pub fn check_dotfiles_recursive(path: &Path) -> bool { + if check_dotfiles(path) { + return true; + } + + if path.is_dir() { + for entry in fs::read_dir(path).unwrap() { + let entry = entry.unwrap(); + let entry_path = entry.path(); + if check_dotfiles_recursive(&entry_path) { + return true; + } + } + } + + false +} + +// Function to prompt the user for confirmation +pub fn prompt_user_for_confirmation() -> bool { + println!("{}", Paint::yellow( + "You are about to include some sensitive files in this version. Are you sure you want to continue?" + )); + println!("{}", Paint::cyan( + "If you are not sure what sensitive files, you can run the dry-run command to check what will be pushed." + )); + + print!("{}", Paint::green("Do you want to continue? (y/n): ")); + std::io::stdout().flush().unwrap(); + + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let input = input.trim().to_lowercase(); + input == "y" || input == "yes" +} diff --git a/src/versioning.rs b/src/versioning.rs index 151bec3..f76ccfe 100644 --- a/src/versioning.rs +++ b/src/versioning.rs @@ -117,7 +117,7 @@ fn zip_file( } for file_path in files_to_copy { - let file = File::open(&file_path.path.clone()).unwrap(); + let file = File::open(file_path.path.clone()).unwrap(); let file_name = file_path.name.clone(); let path = Path::new(&file_path.path); let mut buffer = Vec::new();