From 11c88f698ce70f96c9e3258078b6b341e4f11d92 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Wed, 31 Dec 2025 03:10:45 +0000 Subject: [PATCH] refactor: share XML path extraction between edit and read file regex validation --- src/core/tools/validateToolUse.ts | 77 ++++++++++++++----------------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/src/core/tools/validateToolUse.ts b/src/core/tools/validateToolUse.ts index 86fc69cec5..a9fe107495 100644 --- a/src/core/tools/validateToolUse.ts +++ b/src/core/tools/validateToolUse.ts @@ -78,6 +78,28 @@ function doesFileMatchRegex(filePath: string, pattern: string): boolean { } } +/** + * Extracts file paths from XML args format: ... + * Used by both edit and read file regex validation. + */ +function extractPathsFromXmlArgs(args: string): string[] { + const paths: string[] = [] + const filePathMatches = args.match(/([^<]+)<\/path>/g) + if (filePathMatches) { + for (const match of filePathMatches) { + const pathMatch = match.match(/([^<]+)<\/path>/) + if (pathMatch && pathMatch[1]) { + const extractedPath = pathMatch[1].trim() + // Validate that the path is not empty and doesn't contain invalid characters + if (extractedPath && !extractedPath.includes("<") && !extractedPath.includes(">")) { + paths.push(extractedPath) + } + } + } + } + return paths +} + function tryParseJson(value: string): unknown | undefined { const trimmed = value.trim() if (!trimmed) { @@ -167,18 +189,7 @@ function extractReadFilePaths(toolParams: Record | undefined): // Legacy XML args read_file: { args: "......" } const args = (toolParams as { args?: unknown }).args if (typeof args === "string") { - const filePathMatches = args.match(/([^<]+)<\/path>/g) - if (filePathMatches) { - for (const match of filePathMatches) { - const pathMatch = match.match(/([^<]+)<\/path>/) - if (pathMatch && pathMatch[1]) { - const extractedPath = pathMatch[1].trim() - if (extractedPath && !extractedPath.includes("<") && !extractedPath.includes(">")) { - paths.push(extractedPath) - } - } - } - } + paths.push(...extractPathsFromXmlArgs(args)) } return Array.from(new Set(paths)) @@ -271,40 +282,20 @@ export function isToolAllowedForMode( } // Handle XML args parameter (used by MULTI_FILE_APPLY_DIFF experiment) - if (toolParams?.args && typeof toolParams.args === "string") { - // Extract file paths from XML args with improved validation - try { - const filePathMatches = toolParams.args.match(/([^<]+)<\/path>/g) - if (filePathMatches) { - for (const match of filePathMatches) { - // More robust path extraction with validation - const pathMatch = match.match(/([^<]+)<\/path>/) - if (pathMatch && pathMatch[1]) { - const extractedPath = pathMatch[1].trim() - // Validate that the path is not empty and doesn't contain invalid characters - if (extractedPath && !extractedPath.includes("<") && !extractedPath.includes(">")) { - if (!doesFileMatchRegex(extractedPath, options.fileRegex)) { - throw new FileRestrictionError( - mode.name, - options.fileRegex, - options.description, - extractedPath, - tool, - ) - } - } - } + if (toolParams?.args && typeof toolParams.args === "string") { + const xmlPaths = extractPathsFromXmlArgs(toolParams.args) + for (const extractedPath of xmlPaths) { + if (!doesFileMatchRegex(extractedPath, options.fileRegex)) { + throw new FileRestrictionError( + mode.name, + options.fileRegex, + options.description, + extractedPath, + tool, + ) } } - } catch (error) { - // Re-throw FileRestrictionError as it's an expected validation error - if (error instanceof FileRestrictionError) { - throw error - } - // If XML parsing fails, log the error but don't block the operation - console.warn(`Failed to parse XML args for file restriction validation: ${error}`) } - } } // For the read group, optionally restrict read_file paths if specified