{"record":{"id":"ebe417abf5a2480c","repo":"sxyazi/yazi","slug":"invalidinput","errorCode":"InvalidInput","errorMessage":"invalid trash entry path","messagePattern":"invalid trash entry path","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"yazi-fs/src/trash/macos/ds_store.rs","lineNumber":38,"sourceCode":"\t\t\tlet Value::Ustr(value) = record.value else { continue };\n\t\t\tif value.is_empty() {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet location = locations.entry_ref(OsStr::new(&record.name)).or_default();\n\t\t\tmatch &record.field.fourcc().bytes() {\n\t\t\t\tb\"ptbL\" => location.parent = Some(value.into()),\n\t\t\t\tb\"ptbN\" => location.name = Some(value.into()),\n\t\t\t\t_ => {}\n\t\t\t}\n\t\t}\n\n\t\tOk(locations)\n\t}\n\n\tpub(super) fn join(&self, rel: &Path) -> io::Result<PathBuf> {\n\t\tif !rel.is_relative() || rel.has_parent_component() {\n\t\t\treturn Err(io::Error::new(io::ErrorKind::InvalidInput, \"invalid trash entry path\"));\n\t\t}\n\n\t\tlet parent = self.parent.as_deref().ok_or_else(|| {\n\t\t\tio::Error::new(io::ErrorKind::InvalidData, \"trash item has no put-back location\")\n\t\t})?;\n\n\t\tlet name = self.name.as_deref().ok_or_else(|| {\n\t\t\tio::Error::new(io::ErrorKind::InvalidData, \"trash item has no put-back name\")\n\t\t})?;\n\n\t\tlet mut components = Path::new(name).components();\n\t\tif !matches!(components.next(), Some(Component::Normal(_))) || components.next().is_some() {\n\t\t\treturn Err(io::Error::new(io::ErrorKind::InvalidData, \"invalid trash put-back name\"));\n\t\t}\n\n\t\tlet top_path = Path::new(\"/\").join(parent).join(name);\n\t\tOk(if rel.as_os_str().is_empty() { top_path } else { top_path.join(rel) })\n\t}","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/sxyazi/yazi/blob/8ebf930f1796ae2076b7d6b0c5e93a1002c18134/yazi-fs/src/trash/macos/ds_store.rs#L20-L56","documentation":"DsStore::join rebuilds a macOS trashed item's original absolute path by joining a relative entry under the put-back location from .DS_Store. The relative path must be relative and free of \"..\" components; absolute paths or parent-directory traversals are rejected with this InvalidInput error to prevent path traversal when restoring.","triggerScenarios":"Calling join with an absolute rel path (e.g. \"/foo/bar\") or one containing \"..\" components (e.g. \"../escape\"); rel comes from the trashed item's path relative to ~/.Trash, so only malformed caller input or tampered layout triggers it.","commonSituations":"Custom restore logic passing full absolute paths instead of Trash-relative ones; items moved between trash folders so the relative computation is wrong; adversarial paths crafted to escape the trash.","solutions":["Pass rel as the item's path relative to the trash directory (e.g. \"folder/file.txt\"), not an absolute path.","Strip or reject any \"..\" components before calling join; recompute the relative path from the actual trash root.","Ensure the item being restored actually resides inside the macOS trash so the relative computation is valid."],"exampleFix":"// before\nstore.join(Path::new(\"/Users/alice/file.txt\"))?\n// after\nlet rel = item_path.strip_prefix(trash_dir)?;\nstore.join(rel)?","handlingStrategy":"validation","validationCode":"fn rel_is_safe(rel: &std::path::Path) -> bool {\n    rel.is_relative()\n        && !rel.components().any(|c| matches!(c, std::path::Component::ParentDir | std::path::Component::RootDir))\n}","typeGuard":null,"tryCatchPattern":"match ds_store.join(&rel) {\n    Ok(dest) => restore_to(dest),\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => eprintln!(\"refusing non-relative or traversing trash entry path\"),\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Compute rel via Path::strip_prefix(trash_dir) rather than passing user-supplied paths.","Never pass absolute paths to join; only trash-relative components.","Reject entries whose computed relative path contains '..'.","Confirm the item actually lives inside the trash directory before restoring."],"tags":["filesystem","trash","path-traversal","macos","security"],"backgroundTag":"invalid-trash-entry-path","analyzedSha":"8ebf930f1796ae2076b7d6b0c5e93a1002c18134","analyzedAt":"2026-09-02T18:38:25.566Z","contentChangedAt":"2026-09-02T18:38:25.566Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}