{"record":{"id":"c98e067e03ffbb17","repo":"jdx/mise","slug":"created-path-component-was-replaced-before-it-c","errorCode":null,"errorMessage":"created path component {} was replaced before it could be opened","messagePattern":"created path component (.+?) was replaced before it could be opened","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/system/managed_files.rs","lineNumber":1667,"sourceCode":"                        return Err(error).wrap_err_with(|| {\n                            format!(\n                                \"failed to create path component {}\",\n                                component_path.display()\n                            )\n                        });\n                    }\n                };\n                let created = openat(&directory, name.as_os_str(), flags, Mode::empty())\n                    .wrap_err_with(|| {\n                        format!(\n                            \"failed to open newly available path component {} without following symlinks\",\n                            component_path.display()\n                        )\n                    })?;\n                let stat = nix::sys::stat::fstat(&created)?;\n                if stat.st_uid != nix::unistd::geteuid().as_raw() {\n                    if created_by_us {\n                        bail!(\n                            \"created path component {} was replaced before it could be opened\",\n                            component_path.display()\n                        );\n                    } else {\n                        bail!(\n                            \"path component {} was concurrently created by another user\",\n                            component_path.display()\n                        );\n                    }\n                }\n                created\n            }\n        };\n        current.push(name);\n    }\n    Ok(directory)\n}\n","sourceCodeStart":1649,"sourceCodeEnd":1685,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/managed_files.rs#L1649-L1685","documentation":"A race-condition check after creating a directory component with an exclusive open: the library fstats the freshly created fd and verifies it is owned by the current effective uid. If it is not, and this process created the component (created_by_us), some other actor replaced the path between creation and open. This detects symlink-swap attacks or concurrent interference on freshly created components.","triggerScenarios":"During open_or_create_directory_tree, a component was just created (O_EXCL-style) but the fd's st_uid differs from the process euid — meaning the path was replaced (e.g. by a symlink plant) between mkdir and open. Without created_by_us, a different message ('concurrently created by another user') is used instead.","commonSituations":"Local attacker racing a privileged mise operation in a shared directory; parallel package managers or build systems creating the same path concurrently as different users; container/image build steps running as mixed uids over the same prefix.","solutions":["Ensure the managed prefix directory is only writable by the current user/root (chmod/chown the parent)","Re-run the operation; transient races usually succeed once the path is settled","Remove the path component that was tampered with and recreate it as the correct user","Avoid sharing managed directories between users; give each user its own prefix"],"exampleFix":"// before\n$ ls -ld /opt/tools  # drwxrwxrwx root root\n// after\n$ sudo chown root:root /opt/tools && sudo chmod 755 /opt/tools","handlingStrategy":"retry","validationCode":"fn prefix_is_private(path: &Path) -> std::io::Result<bool> {\n    use std::os::unix::fs::MetadataExt;\n    let mut cur = path.to_path_buf();\n    while let Some(parent) = cur.parent() {\n        let m = std::fs::metadata(parent)?;\n        if m.mode() & 0o022 != 0 { return Ok(false); }\n        cur = parent.to_path_buf();\n    }\n    Ok(true)\n}","typeGuard":"fn component_owned_by_euid(path: &Path) -> bool {\n    use std::os::unix::fs::MetadataExt;\n    std::fs::symlink_metadata(path)\n        .map(|m| m.uid() == unsafe { libc::geteuid() })\n        .unwrap_or(false)\n}","tryCatchPattern":"for attempt in 0..3 {\n    match result {\n        Err(e) if e.to_string().contains(\"was replaced before it could be opened\") && attempt < 2 => continue,\n        Err(e) => return Err(e),\n        Ok(v) => return Ok(v),\n    }\n}","preventionTips":["Restrict managed prefixes to a single trusted user","Avoid concurrent installs by different users into the same prefix","Re-run the operation after transient races","Remove and recreate tampered components as the correct user"],"tags":["security","race-condition","symlink","permissions"],"backgroundTag":"path-traversal-blocked","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}