( resolvedPath: string, context: ToolPermissionContext, operationType: FileOperationType, precomputedPathsToCheck?: readonly string[], )
| 139 | * resolution is still required for those. |
| 140 | */ |
| 141 | export function isPathAllowed( |
| 142 | resolvedPath: string, |
| 143 | context: ToolPermissionContext, |
| 144 | operationType: FileOperationType, |
| 145 | precomputedPathsToCheck?: readonly string[], |
| 146 | ): PathCheckResult { |
| 147 | // Determine which permission type to check based on operation |
| 148 | const permissionType = operationType === 'read' ? 'read' : 'edit' |
| 149 | |
| 150 | // 1. Check deny rules first (they take precedence) |
| 151 | const denyRule = matchingRuleForInput( |
| 152 | resolvedPath, |
| 153 | context, |
| 154 | permissionType, |
| 155 | 'deny', |
| 156 | ) |
| 157 | if (denyRule !== null) { |
| 158 | return { |
| 159 | allowed: false, |
| 160 | decisionReason: { type: 'rule', rule: denyRule }, |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | // 2. For write/create operations, check internal editable paths (plan files, scratchpad, agent memory, job dirs) |
| 165 | // This MUST come before checkPathSafetyForAutoEdit since .claude is a dangerous directory |
| 166 | // and internal editable paths live under ~/.claude/ — matching the ordering in |
| 167 | // checkWritePermissionForTool (filesystem.ts step 1.5) |
| 168 | if (operationType !== 'read') { |
| 169 | const internalEditResult = checkEditableInternalPath(resolvedPath, {}) |
| 170 | if (internalEditResult.behavior === 'allow') { |
| 171 | return { |
| 172 | allowed: true, |
| 173 | decisionReason: internalEditResult.decisionReason, |
| 174 | } |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | // 2.5. For write/create operations, check comprehensive safety validations |
| 179 | // This MUST come before checking working directory to prevent bypass via acceptEdits mode |
| 180 | // Checks: Windows patterns, Claude config files, dangerous files (on original + symlink paths) |
| 181 | if (operationType !== 'read') { |
| 182 | const safetyCheck = checkPathSafetyForAutoEdit( |
| 183 | resolvedPath, |
| 184 | precomputedPathsToCheck, |
| 185 | ) |
| 186 | if (!safetyCheck.safe) { |
| 187 | return { |
| 188 | allowed: false, |
| 189 | decisionReason: { |
| 190 | type: 'safetyCheck', |
| 191 | reason: safetyCheck.message, |
| 192 | classifierApprovable: safetyCheck.classifierApprovable, |
| 193 | }, |
| 194 | } |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | // 3. Check if path is in allowed working directory |
no test coverage detected