Hmbown/CodeWhale · critical
xAI OAuth private basename must be one UTF-8 path component
Error message
xAI OAuth private basename must be one UTF-8 path component
What it means
validate_private_basename requires the name to be exactly one normal, UTF-8 path component — no separators, no parent refs, and byte-identical to its own file_name. This blocks path traversal and non-UTF-8 tricks in low-level raw file operations.
Solutions
- Pass only a bare file name with no separators; build full paths via the store, not the caller.
- Sanitize/validate user input before it reaches these APIs (reject any name containing a path separator).
- Use path_for or validate_owned_auth_name first to map logical names to legal basenames.
- If handling untrusted names, reject early in your own layer with a clear message.
Example fix
// before
store.remove_raw("../escaped.json")?; // traversal attempt
// after
let name = "xai.oauth.v2.retired.json";
assert_eq!(Path::new(name).components().count(), 1);
store.remove_raw(name)?; Defensive patterns
Strategy: validation
Validate before calling
use std::path::{Path, Component};
fn safe_basename(name: &str) -> bool {
let p = Path::new(name);
p.components().count() == 1
&& matches!(p.components().next(), Some(Component::Normal(_)))
&& p.file_name().and_then(|f| f.to_str()) == Some(name)
} Type guard
fn is_single_normal_component(name: &str) -> bool {
!name.contains('/') && !name.contains('\\') && name != ".." && !name.is_empty()
} Prevention
- Reject any user input containing path separators before it reaches the store
- Treat file names as internal identifiers, not user-facing paths
- Add a test asserting traversal strings are rejected
- Only use the raw *_raw APIs with names produced by the store itself
When it happens
Trigger: Calling open_at, remove_raw, rename_raw, or the internal/windows file openers with a name containing '/', '\\', '..', an interior NUL pattern, non-UTF-8 bytes, or any multi-component path.
Common situations: Passing a relative subpath like "sub/dir/file.json"; user-controlled input reaching the name parameter (path traversal attempt); platform path separators slipping in from concatenation code.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- must be a single path component
- must be a single path component
- unsafe bundle path
- append_allow_rules only accepts action = "allow"
- bad_args
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/cda98eb44b89f19a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/config/src/xai_credentials.rs:570
fn validate_owned_auth_name(name: &str) -> Result<()> {
anyhow::ensure!(
name == LEGACY_XAI_OAUTH_FILE_NAME
|| name == LEGACY_CHATGPT_OAUTH_FILE_NAME
|| is_valid_xai_oauth_generation(name)
|| is_valid_chatgpt_oauth_generation(name),
"invalid Codewhale-owned OAuth basename"
);
Ok(())
}
fn is_chatgpt_owned_auth_name(name: &str) -> bool {
name == LEGACY_CHATGPT_OAUTH_FILE_NAME || is_valid_chatgpt_oauth_generation(name)
}
fn validate_private_basename(name: &str) -> Result<()> {
let path = Path::new(name);
anyhow::ensure!(
path.components().count() == 1
&& matches!(path.components().next(), Some(Component::Normal(_)))
&& path.file_name().and_then(|value| value.to_str()) == Some(name),
"xAI OAuth private basename must be one UTF-8 path component"
);
Ok(())
}
#[cfg(unix)]
fn open_owned_credentials_directory(directory: &Path) -> Result<XaiOAuthCredentialStore> {
use std::os::fd::FromRawFd as _;
use std::os::unix::fs::{MetadataExt as _, PermissionsExt as _};
anyhow::ensure!(
directory.is_absolute(),
"xAI OAuth credentials directory must be absolute"
);
// SAFETY: the literal root path contains no interior NUL and the returnedView on GitHub (pinned to 73e0f67d83)