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