Problem
isPathSafe (src/mcp.zig:5075) rejects every absolute path via a blanket if (path[0] == '/') return false. Because handleRead/handleEdit gate on isPathSafe before resolving anything, an absolute path that points at a file inside the indexed project root is rejected as path traversal not allowed.
This is invisible in scalar metrics but shows up in real traces. In the #626 session trajectory the agent called codedb twice, got an instant (10µs) path traversal not allowed / PathNotAllowed rejection both times, then abandoned codedb and fell back to seven bash calls to finish the task:
webfetch, codedb!, codedb!, read_file!, bash, bash, bash, bash, bash, bash, bash
Agents naturally hold and pass absolute paths. A bare rejection with no remediation makes them drop the tool — codedb's correctness is masked by bash fallback, so the task still "passes" while codedb contributed nothing.
Failing Test
src/test_mcp.zig (run zig build test-mcp):
test "issue-629: isPathSafe rejects absolute paths inside the project root" {
const MCP = @import("mcp.zig");
var buf: [std.fs.max_path_bytes]u8 = undefined;
const root_len = try std.Io.Dir.cwd().realPathFile(io, ".", &buf);
const root = buf[0..root_len];
const abs = try std.fs.path.join(testing.allocator, &.{ root, "src", "main.zig" });
defer testing.allocator.free(abs);
// An absolute path inside the project root is safe to read.
try testing.expect(MCP.isPathSafe(abs));
// Security must still hold: paths outside the project root stay rejected.
try testing.expect(!MCP.isPathSafe("/etc/passwd"));
try testing.expect(!MCP.isPathSafe("../../../etc/passwd"));
}
Fails on main at testing.expect(MCP.isPathSafe(abs)) (123 pass, 1 fail).
Expected
An absolute path that resolves under an indexable project root is accepted (and treated as the equivalent relative path). Absolute paths outside the root (/etc/passwd, system dirs) and .. traversal stay rejected — the existing issue-93 security assertions must still pass.
Fix
Make the read/edit path-safety check root-aware instead of blanket-rejecting a leading /:
- If
path is absolute, accept it only when it is an exact-or-child of an indexable root (reuse root_policy.isExactOrChild / isIndexableRoot), then strip the root prefix to the relative form the explorer expects.
- Keep rejecting
.. components, null bytes, backslashes, and out-of-root absolutes.
- Consider returning a remediation hint in the error (the allowed root, or "pass a path relative to ") so agents recover instead of abandoning the tool.
Problem
isPathSafe(src/mcp.zig:5075) rejects every absolute path via a blanketif (path[0] == '/') return false. BecausehandleRead/handleEditgate onisPathSafebefore resolving anything, an absolute path that points at a file inside the indexed project root is rejected aspath traversal not allowed.This is invisible in scalar metrics but shows up in real traces. In the #626 session trajectory the agent called codedb twice, got an instant (10µs)
path traversal not allowed/PathNotAllowedrejection both times, then abandoned codedb and fell back to sevenbashcalls to finish the task:Agents naturally hold and pass absolute paths. A bare rejection with no remediation makes them drop the tool — codedb's correctness is masked by bash fallback, so the task still "passes" while codedb contributed nothing.
Failing Test
src/test_mcp.zig(runzig build test-mcp):Fails on
mainattesting.expect(MCP.isPathSafe(abs))(123 pass, 1 fail).Expected
An absolute path that resolves under an indexable project root is accepted (and treated as the equivalent relative path). Absolute paths outside the root (
/etc/passwd, system dirs) and..traversal stay rejected — the existingissue-93security assertions must still pass.Fix
Make the read/edit path-safety check root-aware instead of blanket-rejecting a leading
/:pathis absolute, accept it only when it is an exact-or-child of an indexable root (reuseroot_policy.isExactOrChild/isIndexableRoot), then strip the root prefix to the relative form the explorer expects...components, null bytes, backslashes, and out-of-root absolutes.