docs: Enhance MONOREPO_CONVERSION_PLAN.md with improved details
- Specify production-ready implementation details for sal-git package. - Add a detailed code review and quality assurance process section. - Include comprehensive success metrics and validation checklists for production readiness. - Improve security considerations and risk mitigation strategies. - Add stricter code review criteria based on sal-git's conversion. - Update README with security configurations and environment variables.
This commit is contained in:
		| @@ -5,14 +5,44 @@ use std::error::Error; | ||||
| use std::fmt; | ||||
| use std::process::{Command, Output}; | ||||
|  | ||||
| // Simple redis client functionality | ||||
| // Simple redis client functionality with configurable connection | ||||
| fn execute_redis_command(cmd: &mut redis::Cmd) -> redis::RedisResult<String> { | ||||
|     // Try to connect to Redis with default settings | ||||
|     let client = redis::Client::open("redis://127.0.0.1/")?; | ||||
|     // Get Redis URL from environment variables with fallback | ||||
|     let redis_url = get_redis_url(); | ||||
|     log::debug!("Connecting to Redis at: {}", mask_redis_url(&redis_url)); | ||||
|  | ||||
|     let client = redis::Client::open(redis_url)?; | ||||
|     let mut con = client.get_connection()?; | ||||
|     cmd.query(&mut con) | ||||
| } | ||||
|  | ||||
| /// Get Redis URL from environment variables with secure fallbacks | ||||
| fn get_redis_url() -> String { | ||||
|     std::env::var("REDIS_URL") | ||||
|         .or_else(|_| std::env::var("SAL_REDIS_URL")) | ||||
|         .unwrap_or_else(|_| "redis://127.0.0.1/".to_string()) | ||||
| } | ||||
|  | ||||
| /// Mask sensitive information in Redis URL for logging | ||||
| fn mask_redis_url(url: &str) -> String { | ||||
|     if let Ok(parsed) = url::Url::parse(url) { | ||||
|         if parsed.password().is_some() { | ||||
|             format!( | ||||
|                 "{}://{}:***@{}:{}/{}", | ||||
|                 parsed.scheme(), | ||||
|                 parsed.username(), | ||||
|                 parsed.host_str().unwrap_or("unknown"), | ||||
|                 parsed.port().unwrap_or(6379), | ||||
|                 parsed.path().trim_start_matches('/') | ||||
|             ) | ||||
|         } else { | ||||
|             url.to_string() | ||||
|         } | ||||
|     } else { | ||||
|         "redis://***masked***".to_string() | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Define a custom error type for GitExecutor operations | ||||
| #[derive(Debug)] | ||||
| pub enum GitExecutorError { | ||||
| @@ -122,7 +152,7 @@ impl GitExecutor { | ||||
|             Err(e) => { | ||||
|                 // If Redis error, we'll proceed without config | ||||
|                 // This is not a fatal error as we might use default git behavior | ||||
|                 eprintln!("Warning: Failed to load git config from Redis: {}", e); | ||||
|                 log::warn!("Failed to load git config from Redis: {}", e); | ||||
|                 self.config = None; | ||||
|                 Ok(()) | ||||
|             } | ||||
| @@ -311,43 +341,58 @@ impl GitExecutor { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Execute git command with username/password | ||||
|     // Execute git command with username/password using secure credential helper | ||||
|     fn execute_with_credentials( | ||||
|         &self, | ||||
|         args: &[&str], | ||||
|         username: &str, | ||||
|         password: &str, | ||||
|     ) -> Result<Output, GitExecutorError> { | ||||
|         // For HTTPS authentication, we need to modify the URL to include credentials | ||||
|         // Create a new vector to hold our modified arguments | ||||
|         let modified_args: Vec<String> = args | ||||
|             .iter() | ||||
|             .map(|&arg| { | ||||
|                 if arg.starts_with("https://") { | ||||
|                     // Replace https:// with https://username:password@ | ||||
|                     format!("https://{}:{}@{}", username, password, &arg[8..]) // Skip the "https://" part | ||||
|                 } else { | ||||
|                     arg.to_string() | ||||
|                 } | ||||
|             }) | ||||
|             .collect(); | ||||
|         // Use git credential helper approach for security | ||||
|         // Create a temporary credential helper script | ||||
|         let temp_dir = std::env::temp_dir(); | ||||
|         let helper_script = temp_dir.join(format!("git_helper_{}", std::process::id())); | ||||
|  | ||||
|         // Execute the command | ||||
|         let mut command = Command::new("git"); | ||||
|         // Create credential helper script content | ||||
|         let script_content = format!( | ||||
|             "#!/bin/bash\necho username={}\necho password={}\n", | ||||
|             username, password | ||||
|         ); | ||||
|  | ||||
|         // Add the modified arguments to the command | ||||
|         for arg in &modified_args { | ||||
|             command.arg(arg.as_str()); | ||||
|         // Write the helper script | ||||
|         std::fs::write(&helper_script, script_content) | ||||
|             .map_err(|e| GitExecutorError::CommandExecutionError(e))?; | ||||
|  | ||||
|         // Make it executable | ||||
|         #[cfg(unix)] | ||||
|         { | ||||
|             use std::os::unix::fs::PermissionsExt; | ||||
|             let mut perms = std::fs::metadata(&helper_script) | ||||
|                 .map_err(|e| GitExecutorError::CommandExecutionError(e))? | ||||
|                 .permissions(); | ||||
|             perms.set_mode(0o755); | ||||
|             std::fs::set_permissions(&helper_script, perms) | ||||
|                 .map_err(|e| GitExecutorError::CommandExecutionError(e))?; | ||||
|         } | ||||
|  | ||||
|         // Execute the command and handle the result | ||||
|         // Execute git command with credential helper | ||||
|         let mut command = Command::new("git"); | ||||
|         command.args(args); | ||||
|         command.env("GIT_ASKPASS", &helper_script); | ||||
|         command.env("GIT_TERMINAL_PROMPT", "0"); // Disable terminal prompts | ||||
|  | ||||
|         log::debug!("Executing git command with credential helper"); | ||||
|         let output = command.output()?; | ||||
|  | ||||
|         // Clean up the temporary helper script | ||||
|         let _ = std::fs::remove_file(&helper_script); | ||||
|  | ||||
|         if output.status.success() { | ||||
|             Ok(output) | ||||
|         } else { | ||||
|             Err(GitExecutorError::GitCommandFailed( | ||||
|                 String::from_utf8_lossy(&output.stderr).to_string(), | ||||
|             )) | ||||
|             let error = String::from_utf8_lossy(&output.stderr); | ||||
|             log::error!("Git command failed: {}", error); | ||||
|             Err(GitExecutorError::GitCommandFailed(error.to_string())) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -171,13 +171,37 @@ pub fn git_repo_push(git_repo: &mut GitRepo) -> Result<GitRepo, Box<EvalAltResul | ||||
|     git_error_to_rhai_error(git_repo.push()) | ||||
| } | ||||
|  | ||||
| /// Dummy implementation of git_clone for testing | ||||
| /// Clone a git repository to a temporary location | ||||
| /// | ||||
| /// This function is used for testing the git module. | ||||
| pub fn git_clone(url: &str) -> Result<(), Box<EvalAltResult>> { | ||||
|     // This is a dummy implementation that always fails with a Git error | ||||
|     Err(Box::new(EvalAltResult::ErrorRuntime( | ||||
|         format!("Git error: Failed to clone repository from URL: {}", url).into(), | ||||
|         rhai::Position::NONE, | ||||
|     ))) | ||||
| /// This function clones a repository from the given URL to a temporary directory | ||||
| /// and returns the GitRepo object for further operations. | ||||
| /// | ||||
| /// # Arguments | ||||
| /// | ||||
| /// * `url` - The URL of the git repository to clone | ||||
| /// | ||||
| /// # Returns | ||||
| /// | ||||
| /// * `Ok(GitRepo)` - The cloned repository object | ||||
| /// * `Err(Box<EvalAltResult>)` - If the clone operation failed | ||||
| pub fn git_clone(url: &str) -> Result<GitRepo, Box<EvalAltResult>> { | ||||
|     // Get base path from environment or use default temp directory | ||||
|     let base_path = std::env::var("GIT_DEFAULT_BASE_PATH").unwrap_or_else(|_| { | ||||
|         std::env::temp_dir() | ||||
|             .join("sal_git_clones") | ||||
|             .to_string_lossy() | ||||
|             .to_string() | ||||
|     }); | ||||
|  | ||||
|     // Create GitTree and clone the repository | ||||
|     let git_tree = git_error_to_rhai_error(GitTree::new(&base_path))?; | ||||
|     let repos = git_error_to_rhai_error(git_tree.get(url))?; | ||||
|  | ||||
|     // Return the first (and should be only) repository | ||||
|     repos.into_iter().next().ok_or_else(|| { | ||||
|         Box::new(EvalAltResult::ErrorRuntime( | ||||
|             "Git error: No repository was cloned".into(), | ||||
|             rhai::Position::NONE, | ||||
|         )) | ||||
|     }) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user