initium 2.1.0

Swiss-army toolbox for Kubernetes initContainers — wait-for, seed, render, fetch in a single static Rust binary
use std::path::{Path, PathBuf};
pub fn validate_file_path(workdir: &str, target: &str) -> Result<PathBuf, String> {
    if workdir.is_empty() {
        return Err("workdir must not be empty".into());
    }
    let target_path = Path::new(target);
    if target_path.is_absolute() {
        return Err(format!("absolute target path not allowed: {:?}", target));
    }
    let abs_workdir = std::env::current_dir()
        .map_err(|e| format!("getting cwd: {}", e))?
        .join(workdir);
    let abs_workdir = normalize_path(&abs_workdir);
    let joined = abs_workdir.join(target);
    let cleaned = normalize_path(&joined);
    let workdir_str = abs_workdir.to_string_lossy().to_string();
    let cleaned_str = cleaned.to_string_lossy().to_string();
    if cleaned_str != workdir_str && !cleaned_str.starts_with(&format!("{}/", workdir_str)) {
        return Err(format!(
            "path traversal detected: {:?} escapes workdir {:?}",
            target, workdir_str
        ));
    }
    Ok(cleaned)
}
fn normalize_path(path: &Path) -> PathBuf {
    let mut components = Vec::new();
    for component in path.components() {
        match component {
            std::path::Component::ParentDir => {
                if !components.is_empty() {
                    components.pop();
                }
            }
            std::path::Component::CurDir => {}
            c => components.push(c),
        }
    }
    let mut result = PathBuf::new();
    for c in components {
        result.push(c.as_os_str());
    }
    result
}
#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::TempDir;
    #[test]
    fn test_valid_path() {
        let dir = TempDir::new().unwrap();
        let result = validate_file_path(dir.path().to_str().unwrap(), "output.txt");
        assert!(result.is_ok());
    }
    #[test]
    fn test_traversal_rejected() {
        let dir = TempDir::new().unwrap();
        let traversal = ["..", "..", "..", "tmp", "x"].join(std::path::MAIN_SEPARATOR_STR);
        let result = validate_file_path(dir.path().to_str().unwrap(), &traversal);
        assert!(result.is_err());
    }
    #[test]
    fn test_absolute_rejected() {
        let dir = TempDir::new().unwrap();
        let result = validate_file_path(dir.path().to_str().unwrap(), "/tmp/test");
        assert!(result.is_err());
    }
    #[test]
    fn test_empty_workdir() {
        let result = validate_file_path("", "output.txt");
        assert!(result.is_err());
    }
    #[test]
    fn test_workdir_itself() {
        let dir = TempDir::new().unwrap();
        let result = validate_file_path(dir.path().to_str().unwrap(), ".");
        assert!(result.is_ok());
    }
}