Advanced Guardrails: - prompt-injection-detector.sh (PreToolUse) - output-validator.sh (PostToolUse heuristics) - claudemd-scanner.sh (SessionStart injection detection) - output-secrets-scanner.sh (PostToolUse secrets leak prevention) Observability & Monitoring: - session-logger.sh (JSONL activity logging) - session-stats.sh (cost tracking & analysis) - guide/observability.md (full documentation) LLM-as-a-Judge Evaluation: - output-evaluator.md agent (Haiku) - /validate-changes command - pre-commit-evaluator.sh (opt-in git hook) Google Agent Whitepaper Integration: - Context Triage Guide (Section 2.2.4) - CLAUDE.md Injection Warning (Section 3.1.3) - Agent Validation Checklist (Section 4.2.4) - MCP Security: Tool Shadowing & Confused Deputy (Section 8.6) - Session vs Memory patterns (Section 3.3.3) Stats: 10 new files, 8 modified, 5 new guide sections Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
97 lines
3.2 KiB
Bash
97 lines
3.2 KiB
Bash
#!/bin/bash
|
|
# =============================================================================
|
|
# Output Secrets Scanner Hook
|
|
# =============================================================================
|
|
# Event: PostToolUse (runs after each tool execution)
|
|
# Purpose: Detect secrets that might leak in tool outputs
|
|
#
|
|
# This complements security-check.sh (which scans inputs). This hook scans
|
|
# outputs to catch secrets that Claude might inadvertently expose.
|
|
#
|
|
# Installation:
|
|
# Add to .claude/settings.json:
|
|
# {
|
|
# "hooks": {
|
|
# "PostToolUse": [{
|
|
# "matcher": "",
|
|
# "hooks": ["bash examples/hooks/bash/output-secrets-scanner.sh"]
|
|
# }]
|
|
# }
|
|
# }
|
|
#
|
|
# What it detects:
|
|
# - API keys (OpenAI, Anthropic, AWS, GCP, Azure, Stripe, etc.)
|
|
# - Private keys and certificates
|
|
# - Database connection strings with passwords
|
|
# - GitHub/GitLab tokens
|
|
# - JWT tokens
|
|
# =============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
# Read the hook input from stdin
|
|
INPUT=$(cat)
|
|
|
|
# Extract tool output from JSON (handle both formats)
|
|
TOOL_OUTPUT=$(echo "$INPUT" | jq -r '.tool_output // .output // ""' 2>/dev/null || echo "")
|
|
|
|
# If no output or empty, exit cleanly
|
|
if [[ -z "$TOOL_OUTPUT" ]]; then
|
|
exit 0
|
|
fi
|
|
|
|
# Secret patterns to detect
|
|
declare -A SECRET_PATTERNS=(
|
|
# API Keys
|
|
["OpenAI API Key"]="sk-[a-zA-Z0-9]{20,}"
|
|
["Anthropic API Key"]="sk-ant-[a-zA-Z0-9]{20,}"
|
|
["AWS Access Key"]="AKIA[0-9A-Z]{16}"
|
|
["AWS Secret Key"]="[0-9a-zA-Z/+]{40}"
|
|
["GCP API Key"]="AIza[0-9A-Za-z_-]{35}"
|
|
["Azure Key"]="[a-zA-Z0-9]{32,}"
|
|
["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}"
|
|
|
|
# Tokens
|
|
["GitHub Token"]="(ghp|gho|ghu|ghs|ghr)_[a-zA-Z0-9]{36,}"
|
|
["GitLab Token"]="glpat-[a-zA-Z0-9_-]{20,}"
|
|
["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_-]*"
|
|
|
|
# Private Keys
|
|
["Private Key"]="-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----"
|
|
["PGP Private Key"]="-----BEGIN PGP PRIVATE KEY BLOCK-----"
|
|
|
|
# Database
|
|
["Database URL with Password"]="(postgres|mysql|mongodb)://[^:]+:[^@]+@"
|
|
["Redis URL with Password"]="redis://:[^@]+@"
|
|
|
|
# Generic
|
|
["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,}"
|
|
)
|
|
|
|
DETECTED_SECRETS=()
|
|
|
|
# Check each pattern
|
|
for secret_type in "${!SECRET_PATTERNS[@]}"; do
|
|
pattern="${SECRET_PATTERNS[$secret_type]}"
|
|
if echo "$TOOL_OUTPUT" | grep -qiE "$pattern" 2>/dev/null; then
|
|
DETECTED_SECRETS+=("$secret_type")
|
|
fi
|
|
done
|
|
|
|
# If secrets detected, warn via systemMessage
|
|
if [[ ${#DETECTED_SECRETS[@]} -gt 0 ]]; then
|
|
SECRETS_LIST=$(printf ", %s" "${DETECTED_SECRETS[@]}")
|
|
SECRETS_LIST=${SECRETS_LIST:2} # Remove leading ", "
|
|
|
|
WARNING_MSG="SECRET LEAK WARNING: Potential secrets detected in output: $SECRETS_LIST. Do NOT commit or share this output. Consider using environment variables or a secrets manager."
|
|
|
|
echo "{\"systemMessage\": \"$WARNING_MSG\"}"
|
|
fi
|
|
|
|
# Always exit 0 (warn, don't block)
|
|
exit 0
|