{"record":{"id":"f6330927303799ad","repo":"astrid-runtime/astrid","slug":"var-name-must-not-contain-path-components","errorCode":null,"errorMessage":"{var_name} must not contain '..' path components","messagePattern":"(.+?) must not contain '\\.\\.' path components","errorType":"validation","errorClass":"std::io::Error::InvalidInput","httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/dirs.rs","lineNumber":228,"sourceCode":"    #[error(\"workspace state directory name must not contain path separators\")]\n    Separator,\n    /// The name contains a non-portable character.\n    #[error(\n        \"workspace state directory name may contain only ASCII letters, digits, '.', '_', and '-'\"\n    )]\n    InvalidCharacter,\n    /// The name exceeds the portable length bound.\n    #[error(\"workspace state directory name must be at most 64 bytes\")]\n    TooLong,\n    /// The name is reserved by a supported filesystem.\n    #[error(\"workspace state directory name is reserved: {0:?}\")]\n    Reserved(String),\n}\n\n/// Reject paths containing `..` (parent directory) components.\nfn reject_parent_traversal(path: &Path, var_name: &str) -> io::Result<()> {\n    if path.components().any(|c| matches!(c, Component::ParentDir)) {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidInput,\n            format!(\"{var_name} must not contain '..' path components\"),\n        ));\n    }\n    Ok(())\n}\n\n// ── AstridHome (system-level) ────────────────────────────────────────────\n\n/// Global Astrid home directory (`~/.astrid/`, Windows `LocalAppData`, or\n/// `$ASTRID_HOME`).\n///\n/// FHS-aligned system layout with config (`etc/`), persistent state (`var/`),\n/// runtime (`run/`), logs (`log/`), keys (`keys/`), and shared modules (`lib/`).\n/// Principal content is authoritative in `AstridFilesystem`; native `home/` is\n/// retained only as a legacy migration source.\n#[derive(Debug, Clone)]\npub struct AstridHome {","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/dirs.rs#L210-L246","documentation":"Thrown by reject_parent_traversal when a path configured via an environment variable contains '..' parent-directory components. This blocks path-traversal attacks and accidental escapes from the intended Astrid home directory.","triggerScenarios":"Calling resolve_with_env with ASTRID_HOME (or HOME) set to a value like '/home/user/.astrid/../evil' or '~/..'; reject_parent_traversal scans Path components for Component::ParentDir.","commonSituations":"Shell config exporting a relative or dot-dot-containing ASTRID_HOME, copy-pasted paths with '..' segments, or malicious environment in untrusted contexts.","solutions":["Remove '..' segments from the ASTRID_HOME value and use a clean absolute path","Re-export ASTRID_HOME with a canonicalized path (e.g. via realpath)"],"exampleFix":"// before\nexport ASTRID_HOME=/home/user/.astrid/../shared\n// after\nexport ASTRID_HOME=/home/user/shared","handlingStrategy":"validation","validationCode":"fn is_safe(p: &str) -> bool {\n    let pb = std::path::PathBuf::from(p);\n    pb.is_absolute() && !pb.components().any(|c| matches!(c, std::path::Component::ParentDir))\n}\nassert!(is_safe(&astrid_home));","typeGuard":"fn safe_env_path(v: &str) -> Option<std::path::PathBuf> {\n    let p = std::path::PathBuf::from(v);\n    (p.is_absolute()\n        && !p.components().any(|c| matches!(c, std::path::Component::ParentDir)))\n    .then_some(p)\n}","tryCatchPattern":"match result {\n    Err(e) if e.kind() == io::ErrorKind::InvalidInput && e.to_string().contains(\"'..'\") => {\n        eprintln!(\"Fix the env var: remove '..' components\");\n    },\n    other => other?,\n}","preventionTips":["Set env paths via canonicalized absolute values","Never include '..' in configuration paths","Lint shell rc files for dot-dot path exports"],"tags":["security","path","env-var"],"backgroundTag":"path-traversal-blocked","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}