{"record":{"id":"fd6fc41320c05e4f","repo":"Hmbown/CodeWhale","slug":"external-credential-path-must-be-absolute","errorCode":null,"errorMessage":"external credential path must be absolute","messagePattern":"external credential path must be absolute","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/external_credentials.rs","lineNumber":148,"sourceCode":"        );\n    }\n    String::from_utf8(bytes).map(Some).with_context(|| {\n        format!(\n            \"Codewhale-owned credential file {} is not valid UTF-8\",\n            codewhale_config::quote_os_path(path)\n        )\n    })\n}\n\n#[cfg(unix)]\nfn open_secure_regular_file(path: &Path, require_owner_only: bool) -> io::Result<File> {\n    use std::ffi::CString;\n    use std::os::fd::FromRawFd;\n    use std::os::unix::ffi::OsStrExt;\n    use std::path::Component;\n\n    if !path.is_absolute() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidInput,\n            \"external credential path must be absolute\",\n        ));\n    }\n\n    let root = CString::new(\"/\").expect(\"static root contains no NUL\");\n    // SAFETY: `root` is a valid C string and flags require no variadic mode.\n    let root_fd = unsafe {\n        libc::open(\n            root.as_ptr(),\n            libc::O_RDONLY | libc::O_DIRECTORY | libc::O_CLOEXEC,\n        )\n    };\n    if root_fd < 0 {\n        return Err(io::Error::last_os_error());\n    }\n    // SAFETY: `root_fd` is newly owned after the successful `open`.\n    let mut current = unsafe { File::from_raw_fd(root_fd) };","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/external_credentials.rs#L130-L166","documentation":"`open_secure_regular_file` is the hardened opener used to read external credentials. Before opening anything it requires the path to be absolute; relative paths are rejected up-front with this `InvalidInput` error so the subsequent component-by-component `openat` walk rooted at `/` is well-defined and cannot be influenced by the process's current working directory.","triggerScenarios":"Calling `read_to_string`/`read_codewhale_owned_to_string` (-> `open_secure_regular_file`) with a relative path such as `credentials/token` instead of `/home/me/.config/.../credentials/token`.","commonSituations":"Config files or environment variables holding a credential path written as a relative path; code constructing the path by string concatenation without a leading `/`; moving a working config between machines where the base directory differs.","solutions":["Provide the credential path as an absolute path, e.g. `/home/me/.config/codewhale/credentials.json`.","Build the path from an explicit base directory (`dirs::home_dir()` or a config root) joined with the relative part, then pass the joined absolute result.","Check the config/env entry that supplies the path and add the missing leading `/`.","If the path comes from another tool, print it (`path.is_absolute()`) and see where the relative form originates."],"exampleFix":"// before\nlet creds = read_to_string(\"credentials/token\")?;\n// after\nlet path = std::path::Path::new(&home).join(\".config/codewhale/credentials/token\");\nlet creds = read_to_string(&path)?; // absolute","handlingStrategy":"validation","validationCode":"if !path.is_absolute() {\n    return Err(anyhow::anyhow!(\"credential path must be absolute: {path:?}\"));\n}","typeGuard":"fn is_absolute_credential_path(p: &Path) -> bool {\n    p.is_absolute()\n}","tryCatchPattern":"match read_to_string(&cred_path) {\n    Err(e) if e.to_string().contains(\"must be absolute\") => {\n        eprintln!(\"{cred_path:?} is relative; build it from an absolute base dir\");\n    }\n    other => other?,\n}","preventionTips":["Always construct credential paths from an absolute base (home dir or explicit config root).","Validate configured paths at startup (fail loud on relative values).","Avoid string-concatenating paths; use PathBuf::join on an absolute base.","Document that credential path config values must be absolute."],"tags":["security","filesystem","path-validation","credentials"],"backgroundTag":"invalid-argument-value","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"}