{"record":{"id":"e83dd36d6ade926a","repo":"Hmbown/CodeWhale","slug":"runtime-store-root-cannot-contain-components","errorCode":null,"errorMessage":"Runtime store root cannot contain '..' components","messagePattern":"Runtime store root cannot contain '\\.\\.' components","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":13695,"sourceCode":"fn panic_payload_message(payload: &(dyn std::any::Any + Send)) -> String {\n    if let Some(message) = payload.downcast_ref::<&str>() {\n        (*message).to_string()\n    } else if let Some(message) = payload.downcast_ref::<String>() {\n        message.clone()\n    } else {\n        \"unknown panic payload\".to_string()\n    }\n}\n\nfn checked_runtime_store_root(root: PathBuf) -> Result<PathBuf> {\n    if root.as_os_str().is_empty() {\n        bail!(\"Runtime store root cannot be empty\");\n    }\n    if root\n        .components()\n        .any(|component| matches!(component, Component::ParentDir))\n    {\n        bail!(\"Runtime store root cannot contain '..' components\");\n    }\n    let absolute = if root.is_absolute() {\n        root\n    } else {\n        std::env::current_dir()\n            .context(\"failed to resolve current directory for runtime store\")?\n            .join(root)\n    };\n    match absolute.canonicalize() {\n        Ok(path) => Ok(path),\n        Err(err) if err.kind() == std::io::ErrorKind::NotFound => {\n            Ok(normalize_path_components(&absolute))\n        }\n        Err(err) => Err(err).with_context(|| {\n            format!(\n                \"Failed to resolve runtime store root {}\",\n                absolute.display()\n            )","sourceCodeStart":13677,"sourceCodeEnd":13713,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_threads.rs#L13677-L13713","documentation":"The runtime store root must not contain '..' parent-directory components; such paths can escape the intended store location and are rejected as a path-traversal guard. After this check the root is resolved to an absolute path.","triggerScenarios":"Configuring a store root like /var/data/../etc/store or ../store, or building a path by joining user-supplied segments containing '..'.","commonSituations":"Relative default paths that include '..'; users pointing the store at a sibling directory with ../; untrusted input interpolated into the store path.","solutions":["Provide a canonical absolute path without '..' components","Canonicalize/normalize the intended directory (e.g. std::fs::canonicalize or path clean-up) before passing it","Never interpolate untrusted path segments into the store root"],"exampleFix":"// before\nlet root = PathBuf::from(\"/var/lib/../tmp/store\");\n// after\nlet root = std::fs::canonicalize(\"/var/tmp/store\")?;","handlingStrategy":"validation","validationCode":"fn has_parent_dir(p: &Path) -> bool {\n    p.components().any(|c| matches!(c, std::path::Component::ParentDir))\n}","typeGuard":"fn safe_root(p: PathBuf) -> Option<PathBuf> {\n    (!p.components().any(|c| matches!(c, std::path::Component::ParentDir))).then_some(p)\n}","tryCatchPattern":"let root = std::fs::canonicalize(user_path)\n    .map_err(|e| anyhow!(\"invalid store root: {e}\"))?; // canonicalize removes '..'","preventionTips":["Canonicalize user-supplied paths before storing them","Never join untrusted segments into the store root","Validate store roots at config load, before the store opens"],"tags":["security","path","traversal","validation"],"backgroundTag":"path-traversal-blocked","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}