{"record":{"id":"7b8a13cc50d3e845","repo":"windmill-labs/windmill","slug":"invalid-path","errorCode":null,"errorMessage":"Invalid path.","messagePattern":"Invalid path\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-worker/src/ansible_executor.rs","lineNumber":2226,"sourceCode":"\n    ret\n}\n\nfn define_nsjail_mount(job_dir: &str, path: &PathBuf) -> anyhow::Result<String> {\n    Ok(format!(\n        r#\"\nmount {{\n    src: \"{0}/{1}\"\n    dst: \"/tmp/{1}\"\n    is_bind: true\n    rw: false\n    mandatory: false\n}}\n        \"#,\n        job_dir,\n        path.strip_prefix(job_dir)?\n            .to_str()\n            .ok_or(anyhow!(\"Invalid path.\"))?\n    ))\n}\n\nasync fn create_file_resources(\n    job_id: &Uuid,\n    w_id: &str,\n    job_dir: &str,\n    args: Option<&HashMap<String, Box<RawValue>>>,\n    r: &AnsibleRequirements,\n    client: &AuthedClient,\n    conn: &Connection,\n) -> error::Result<Vec<String>> {\n    let mut logs = String::new();\n    let mut nsjail_mounts: Vec<String> = vec![];\n\n    for inventory in &r.inventories {\n        let content;\n        if let Some(resource_path) = &inventory.pinned_resource {","sourceCodeStart":2208,"sourceCodeEnd":2244,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-worker/src/ansible_executor.rs#L2208-L2244","documentation":"define_nsjail_mount builds an nsjail mount spec by stripping the job_dir prefix off a file path inside the job directory. If the path does not actually live under job_dir, strip_prefix returns None and the `?` surfaces this generic anyhow error. It indicates a path-escape / miscomputed path, not user input directly.","triggerScenarios":"write_file_at_user_defined_location (or a later mount definition in create_file_resources) returned a validated path that is not under job_dir, so `path.strip_prefix(job_dir)?` fails in define_nsjail_mount, called from create_file_resources.","commonSituations":"A file resource or inventory `name` containing `..` or absolute-path components escaping the job dir; a change in how job_dir is joined (trailing slash / relative vs absolute) making strip_prefix mismatch; symlink resolving outside job_dir.","solutions":["Inspect the inventory/file resource `name` for path traversal (`..`, leading `/`) and use a plain relative filename.","Check the error's source path: confirm job_dir is the same absolute prefix used when writing the file.","If hit after an internal refactor, verify write_file_at_user_defined_location still returns paths rooted at job_dir.","Report as a bug if the name looks normal — it likely means the mount path computation broke."],"exampleFix":"// before: escaping name\ninventory: { name: \"../../../etc/prod\" }\n// after\ninventory: { name: \"prod_inventory\" }","handlingStrategy":"validation","validationCode":"fn is_safe_name(name: &str) -> bool {\n    !name.is_empty()\n        && !name.contains(\"..\")\n        && !name.starts_with('/')\n        && name.chars().all(|c| c.is_alphanumeric() || matches!(c, '-' | '_' | '.'))\n}","typeGuard":"fn safe_rel_path(job_dir: &Path, p: &Path) -> Option<PathBuf> {\n    p.strip_prefix(job_dir).ok().map(|r| r.to_path_buf())\n}","tryCatchPattern":null,"preventionTips":["Never use `..`, absolute paths, or separators in inventory/file resource names.","Keep names flat single-segment identifiers.","After Windmill upgrades that touch job_dir handling, smoke-test a job with file resources.","Report unexpected occurrences as a bug — user input should have been validated earlier."],"tags":["path","nsjail","security","ansible"],"backgroundTag":"path-traversal","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}