From da57842686ebfd157396551fc76d0c18f7676335 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Wed, 27 May 2026 17:22:52 +0900 Subject: feat: tool-output truncation+spill, read_file pagination, read_file_slice, symlink-safe path resolution --- packages/core/src/tools/write-file.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'packages/core/src/tools/write-file.ts') diff --git a/packages/core/src/tools/write-file.ts b/packages/core/src/tools/write-file.ts index 23bc72a..763b083 100644 --- a/packages/core/src/tools/write-file.ts +++ b/packages/core/src/tools/write-file.ts @@ -1,7 +1,8 @@ import { mkdir, writeFile } from "node:fs/promises"; -import { dirname, join, resolve } from "node:path"; +import { dirname } from "node:path"; import { z } from "zod"; import type { ToolDefinition } from "../types/index.js"; +import { canonicalize } from "./path-utils.js"; export function createWriteFileTool(workingDirectory: string): ToolDefinition { return { @@ -14,10 +15,19 @@ export function createWriteFileTool(workingDirectory: string): ToolDefinition { execute: async (args: Record): Promise => { const filePath = args.path as string; const content = args.content as string; - const absolutePath = resolve(join(workingDirectory, filePath)); - const absoluteWorkDir = resolve(workingDirectory); + // Canonicalize so a workdir-relative path that resolves through + // symlinks to outside the workdir is detected and blocked. The + // canonicalize walks up to the nearest existing ancestor when the + // leaf doesn't exist (typical for write_file), so a path like + // `workdir/escape-link/new-file.txt` where `escape-link` symlinks + // to /etc still resolves through the symlink and is caught here. + const absolutePath = await canonicalize(workingDirectory, filePath); + const absoluteWorkDir = await canonicalize(workingDirectory); - if (!absolutePath.startsWith(`${absoluteWorkDir}/`) && absolutePath !== absoluteWorkDir) { + if ( + absolutePath !== absoluteWorkDir && + !absolutePath.startsWith(`${absoluteWorkDir}/`) + ) { return `Error: Path "${filePath}" is outside the working directory.`; } -- cgit v1.2.3