diff --git a/src/agents/apply-patch.ts b/src/agents/apply-patch.ts index 806333af2ee28..ed4c2b9db10fb 100644 --- a/src/agents/apply-patch.ts +++ b/src/agents/apply-patch.ts @@ -1,7 +1,6 @@ import type { AgentTool } from "@mariozechner/pi-agent-core"; import { Type } from "@sinclair/typebox"; import fs from "node:fs/promises"; -import os from "node:os"; import path from "node:path"; import { applyUpdateHunk } from "./apply-patch-update.js"; import { assertSandboxPath } from "./sandbox-paths.js"; @@ -15,7 +14,6 @@ const MOVE_TO_MARKER = "*** Move to: "; const EOF_MARKER = "*** End of File"; const CHANGE_CONTEXT_MARKER = "@@ "; const EMPTY_CHANGE_CONTEXT_MARKER = "@@"; -const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g; type AddFileHunk = { kind: "add"; @@ -228,46 +226,23 @@ async function resolvePatchPath( }; } - const resolved = resolvePathFromCwd(filePath, options.cwd); + // When no sandbox is provided, use the current working directory as a safe default sandbox + // This prevents path traversal attacks by constraining all operations to the project directory + const effectiveSandbox = options.cwd; + const resolved = await assertSandboxPath({ + filePath, + cwd: options.cwd, + root: effectiveSandbox, + }); return { - resolved, - display: toDisplayPath(resolved, options.cwd), + resolved: resolved.resolved, + display: resolved.relative || resolved.resolved, }; } -function normalizeUnicodeSpaces(value: string): string { - return value.replace(UNICODE_SPACES, " "); -} - -function expandPath(filePath: string): string { - const normalized = normalizeUnicodeSpaces(filePath); - if (normalized === "~") { - return os.homedir(); - } - if (normalized.startsWith("~/")) { - return os.homedir() + normalized.slice(1); - } - return normalized; -} - -function resolvePathFromCwd(filePath: string, cwd: string): string { - const expanded = expandPath(filePath); - if (path.isAbsolute(expanded)) { - return path.normalize(expanded); - } - return path.resolve(cwd, expanded); -} - -function toDisplayPath(resolved: string, cwd: string): string { - const relative = path.relative(cwd, resolved); - if (!relative || relative === "") { - return path.basename(resolved); - } - if (relative.startsWith("..") || path.isAbsolute(relative)) { - return resolved; - } - return relative; -} +// Note: normalizeUnicodeSpaces, expandPath, resolvePathFromCwd, and toDisplayPath +// functions have been removed as they are now handled by the secure assertSandboxPath function +// in sandbox-paths.ts, which provides the same functionality with proper security validation. function parsePatchText(input: string): { hunks: Hunk[]; patch: string } { const trimmed = input.trim();