{"record":{"id":"8bad98f61e5bde18","repo":"Hmbown/CodeWhale","slug":"codewhale-owned-xai-oauth-path-must-not-be-a-repar","errorCode":null,"errorMessage":"Codewhale-owned xAI OAuth path must not be a reparse point","messagePattern":"Codewhale-owned xAI OAuth path must not be a reparse point","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/xai_credentials.rs","lineNumber":1345,"sourceCode":"    expected: &Path,\n    expect_directory: bool,\n) -> Result<fs::Metadata> {\n    use std::ffi::OsString;\n    use std::os::windows::ffi::OsStringExt as _;\n    use std::os::windows::fs::MetadataExt as _;\n    use std::os::windows::io::AsRawHandle as _;\n    use windows_sys::Win32::Storage::FileSystem::{\n        FILE_ATTRIBUTE_REPARSE_POINT, FILE_NAME_NORMALIZED, GetFinalPathNameByHandleW,\n        VOLUME_NAME_DOS,\n    };\n\n    let metadata = file.metadata().with_context(|| {\n        format!(\n            \"inspecting Codewhale-owned path {}\",\n            crate::quote_os_path(expected)\n        )\n    })?;\n    anyhow::ensure!(\n        metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT == 0,\n        \"Codewhale-owned xAI OAuth path must not be a reparse point\"\n    );\n    anyhow::ensure!(\n        if expect_directory {\n            metadata.is_dir()\n        } else {\n            metadata.is_file()\n        },\n        \"Codewhale-owned xAI OAuth path has the wrong filesystem type\"\n    );\n    let flags = FILE_NAME_NORMALIZED | VOLUME_NAME_DOS;\n    let handle = file.as_raw_handle();\n    // SAFETY: null output asks only for the required UTF-16 length.\n    let needed = unsafe { GetFinalPathNameByHandleW(handle, std::ptr::null_mut(), 0, flags) };\n    if needed == 0 {\n        return Err(std::io::Error::last_os_error())\n            .context(\"resolving Codewhale-owned xAI OAuth handle path\");","sourceCodeStart":1327,"sourceCodeEnd":1363,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/config/src/xai_credentials.rs#L1327-L1363","documentation":"On Windows, validate_windows_handle_path inspects the opened handle's file attributes and refuses any path with FILE_ATTRIBUTE_REPARSE_POINT set. Reparse points (symlinks, junctions, mount points) could redirect the credential path to attacker-controlled storage, so a Codewhale-owned path that resolves to a reparse point is rejected.","triggerScenarios":"Opening the credentials directory or file when any component (or the leaf) is a symlink/junction — e.g. `mklink /J` on ~/.codewhale, OneDrive/Dropbox folder redirection, or a user profile symlink.","commonSituations":"Redirecting the .codewhale folder into a synced drive (OneDrive/Dropbox junctions); moving credentials to another drive with a junction; per-user profile folder redirection policies.","solutions":["Remove the symlink/junction and move the real directory back to its normal location (e.g. %USERPROFILE%\\.codewhale)","Copy (not link) the directory contents to a physical path and update the config to the real location","Disable folder redirection/sync for the credentials directory"],"exampleFix":"// before\nmklink /J C:\\Users\\me\\.codewhale D:\\sync\\codewhale\n// after\nrobocopy D:\\sync\\codewhale C:\\Users\\me\\.codewhale /E\nrmdir C:\\Users\\me\\.codewhale_link_placeholder","handlingStrategy":"validation","validationCode":"use std::os::windows::fs::MetadataExt;\nfn is_reparse_point(p: &std::path::Path) -> std::io::Result<bool> {\n    Ok(std::fs::metadata(p)?.file_attributes() & 0x400 != 0) // FILE_ATTRIBUTE_REPARSE_POINT\n}","typeGuard":null,"tryCatchPattern":"if is_reparse_point(&dir)? {\n    eprintln!(\"{} is a symlink/junction; move it to a real path\", dir.display());\n    return Err(anyhow::anyhow!(\"credentials path must not be a reparse point\"));\n}\nlet store = XaiCredentialStore::open(&dir)?;","preventionTips":["Never junction/symlink ~/.codewhale into OneDrive/Dropbox or another drive","Check existing paths with `fsutil reparsepoint query` before configuring","Keep credentials on a physical local path under the user profile"],"tags":["security","windows","symlink","credentials"],"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"}