diff --git a/core/tools/definitions/readFile.ts b/core/tools/definitions/readFile.ts index b81ab3cf2..8fcd1d5f4 100644 --- a/core/tools/definitions/readFile.ts +++ b/core/tools/definitions/readFile.ts @@ -1,6 +1,6 @@ import { ToolPolicy } from "@continuedev/terminal-security"; import { Tool } from "../.."; -import { resolveInputPath } from "../../util/pathResolver"; +import { ResolvedPath, resolveInputPath } from "../../util/pathResolver"; import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn"; import { evaluateFileAccessPolicy } from "../policies/fileAccess"; @@ -49,7 +49,10 @@ export const readFileTool: Tool = { basePolicy: ToolPolicy, parsedArgs: Record, ): ToolPolicy => { - const resolvedPath = parsedArgs._resolvedPath as any; + const resolvedPath = parsedArgs._resolvedPath as + | ResolvedPath + | null + | undefined; if (!resolvedPath) return basePolicy; return evaluateFileAccessPolicy(basePolicy, resolvedPath.isWithinWorkspace); diff --git a/core/tools/definitions/readFileRange.ts b/core/tools/definitions/readFileRange.ts index 04599581c..6753e76fe 100644 --- a/core/tools/definitions/readFileRange.ts +++ b/core/tools/definitions/readFileRange.ts @@ -1,6 +1,6 @@ import { ToolPolicy } from "@continuedev/terminal-security"; import { Tool } from "../.."; -import { resolveInputPath } from "../../util/pathResolver"; +import { ResolvedPath, resolveInputPath } from "../../util/pathResolver"; import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn"; import { evaluateFileAccessPolicy } from "../policies/fileAccess"; @@ -66,7 +66,7 @@ export const readFileRangeTool: Tool = { basePolicy: ToolPolicy, parsedArgs: Record, ): ToolPolicy => { - const resolvedPath = parsedArgs._resolvedPath as any; + const resolvedPath = parsedArgs._resolvedPath as ResolvedPath | null | undefined; if (!resolvedPath) return basePolicy; return evaluateFileAccessPolicy(basePolicy, resolvedPath.isWithinWorkspace); diff --git a/core/util/pathResolver.test.ts b/core/util/pathResolver.test.ts index 5df996097..cdbd9b046 100644 --- a/core/util/pathResolver.test.ts +++ b/core/util/pathResolver.test.ts @@ -3,13 +3,15 @@ import * as path from "path"; import { IDE } from ".."; import { normalizeDisplayPath, resolveInputPath } from "./pathResolver"; import * as ideUtils from "./ideUtils"; +import * as uri from "./uri"; // Mock the resolveRelativePathInDir function jest.mock("./ideUtils"); +jest.mock("./uri"); describe("resolveUserProvidedPath", () => { const mockIde = { - getWorkspaceDirs: jest.fn().mockResolvedValue(["/workspace"]), + getWorkspaceDirs: jest.fn().mockResolvedValue(["file:///workspace"]), fileExists: jest.fn(), } as unknown as IDE; @@ -29,6 +31,24 @@ describe("resolveUserProvidedPath", () => { return null; } ); + + // Setup the mock for findUriInDirs + (uri.findUriInDirs as jest.Mock).mockImplementation((uri, dirUriCandidates) => { + for (const dir of dirUriCandidates) { + if (uri.startsWith(dir)) { + return { + uri, + relativePathOrBasename: uri.slice(dir.length + 1), + foundInDir: dir, + }; + } + } + return { + uri, + relativePathOrBasename: uri.split('/').pop() || '', + foundInDir: null, + }; + }); }); describe("file:// URIs", () => { diff --git a/core/util/pathResolver.ts b/core/util/pathResolver.ts index a7d8baac2..e39a2e84b 100644 --- a/core/util/pathResolver.ts +++ b/core/util/pathResolver.ts @@ -3,6 +3,7 @@ import * as path from "path"; import { IDE } from ".."; import { resolveRelativePathInDir } from "./ideUtils"; import { localPathToUri } from "./pathToUri"; +import { findUriInDirs } from "./uri"; export interface ResolvedPath { uri: string; @@ -12,23 +13,15 @@ export interface ResolvedPath { } /** - * Checks if a path is within any of the workspace directories + * Checks if a URI is within any of the workspace directories */ -async function isPathWithinWorkspace( +async function isUriWithinWorkspace( ide: IDE, - absolutePath: string + uri: string ): Promise { const workspaceDirs = await ide.getWorkspaceDirs(); - const normalizedPath = path.normalize(absolutePath).toLowerCase(); - - for (const dir of workspaceDirs) { - const normalizedDir = path.normalize(dir).toLowerCase(); - if (normalizedPath.startsWith(normalizedDir)) { - return true; - } - } - - return false; + const { foundInDir } = findUriInDirs(uri, workspaceDirs); + return foundInDir !== null; } /** @@ -52,7 +45,7 @@ export async function resolveInputPath( const uri = trimmedPath; // Extract path from URI for display const displayPath = decodeURIComponent(uri.slice(7)); - const isWithinWorkspace = await isPathWithinWorkspace(ide, displayPath); + const isWithinWorkspace = await isUriWithinWorkspace(ide, uri); return { uri, displayPath, @@ -87,7 +80,7 @@ export async function resolveInputPath( if (expandedPath.startsWith("\\\\")) { const networkPath = expandedPath.replace(/\\/g, "/"); const uri = "file:" + networkPath; // file://server/share format - const isWithinWorkspace = await isPathWithinWorkspace(ide, expandedPath); + const isWithinWorkspace = await isUriWithinWorkspace(ide, uri); return { uri, displayPath: expandedPath, @@ -97,7 +90,7 @@ export async function resolveInputPath( } // Convert absolute path to URI const uri = localPathToUri(expandedPath); - const isWithinWorkspace = await isPathWithinWorkspace(ide, expandedPath); + const isWithinWorkspace = await isUriWithinWorkspace(ide, uri); return { uri, displayPath: expandedPath, diff --git a/gui/src/redux/thunks/evaluateToolPolicies.ts b/gui/src/redux/thunks/evaluateToolPolicies.ts index 570855261..15b052f31 100644 --- a/gui/src/redux/thunks/evaluateToolPolicies.ts +++ b/gui/src/redux/thunks/evaluateToolPolicies.ts @@ -38,8 +38,12 @@ async function evaluateToolPolicy( )?.defaultToolPolicy ?? DEFAULT_TOOL_SETTING; - // Use preprocessed arguments if available, otherwise fall back to parsed arguments - const args = toolCallState.processedArgs || toolCallState.parsedArgs || {}; + // Merge parsed and preprocessed arguments to ensure we have both original args and any added metadata + // processedArgs may add metadata like _resolvedPath but should include all parsedArgs fields + const args = { + ...toolCallState.parsedArgs, + ...toolCallState.processedArgs, + }; const toolName = toolCallState.toolCall.function.name; const result = await ideMessenger.request("tools/evaluatePolicy", {