refactor: share XML path extraction between edit and read file regex validation

This commit is contained in:
Roo Code 2025-12-31 03:10:45 +00:00
parent 0e9286e5c2
commit 11c88f698c

View file

@ -78,6 +78,28 @@ function doesFileMatchRegex(filePath: string, pattern: string): boolean {
}
}
/**
* Extracts file paths from XML args format: <path>...</path>
* Used by both edit and read file regex validation.
*/
function extractPathsFromXmlArgs(args: string): string[] {
const paths: string[] = []
const filePathMatches = args.match(/<path>([^<]+)<\/path>/g)
if (filePathMatches) {
for (const match of filePathMatches) {
const pathMatch = match.match(/<path>([^<]+)<\/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<string, unknown> | undefined):
// Legacy XML args read_file: { args: "<args><file><path>...</path>...</file></args>" }
const args = (toolParams as { args?: unknown }).args
if (typeof args === "string") {
const filePathMatches = args.match(/<path>([^<]+)<\/path>/g)
if (filePathMatches) {
for (const match of filePathMatches) {
const pathMatch = match.match(/<path>([^<]+)<\/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>([^<]+)<\/path>/g)
if (filePathMatches) {
for (const match of filePathMatches) {
// More robust path extraction with validation
const pathMatch = match.match(/<path>([^<]+)<\/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