getzola/zola · error

{:?} is not inside the base site directory {:?}

Error message

{:?} is not inside the base site directory {:?}

What it means

The `search_for_file` template helper resolves a user-supplied path against the site base path. If the resulting file exists but is not contained within the base site directory (checked via `is_path_in_directory`), it bails to prevent reading files outside the site — a path-traversal guard.

Source

Thrown at components/templates/src/helpers.rs:40

    theme: &Option<String>,
    output_path: &Path,
) -> Result<Option<(PathBuf, String)>> {
    let mut search_paths =
        vec![base_path.join("static"), base_path.join("content"), base_path.join(output_path)];
    if let Some(t) = theme {
        search_paths.push(base_path.join("themes").join(t).join("static"));
    }
    let actual_path = if path.starts_with("@/") {
        Cow::Owned(path.replace("@/", "content/"))
    } else {
        Cow::Borrowed(path.trim_start_matches('/'))
    };

    let mut file_path = base_path.join(&*actual_path);
    let mut file_exists = file_path.exists();

    if file_exists && !is_path_in_directory(base_path, &file_path)? {
        bail!("{:?} is not inside the base site directory {:?}", path, base_path);
    }

    if !file_exists {
        // we need to search in all search folders now
        for dir in &search_paths {
            let p = dir.join(&*actual_path);
            if p.exists() {
                file_path = p;
                file_exists = true;
                break;
            }
        }
    }

    if file_exists { Ok(Some((file_path, actual_path.into_owned()))) } else { Ok(None) }
}

View on GitHub (pinned to 61d3082821)

Solutions

  1. Use a path relative to the site root without `..` segments
  2. Move the needed file inside the site directory (e.g. into `static/` or `content/`)
  3. Remove or re-point symlinks that escape the base directory

Example fix

// before
{{ get_file(path="../../shared/config.json") }}
// after
{{ get_file(path="static/config.json") }}
Defensive patterns

Strategy: validation

Validate before calling

fn safe_relative(path: &str) -> bool {
    let p = std::path::Path::new(path);
    !p.is_absolute() && p.components().all(|c| !matches!(c, std::path::Component::ParentDir))
}

Try / catch

match search_for_file(base_path, path, &search_paths) {
    Ok(f) => read(f),
    Err(e) if e.to_string().contains("not inside the base site directory") => {
        eprintln!("path escapes site root, refusing: {path}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling the template helper (`call`/`from_args`) with a path containing `..` segments or an absolute path that, after `base_path.join`, escapes the site directory (e.g. `../secrets.txt` or `/etc/passwd`).

Common situations: Template code using `{{ get_file(path="../../outside.txt") }}`; symlinks inside the site pointing outside the project; mistakenly passing absolute paths.

Related errors


AI-assisted analysis of getzola/zola@61d3082821 (2026-09-03). Data as JSON: /api/errors/aa016f19ef84864e. Report an issue: GitHub.