jdx/mise · error

[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 into an in-image path: it must be absolute (checked earlier), have the leading '/' stripped, use forward slashes, and every path component must be non-empty and not '..'. If the resulting path is empty or contains a '..' component, the builder rejects it because '..' could escape the intended home directory inside the image (path traversal in the layer).

Solutions

  1. Rewrite the target as an absolute path without '..' components, e.g. "/etc/passwd" instead of "~/../etc/passwd".
  2. If a variable is templated into the target, validate/sanitize it (no '..') before rendering the config.
  3. Fix empty targets — give each [dotfiles] entry a concrete absolute target path.

Example fix

// before
[dotfiles."~/../etc/motd"]
mode = "content"
contents = "welcome"

// after
[dotfiles."/etc/motd"]
mode = "content"
contents = "welcome"
Defensive patterns

Strategy: validation

Validate before calling

// reject targets containing '..' or empty before config load
let t = target.trim_start_matches('~');
if t.split('/').any(|c| c == "..") || t.is_empty() {
    return Err(format!("unsafe dotfiles target: {target}"));
}

Type guard

fn is_safe_oci_target(target: &str) -> bool {
    let p = target.strip_prefix('/').unwrap_or(target);
    !p.is_empty()
        && !p.split('/').any(|c| c.is_empty() || c == "." || c == "..")
}

Prevention

When it happens

Trigger: build_dotfiles_layer with a [dotfiles] target like "~/../etc/passwd", "/a/../b", "..", or an empty target after stripping '/', producing a path with a '..' component or an empty string.

Common situations: Hand-written targets using relative escapes to reach system paths; templated targets where a variable resolves to '..' or empty; typos like a trailing '..' when attempting to install a file outside the home directory.

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


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/5718d76a30f6f007. Report an issue: GitHub.

Appendix: source

Thrown at src/oci/builder.rs:1155

        (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 533346cc37)