{"record":{"id":"a0b60a2dd2b8c054","repo":"Hmbown/CodeWhale","slug":"provider-catalog-lock-must-not-be-a-reparse-point","errorCode":null,"errorMessage":"provider catalog lock {} must not be a reparse point","messagePattern":"provider catalog lock (.+?) must not be a reparse point","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/provider_catalog_live.rs","lineNumber":555,"sourceCode":"    anyhow::ensure!(\n        metadata.is_file(),\n        \"provider catalog lock {} must be a regular file\",\n        path.display()\n    );\n    #[cfg(unix)]\n    {\n        use std::os::unix::fs::MetadataExt as _;\n        anyhow::ensure!(\n            metadata.nlink() == 1,\n            \"provider catalog lock {} must not be hard linked\",\n            path.display()\n        );\n    }\n    #[cfg(windows)]\n    {\n        use std::os::windows::fs::MetadataExt as _;\n        const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x0000_0400;\n        anyhow::ensure!(\n            metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT == 0,\n            \"provider catalog lock {} must not be a reparse point\",\n            path.display()\n        );\n    }\n    Ok(file)\n}\n\nfn load_from_disk_unlocked_with_limit(path: &Path, max_bytes: u64) -> Option<ProviderCatalogCache> {\n    let mut options = OpenOptions::new();\n    options.read(true);\n    #[cfg(unix)]\n    {\n        use std::os::unix::fs::OpenOptionsExt as _;\n        options.custom_flags(libc::O_NOFOLLOW | libc::O_CLOEXEC | libc::O_NONBLOCK);\n    }\n    #[cfg(windows)]\n    {","sourceCodeStart":537,"sourceCodeEnd":573,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/provider_catalog_live.rs#L537-L573","documentation":"open_cache_lock verifies that the provider catalog lock file on Windows is not a reparse point (symlink, junction, mount point). Reparse points could redirect the lock to an attacker-controlled or unexpected location, so the code refuses to open such a lock file to keep the cache directory trustworthy.","triggerScenarios":"On Windows, opening the provider catalog cache lock when the file at the cache path has FILE_ATTRIBUTE_REPARSE_POINT set — e.g. the lock file (or an ancestor) was replaced by a symlink or junction, or the cache dir lives on a mounted/substituted path.","commonSituations":"Users syncing their config/cache directory via symlinks (dotfile managers, OneDrive/Dropbox folder redirection), moving the cache dir with junctions, or CI setups that substitute drives.","solutions":["Remove the symlink/junction at the lock path and replace it with a real file (delete and let the app recreate the lock)","Point the cache/config directory at a real, non-reparse location instead of a symlinked folder","Copy the cache directory contents to a physical directory and update any redirection","If the path is on a subst/mounted volume, relocate the catalog cache to a normal local path"],"exampleFix":"// before (broken: lock path is a junction)\nC:\\Users\\me\\.codewhale\\cache\\catalog.lock -> D:\\cache\\catalog.lock\n// after\nmklink /j removed; real file C:\\Users\\me\\.codewhale\\cache\\catalog.lock recreated by the app","handlingStrategy":"validation","validationCode":"#[cfg(windows)]\nfn is_reparse_point(path: &Path) -> std::io::Result<bool> {\n    use std::os::windows::fs::MetadataExt;\n    const REPARSE: u32 = 0x0000_0400;\n    Ok(std::fs::symlink_metadata(path)?.file_attributes() & REPARSE != 0)\n}\n// call before opening: if is_reparse_point(&lock_path)? { relocate / warn }","typeGuard":null,"tryCatchPattern":"match open_cache_lock(&path) {\n    Err(e) if e.to_string().contains(\"reparse point\") => {\n        // remove the symlink/junction and recreate a real lock file\n    }\n    Ok(f) => { /* proceed */ }\n    Err(e) => return Err(e),\n}","preventionTips":["Do not symlink or junction-redirect the cache/config directory on Windows","Exclude cache lock files from dotfile-manager symlink farms","Keep the catalog cache on a physical local volume, not subst/mounted paths","If you must redirect, redirect the whole app data dir via official settings, not filesystem links"],"tags":["windows","filesystem","security","lockfile"],"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"}