{"record":{"id":"ff0b552f1b5db428","repo":"Hmbown/CodeWhale","slug":"fleet-task-task-id-field-path-must-be-o","errorCode":null,"errorMessage":"fleet task '{task_id}' {field} path '{}' must be one repo-relative line and cannot escape the workspace","messagePattern":"fleet task '(.+?)' (.+?) path '(.+?)' must be one repo-relative line and cannot escape the workspace","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/fleet/worker_runtime.rs","lineNumber":353,"sourceCode":"\nfn normalize_fleet_relative_path(\n    path: &std::path::Path,\n    task_id: &str,\n    field: &str,\n) -> Result<String> {\n    let raw = path.to_string_lossy().replace('\\\\', \"/\");\n    if raw.chars().any(|ch| matches!(ch, '\\0' | '\\r' | '\\n'))\n        || path.is_absolute()\n        || path.components().any(|component| {\n            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() {","sourceCodeStart":335,"sourceCodeEnd":371,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/fleet/worker_runtime.rs#L335-L371","documentation":"A path in `workspace.writable_paths` failed `normalize_fleet_relative_path`: it must be a single repo-relative line. Rejected shapes are absolute paths (leading `/`, Windows drive prefixes, root-dir components), any `..` component, and embedded NUL/CR/LF characters. Backslashes are treated as separators (they are replaced with `/` first), so Windows-style paths do not slip through on Unix.","triggerScenarios":"`writable_paths = [\"/tmp/work\"]` (absolute), `[\"C:\\\\repo\\\\src\"]` (Windows prefix), `[\"src\\\\nother\"]` or a value containing a literal newline, or any entry with a `..` component.","commonSituations":"Authors pasting absolute output directories from their shell; specs written on Windows using backslash paths; templating that embeds multi-line values into a path field.","solutions":["Express the path relative to the workspace root, e.g. \"src\" or \"pkg-a/src\".","Remove drive letters, leading slashes, and any newline characters from the value.","For paths outside the repo, use a coordination mechanism instead of an absolute writable path — the sandbox deliberately forbids escapes."],"exampleFix":"# before\n[workspace]\nwritable_paths = [\"/home/me/repo/src\", \"C:\\\\work\\\\pkg\"]\n\n# after\n[workspace]\nwritable_paths = [\"src\", \"pkg\"]","handlingStrategy":"validation","validationCode":"fn is_safe_relative_path(raw: &str) -> bool {\n    let normalized = raw.replace('\\\\', \"/\");\n    !normalized.chars().any(|c| matches!(c, '\\0' | '\\r' | '\\n'))\n        && !normalized.starts_with('/')\n        && !normalized.split('/').any(|seg| seg == \"..\" || seg.contains(':'))\n        && !normalized.trim().is_empty()\n}","typeGuard":"fn is_workspace_relative(path: &std::path::Path) -> bool {\n    let raw = path.to_string_lossy().replace('\\\\', \"/\");\n    !path.is_absolute() && !raw.split('/').any(|seg| seg == \"..\")\n}","tryCatchPattern":null,"preventionTips":["Always author writable_paths relative to the workspace root; never absolute or drive-prefixed.","Canonicalize externally sourced paths, then re-anchor them under the workspace before putting them in a spec.","Reject multi-line values in path fields at template-render time."],"tags":["fleet","path-validation","sandbox","workspace","rust"],"backgroundTag":"path-traversal-rejected","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}