mirror of
https://github.com/nodnarbnitram/claude-code-extensions.git
synced 2026-09-14 18:36:21 +08:00
7f02d5064f
Add comprehensive deep research agent suite with 5 specialized agents (academic-researcher, data-analyst, research-coordinator, technical-researcher, web-researcher) for multi-faceted research tasks. Add new commands: - git-commit: Smart commit creation with emoji conventional format - frontend-mode: Load ultracite rules for JS/TS development - security-scan: Run security scans on project files Add ultracite.md documentation with comprehensive frontend development rules covering accessibility, JSX/React, TypeScript, and code quality standards. Update existing agents with improved proactive usage patterns and tool configurations. Update hooks and settings to support new functionality.
139 lines
5.2 KiB
Python
Executable File
139 lines
5.2 KiB
Python
Executable File
#!/usr/bin/env -S uv run --script
|
|
# /// script
|
|
# requires-python = ">=3.8"
|
|
# ///
|
|
|
|
import json
|
|
import sys
|
|
import re
|
|
from pathlib import Path
|
|
|
|
def is_dangerous_rm_command(command):
|
|
"""
|
|
Comprehensive detection of dangerous rm commands.
|
|
Matches various forms of rm -rf and similar destructive patterns.
|
|
"""
|
|
# Normalize command by removing extra spaces and converting to lowercase
|
|
normalized = ' '.join(command.lower().split())
|
|
|
|
# Pattern 1: Standard rm -rf variations
|
|
patterns = [
|
|
r'\brm\s+.*-[a-z]*r[a-z]*f', # rm -rf, rm -fr, rm -Rf, etc.
|
|
r'\brm\s+.*-[a-z]*f[a-z]*r', # rm -fr variations
|
|
r'\brm\s+--recursive\s+--force', # rm --recursive --force
|
|
r'\brm\s+--force\s+--recursive', # rm --force --recursive
|
|
r'\brm\s+-r\s+.*-f', # rm -r ... -f
|
|
r'\brm\s+-f\s+.*-r', # rm -f ... -r
|
|
]
|
|
|
|
# Check for dangerous patterns
|
|
for pattern in patterns:
|
|
if re.search(pattern, normalized):
|
|
return True
|
|
|
|
# Pattern 2: Check for rm with recursive flag targeting dangerous paths
|
|
dangerous_paths = [
|
|
r'/', # Root directory
|
|
r'/\*', # Root with wildcard
|
|
r'~', # Home directory
|
|
r'~/', # Home directory path
|
|
r'\$HOME', # Home environment variable
|
|
r'\.\.', # Parent directory references
|
|
r'\*', # Wildcards in general rm -rf context
|
|
r'\.', # Current directory
|
|
r'\.\s*$', # Current directory at end of command
|
|
]
|
|
|
|
if re.search(r'\brm\s+.*-[a-z]*r', normalized): # If rm has recursive flag
|
|
for path in dangerous_paths:
|
|
if re.search(path, normalized):
|
|
return True
|
|
|
|
return False
|
|
|
|
def is_env_file_access(tool_name, tool_input):
|
|
"""
|
|
Check if any tool is trying to access .env files containing sensitive data.
|
|
"""
|
|
if tool_name in ['Read', 'Edit', 'MultiEdit', 'Write', 'Bash']:
|
|
# Check file paths for file-based tools
|
|
if tool_name in ['Read', 'Edit', 'MultiEdit', 'Write']:
|
|
file_path = tool_input.get('file_path', '')
|
|
if '.env' in file_path and not (file_path.endswith('.env.sample') or file_path.endswith('.env.example')):
|
|
return True
|
|
|
|
# Check bash commands for .env file access
|
|
elif tool_name == 'Bash':
|
|
command = tool_input.get('command', '')
|
|
# Pattern to detect .env file access (but allow .env.sample and .env.example)
|
|
env_patterns = [
|
|
r'\b\.env\b(?!\.sample)(?!\.example)', # .env but not .env.sample or .env.example
|
|
r'cat\s+.*\.env\b(?!\.sample)(?!\.example)', # cat .env
|
|
r'echo\s+.*>\s*\.env\b(?!\.sample)(?!\.example)', # echo > .env
|
|
r'touch\s+.*\.env\b(?!\.sample)(?!\.example)', # touch .env
|
|
r'cp\s+.*\.env\b(?!\.sample)(?!\.example)', # cp .env
|
|
r'mv\s+.*\.env\b(?!\.sample)(?!\.example)', # mv .env
|
|
]
|
|
|
|
for pattern in env_patterns:
|
|
if re.search(pattern, command):
|
|
return True
|
|
|
|
return False
|
|
|
|
def main():
|
|
try:
|
|
# Read JSON input from stdin
|
|
input_data = json.load(sys.stdin)
|
|
|
|
tool_name = input_data.get('tool_name', '')
|
|
tool_input = input_data.get('tool_input', {})
|
|
|
|
# Check for .env file access (blocks access to sensitive environment files)
|
|
if is_env_file_access(tool_name, tool_input):
|
|
print("BLOCKED: Access to .env files containing sensitive data is prohibited", file=sys.stderr)
|
|
print("Use .env.sample for template files instead", file=sys.stderr)
|
|
sys.exit(2) # Exit code 2 blocks tool call and shows error to Claude
|
|
|
|
# Check for dangerous rm -rf commands
|
|
if tool_name == 'Bash':
|
|
command = tool_input.get('command', '')
|
|
|
|
# # Block rm -rf commands with comprehensive pattern matching
|
|
if is_dangerous_rm_command(command):
|
|
print("BLOCKED: Dangerous rm command detected and prevented", file=sys.stderr)
|
|
sys.exit(2) # Exit code 2 blocks tool call and shows error to Claude
|
|
|
|
# Ensure log directory exists
|
|
log_dir = Path.cwd() / 'logs'
|
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
log_path = log_dir / 'pre_tool_use.json'
|
|
|
|
# Read existing log data or initialize empty list
|
|
if log_path.exists():
|
|
with open(log_path, 'r') as f:
|
|
try:
|
|
log_data = json.load(f)
|
|
except (json.JSONDecodeError, ValueError):
|
|
log_data = []
|
|
else:
|
|
log_data = []
|
|
|
|
# Append new data
|
|
log_data.append(input_data)
|
|
|
|
# Write back to file with formatting
|
|
with open(log_path, 'w') as f:
|
|
json.dump(log_data, f, indent=2)
|
|
|
|
sys.exit(0)
|
|
|
|
except json.JSONDecodeError:
|
|
# Gracefully handle JSON decode errors
|
|
sys.exit(0)
|
|
except Exception:
|
|
# Handle any other errors gracefully
|
|
sys.exit(0)
|
|
|
|
if __name__ == '__main__':
|
|
main() |