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