{"record":{"id":"0b47374331ea5a02","repo":"Hmbown/CodeWhale","slug":"fleet-task-task-id-field-path-cannot-contain-parent","errorCode":null,"errorMessage":"Fleet task '{task_id}' {field} path '{}' cannot contain parent traversal","messagePattern":"Fleet task '(.+?)' (.+?) path '(.+?)' cannot contain parent traversal","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/fleet/worker_runtime.rs","lineNumber":572,"sourceCode":"            matches!(\n                component,\n                std::path::Component::ParentDir\n                    | std::path::Component::RootDir\n                    | std::path::Component::Prefix(_)\n            )\n        })\n    {\n        bail!(\n            \"Fleet task '{task_id}' {field} path '{}' must be one repo-relative line and cannot escape the workspace\",\n            path.display()\n        );\n    }\n    let mut segments = Vec::new();\n    for segment in raw.split('/') {\n        match segment {\n            \"\" | \".\" => {}\n            \"..\" => {\n                bail!(\n                    \"Fleet task '{task_id}' {field} path '{}' cannot contain parent traversal\",\n                    path.display()\n                );\n            }\n            value => segments.push(value),\n        }\n    }\n    Ok(if segments.is_empty() {\n        \".\".to_string()\n    } else {\n        segments.join(\"/\")\n    })\n}\n\nfn fleet_coordination_contracts(task_spec: &FleetTaskSpec) -> Result<Vec<String>> {\n    let Some(value) = task_spec.metadata.get(\"coordination_contracts\") else {\n        return Ok(Vec::new());\n    };","sourceCodeStart":554,"sourceCodeEnd":590,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/fleet/worker_runtime.rs#L554-L590","documentation":"After component-level screening, normalize_fleet_relative_path splits the path on `/` and rejects any literal `..` segment — even ones that pass earlier checks — because a parent-traversal segment would let a write claim escape the workspace boundary. This is the defense-in-depth guard for Fleet write roots.","triggerScenarios":"fleet_write_roots or fleet_runtime_write_roots passes a path whose segments include `..` after normalization (e.g. `src/../../escape` or `a/../b` residue), and the bail fires with the original path and the owning field name.","commonSituations":"Hand-written task specs using relative shorthands with `..`; generated configs concatenating prefixes that leave `..` segments; users trying to share one writable root across sibling repos via traversal.","solutions":["Remove the `..` segments and express the path from the repository root.","List each target directory explicitly under writable_paths instead of traversing upward.","If the target lies outside the workspace, move it inside or change the task's workspace.","Trim empty/`.` segments first if the intent was a plain relative path like `./src` — write `src`."],"exampleFix":"// before\nwritable_paths = [\"src/../../shared/out\"]\n\n// after\nwritable_paths = [\"shared/out\"] // if actually in-repo, or restructure","handlingStrategy":"validation","validationCode":"if p.to_string_lossy().split('/').any(|s| s == \"..\") {\n    return Err(\"write paths cannot contain `..` segments\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Express all write claims from the workspace root — no upward traversal.","Add spec linting that rejects any `..` segment.","Split out-of-workspace needs into separate workspaces, not traversals."],"tags":["fleet","path-validation","security","path-traversal"],"backgroundTag":"path-traversal-blocked","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}