{"record":{"id":"3b47be8c5fa975c1","repo":"Hmbown/CodeWhale","slug":"external-credential-path-must-name-a-regular-file","errorCode":null,"errorMessage":"external credential path must name a regular file","messagePattern":"external credential path must name a regular file","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/external_credentials.rs","lineNumber":224,"sourceCode":"        // call and flags require no variadic mode.\n        let fd = unsafe { libc::openat(current.as_raw_fd(), component.as_ptr(), flags) };\n        if fd < 0 {\n            return Err(io::Error::last_os_error());\n        }\n        // SAFETY: `fd` is newly owned after the successful `openat`.\n        current = unsafe { File::from_raw_fd(fd) };\n        opened_leaf = leaf;\n    }\n\n    if !opened_leaf {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidInput,\n            \"external credential path must name a file\",\n        ));\n    }\n    let metadata = current.metadata()?;\n    if !metadata.file_type().is_file() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidInput,\n            \"external credential path must name a regular file\",\n        ));\n    }\n    if require_owner_only {\n        use std::os::unix::fs::MetadataExt as _;\n        // SAFETY: geteuid(2) dereferences no pointers.\n        if metadata.uid() != unsafe { libc::geteuid() }\n            || metadata.mode() & 0o077 != 0\n            || metadata.nlink() != 1\n        {\n            return Err(io::Error::new(\n                io::ErrorKind::PermissionDenied,\n                \"Codewhale-owned credential file must be singly linked, owned by this user, and mode 0600 or stricter\",\n            ));\n        }\n    }\n    Ok(current)","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/external_credentials.rs#L206-L242","documentation":"open_secure_regular_file performs TOCTOU-safe hardened opens of credential files. After opening the path it re-checks the metadata of the actually-opened handle and refuses anything that is not a regular file (e.g. a directory, FIFO, device, or socket). The library throws this to prevent reading credentials from special files or being tricked into following a swapped path.","triggerScenarios":"read_to_string or read_codewhale_owned_to_string is given a path whose open handle's metadata reports a non-regular file type: a directory, named pipe, /dev node, or socket was passed as the credential path.","commonSituations":"A config file points at a directory instead of a file; a credentials path is a FIFO created by a wrapper script; a symlink to /dev/null or a procfs pseudo-file is configured; a provisioning tool created the wrong file type.","solutions":["Point the credential path at a real regular file (check with `file` or `stat -c %F <path>`).","If the path is a directory, append the credential filename (e.g. /path/to/dir/token.json instead of /path/to/dir).","Recreate the credential with a plain file: remove the FIFO/device and write the secret with a normal file write.","Ensure no volume manager or container mount is substituting a device/pipe at that path."],"exampleFix":"// before\nlet creds = read_codewhale_owned_to_string(Path::new(\"/home/me/.codewhale/credentials\"))?; // path is a directory\n// after\nlet creds = read_codewhale_owned_to_string(Path::new(\"/home/me/.codewhale/credentials/token.json\"))?; // regular file","handlingStrategy":"validation","validationCode":"fn ensure_regular_file(path: &Path) -> std::io::Result<()> {\n    let md = std::fs::metadata(path)?;\n    if !md.file_type().is_file() {\n        return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!(\"{} is not a regular file\", path.display())));\n    }\n    Ok(())\n}","typeGuard":"fn is_regular_file(path: &Path) -> bool {\n    std::fs::metadata(path).map(|m| m.file_type().is_file()).unwrap_or(false)\n}","tryCatchPattern":"match read_codewhale_owned_to_string(&path) {\n    Ok(creds) => use(creds),\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput && e.to_string().contains(\"regular file\") => {\n        eprintln!(\"credential path {} is not a regular file; fix the config\", path.display());\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Stat the path (file type, not just existence) before configuring it as a credential location.","Never point credential config at directories, FIFOs, or /dev nodes.","Use canonicalize() to resolve and inspect what a path actually is.","Document the expected file type wherever credential paths are configured."],"tags":["io","security","filesystem","credentials"],"backgroundTag":"file-open-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}