Read tool bypasses PreToolUse hooks and permissions.deny rules in VSCode extension
What's Wrong?
permissions.deny rules and PreToolUse/PostToolUse hooks are not enforced for the Read tool in the VSCode extension. Files that should be blocked by both layers are read successfully with full contents returned to the conversation.
We tested each layer independently:
permissions.denyonly — addedRead(/workspaces/teamster/env/**)to deny, removed all broad Read allow rules, restarted session. Read still succeeded.- PreToolUse hook only — hook correctly returns
exit 0with{"permissionDecision": "deny"}on stdout when tested via shell. Read still succeeded in the extension. - PostToolUse hook — output scanner for secret patterns never fires because the Read completes without triggering any hooks.
The file in question (env/.env) contains 160 lines of plaintext production credentials (SFTP passwords, API keys, database credentials, OAuth secrets).
What Should Happen?
The Read call should be blocked by either:
- The
permissions.denyrule (evaluated before allow), or - The PreToolUse hook returning a deny decision on stdout with exit 0
Error Messages/Logs
No errors — the Read silently succeeds and returns full file contents.
Steps to Reproduce
- Create
.claude/settings.jsonwith deny rules and a PreToolUse hook:
``json``
{
"permissions": {
"deny": ["Read(/workspaces/teamster/env/**)"],
"allow": ["Read(~/.claude/plugins/**)"]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Edit|Write|Bash|Grep|Glob",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/check-sensitive.sh"
}
]
}
]
}
}
- Create
.claude/hooks/check-sensitive.shthat denies access toenv/:
``bash``
#!/bin/bash
input=$(cat)
file_path=$(echo "${input}" | jq -r '.tool_input.file_path // empty')
if [[ "${file_path}" =~ env/.env ]]; then
echo '{"hookSpecificOutput": {"permissionDecision": "deny", "permissionDecisionReason": "blocked"}}'
exit 0
fi
- Verify the hook works in the shell:
``bash``
$ echo '{"tool_name":"Read","tool_input":{"file_path":"env/.env"}}' \
| bash .claude/hooks/check-sensitive.sh
{"hookSpecificOutput": {"permissionDecision": "deny", "permissionDecisionReason": "blocked"}}
$ echo $?
0
- Restart the VSCode extension session.
- Ask Claude to read
env/.env— it succeeds with full contents returned.
Additional Findings
- The suggested
chmod 600workaround does not help when Claude Code runs as the file owner (vscode), which is the default in GitHub Codespaces. - Sandbox (
sandbox.enabled) is not viable in Codespaces —--cap-addis silently stripped fromrunArgs, so bwrap/unshare-based sandboxing cannot work. Hooks and permissions are the only enforcement layers available. - Zero layers currently block Read in the VSCode extension.
Environment
- Claude Code Version: 1.0.33
- Model: Opus
- Platform: Anthropic API
- OS: Ubuntu/Debian Linux (GitHub Codespace, Linux 6.8.0-1044-azure)
- Terminal: VS Code integrated terminal
- Regression?: Unknown
- Related issues: #37518, #37533
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗