-
Notifications
You must be signed in to change notification settings - Fork 675
Description
Summary
The Edit tool reads file content to validate old_string parameter before PreToolUse hooks are executed. This allows attackers to probe the content of files outside allowed directories by observing error messages, even when PreToolUse hooks are configured to block such access.
Vulnerability Details
Current Behavior
When an Edit operation is attempted with an old_string that:
- Has multiple matches in the file → Returns "Found X matches" error
- Does not exist in the file → Returns "String not found" error
The file content is read before PreToolUse hooks can validate the file path.
Attack Vector
An attacker can probe file content outside allowed directories:
- Test for string existence: Use
old_string="secret_api_key"→ If "String not found", the string doesn't exist; if "Found X matches", it does - Enumerate file content: Use common strings (
"password","token","key") to discover what's in the file - Bypass security boundary: Even though the edit is ultimately blocked, the information leak has already occurred
Proof of Concept
- Configure a PreToolUse hook to restrict file access to a specific directory:
async def edit_only_cwd_hook(hook_input: HookInput, _session_id: str | None, _ctx: HookContext):
cwd = Path(hook_input["cwd"])
tool_input = hook_input.get("tool_input", {})
file_path_str = tool_input.get("file_path", "")
try:
file_path.relative_to(cwd)
except ValueError:
return SyncHookJSONOutput(decision="block", reason="...")
return SyncHookJSONOutput()
hooks = {
"PreToolUse": [
HookMatcher(matcher="Edit", hooks=[edit_only_cwd_hook])
]
}- Attempt to edit a file outside the allowed directory with a unique string:
Edit tool:
file_path: "/etc/passwd" (or any restricted file)
old_string: "root"
new_string: "hacked"
- Result: Returns "Found X matches of the string to replace..." error
The error message confirms the string "root" exists in the file, leaking information about file content.
Expected Behavior
PreToolUse hooks should be executed before any file content is read. The validation order should be:
- ✅ PreToolUse hook validates file path
- ✅ Hook blocks access to restricted paths
- ❌ File content is never read (currently this happens before step 1)
Security Impact
- Information disclosure: Attackers can probe file content character by character
- Security boundary bypass: Path-based access control is ineffective
- Credential discovery: Can detect presence of secrets, API keys, passwords in restricted files
Affected Components
- Tool:
Edit - Hook Event:
PreToolUse - May also affect:
Write(similar pattern)
Environment
- Claude Code version 2.1.50
- Claude Agent SDK Version: 0.1.39
- Platform: macOS 26.3
Related Issues (Claude Code Repository)
- anthropics/claude-code#24908 - PostToolUse/PostToolUseFailure not fired for
<tool_use_error>responses - anthropics/claude-code#21460 - PreToolUse hooks not enforced on subagent tool calls
Suggested Fix
The PreToolUse hook should be triggered and validated before the Edit tool reads the file content. Only after the hook approves the operation should the file be accessed to validate old_string.
Alternative: Temporary Workaround
For users affected by this issue, consider:
- Disable Edit tool entirely - Only allow Write tool (may have similar issue, needs verification)
- Use MCP filesystem tools with path restrictions instead of built-in tools
- Implement additional sandboxing at the OS level (e.g., containerization)