{"record":{"id":"486c45bb2beb6f79","repo":"BigPizzaV3/CodexPlusPlus","slug":"codex-home","errorCode":null,"errorMessage":"拒绝删除文件系统根目录：{}","messagePattern":"拒绝删除文件系统根目录：(.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"crates/codex-plus-core/src/codex_home.rs","lineNumber":36,"sourceCode":"/// `%USERPROFILE%\\.codex`（454 MB 会话历史）被永久删除，只剩 1.1% 可恢复。我审计了\n/// 全部 `remove_dir_all` 调用点，没有找到一条能删到 `.codex` 的既定路径——也就是说\n/// 现有代码在正常输入下是安全的。但这也意味着：**一旦某个上游值（环境变量、被解析\n/// 坏的路径、更新后失效的目录）指向了 home 本身，就没有任何东西拦得住它。**\n/// 数据丢失不可逆，所以这里加一道与具体触发源无关的兜底。\n///\n/// 判定在**规范化之后**做，`..`、符号链接、大小写差异都拦得住；同时容忍路径尚不存在\n/// （清理临时目录的常见情形，此时用词法规范化比较）。\npub fn ensure_safe_recursive_removal(target: &Path, codex_home: &Path) -> anyhow::Result<()> {\n    let target = normalize_for_comparison(target);\n\n    // 根路径 = 有根前缀且没有父目录，覆盖 POSIX 根（`/`）与 Windows 的各种写法\n    // （`C:\\`、`\\\\?\\C:\\`、UNC `\\\\server\\share\\`）。\n    //\n    // 不能只与 `Path::new(\"/\")` 比较：Windows 上 `/` 不是绝对路径，会被 normalize\n    // 成当前盘符根（如 `C:\\`），相等比较拦不住它——也就是说递归删除盘符根本可以\n    // 绕过这道守卫。`has_root()` 这一半也不可省：没有它 `C:` 会被误判成根。\n    if target.as_os_str().is_empty() || is_filesystem_root(&target) {\n        anyhow::bail!(\"拒绝删除文件系统根目录：{}\", target.display());\n    }\n    let home = normalize_for_comparison(codex_home);\n    if target == home {\n        anyhow::bail!(\n            \"拒绝递归删除 CODEX_HOME 本身（{}）——这会连同全部会话历史一起丢失\",\n            target.display()\n        );\n    }\n    if home.starts_with(&target) {\n        anyhow::bail!(\n            \"拒绝删除 CODEX_HOME 的祖先目录 {}（CODEX_HOME = {}）\",\n            target.display(),\n            home.display()\n        );\n    }\n    Ok(())\n}\n","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/codex_home.rs#L18-L54","documentation":"Thrown by `ensure_safe_recursive_removal` when the requested deletion target is empty or normalizes to a filesystem root (`/` on Unix; `C:\\`, `\\\\?\\C:\\`, or UNC `\\\\server\\share\\` variants on Windows). This guard prevents a recursive delete from wiping an entire drive or share, including Windows cases where a plain `/` normalizes to the current drive root.","triggerScenarios":"Calling the removal API with an empty path, `Path::new(\"/\")`, a drive root like `C:\\` or `C:`, a `\\\\?\\C:\\` verbatim path, or a UNC share root `\\\\server\\share\\`.","commonSituations":"Uninitialized/default empty path variables reaching the delete call, path-joining bugs that collapse to `/`, or user input containing a drive root on Windows.","solutions":["Fix the caller so it never passes an empty or root path — ensure a real subdirectory is computed before deletion","Validate the target path before calling (non-empty, has a parent, not `has_root()` alone)","Pass an explicit CODEX_HOME so normalization has a correct baseline and the other guards work","Log/inspect `target.display()` (included in the error) to find where the root path originated"],"exampleFix":"// before\nlet target = std::env::var(\"CODEX_TARGET_DIR\").unwrap_or_default(); // empty\nensure_safe_recursive_removal(Path::new(&target), &codex_home)?;\n// after\nlet target = std::env::var(\"CODEX_TARGET_DIR\").context(\"CODEX_TARGET_DIR must be set\")?;\nlet path = Path::new(&target);\nensure!(path.is_absolute() && path.parent().is_some(), \"refusing non-subdirectory target\");\nensure_safe_recursive_removal(path, &codex_home)?;","handlingStrategy":"validation","validationCode":"fn safe_removal_target(p: &Path) -> Result<(), String> {\n    if p.as_os_str().is_empty() { return Err(\"empty path\".into()); }\n    if p.has_root() && p.parent().is_none() { return Err(\"filesystem root\".into()); }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never pass user-supplied paths straight into recursive removal","Require absolute, non-root subdirectory targets","Be extra careful with Windows verbatim (\\\\?\\) and UNC paths","Keep the empty-path check — an empty Path is normalized to CWD, not caught by root checks"],"tags":["filesystem","safety-guard","recursive-delete","windows"],"backgroundTag":"path-traversal-blocked","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}