{"record":{"id":"cda98eb44b89f19a","repo":"Hmbown/CodeWhale","slug":"xai-oauth-private-basename-must-be-one-utf-8-path","errorCode":null,"errorMessage":"xAI OAuth private basename must be one UTF-8 path component","messagePattern":"xAI OAuth private basename must be one UTF-8 path component","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/config/src/xai_credentials.rs","lineNumber":440,"sourceCode":"            if store.remove_raw(&target)? {\n                removed += 1;\n            }\n        }\n        Ok(removed)\n    }\n}\n\nfn validate_owned_auth_name(name: &str) -> Result<()> {\n    anyhow::ensure!(\n        name == LEGACY_XAI_OAUTH_FILE_NAME || is_valid_xai_oauth_generation(name),\n        \"invalid Codewhale-owned xAI OAuth basename\"\n    );\n    Ok(())\n}\n\nfn validate_private_basename(name: &str) -> Result<()> {\n    let path = Path::new(name);\n    anyhow::ensure!(\n        path.components().count() == 1\n            && matches!(path.components().next(), Some(Component::Normal(_)))\n            && path.file_name().and_then(|value| value.to_str()) == Some(name),\n        \"xAI OAuth private basename must be one UTF-8 path component\"\n    );\n    Ok(())\n}\n\n#[cfg(unix)]\nfn open_owned_credentials_directory(directory: &Path) -> Result<XaiOAuthCredentialStore> {\n    use std::os::fd::FromRawFd as _;\n    use std::os::unix::fs::{MetadataExt as _, PermissionsExt as _};\n\n    anyhow::ensure!(\n        directory.is_absolute(),\n        \"xAI OAuth credentials directory must be absolute\"\n    );\n    // SAFETY: the literal root path contains no interior NUL and the returned","sourceCodeStart":422,"sourceCodeEnd":458,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/config/src/xai_credentials.rs#L422-L458","documentation":"Enforced by validate_private_basename before every raw openat/renameat/unlinkat (open_at, remove_raw, rename_raw, open_windows_file): the name must be exactly one Normal UTF-8 path component with no separators and not `.`/`..`. This guarantees the *at() calls cannot escape the pinned credentials directory. Public entry points funnel input through validate_owned_auth_name first, so hitting this means an in-crate caller passed a compound or non-UTF-8 name.","triggerScenarios":"Calling the private rename_raw/remove_raw/open_at helpers with names like \"dir/xai-auth.json\", \"..\", \".\", or a non-UTF-8 OsStr name; only reachable from code inside the codewhale-config crate or its unit tests.","commonSituations":"Contributors extending the store (new lifecycle files, tombstones) passing full paths instead of basenames; refactors that bypass validate_owned_auth_name; fuzz tests feeding path-shaped strings into internal helpers.","solutions":["Pass a single basename (no '/', '\\\\', '.', or '..') to internal store helpers","Route new public entry points through validate_owned_auth_name and keep validate_private_basename on the private raw layer","In tests, build names with the same helper functions the production code uses"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn is_single_component(name: &str) -> bool {\n    let p = std::path::Path::new(name);\n    p.components().count() == 1\n        && matches!(p.components().next(), Some(std::path::Component::Normal(_)))\n        && p.file_name().and_then(|n| n.to_str()) == Some(name)\n}","typeGuard":"fn is_private_basename(name: &str) -> bool {\n    !name.is_empty()\n        && !name.contains(['/', '\\\\', '\\u{0}'])\n        && name != \".\"\n        && name != \"..\"\n        && std::path::Path::new(name).file_name().and_then(|n| n.to_str()) == Some(name)\n}","tryCatchPattern":null,"preventionTips":["Inside the crate, always funnel new public entry points through validate_owned_auth_name","Keep raw *at()-style helpers private and prefix their inputs with the basename check","In tests, generate names with the same helpers production code uses"],"tags":["rust","validation","security","filesystem","xai-oauth","internal-invariant"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}