jdx/mise · error · eyre::Report
[dotfiles]."{}": target is not a safe OCI path
Error message
[dotfiles]."{}": target is not a safe OCI path What it means
oci_target_path normalizes a [dotfiles] target (~ -> root, ~/x -> root/x, absolute /x -> x, backslashes to slashes) and then rejects the result if it is empty or contains a '..' component. This is a safety gate: '..' in the image path would escape the intended location inside the container filesystem, and an empty path has no tar meaning.
Source
Thrown at src/oci/builder.rs:1031
(files, dirs)
}
}
fn oci_target_path(req: &FileRequest) -> Result<String> {
let raw = req.target_raw.as_str();
let path = if raw == "~" {
"root".to_string()
} else if let Some(rest) = raw.strip_prefix("~/") {
format!("root/{rest}")
} else {
req.target
.strip_prefix("/")
.map_err(|_| eyre::eyre!("dotfile target must be absolute: {}", req.target_raw))?
.to_string_lossy()
.replace('\\', "/")
};
if path.is_empty() || path.split('/').any(|p| p == "..") {
bail!(
"[dotfiles].\"{}\": target is not a safe OCI path",
req.target_raw
);
}
Ok(path)
}
fn source_mode(path: &std::path::Path) -> Result<u32> {
let md = path.metadata()?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
Ok(md.permissions().mode() & 0o7777)
}
#[cfg(not(unix))]
{
let _ = md;
Ok(0o644)View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Rewrite the target as an absolute path without any '..' segment, e.g. "/etc/mise/config.toml" or "~/.config/mise/config.toml".
- Replace a "/" target with the concrete destination file name under a real directory.
- If you need path joining, build it from components and reject '..' programmatically instead of concatenating strings.
Example fix
# before [dotfiles."~/secrets"] target = "/../etc/passwd" # after [dotfiles."~/secrets"] target = "/etc/mise/secrets"
Defensive patterns
Strategy: validation
Validate before calling
def safe_oci_target(t: str) -> bool:
if t == "~":
return True
if t.startswith("~/"):
p = "root/" + t[2:]
elif t.startswith("/"):
p = t[1:]
else:
return False # relative -> different error, still reject up front
p = p.replace("\\", "/")
return bool(p) and all(part != ".." for part in p.split("/"))
assert all(safe_oci_target(t) for t in targets) Prevention
- Always write targets as absolute paths or ~/ relative, never relative or containing '..'.
- If targets come from templates, lint them for '..' segments before running mise oci build.
- Remember backslashes are normalized to '/', so Windows-style escapes do not bypass the check.
When it happens
Trigger: A [dotfiles] target like "/../etc/passwd", "~/../..", "a/../../b", or a bare "/" (which strips to the empty string). Windows-style separators are converted before the check, so "~\..\x" is caught too.
Common situations: Copy-pasted relative targets from another tool, targets built by string concatenation that accidentally include '..', or a target of just "/" expecting the image root.
Related errors
- invalid blob digest (expected sha256: prefix): {digest}
- invalid blob digest (expected 64 lowercase hex chars): {dige
- oci mount_point must not be empty
- oci mount_point must be an absolute path (got {mount_point:?
- [dotfiles]."{}": source does not exist: {}
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/5718d76a30f6f007.
Report an issue: GitHub.