{"record":{"id":"aa016f19ef84864e","repo":"getzola/zola","slug":"is-not-inside-the-base-site-directory","errorCode":null,"errorMessage":"{:?} is not inside the base site directory {:?}","messagePattern":"(.+?) is not inside the base site directory (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"components/templates/src/helpers.rs","lineNumber":40,"sourceCode":"    theme: &Option<String>,\n    output_path: &Path,\n) -> Result<Option<(PathBuf, String)>> {\n    let mut search_paths =\n        vec![base_path.join(\"static\"), base_path.join(\"content\"), base_path.join(output_path)];\n    if let Some(t) = theme {\n        search_paths.push(base_path.join(\"themes\").join(t).join(\"static\"));\n    }\n    let actual_path = if path.starts_with(\"@/\") {\n        Cow::Owned(path.replace(\"@/\", \"content/\"))\n    } else {\n        Cow::Borrowed(path.trim_start_matches('/'))\n    };\n\n    let mut file_path = base_path.join(&*actual_path);\n    let mut file_exists = file_path.exists();\n\n    if file_exists && !is_path_in_directory(base_path, &file_path)? {\n        bail!(\"{:?} is not inside the base site directory {:?}\", path, base_path);\n    }\n\n    if !file_exists {\n        // we need to search in all search folders now\n        for dir in &search_paths {\n            let p = dir.join(&*actual_path);\n            if p.exists() {\n                file_path = p;\n                file_exists = true;\n                break;\n            }\n        }\n    }\n\n    if file_exists { Ok(Some((file_path, actual_path.into_owned()))) } else { Ok(None) }\n}\n","sourceCodeStart":22,"sourceCodeEnd":57,"githubUrl":"https://github.com/getzola/zola/blob/61d30828217957120309db657950224edf9707e2/components/templates/src/helpers.rs#L22-L57","documentation":"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.","triggerScenarios":"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`).","commonSituations":"Template code using `{{ get_file(path=\"../../outside.txt\") }}`; symlinks inside the site pointing outside the project; mistakenly passing absolute paths.","solutions":["Use a path relative to the site root without `..` segments","Move the needed file inside the site directory (e.g. into `static/` or `content/`)","Remove or re-point symlinks that escape the base directory"],"exampleFix":"// before\n{{ get_file(path=\"../../shared/config.json\") }}\n// after\n{{ get_file(path=\"static/config.json\") }}","handlingStrategy":"validation","validationCode":"fn safe_relative(path: &str) -> bool {\n    let p = std::path::Path::new(path);\n    !p.is_absolute() && p.components().all(|c| !matches!(c, std::path::Component::ParentDir))\n}","typeGuard":null,"tryCatchPattern":"match search_for_file(base_path, path, &search_paths) {\n    Ok(f) => read(f),\n    Err(e) if e.to_string().contains(\"not inside the base site directory\") => {\n        eprintln!(\"path escapes site root, refusing: {path}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Never use ../ or absolute paths in template file helpers","Keep referenced assets inside the site directory","Audit symlinks in the project that point outside the site root"],"tags":["rust","path-traversal","security","templates"],"backgroundTag":"path-escapes-base-directory","analyzedSha":"61d30828217957120309db657950224edf9707e2","analyzedAt":"2026-09-03T14:39:09.727Z","contentChangedAt":"2026-09-03T14:39:09.727Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}