mirror of
https://github.com/anthropics/claude-code.git
synced 2026-02-19 04:27:33 -08:00
Compare commits
1 Commits
ci/update-
...
claude/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff0fdc0676 |
6
.github/workflows/claude-dedupe-issues.yml
vendored
6
.github/workflows/claude-dedupe-issues.yml
vendored
@@ -23,13 +23,13 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run Claude Code slash command
|
||||
uses: anthropics/claude-code-base-action@v1
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
uses: anthropics/claude-code-base-action@beta
|
||||
with:
|
||||
prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
claude_args: "--model claude-sonnet-4-5-20250929"
|
||||
claude_env: |
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Log duplicate comment event to Statsig
|
||||
if: always()
|
||||
|
||||
15
.github/workflows/claude-issue-triage.yml
vendored
15
.github/workflows/claude-issue-triage.yml
vendored
@@ -95,14 +95,13 @@ jobs:
|
||||
EOF
|
||||
|
||||
- name: Run Claude Code for Issue Triage
|
||||
timeout-minutes: 5
|
||||
uses: anthropics/claude-code-base-action@v1
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
uses: anthropics/claude-code-base-action@beta
|
||||
with:
|
||||
prompt_file: /tmp/claude-prompts/triage-prompt.txt
|
||||
allowed_tools: "Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues"
|
||||
timeout_minutes: "5"
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
claude_args: |
|
||||
--model claude-sonnet-4-5-20250929
|
||||
--mcp-config /tmp/mcp-config/mcp-servers.json
|
||||
--allowedTools "Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues"
|
||||
mcp_config: /tmp/mcp-config/mcp-servers.json
|
||||
claude_args: "--model claude-sonnet-4-5-20250929"
|
||||
claude_env: |
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
2
.github/workflows/claude.yml
vendored
2
.github/workflows/claude.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
|
||||
- name: Run Claude Code
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@v1
|
||||
uses: anthropics/claude-code-action@beta
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
claude_args: "--model claude-sonnet-4-5-20250929"
|
||||
|
||||
13
.github/workflows/oncall-triage.yml
vendored
13
.github/workflows/oncall-triage.yml
vendored
@@ -109,13 +109,12 @@ jobs:
|
||||
EOF
|
||||
|
||||
- name: Run Claude Code for Oncall Triage
|
||||
timeout-minutes: 10
|
||||
uses: anthropics/claude-code-base-action@v1
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
uses: anthropics/claude-code-base-action@beta
|
||||
with:
|
||||
prompt_file: /tmp/claude-prompts/oncall-triage-prompt.txt
|
||||
allowed_tools: "mcp__github__list_issues,mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue"
|
||||
timeout_minutes: "10"
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
claude_args: |
|
||||
--mcp-config /tmp/mcp-config/mcp-servers.json
|
||||
--allowedTools "mcp__github__list_issues,mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue"
|
||||
mcp_config: /tmp/mcp-config/mcp-servers.json
|
||||
claude_env: |
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
41
CHANGELOG.md
41
CHANGELOG.md
@@ -1,46 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 2.1.3
|
||||
|
||||
- Merged slash commands and skills, simplifying the mental model with no change in behavior
|
||||
- Added release channel (`stable` or `latest`) toggle to `/config`
|
||||
- Added detection and warnings for unreachable permission rules, with warnings in `/doctor` and after saving rules that include the source of each rule and actionable fix guidance
|
||||
- Fixed plan files persisting across `/clear` commands, now ensuring a fresh plan file is used after clearing a conversation
|
||||
- Fixed false skill duplicate detection on filesystems with large inodes (e.g., ExFAT) by using 64-bit precision for inode values
|
||||
- Fixed mismatch between background task count in status bar and items shown in tasks dialog
|
||||
- Fixed sub-agents using the wrong model during conversation compaction
|
||||
- Fixed web search in sub-agents using incorrect model
|
||||
- Fixed trust dialog acceptance when running from the home directory not enabling trust-requiring features like hooks during the session
|
||||
- Improved terminal rendering stability by preventing uncontrolled writes from corrupting cursor state
|
||||
- Improved slash command suggestion readability by truncating long descriptions to 2 lines
|
||||
- Changed tool hook execution timeout from 60 seconds to 10 minutes
|
||||
- [VSCode] Added clickable destination selector for permission requests, allowing you to choose where settings are saved (this project, all projects, shared with team, or session only)
|
||||
|
||||
## 2.1.2
|
||||
|
||||
- Added source path metadata to images dragged onto the terminal, helping Claude understand where images originated
|
||||
- Added clickable hyperlinks for file paths in tool output in terminals that support OSC 8 (like iTerm)
|
||||
- Added support for Windows Package Manager (winget) installations with automatic detection and update instructions
|
||||
- Added Shift+Tab keyboard shortcut in plan mode to quickly select "auto-accept edits" option
|
||||
- Added `FORCE_AUTOUPDATE_PLUGINS` environment variable to allow plugin autoupdate even when the main auto-updater is disabled
|
||||
- Added `agent_type` to SessionStart hook input, populated if `--agent` is specified
|
||||
- Fixed a command injection vulnerability in bash command processing where malformed input could execute arbitrary commands
|
||||
- Fixed a memory leak where tree-sitter parse trees were not being freed, causing WASM memory to grow unbounded over long sessions
|
||||
- Fixed binary files (images, PDFs, etc.) being accidentally included in memory when using `@include` directives in CLAUDE.md files
|
||||
- Fixed updates incorrectly claiming another installation is in progress
|
||||
- Fixed crash when socket files exist in watched directories (defense-in-depth for EOPNOTSUPP errors)
|
||||
- Fixed remote session URL and teleport being broken when using `/tasks` command
|
||||
- Fixed MCP tool names being exposed in analytics events by sanitizing user-specific server configurations
|
||||
- Improved Option-as-Meta hint on macOS to show terminal-specific instructions for native CSIu terminals like iTerm2, Kitty, and WezTerm
|
||||
- Improved error message when pasting images over SSH to suggest using `scp` instead of the unhelpful clipboard shortcut hint
|
||||
- Improved permission explainer to not flag routine dev workflows (git fetch/rebase, npm install, tests, PRs) as medium risk
|
||||
- Changed large bash command outputs to be saved to disk instead of truncated, allowing Claude to read the full content
|
||||
- Changed large tool outputs to be persisted to disk instead of truncated, providing full output access via file references
|
||||
- Changed `/plugins` installed tab to unify plugins and MCPs with scope-based grouping
|
||||
- Deprecated Windows managed settings path `C:\ProgramData\ClaudeCode\managed-settings.json` - administrators should migrate to `C:\Program Files\ClaudeCode\managed-settings.json`
|
||||
- [SDK] Changed minimum zod peer dependency to ^4.0.0
|
||||
- [VSCode] Fixed usage display not updating after manual compact
|
||||
|
||||
## 2.1.0
|
||||
|
||||
- Added automatic skill hot-reload - skills created or modified in `~/.claude/skills` or `.claude/skills` are now immediately available without restarting the session
|
||||
|
||||
@@ -5,10 +5,6 @@ description: Code review a pull request
|
||||
|
||||
Provide a code review for the given pull request.
|
||||
|
||||
**Agent assumptions (applies to all agents and subagents):**
|
||||
- All tools are functional and will work without error. Do not test tools or make exploratory calls.
|
||||
- Only call a tool if it is required to complete the task. Every tool call should have a clear purpose.
|
||||
|
||||
To do this, follow these steps precisely:
|
||||
|
||||
1. Launch a haiku agent to check if any of the following are true:
|
||||
@@ -38,15 +34,15 @@ Note: Still review Claude generated PR's.
|
||||
Agent 4: Opus bug agent (parallel subagent with agent 3)
|
||||
Look for problems that exist in the introduced code. This could be security issues, incorrect logic, etc. Only look for issues that fall within the changed code.
|
||||
|
||||
**CRITICAL: We only want HIGH SIGNAL issues.** Flag issues where:
|
||||
- The code will fail to compile or parse (syntax errors, type errors, missing imports, unresolved references)
|
||||
- The code will definitely produce wrong results regardless of inputs (clear logic errors)
|
||||
**CRITICAL: We only want HIGH SIGNAL issues.** This means:
|
||||
- Objective bugs that will cause incorrect behavior at runtime
|
||||
- Clear, unambiguous CLAUDE.md violations where you can quote the exact rule being broken
|
||||
|
||||
Do NOT flag:
|
||||
- Code style or quality concerns
|
||||
- Potential issues that depend on specific inputs or state
|
||||
- Subjective suggestions or improvements
|
||||
We do NOT want:
|
||||
- Subjective concerns or "suggestions"
|
||||
- Style preferences not explicitly required by CLAUDE.md
|
||||
- Potential issues that "might" be problems
|
||||
- Anything requiring interpretation or judgment calls
|
||||
|
||||
If you are not certain an issue is real, do not flag it. False positives erode trust and waste reviewer time.
|
||||
|
||||
@@ -61,10 +57,23 @@ Note: Still review Claude generated PR's.
|
||||
If NO issues were found, post a summary comment using `gh pr comment` (if `--comment` argument is provided):
|
||||
"No issues found. Checked for bugs and CLAUDE.md compliance."
|
||||
|
||||
8. Post inline comments for each issue using `mcp__github_inline_comment__create_inline_comment`. For each comment:
|
||||
- Provide a brief description of the issue
|
||||
- For small, self-contained fixes, include a committable suggestion block
|
||||
- For larger fixes (6+ lines, structural changes, or changes spanning multiple locations), describe the issue and suggested fix without a suggestion block
|
||||
8. Post inline comments for each issue using `mcp__github_inline_comment__create_inline_comment`:
|
||||
- `path`: the file path
|
||||
- `line` (and `startLine` for ranges): select the buggy lines so the user sees them
|
||||
- `body`: Brief description of the issue (no "Bug:" prefix). For small fixes (up to 5 lines changed), include a committable suggestion:
|
||||
```suggestion
|
||||
corrected code here
|
||||
```
|
||||
|
||||
**Suggestions must be COMPLETE.** If a fix requires additional changes elsewhere (e.g., renaming a variable requires updating all usages), do NOT use a suggestion block. The author should be able to click "Commit suggestion" and have a working fix - no followup work required.
|
||||
|
||||
For larger fixes (6+ lines, structural changes, or changes spanning multiple locations), do NOT use suggestion blocks. Instead:
|
||||
1. Describe what the issue is
|
||||
2. Explain the suggested fix at a high level
|
||||
3. Include a copyable prompt for Claude Code that the user can use to fix the issue, formatted as:
|
||||
```
|
||||
Fix [file:line]: [brief description of issue and suggested fix]
|
||||
```
|
||||
|
||||
**IMPORTANT: Only post ONE comment per unique issue. Do not post duplicate comments.**
|
||||
|
||||
|
||||
@@ -141,6 +141,39 @@ class RuleEngine:
|
||||
patterns = matcher.split('|')
|
||||
return tool_name in patterns
|
||||
|
||||
def _resolve_symlink_path(self, file_path: str) -> str:
|
||||
"""Resolve symlinks in file path to get canonical path.
|
||||
|
||||
Security fix for CVE-2025-59829: Deny rules could be bypassed by creating
|
||||
a symlink to a restricted file. This method resolves the symlink to its
|
||||
target path so that deny rules are checked against the actual file.
|
||||
|
||||
Args:
|
||||
file_path: The file path that may contain symlinks
|
||||
|
||||
Returns:
|
||||
The canonical path with symlinks resolved, or original path if
|
||||
resolution fails (e.g., file doesn't exist yet)
|
||||
"""
|
||||
import os
|
||||
|
||||
if not file_path:
|
||||
return file_path
|
||||
|
||||
try:
|
||||
# Expand user home directory first
|
||||
expanded_path = os.path.expanduser(file_path)
|
||||
|
||||
# Use realpath to resolve all symlinks and get canonical path
|
||||
# This handles nested symlinks and relative path components
|
||||
resolved = os.path.realpath(expanded_path)
|
||||
|
||||
return resolved
|
||||
except (OSError, ValueError):
|
||||
# If resolution fails (e.g., permission denied, invalid path),
|
||||
# return the original path to avoid blocking legitimate operations
|
||||
return file_path
|
||||
|
||||
def _check_condition(self, condition: Condition, tool_name: str,
|
||||
tool_input: Dict[str, Any], input_data: Dict[str, Any] = None) -> bool:
|
||||
"""Check if a single condition matches.
|
||||
@@ -196,6 +229,10 @@ class RuleEngine:
|
||||
if field in tool_input:
|
||||
value = tool_input[field]
|
||||
if isinstance(value, str):
|
||||
# Security fix: resolve symlinks for file_path fields to prevent bypass
|
||||
# CVE-2025-59829: Deny rules could be bypassed via symlinks
|
||||
if field == 'file_path':
|
||||
value = self._resolve_symlink_path(value)
|
||||
return value
|
||||
return str(value)
|
||||
|
||||
@@ -241,11 +278,18 @@ class RuleEngine:
|
||||
elif field == 'old_text' or field == 'old_string':
|
||||
return tool_input.get('old_string', '')
|
||||
elif field == 'file_path':
|
||||
return tool_input.get('file_path', '')
|
||||
# Security fix: resolve symlinks to prevent deny rule bypass
|
||||
return self._resolve_symlink_path(tool_input.get('file_path', ''))
|
||||
|
||||
elif tool_name == 'Read':
|
||||
# Security fix for CVE-2025-59829: Read tool symlink bypass
|
||||
if field == 'file_path':
|
||||
return self._resolve_symlink_path(tool_input.get('file_path', ''))
|
||||
|
||||
elif tool_name == 'MultiEdit':
|
||||
if field == 'file_path':
|
||||
return tool_input.get('file_path', '')
|
||||
# Security fix: resolve symlinks to prevent deny rule bypass
|
||||
return self._resolve_symlink_path(tool_input.get('file_path', ''))
|
||||
elif field in ['new_text', 'content']:
|
||||
# Concatenate all edits
|
||||
edits = tool_input.get('edits', [])
|
||||
|
||||
@@ -45,7 +45,9 @@ def main():
|
||||
event = None
|
||||
if tool_name == 'Bash':
|
||||
event = 'bash'
|
||||
elif tool_name in ['Edit', 'Write', 'MultiEdit']:
|
||||
elif tool_name in ['Edit', 'Write', 'MultiEdit', 'Read']:
|
||||
# Include Read tool in file events to check symlink bypass
|
||||
# Security fix for CVE-2025-59829
|
||||
event = 'file'
|
||||
|
||||
# Load rules
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
{
|
||||
"description": "Security reminder hook that warns about potential security issues when editing files",
|
||||
"description": "Security hooks for file access validation and security pattern warnings",
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/symlink_deny_hook.py"
|
||||
}
|
||||
],
|
||||
"matcher": "Edit|Write|MultiEdit|Read"
|
||||
},
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
@@ -9,7 +18,7 @@
|
||||
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/security_reminder_hook.py"
|
||||
}
|
||||
],
|
||||
"matcher": "Edit|Write|MultiEdit"
|
||||
"matcher": "Edit|Write|MultiEdit|Read"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -180,10 +180,46 @@ def save_state(session_id, shown_warnings):
|
||||
pass # Fail silently if we can't save state
|
||||
|
||||
|
||||
def resolve_symlink_path(file_path):
|
||||
"""Resolve symlinks in file path to get canonical path.
|
||||
|
||||
Security fix for CVE-2025-59829: Deny rules could be bypassed by creating
|
||||
a symlink to a restricted file. This method resolves the symlink to its
|
||||
target path so that security patterns are checked against the actual file.
|
||||
|
||||
Args:
|
||||
file_path: The file path that may contain symlinks
|
||||
|
||||
Returns:
|
||||
The canonical path with symlinks resolved, or original path if
|
||||
resolution fails (e.g., file doesn't exist yet)
|
||||
"""
|
||||
if not file_path:
|
||||
return file_path
|
||||
|
||||
try:
|
||||
# Expand user home directory first
|
||||
expanded_path = os.path.expanduser(file_path)
|
||||
|
||||
# Use realpath to resolve all symlinks and get canonical path
|
||||
# This handles nested symlinks and relative path components
|
||||
resolved = os.path.realpath(expanded_path)
|
||||
|
||||
return resolved
|
||||
except (OSError, ValueError):
|
||||
# If resolution fails (e.g., permission denied, invalid path),
|
||||
# return the original path to avoid blocking legitimate operations
|
||||
return file_path
|
||||
|
||||
|
||||
def check_patterns(file_path, content):
|
||||
"""Check if file path or content matches any security patterns."""
|
||||
# Security fix: resolve symlinks before checking patterns
|
||||
# CVE-2025-59829: Security patterns could be bypassed via symlinks
|
||||
resolved_path = resolve_symlink_path(file_path)
|
||||
|
||||
# Normalize path by removing leading slashes
|
||||
normalized_path = file_path.lstrip("/")
|
||||
normalized_path = resolved_path.lstrip("/")
|
||||
|
||||
for pattern in SECURITY_PATTERNS:
|
||||
# Check path-based patterns
|
||||
@@ -241,7 +277,7 @@ def main():
|
||||
tool_input = input_data.get("tool_input", {})
|
||||
|
||||
# Check if this is a relevant tool
|
||||
if tool_name not in ["Edit", "Write", "MultiEdit"]:
|
||||
if tool_name not in ["Edit", "Write", "MultiEdit", "Read"]:
|
||||
sys.exit(0) # Allow non-file tools to proceed
|
||||
|
||||
# Extract file path from tool_input
|
||||
|
||||
137
plugins/security-guidance/hooks/symlink_deny_hook.py
Normal file
137
plugins/security-guidance/hooks/symlink_deny_hook.py
Normal file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Symlink Deny Hook for Claude Code
|
||||
Security fix for CVE-2025-59829: Deny rules could be bypassed via symlinks.
|
||||
|
||||
This hook resolves symlinks before checking file paths against deny patterns,
|
||||
preventing attackers from using symlinks to access restricted files.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from fnmatch import fnmatch
|
||||
|
||||
|
||||
# System directories that should be blocked by default
|
||||
# These match common deny rule patterns
|
||||
BLOCKED_PATHS = [
|
||||
"/etc/**",
|
||||
"/etc/passwd",
|
||||
"/etc/shadow",
|
||||
"/etc/sudoers",
|
||||
"/etc/ssh/**",
|
||||
"/etc/ssl/**",
|
||||
"/root/**",
|
||||
"/var/log/**",
|
||||
"/proc/**",
|
||||
"/sys/**",
|
||||
"/boot/**",
|
||||
]
|
||||
|
||||
|
||||
def resolve_symlink_path(file_path: str) -> str:
|
||||
"""Resolve symlinks in file path to get canonical path.
|
||||
|
||||
Args:
|
||||
file_path: The file path that may contain symlinks
|
||||
|
||||
Returns:
|
||||
The canonical path with symlinks resolved, or original path if
|
||||
resolution fails (e.g., file doesn't exist)
|
||||
"""
|
||||
if not file_path:
|
||||
return file_path
|
||||
|
||||
try:
|
||||
# Expand user home directory first
|
||||
expanded_path = os.path.expanduser(file_path)
|
||||
|
||||
# Use realpath to resolve all symlinks and get canonical path
|
||||
resolved = os.path.realpath(expanded_path)
|
||||
|
||||
return resolved
|
||||
except (OSError, ValueError):
|
||||
return file_path
|
||||
|
||||
|
||||
def is_path_blocked(resolved_path: str, original_path: str) -> tuple:
|
||||
"""Check if the resolved path matches any blocked patterns.
|
||||
|
||||
Only blocks if:
|
||||
1. The path was a symlink (resolved != original)
|
||||
2. The resolved path matches a blocked pattern
|
||||
|
||||
Args:
|
||||
resolved_path: The canonical path after symlink resolution
|
||||
original_path: The original path before resolution
|
||||
|
||||
Returns:
|
||||
Tuple of (is_blocked: bool, reason: str)
|
||||
"""
|
||||
# Only apply symlink protection if path was actually a symlink
|
||||
original_real = os.path.realpath(os.path.expanduser(original_path))
|
||||
if original_real == resolved_path:
|
||||
# Check if original was a symlink
|
||||
expanded_original = os.path.expanduser(original_path)
|
||||
if not os.path.islink(expanded_original):
|
||||
# Not a symlink, allow normal deny rule checking to handle this
|
||||
return False, ""
|
||||
|
||||
# Check if resolved path matches any blocked patterns
|
||||
for pattern in BLOCKED_PATHS:
|
||||
if pattern.endswith("/**"):
|
||||
# Directory wildcard pattern
|
||||
base_dir = pattern[:-3]
|
||||
if resolved_path.startswith(base_dir + "/") or resolved_path == base_dir:
|
||||
return True, f"Symlink bypass blocked: '{original_path}' resolves to '{resolved_path}' which matches blocked pattern '{pattern}'"
|
||||
elif fnmatch(resolved_path, pattern):
|
||||
return True, f"Symlink bypass blocked: '{original_path}' resolves to '{resolved_path}' which matches blocked pattern '{pattern}'"
|
||||
|
||||
return False, ""
|
||||
|
||||
|
||||
def main():
|
||||
"""Main hook function."""
|
||||
try:
|
||||
input_data = json.load(sys.stdin)
|
||||
except json.JSONDecodeError:
|
||||
sys.exit(0) # Allow on parse error
|
||||
|
||||
tool_name = input_data.get("tool_name", "")
|
||||
tool_input = input_data.get("tool_input", {})
|
||||
|
||||
# Only check file-related tools
|
||||
if tool_name not in ["Read", "Edit", "Write", "MultiEdit"]:
|
||||
sys.exit(0)
|
||||
|
||||
# Extract file path
|
||||
file_path = tool_input.get("file_path", "")
|
||||
if not file_path:
|
||||
sys.exit(0)
|
||||
|
||||
# Resolve symlinks
|
||||
resolved_path = resolve_symlink_path(file_path)
|
||||
|
||||
# Check if blocked
|
||||
is_blocked, reason = is_path_blocked(resolved_path, file_path)
|
||||
|
||||
if is_blocked:
|
||||
# Output denial response
|
||||
response = {
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PreToolUse",
|
||||
"permissionDecision": "deny"
|
||||
},
|
||||
"systemMessage": f"Security: {reason}"
|
||||
}
|
||||
print(json.dumps(response))
|
||||
sys.exit(0)
|
||||
|
||||
# Allow the operation
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user