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