{"record":{"id":"0b314b4a3eb8a74a","repo":"zeroclaw-labs/zeroclaw","slug":"path-not-allowed-contains-null-byte-0b314b","errorCode":null,"errorMessage":"Path not allowed: contains null byte","messagePattern":"Path not allowed: contains null byte","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-tools/src/git_operations.rs","lineNumber":110,"sourceCode":"                    anyhow::Error::msg(format!(\"Cannot resolve path '{}': {}\", p, e))\n                })?;\n                let workspace_canonical = self\n                    .workspace_dir\n                    .canonicalize()\n                    .unwrap_or_else(|_| self.workspace_dir.clone());\n                if !resolved.starts_with(&workspace_canonical) {\n                    anyhow::bail!(\"Path '{}' resolves outside the workspace directory\", p);\n                }\n                resolved\n            }\n            _ => self.workspace_dir.clone(),\n        };\n        Ok(base)\n    }\n\n    fn candidate_path(&self, raw_path: &str) -> anyhow::Result<PathBuf> {\n        if raw_path.contains('\\0') {\n            anyhow::bail!(\"Path not allowed: contains null byte\");\n        }\n        if Path::new(raw_path)\n            .components()\n            .any(|c| matches!(c, std::path::Component::ParentDir))\n        {\n            anyhow::bail!(\"Path not allowed: parent-directory traversal is not allowed\");\n        }\n\n        let raw = Path::new(raw_path);\n        Ok(if raw.is_absolute() {\n            raw.to_path_buf()\n        } else {\n            self.workspace_dir.join(raw)\n        })\n    }\n\n    fn ensure_worktree_add_target_allowed(&self, raw_path: &str) -> anyhow::Result<PathBuf> {\n        let candidate = self.candidate_path(raw_path)?;","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/git_operations.rs#L92-L128","documentation":"candidate_path, the shared pre-check for worktree add and remove targets, rejects any raw_path containing a NUL byte (git_operations.rs:109-110). A NUL cannot appear in a legal filesystem path and is the classic marker of corrupted input — a truncated C string, a binary blob leaked into JSON, or an LLM emitting \\u0000 — so it is refused before any filesystem access happens.","triggerScenarios":"Calling the git tool's worktree operation with a path string that embeds a NUL, such as \".worktrees/feat\\u0000\", a value decoded from malformed JSON with embedded control characters, or a path copied from a buffer that was not NUL-terminated and sliced past its end.","commonSituations":"Prompt-injection or fuzzing payloads reaching tool args; deserialized JSON where \\u0000 survived into a Rust String; copy-paste from logs containing binary garbage; downstream systems that build paths from fixed-size buffers.","solutions":["Sanitize the path upstream: strip or reject control characters before the tool call.","Fix the source of the string — usually JSON decoding or buffer slicing that let \\u0000 through.","Re-issue the worktree call with a clean, relative path under the workspace."],"exampleFix":"// before\nworktree(op: \"add\", path: \".worktrees/feat\\u0000\")\n// -> Path not allowed: contains null byte\n\n// after\nworktree(op: \"add\", path: \".worktrees/feat\")","handlingStrategy":"validation","validationCode":"fn path_is_clean(raw: &str) -> bool {\n    !raw.contains('\\0')\n}\nassert!(path_is_clean(worktree_path), \"worktree path contains a null byte\");","typeGuard":"fn path_is_clean(raw: &str) -> bool { !raw.contains('\\0') }","tryCatchPattern":"match git_tool.execute(params).await {\n    Err(e) if e.to_string().contains(\"contains null byte\") => {\n        // input corruption: strip control characters and retry once with the\n        // sanitized string; if another NUL appears, reject the input source\n    }\n    r => r,\n}","preventionTips":["Validate strings decoded from JSON or fuzzed sources before they reach tool args","Reject all control characters (not just NUL) in path inputs at the API boundary","Treat NUL bytes as a security signal: log the originating request, not just the path"],"tags":["git","worktree","path","validation","null-byte"],"backgroundTag":"null-byte-in-path","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}