(input: FileEditInput, toolUseContext: ToolUseContext)
| 135 | renderToolUseRejectedMessage, |
| 136 | renderToolUseErrorMessage, |
| 137 | async validateInput(input: FileEditInput, toolUseContext: ToolUseContext) { |
| 138 | const { file_path, old_string, new_string, replace_all = false } = input |
| 139 | // Use expandPath for consistent path normalization (especially on Windows |
| 140 | // where "/" vs "\" can cause readFileState lookup mismatches) |
| 141 | const fullFilePath = expandPath(file_path) |
| 142 | |
| 143 | // Reject edits to team memory files that introduce secrets |
| 144 | const secretError = checkTeamMemSecrets(fullFilePath, new_string) |
| 145 | if (secretError) { |
| 146 | return { result: false, message: secretError, errorCode: 0 } |
| 147 | } |
| 148 | if (old_string === new_string) { |
| 149 | return { |
| 150 | result: false, |
| 151 | behavior: 'ask', |
| 152 | message: |
| 153 | 'No changes to make: old_string and new_string are exactly the same.', |
| 154 | errorCode: 1, |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | // Check if path should be ignored based on permission settings |
| 159 | const appState = toolUseContext.getAppState() |
| 160 | const denyRule = matchingRuleForInput( |
| 161 | fullFilePath, |
| 162 | appState.toolPermissionContext, |
| 163 | 'edit', |
| 164 | 'deny', |
| 165 | ) |
| 166 | if (denyRule !== null) { |
| 167 | return { |
| 168 | result: false, |
| 169 | behavior: 'ask', |
| 170 | message: |
| 171 | 'File is in a directory that is denied by your permission settings.', |
| 172 | errorCode: 2, |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | // SECURITY: Skip filesystem operations for UNC paths to prevent NTLM credential leaks. |
| 177 | // On Windows, fs.existsSync() on UNC paths triggers SMB authentication which could |
| 178 | // leak credentials to malicious servers. Let the permission check handle UNC paths. |
| 179 | if (fullFilePath.startsWith('\\\\') || fullFilePath.startsWith('//')) { |
| 180 | return { result: true } |
| 181 | } |
| 182 | |
| 183 | const fs = getFsImplementation() |
| 184 | |
| 185 | // Prevent OOM on multi-GB files. |
| 186 | try { |
| 187 | const { size } = await fs.stat(fullFilePath) |
| 188 | if (size > MAX_EDIT_FILE_SIZE) { |
| 189 | return { |
| 190 | result: false, |
| 191 | behavior: 'ask', |
| 192 | message: `File is too large to edit (${formatFileSize(size)}). Maximum editable file size is ${formatFileSize(MAX_EDIT_FILE_SIZE)}.`, |
| 193 | errorCode: 10, |
| 194 | } |
nothing calls this directly
no test coverage detected