diff options
| author | Adam Malczewski <[email protected]> | 2026-06-21 23:32:23 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-21 23:32:23 +0900 |
| commit | 7f6fd218ceeb3a1cf9420f5f6cfa4d70da6987bb (patch) | |
| tree | e706f49d2df591c468fb66febe2a3f29d23cb0bb /packages/tool-read-file/src/read-file.ts | |
| parent | 62ea07f56ff066bbf05041aadf8006cbc65e5c53 (diff) | |
| download | dispatch-7f6fd218ceeb3a1cf9420f5f6cfa4d70da6987bb.tar.gz dispatch-7f6fd218ceeb3a1cf9420f5f6cfa4d70da6987bb.zip | |
feat: remove CWD path containment from file tools
read_file, write_file, and edit_file no longer restrict access to
paths outside the working directory. The isPathWithinWorkdir prefix
check and symlink hardening have been removed from all three tools.
This allows agents to read and write files anywhere on the filesystem,
not just within the per-turn cwd. The shell tool already had no such
restriction.
Diffstat (limited to 'packages/tool-read-file/src/read-file.ts')
| -rw-r--r-- | packages/tool-read-file/src/read-file.ts | 47 |
1 files changed, 2 insertions, 45 deletions
diff --git a/packages/tool-read-file/src/read-file.ts b/packages/tool-read-file/src/read-file.ts index 99b396e..216f165 100644 --- a/packages/tool-read-file/src/read-file.ts +++ b/packages/tool-read-file/src/read-file.ts @@ -1,5 +1,5 @@ -import { readdir, readFile, realpath, stat } from "node:fs/promises"; -import { resolve, sep } from "node:path"; +import { readdir, readFile, stat } from "node:fs/promises"; +import { resolve } from "node:path"; import type { ToolContract, ToolResult } from "@dispatch/kernel"; const DEFAULT_LIMIT = 500; @@ -59,12 +59,6 @@ export function sliceLines( return { lines: sliced, totalLines }; } -/** Pure: check that a resolved absolute path is within the workdir (prefix check). */ -export function isPathWithinWorkdir(resolvedPath: string, workdir: string): boolean { - const normalizedWorkdir = workdir.endsWith(sep) ? workdir : workdir + sep; - return resolvedPath === workdir || resolvedPath.startsWith(normalizedWorkdir); -} - /** Pure: render lines into a string with line numbers. */ export function renderLines(lines: readonly string[], offset: number): string { return lines.map((line, i) => `${offset + i}: ${line}`).join("\n"); @@ -131,46 +125,9 @@ export function createReadFileTool(workingDirectory: string): ToolContract { const { path: relPath, offset, limit } = validated; - // Effective base: per-turn ctx.cwd overrides the baked workdir. const effectiveBase = ctx.cwd ? resolve(ctx.cwd) : workdir; - - // Resolve the requested path against the effective base. const resolvedPath = resolve(effectiveBase, relPath); - // Basic prefix check (catches ".." and absolute paths outside effectiveBase). - if (!isPathWithinWorkdir(resolvedPath, effectiveBase)) { - return { - content: `Error: Path "${relPath}" is outside the working directory.`, - isError: true, - }; - } - - // Symlink hardening: realpath both and re-check containment. - let realResolved: string; - let realBase: string; - try { - [realResolved, realBase] = await Promise.all([ - realpath(resolvedPath), - realpath(effectiveBase), - ]); - } catch (err: unknown) { - const code = (err as NodeJS.ErrnoException).code; - if (code === "ENOENT") { - return { content: `Error: File "${relPath}" not found.`, isError: true }; - } - return { - content: `Error reading file: ${err instanceof Error ? err.message : String(err)}`, - isError: true, - }; - } - - if (!isPathWithinWorkdir(realResolved, realBase)) { - return { - content: `Error: Path "${relPath}" is outside the working directory.`, - isError: true, - }; - } - // Stat to determine if this is a file or directory. let pathStat: import("node:fs").Stats; try { |
