{"record":{"id":"15bb23ab0a182333","repo":"Hmbown/CodeWhale","slug":"xai-oauth-file-must-be-owned-by-the-current-user","errorCode":null,"errorMessage":"xAI OAuth file must be owned by the current user","messagePattern":"xAI OAuth file must be owned by the current user","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/xai_credentials.rs","lineNumber":904,"sourceCode":"        {\n            return Err(std::io::Error::last_os_error()).context(\"retiring xAI OAuth file\");\n        }\n        Ok(())\n    }\n}\n\n#[cfg(unix)]\nfn validate_owned_file_handle(file: &File, path: &Path) -> Result<fs::Metadata> {\n    use std::os::unix::fs::MetadataExt as _;\n    let metadata = file.metadata().with_context(|| {\n        format!(\n            \"inspecting Codewhale-owned xAI OAuth file {}\",\n            crate::quote_os_path(path)\n        )\n    })?;\n    anyhow::ensure!(metadata.is_file(), \"xAI OAuth path must be a regular file\");\n    // SAFETY: geteuid(2) dereferences no pointers.\n    anyhow::ensure!(\n        metadata.uid() == unsafe { libc::geteuid() },\n        \"xAI OAuth file must be owned by the current user\"\n    );\n    anyhow::ensure!(\n        metadata.nlink() == 1,\n        \"xAI OAuth file must not have multiple filesystem links\"\n    );\n    Ok(metadata)\n}\n\n#[cfg(windows)]\nfn open_owned_credentials_directory(directory: &Path) -> Result<XaiOAuthCredentialStore> {\n    use std::os::windows::fs::OpenOptionsExt as _;\n    use windows_sys::Win32::Storage::FileSystem::{\n        FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, FILE_GENERIC_READ,\n        FILE_SHARE_READ, FILE_SHARE_WRITE, WRITE_DAC, WRITE_OWNER,\n    };\n","sourceCodeStart":886,"sourceCodeEnd":922,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/config/src/xai_credentials.rs#L886-L922","documentation":"The opened credential file must be owned by the current effective UID. Like the directory ownership check, this hardens against reading tokens planted or readable via another account's file, and complements the single-link and regular-file checks in validate_owned_file_handle.","triggerScenarios":"validate_owned_file_handle (reached from read_to_string, write, remove, rename, open_internal_file, open_windows_file) finds metadata.uid() != geteuid() — e.g. the token file was written by root via sudo or restored from a root-owned backup.","commonSituations":"Running the app once as root created root-owned token files; rsync/tar restore preserved foreign ownership; a shared machine admin pre-populated credentials.","solutions":["Fix ownership: `sudo chown $(id -u):$(id -g) <credential file>` (and the parent directory).","Re-authenticate so the store writes a fresh file owned by the current user.","Avoid sudo for the CLI; copy credentials with correct ownership instead.","In packaging/containers, ensure the credential provisioning step runs as the runtime UID."],"exampleFix":"// shell fix\n# sudo chown $(id -u):$(id -g) ~/.codewhale/xai.oauth.v1.json\n// guard before calling\nlet md = std::fs::metadata(path)?;\nanyhow::ensure!(md.uid() == unsafe { libc::geteuid() }, \"token file not owned by current user\");","handlingStrategy":"validation","validationCode":"#[cfg(unix)]\nfn file_owned_by_current_user(path: &Path) -> bool {\n    use std::os::unix::fs::MetadataExt;\n    std::fs::metadata(path).map(|m| m.uid() == unsafe { libc::geteuid() }).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Chown restored credentials to the runtime UID before use","Avoid sudo runs that create root-owned token files","Provision credentials in containers as the runtime USER"],"tags":["permissions","ownership","security","unix"],"backgroundTag":"permission-denied","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"}