( path: string, toolPermissionContext: ToolPermissionContext, precomputedPathsToCheck?: readonly string[], )
| 681 | export const getResolvedWorkingDirPaths = memoize(getPathsForPermissionCheck) |
| 682 | |
| 683 | export function pathInAllowedWorkingPath( |
| 684 | path: string, |
| 685 | toolPermissionContext: ToolPermissionContext, |
| 686 | precomputedPathsToCheck?: readonly string[], |
| 687 | ): boolean { |
| 688 | // Check both the original path and the resolved symlink path |
| 689 | const pathsToCheck = |
| 690 | precomputedPathsToCheck ?? getPathsForPermissionCheck(path) |
| 691 | |
| 692 | // Resolve working directories the same way we resolve input paths so |
| 693 | // comparisons are symmetric. Without this, a resolved input path |
| 694 | // (e.g. /System/Volumes/Data/home/... on macOS) would not match an |
| 695 | // unresolved working directory (/home/...), causing false denials. |
| 696 | const workingPaths = Array.from( |
| 697 | allWorkingDirectories(toolPermissionContext), |
| 698 | ).flatMap(wp => getResolvedWorkingDirPaths(wp)) |
| 699 | |
| 700 | // All paths must be within allowed working paths |
| 701 | // If any resolved path is outside, deny access |
| 702 | return pathsToCheck.every(pathToCheck => |
| 703 | workingPaths.some(workingPath => |
| 704 | pathInWorkingPath(pathToCheck, workingPath), |
| 705 | ), |
| 706 | ) |
| 707 | } |
| 708 | |
| 709 | export function pathInWorkingPath(path: string, workingPath: string): boolean { |
| 710 | const absolutePath = expandPath(path) |
no test coverage detected