read-settings-hook.sh 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. #!/bin/bash
  2. # Example hook that reads plugin settings from .claude/my-plugin.local.md
  3. # Demonstrates the complete pattern for settings-driven hook behavior
  4. set -euo pipefail
  5. # Define settings file path
  6. SETTINGS_FILE=".claude/my-plugin.local.md"
  7. # Quick exit if settings file doesn't exist
  8. if [[ ! -f "$SETTINGS_FILE" ]]; then
  9. # Plugin not configured - use defaults or skip
  10. exit 0
  11. fi
  12. # Parse YAML frontmatter (everything between --- markers)
  13. FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$SETTINGS_FILE")
  14. # Extract configuration fields
  15. ENABLED=$(echo "$FRONTMATTER" | grep '^enabled:' | sed 's/enabled: *//' | sed 's/^"\(.*\)"$/\1/')
  16. STRICT_MODE=$(echo "$FRONTMATTER" | grep '^strict_mode:' | sed 's/strict_mode: *//' | sed 's/^"\(.*\)"$/\1/')
  17. MAX_SIZE=$(echo "$FRONTMATTER" | grep '^max_file_size:' | sed 's/max_file_size: *//')
  18. # Quick exit if disabled
  19. if [[ "$ENABLED" != "true" ]]; then
  20. exit 0
  21. fi
  22. # Read hook input
  23. input=$(cat)
  24. file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
  25. # Apply configured validation
  26. if [[ "$STRICT_MODE" == "true" ]]; then
  27. # Strict mode: apply all checks
  28. if [[ "$file_path" == *".."* ]]; then
  29. echo '{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "Path traversal blocked (strict mode)"}' >&2
  30. exit 2
  31. fi
  32. if [[ "$file_path" == *".env"* ]] || [[ "$file_path" == *"secret"* ]]; then
  33. echo '{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "Sensitive file blocked (strict mode)"}' >&2
  34. exit 2
  35. fi
  36. else
  37. # Standard mode: basic checks only
  38. if [[ "$file_path" == "/etc/"* ]] || [[ "$file_path" == "/sys/"* ]]; then
  39. echo '{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "System path blocked"}' >&2
  40. exit 2
  41. fi
  42. fi
  43. # Check file size if configured
  44. if [[ -n "$MAX_SIZE" ]] && [[ "$MAX_SIZE" =~ ^[0-9]+$ ]]; then
  45. content=$(echo "$input" | jq -r '.tool_input.content // empty')
  46. content_size=${#content}
  47. if [[ $content_size -gt $MAX_SIZE ]]; then
  48. echo '{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "File exceeds configured max size: '"$MAX_SIZE"' bytes"}' >&2
  49. exit 2
  50. fi
  51. fi
  52. # All checks passed
  53. exit 0