@zenuml/core 3.32.6 → 3.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/README.md +162 -0
- package/.claude/commands/code-review.md +322 -0
- package/.claude/commands/create-docs.md +309 -0
- package/.claude/commands/full-context.md +121 -0
- package/.claude/commands/gemini-consult.md +164 -0
- package/.claude/commands/handoff.md +146 -0
- package/.claude/commands/refactor.md +188 -0
- package/.claude/commands/update-docs.md +314 -0
- package/.claude/hooks/README.md +270 -0
- package/.claude/hooks/config/sensitive-patterns.json +86 -0
- package/.claude/hooks/gemini-context-injector.sh +129 -0
- package/.claude/hooks/mcp-security-scan.sh +147 -0
- package/.claude/hooks/notify.sh +103 -0
- package/.claude/hooks/setup/hook-setup.md +96 -0
- package/.claude/hooks/setup/settings.json.template +63 -0
- package/.claude/hooks/sounds/complete.wav +0 -0
- package/.claude/hooks/sounds/input-needed.wav +0 -0
- package/.claude/hooks/subagent-context-injector.sh +65 -0
- package/.storybook/main.ts +25 -0
- package/.storybook/preview.ts +29 -0
- package/MCP-ASSISTANT-RULES.md +85 -0
- package/README.md +1 -1
- package/TUTORIAL.md +116 -0
- package/dist/zenuml.esm.mjs +4649 -4598
- package/dist/zenuml.js +52 -52
- package/docs/CONTEXT-tier2-component.md +96 -0
- package/docs/CONTEXT-tier3-feature.md +162 -0
- package/docs/README.md +207 -0
- package/docs/ai-context/deployment-infrastructure.md +21 -0
- package/docs/ai-context/docs-overview.md +89 -0
- package/docs/ai-context/handoff.md +174 -0
- package/docs/ai-context/project-structure.md +160 -0
- package/docs/ai-context/system-integration.md +21 -0
- package/docs/open-issues/example-api-performance-issue.md +79 -0
- package/eslint.config.mjs +26 -26
- package/package.json +9 -2
- package/tailwind.config.js +0 -4
- package/docs/asciidoc/integration-guide.adoc +0 -121
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Claude Code Hooks
|
|
2
|
+
|
|
3
|
+
This directory contains battle-tested hooks that enhance your Claude Code development experience with automated security scanning, intelligent context injection, and pleasant audio feedback.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Claude Code Lifecycle
|
|
9
|
+
│
|
|
10
|
+
├── PreToolUse ──────► Security Scanner
|
|
11
|
+
│ ├── Context Injector (Gemini)
|
|
12
|
+
│ └── Context Injector (Subagents)
|
|
13
|
+
│
|
|
14
|
+
├── Tool Execution
|
|
15
|
+
│
|
|
16
|
+
├── PostToolUse
|
|
17
|
+
│
|
|
18
|
+
├── Notification ────────► Audio Feedback
|
|
19
|
+
│
|
|
20
|
+
└── Stop/SubagentStop ───► Completion Sound
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
These hooks execute at specific points in Claude Code's lifecycle, providing deterministic control over AI behavior.
|
|
24
|
+
|
|
25
|
+
## Available Hooks
|
|
26
|
+
|
|
27
|
+
### 1. Gemini Context Injector (`gemini-context-injector.sh`)
|
|
28
|
+
|
|
29
|
+
**Purpose**: Automatically includes your project documentation and assistant rules when starting new Gemini consultation sessions, ensuring the AI has complete context about your codebase and project standards.
|
|
30
|
+
|
|
31
|
+
**Trigger**: `PreToolUse` for `mcp__gemini__consult_gemini`
|
|
32
|
+
|
|
33
|
+
**Features**:
|
|
34
|
+
- Detects new Gemini consultation sessions (no session_id)
|
|
35
|
+
- Automatically attaches two key files:
|
|
36
|
+
- `docs/ai-context/project-structure.md` - Complete project structure and tech stack
|
|
37
|
+
- `MCP-ASSISTANT-RULES.md` - Project-specific coding standards and guidelines
|
|
38
|
+
- Preserves existing file attachments
|
|
39
|
+
- Session-aware (only injects on new sessions)
|
|
40
|
+
- Logs all injection events for debugging
|
|
41
|
+
- Fails gracefully if either file is missing
|
|
42
|
+
- Handles partial availability (will attach whichever files exist)
|
|
43
|
+
|
|
44
|
+
**Customization**:
|
|
45
|
+
- Copy `docs/MCP-ASSISTANT-RULES.md` template to your project root
|
|
46
|
+
- Customize it with your project-specific standards, principles, and constraints
|
|
47
|
+
- The hook will automatically include it in Gemini consultations
|
|
48
|
+
|
|
49
|
+
### 2. MCP Security Scanner (`mcp-security-scan.sh`)
|
|
50
|
+
|
|
51
|
+
**Purpose**: Prevents accidental exposure of secrets, API keys, and sensitive data when using MCP servers like Gemini or Context7.
|
|
52
|
+
|
|
53
|
+
**Trigger**: `PreToolUse` for all MCP tools (`mcp__.*`)
|
|
54
|
+
|
|
55
|
+
**Features**:
|
|
56
|
+
- Pattern-based detection for API keys, passwords, and secrets
|
|
57
|
+
- Scans code context, problem descriptions, and attached files
|
|
58
|
+
- File content scanning with size limits
|
|
59
|
+
- Configurable pattern matching via `config/sensitive-patterns.json`
|
|
60
|
+
- Whitelisting for placeholder values
|
|
61
|
+
- Command injection protection for Context7
|
|
62
|
+
- Comprehensive logging of security events to `.claude/logs/`
|
|
63
|
+
|
|
64
|
+
**Customization**: Edit `config/sensitive-patterns.json` to:
|
|
65
|
+
- Add custom API key patterns
|
|
66
|
+
- Modify credential detection rules
|
|
67
|
+
- Update sensitive file patterns
|
|
68
|
+
- Extend the whitelist for your placeholders
|
|
69
|
+
|
|
70
|
+
### 3. Subagent Context Injector (`subagent-context-injector.sh`)
|
|
71
|
+
|
|
72
|
+
**Purpose**: Automatically includes core project documentation in all sub-agent Task prompts, ensuring consistent context across multi-agent workflows.
|
|
73
|
+
|
|
74
|
+
**Trigger**: `PreToolUse` for `Task` tool
|
|
75
|
+
|
|
76
|
+
**Features**:
|
|
77
|
+
- Intercepts all Task tool calls before execution
|
|
78
|
+
- Prepends references to three core documentation files:
|
|
79
|
+
- `docs/CLAUDE.md` - Project overview, coding standards, AI instructions
|
|
80
|
+
- `docs/ai-context/project-structure.md` - Complete file tree and tech stack
|
|
81
|
+
- `docs/ai-context/docs-overview.md` - Documentation architecture
|
|
82
|
+
- Passes through non-Task tools unchanged
|
|
83
|
+
- Preserves original task prompt by prepending context
|
|
84
|
+
- Enables consistent knowledge across all sub-agents
|
|
85
|
+
- Eliminates need for manual context inclusion in Task prompts
|
|
86
|
+
|
|
87
|
+
**Benefits**:
|
|
88
|
+
- Every sub-agent starts with the same foundational knowledge
|
|
89
|
+
- No manual context specification needed in each Task prompt
|
|
90
|
+
- Token-efficient through @ references instead of content duplication
|
|
91
|
+
- Update context in one place, affects all sub-agents
|
|
92
|
+
- Clean operation with simple pass-through for non-Task tools
|
|
93
|
+
|
|
94
|
+
### 4. Notification System (`notify.sh`)
|
|
95
|
+
|
|
96
|
+
**Purpose**: Provides pleasant audio feedback when Claude Code needs your attention or completes tasks.
|
|
97
|
+
|
|
98
|
+
**Triggers**:
|
|
99
|
+
- `Notification` events (all notifications including input needed)
|
|
100
|
+
- `Stop` events (main task completion)
|
|
101
|
+
|
|
102
|
+
**Features**:
|
|
103
|
+
- Cross-platform audio support (macOS, Linux, Windows)
|
|
104
|
+
- Non-blocking audio playback (runs in background)
|
|
105
|
+
- Multiple audio playback fallbacks
|
|
106
|
+
- Pleasant notification sounds
|
|
107
|
+
- Two notification types:
|
|
108
|
+
- `input`: When Claude needs user input
|
|
109
|
+
- `complete`: When Claude completes tasks
|
|
110
|
+
|
|
111
|
+
## Installation
|
|
112
|
+
|
|
113
|
+
1. **Copy the hooks to your project**:
|
|
114
|
+
```bash
|
|
115
|
+
cp -r hooks your-project/.claude/
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
2. **Configure hooks in your project**:
|
|
119
|
+
```bash
|
|
120
|
+
cp hooks/setup/settings.json.template your-project/.claude/settings.json
|
|
121
|
+
```
|
|
122
|
+
Then edit the WORKSPACE path in the settings file.
|
|
123
|
+
|
|
124
|
+
3. **Test the hooks**:
|
|
125
|
+
```bash
|
|
126
|
+
# Test notification
|
|
127
|
+
.claude/hooks/notify.sh input
|
|
128
|
+
.claude/hooks/notify.sh complete
|
|
129
|
+
|
|
130
|
+
# View logs
|
|
131
|
+
tail -f .claude/logs/context-injection.log
|
|
132
|
+
tail -f .claude/logs/security-scan.log
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Hook Configuration
|
|
136
|
+
|
|
137
|
+
Add to your Claude Code `settings.json`:
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"hooks": {
|
|
142
|
+
"PreToolUse": [
|
|
143
|
+
{
|
|
144
|
+
"matcher": "mcp__gemini__consult_gemini",
|
|
145
|
+
"hooks": [
|
|
146
|
+
{
|
|
147
|
+
"type": "command",
|
|
148
|
+
"command": "${WORKSPACE}/.claude/hooks/gemini-context-injector.sh"
|
|
149
|
+
}
|
|
150
|
+
]
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"matcher": "mcp__.*",
|
|
154
|
+
"hooks": [
|
|
155
|
+
{
|
|
156
|
+
"type": "command",
|
|
157
|
+
"command": "${WORKSPACE}/.claude/hooks/mcp-security-scan.sh"
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"matcher": "Task",
|
|
163
|
+
"hooks": [
|
|
164
|
+
{
|
|
165
|
+
"type": "command",
|
|
166
|
+
"command": "${WORKSPACE}/.claude/hooks/subagent-context-injector.sh"
|
|
167
|
+
}
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
],
|
|
171
|
+
"Notification": [
|
|
172
|
+
{
|
|
173
|
+
"matcher": ".*",
|
|
174
|
+
"hooks": [
|
|
175
|
+
{
|
|
176
|
+
"type": "command",
|
|
177
|
+
"command": "${WORKSPACE}/.claude/hooks/notify.sh input"
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
],
|
|
182
|
+
"Stop": [
|
|
183
|
+
{
|
|
184
|
+
"matcher": ".*",
|
|
185
|
+
"hooks": [
|
|
186
|
+
{
|
|
187
|
+
"type": "command",
|
|
188
|
+
"command": "${WORKSPACE}/.claude/hooks/notify.sh complete"
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
See `hooks/setup/settings.json.template` for the complete configuration including all hooks and MCP servers.
|
|
198
|
+
|
|
199
|
+
## Security Model
|
|
200
|
+
|
|
201
|
+
1. **Execution Context**: Hooks run with full user permissions
|
|
202
|
+
2. **Blocking Behavior**: Exit code 2 blocks tool execution
|
|
203
|
+
3. **Data Flow**: Hooks can modify tool inputs via JSON transformation
|
|
204
|
+
4. **Isolation**: Each hook runs in its own process
|
|
205
|
+
5. **Logging**: All security events logged to `.claude/logs/`
|
|
206
|
+
|
|
207
|
+
## Integration with MCP Servers
|
|
208
|
+
|
|
209
|
+
The hooks system complements MCP server integrations:
|
|
210
|
+
|
|
211
|
+
- **Gemini Consultation**: Context injector ensures both project structure and MCP assistant rules are included
|
|
212
|
+
- **Context7 Documentation**: Security scanner protects library ID inputs
|
|
213
|
+
- **All MCP Tools**: Universal security scanning before external calls
|
|
214
|
+
|
|
215
|
+
## Best Practices
|
|
216
|
+
|
|
217
|
+
1. **Hook Design**:
|
|
218
|
+
- Fail gracefully - never break the main workflow
|
|
219
|
+
- Log important events for debugging
|
|
220
|
+
- Use exit codes appropriately (0=success, 2=block)
|
|
221
|
+
- Keep execution time minimal
|
|
222
|
+
|
|
223
|
+
2. **Security**:
|
|
224
|
+
- Regularly update sensitive patterns
|
|
225
|
+
- Review security logs periodically
|
|
226
|
+
- Test hooks in safe environments first
|
|
227
|
+
- Never log sensitive data in hooks
|
|
228
|
+
|
|
229
|
+
3. **Configuration**:
|
|
230
|
+
- Use `${WORKSPACE}` variable for portability
|
|
231
|
+
- Keep hooks executable (`chmod +x`)
|
|
232
|
+
- Version control hook configurations
|
|
233
|
+
- Document custom modifications
|
|
234
|
+
|
|
235
|
+
## Troubleshooting
|
|
236
|
+
|
|
237
|
+
### Hooks not executing
|
|
238
|
+
- Check file permissions: `chmod +x *.sh`
|
|
239
|
+
- Verify paths in settings.json
|
|
240
|
+
- Check Claude Code logs for errors
|
|
241
|
+
|
|
242
|
+
### Security scanner too restrictive
|
|
243
|
+
- Review patterns in `config/sensitive-patterns.json`
|
|
244
|
+
- Add legitimate patterns to the whitelist
|
|
245
|
+
- Check logs for what triggered the block
|
|
246
|
+
|
|
247
|
+
### No sound playing
|
|
248
|
+
- Verify sound files exist in `sounds/` directory
|
|
249
|
+
- Test audio playback: `.claude/hooks/notify.sh input`
|
|
250
|
+
- Check system audio settings
|
|
251
|
+
- Ensure you have an audio player installed (afplay, paplay, aplay, pw-play, play, ffplay, or PowerShell on Windows)
|
|
252
|
+
|
|
253
|
+
## Hook Setup Command
|
|
254
|
+
|
|
255
|
+
For comprehensive setup verification and testing, use:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
/hook-setup
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
This command uses multi-agent orchestration to verify installation, check configuration, and run comprehensive tests. See [hook-setup.md](setup/hook-setup.md) for details.
|
|
262
|
+
|
|
263
|
+
## Extension Points
|
|
264
|
+
|
|
265
|
+
The kit is designed for extensibility:
|
|
266
|
+
|
|
267
|
+
1. **Custom Hooks**: Add new scripts following the existing patterns
|
|
268
|
+
2. **Event Handlers**: Configure hooks for any Claude Code event
|
|
269
|
+
3. **Pattern Updates**: Modify security patterns for your needs
|
|
270
|
+
4. **Sound Customization**: Replace audio files with your preferences
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"patterns": {
|
|
3
|
+
"api_keys": [],
|
|
4
|
+
"credentials": [
|
|
5
|
+
"password\\s*[:=]\\s*[\"']?[^\\s\"']+[\"']?",
|
|
6
|
+
"passwd\\s*[:=]\\s*[\"']?[^\\s\"']+[\"']?",
|
|
7
|
+
"secret\\s*[:=]\\s*[\"']?[^\\s\"']+[\"']?",
|
|
8
|
+
"private[_-]?key\\s*[:=]\\s*[\"']?[^\\s\"']+[\"']?",
|
|
9
|
+
"access[_-]?key\\s*[:=]\\s*[\"']?[^\\s\"']+[\"']?",
|
|
10
|
+
"auth[_-]?token\\s*[:=]\\s*[\"']?[^\\s\"']+[\"']?",
|
|
11
|
+
"api[_-]?key\\s*[:=]\\s*[\"']?[^\\s\"']+[\"']?",
|
|
12
|
+
"client[_-]?secret\\s*[:=]\\s*[\"']?[^\\s\"']+[\"']?"
|
|
13
|
+
],
|
|
14
|
+
"sensitive_files": [
|
|
15
|
+
".env",
|
|
16
|
+
".env.local",
|
|
17
|
+
".env.production",
|
|
18
|
+
".env.development",
|
|
19
|
+
".env.staging",
|
|
20
|
+
"credentials.json",
|
|
21
|
+
"google-credentials.json",
|
|
22
|
+
"service-account.json",
|
|
23
|
+
"private.key",
|
|
24
|
+
"id_rsa",
|
|
25
|
+
"id_ed25519",
|
|
26
|
+
"id_dsa",
|
|
27
|
+
"id_ecdsa",
|
|
28
|
+
".pem",
|
|
29
|
+
".key",
|
|
30
|
+
".p12",
|
|
31
|
+
".pfx",
|
|
32
|
+
"keystore",
|
|
33
|
+
".jks"
|
|
34
|
+
],
|
|
35
|
+
"regex_patterns": [
|
|
36
|
+
"sk-[a-zA-Z0-9]{32,}",
|
|
37
|
+
"sk-proj-[a-zA-Z0-9]{32,}",
|
|
38
|
+
"AIza[0-9A-Za-z\\-_]{35}",
|
|
39
|
+
"gsk_[a-zA-Z0-9]{32,}",
|
|
40
|
+
"AKIA[0-9A-Z]{16}",
|
|
41
|
+
"aws_secret_access_key\\s*=\\s*[a-zA-Z0-9/+=]{40}",
|
|
42
|
+
"(postgres|postgresql|mysql|mongodb|redis)://[^:]+:[^@]+@[^/]+",
|
|
43
|
+
"-----BEGIN.*(RSA|DSA|EC|OPENSSH|PGP).*PRIVATE KEY-----",
|
|
44
|
+
"Bearer\\s+[a-zA-Z0-9\\-_]+\\.[a-zA-Z0-9\\-_]+\\.[a-zA-Z0-9\\-_]+",
|
|
45
|
+
"[A-Z_]+(KEY|TOKEN|SECRET|PASSWORD|PASSWD)\\s*=\\s*[\"']?[^\\s\"']+[\"']?",
|
|
46
|
+
"(api[_-]?key|access[_-]?token|auth[_-]?token)\\s*[:=]\\s*[\"']?[a-zA-Z0-9_\\-]{20,}[\"']?",
|
|
47
|
+
"(password|secret|key)\\s*[:=]\\s*[\"']?[A-Za-z0-9+/]{40,}={0,2}[\"']?",
|
|
48
|
+
"ghp_[a-zA-Z0-9]{36}",
|
|
49
|
+
"gho_[a-zA-Z0-9]{36}",
|
|
50
|
+
"github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}",
|
|
51
|
+
"glpat-[a-zA-Z0-9\\-_]{20}",
|
|
52
|
+
"xoxb-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}",
|
|
53
|
+
"sq0atp-[0-9A-Za-z\\-_]{22}",
|
|
54
|
+
"sq0csp-[0-9A-Za-z\\-_]{43}",
|
|
55
|
+
"SK[a-z0-9]{32}"
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"whitelist": {
|
|
59
|
+
"allowed_mentions": [
|
|
60
|
+
"GOOGLE_API_KEY=your_google_api_key_here",
|
|
61
|
+
"GROQ_API_KEY=your_groq_api_key_here",
|
|
62
|
+
"OPENAI_API_KEY=your_openai_api_key_here",
|
|
63
|
+
"GEMINI_API_KEY=your_gemini_api_key_here",
|
|
64
|
+
"AWS_ACCESS_KEY_ID=your_access_key_here",
|
|
65
|
+
"api_key=settings.api_key",
|
|
66
|
+
"api_key=process.env.API_KEY",
|
|
67
|
+
"api_key=YOUR_API_KEY",
|
|
68
|
+
"password=your_password_here",
|
|
69
|
+
"password=<your_password>",
|
|
70
|
+
"password=${PASSWORD}",
|
|
71
|
+
"token=your_token_here",
|
|
72
|
+
"token=<auth_token>",
|
|
73
|
+
"secret=your_secret_here",
|
|
74
|
+
"Bearer YOUR_TOKEN_HERE",
|
|
75
|
+
"Bearer <token>",
|
|
76
|
+
"postgres://user:password@localhost",
|
|
77
|
+
"mysql://root:password@localhost",
|
|
78
|
+
"mongodb://user:password@localhost",
|
|
79
|
+
"redis://user:password@localhost",
|
|
80
|
+
"connection_string = \"your_connection_string_here\"",
|
|
81
|
+
"API_KEY=<your_api_key>",
|
|
82
|
+
"SECRET_KEY=<your_secret_key>",
|
|
83
|
+
"DATABASE_URL=<your_database_url>"
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Gemini Context Injector Hook
|
|
3
|
+
# Automatically adds project context files to new Gemini consultation sessions:
|
|
4
|
+
# - docs/ai-context/project-structure.md
|
|
5
|
+
# - MCP-ASSISTANT-RULES.md
|
|
6
|
+
#
|
|
7
|
+
# This hook enhances Gemini consultations by automatically including your project's
|
|
8
|
+
# structure documentation and assistant rules, ensuring the AI has complete context.
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
14
|
+
PROJECT_STRUCTURE_FILE="$PROJECT_ROOT/docs/ai-context/project-structure.md"
|
|
15
|
+
MCP_RULES_FILE="$PROJECT_ROOT/MCP-ASSISTANT-RULES.md"
|
|
16
|
+
LOG_FILE="$SCRIPT_DIR/../logs/context-injection.log"
|
|
17
|
+
|
|
18
|
+
# Ensure log directory exists
|
|
19
|
+
mkdir -p "$(dirname "$LOG_FILE")"
|
|
20
|
+
|
|
21
|
+
# Read input from stdin
|
|
22
|
+
INPUT_JSON=$(cat)
|
|
23
|
+
|
|
24
|
+
# Function to log injection events
|
|
25
|
+
log_injection_event() {
|
|
26
|
+
local event_type="$1"
|
|
27
|
+
local details="$2"
|
|
28
|
+
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
29
|
+
echo "{\"timestamp\": \"$timestamp\", \"event\": \"$event_type\", \"details\": \"$details\"}" >> "$LOG_FILE"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Main logic
|
|
33
|
+
main() {
|
|
34
|
+
# Extract tool information from stdin
|
|
35
|
+
local tool_name=$(echo "$INPUT_JSON" | jq -r '.tool_name // ""')
|
|
36
|
+
|
|
37
|
+
# Only process Gemini consultation requests
|
|
38
|
+
if [[ "$tool_name" != "mcp__gemini__consult_gemini" ]]; then
|
|
39
|
+
echo '{"continue": true}'
|
|
40
|
+
exit 0
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Extract tool arguments
|
|
44
|
+
local tool_args=$(echo "$INPUT_JSON" | jq -r '.tool_input // "{}"')
|
|
45
|
+
|
|
46
|
+
# Check if this is a new session (no session_id provided)
|
|
47
|
+
local session_id=$(echo "$tool_args" | jq -r '.session_id // ""' 2>/dev/null || echo "")
|
|
48
|
+
|
|
49
|
+
if [[ -z "$session_id" || "$session_id" == "null" ]]; then
|
|
50
|
+
log_injection_event "new_session_detected" "preparing_context_injection"
|
|
51
|
+
|
|
52
|
+
# Check if required files exist
|
|
53
|
+
local missing_files=""
|
|
54
|
+
if [[ ! -f "$PROJECT_STRUCTURE_FILE" ]]; then
|
|
55
|
+
missing_files="$missing_files project_structure.md"
|
|
56
|
+
fi
|
|
57
|
+
if [[ ! -f "$MCP_RULES_FILE" ]]; then
|
|
58
|
+
missing_files="$missing_files MCP-ASSISTANT-RULES.md"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# If either file is missing, log warning but continue
|
|
62
|
+
if [[ -n "$missing_files" ]]; then
|
|
63
|
+
log_injection_event "warning" "missing_files:$missing_files"
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# If both files are missing, exit early
|
|
67
|
+
if [[ ! -f "$PROJECT_STRUCTURE_FILE" ]] && [[ ! -f "$MCP_RULES_FILE" ]]; then
|
|
68
|
+
echo '{"continue": true}'
|
|
69
|
+
exit 0
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Extract current attached_files if any
|
|
73
|
+
local current_files=$(echo "$tool_args" | jq -c '.attached_files // []' 2>/dev/null || echo "[]")
|
|
74
|
+
|
|
75
|
+
# Check if files are already included
|
|
76
|
+
local has_project_structure=$(echo "$current_files" | jq -e ".[] | select(. == \"$PROJECT_STRUCTURE_FILE\")" > /dev/null 2>&1 && echo "true" || echo "false")
|
|
77
|
+
local has_mcp_rules=$(echo "$current_files" | jq -e ".[] | select(. == \"$MCP_RULES_FILE\")" > /dev/null 2>&1 && echo "true" || echo "false")
|
|
78
|
+
|
|
79
|
+
# If both files exist and are already included, skip
|
|
80
|
+
if [[ -f "$PROJECT_STRUCTURE_FILE" ]] && [[ "$has_project_structure" == "true" ]] && \
|
|
81
|
+
[[ -f "$MCP_RULES_FILE" ]] && [[ "$has_mcp_rules" == "true" ]]; then
|
|
82
|
+
log_injection_event "skipped" "all_required_files_already_included"
|
|
83
|
+
echo '{"continue": true}'
|
|
84
|
+
exit 0
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# Add missing files to attached_files
|
|
88
|
+
local modified_args="$tool_args"
|
|
89
|
+
local files_added=""
|
|
90
|
+
|
|
91
|
+
if [[ -f "$PROJECT_STRUCTURE_FILE" ]] && [[ "$has_project_structure" == "false" ]]; then
|
|
92
|
+
modified_args=$(echo "$modified_args" | jq --arg file "$PROJECT_STRUCTURE_FILE" '
|
|
93
|
+
.attached_files = ((.attached_files // []) + [$file])
|
|
94
|
+
' 2>/dev/null)
|
|
95
|
+
files_added="$files_added project_structure.md"
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
if [[ -f "$MCP_RULES_FILE" ]] && [[ "$has_mcp_rules" == "false" ]]; then
|
|
99
|
+
modified_args=$(echo "$modified_args" | jq --arg file "$MCP_RULES_FILE" '
|
|
100
|
+
.attached_files = ((.attached_files // []) + [$file])
|
|
101
|
+
' 2>/dev/null)
|
|
102
|
+
files_added="$files_added MCP-ASSISTANT-RULES.md"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
if [[ -n "$modified_args" ]] && [[ "$modified_args" != "$tool_args" ]]; then
|
|
106
|
+
log_injection_event "context_injected" "added_files:$files_added"
|
|
107
|
+
|
|
108
|
+
# Update the input JSON with modified tool_input
|
|
109
|
+
local output_json=$(echo "$INPUT_JSON" | jq --argjson new_args "$modified_args" '.tool_input = $new_args')
|
|
110
|
+
|
|
111
|
+
# Return the modified input to stdout
|
|
112
|
+
echo "$output_json"
|
|
113
|
+
exit 0
|
|
114
|
+
else
|
|
115
|
+
log_injection_event "error" "failed_to_modify_arguments"
|
|
116
|
+
# Continue without modification on error
|
|
117
|
+
echo '{"continue": true}'
|
|
118
|
+
exit 0
|
|
119
|
+
fi
|
|
120
|
+
else
|
|
121
|
+
log_injection_event "existing_session" "session_id:$session_id"
|
|
122
|
+
# For existing sessions, continue without modification
|
|
123
|
+
echo '{"continue": true}'
|
|
124
|
+
exit 0
|
|
125
|
+
fi
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# Run main function
|
|
129
|
+
main
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# MCP Security Scanner Hook
|
|
3
|
+
# Scans MCP requests for sensitive data before sending to external services
|
|
4
|
+
#
|
|
5
|
+
# This hook protects against accidental exposure of secrets, API keys, and other
|
|
6
|
+
# sensitive information when using MCP servers like Gemini or Context7.
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
PATTERNS_FILE="$SCRIPT_DIR/config/sensitive-patterns.json"
|
|
12
|
+
LOG_FILE="$SCRIPT_DIR/../logs/security-scan.log"
|
|
13
|
+
|
|
14
|
+
# Ensure log directory exists
|
|
15
|
+
mkdir -p "$(dirname "$LOG_FILE")"
|
|
16
|
+
|
|
17
|
+
# Read input from stdin
|
|
18
|
+
INPUT_JSON=$(cat)
|
|
19
|
+
|
|
20
|
+
# Function to log security events
|
|
21
|
+
log_security_event() {
|
|
22
|
+
local event_type="$1"
|
|
23
|
+
local details="$2"
|
|
24
|
+
local tool_name="${3:-unknown}"
|
|
25
|
+
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
26
|
+
echo "{\"timestamp\": \"$timestamp\", \"tool\": \"$tool_name\", \"event\": \"$event_type\", \"details\": \"$details\"}" >> "$LOG_FILE"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Function to check if content matches sensitive patterns
|
|
30
|
+
check_sensitive_content() {
|
|
31
|
+
local content="$1"
|
|
32
|
+
local pattern_type="$2"
|
|
33
|
+
|
|
34
|
+
# Get patterns from JSON config
|
|
35
|
+
local patterns=$(jq -r ".patterns.$pattern_type[]" "$PATTERNS_FILE" 2>/dev/null || echo "")
|
|
36
|
+
|
|
37
|
+
for pattern in $patterns; do
|
|
38
|
+
if echo "$content" | grep -qiE "$pattern"; then
|
|
39
|
+
# Check whitelist
|
|
40
|
+
local whitelisted=false
|
|
41
|
+
local whitelist_patterns=$(jq -r '.whitelist.allowed_mentions[]' "$PATTERNS_FILE" 2>/dev/null || echo "")
|
|
42
|
+
|
|
43
|
+
for whitelist in $whitelist_patterns; do
|
|
44
|
+
if echo "$content" | grep -qF "$whitelist"; then
|
|
45
|
+
whitelisted=true
|
|
46
|
+
break
|
|
47
|
+
fi
|
|
48
|
+
done
|
|
49
|
+
|
|
50
|
+
if [[ "$whitelisted" == "false" ]]; then
|
|
51
|
+
return 0 # Found sensitive data
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
done
|
|
55
|
+
|
|
56
|
+
return 1 # No sensitive data found
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Function to scan file content
|
|
60
|
+
scan_file_content() {
|
|
61
|
+
local file_path="$1"
|
|
62
|
+
|
|
63
|
+
# Check if file name itself is sensitive
|
|
64
|
+
local filename=$(basename "$file_path")
|
|
65
|
+
if check_sensitive_content "$filename" "sensitive_files"; then
|
|
66
|
+
return 0 # Sensitive file
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Don't scan files that don't exist or are too large
|
|
70
|
+
if [[ ! -f "$file_path" ]] || [[ $(stat -f%z "$file_path" 2>/dev/null || stat -c%s "$file_path" 2>/dev/null || echo "999999999") -gt 1048576 ]]; then
|
|
71
|
+
return 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Read and scan file content
|
|
75
|
+
local content=$(cat "$file_path" 2>/dev/null || echo "")
|
|
76
|
+
|
|
77
|
+
# Check all pattern types
|
|
78
|
+
for pattern_type in api_keys credentials regex_patterns; do
|
|
79
|
+
if check_sensitive_content "$content" "$pattern_type"; then
|
|
80
|
+
return 0 # Found sensitive data
|
|
81
|
+
fi
|
|
82
|
+
done
|
|
83
|
+
|
|
84
|
+
return 1
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Main scanning logic
|
|
88
|
+
main() {
|
|
89
|
+
# Extract tool information from stdin
|
|
90
|
+
local tool_name=$(echo "$INPUT_JSON" | jq -r '.tool_name // ""')
|
|
91
|
+
local tool_args=$(echo "$INPUT_JSON" | jq -r '.tool_input // "{}"')
|
|
92
|
+
|
|
93
|
+
log_security_event "scan_started" "$tool_name" "$tool_name"
|
|
94
|
+
|
|
95
|
+
# Check code_context for sensitive data
|
|
96
|
+
local code_context=$(echo "$tool_args" | jq -r '.code_context // ""' 2>/dev/null || echo "")
|
|
97
|
+
if [[ -n "$code_context" ]]; then
|
|
98
|
+
for pattern_type in api_keys credentials regex_patterns; do
|
|
99
|
+
if check_sensitive_content "$code_context" "$pattern_type"; then
|
|
100
|
+
log_security_event "blocked" "sensitive_data_in_code_context" "$tool_name"
|
|
101
|
+
echo '{"decision": "block", "reason": "Security Alert: Detected sensitive data in code_context. Found patterns matching actual credentials (API keys, passwords, or secrets with values). For discussions about security topics, use placeholders like YOUR_API_KEY, <password>, or example values instead of real credentials."}'
|
|
102
|
+
exit 2
|
|
103
|
+
fi
|
|
104
|
+
done
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
# Check problem_description for sensitive data
|
|
108
|
+
local problem_desc=$(echo "$tool_args" | jq -r '.problem_description // ""' 2>/dev/null || echo "")
|
|
109
|
+
if [[ -n "$problem_desc" ]]; then
|
|
110
|
+
for pattern_type in api_keys credentials regex_patterns; do
|
|
111
|
+
if check_sensitive_content "$problem_desc" "$pattern_type"; then
|
|
112
|
+
log_security_event "blocked" "sensitive_data_in_problem_description" "$tool_name"
|
|
113
|
+
echo '{"decision": "block", "reason": "Security Alert: Detected sensitive data in problem description. Found patterns matching actual credentials (API keys, passwords, connection strings, or tokens with values). For security discussions, use placeholders: YOUR_API_KEY, <password>, postgres://user:password@localhost, or example-token-here."}'
|
|
114
|
+
exit 2
|
|
115
|
+
fi
|
|
116
|
+
done
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# Check attached files
|
|
120
|
+
local attached_files=$(echo "$tool_args" | jq -r '.attached_files[]?' 2>/dev/null || echo "")
|
|
121
|
+
for file in $attached_files; do
|
|
122
|
+
if scan_file_content "$file"; then
|
|
123
|
+
log_security_event "blocked" "sensitive_file_attached:$file" "$tool_name"
|
|
124
|
+
echo "{\"decision\": \"block\", \"reason\": \"Security Alert: Detected sensitive content in attached file $file. Found credentials, private keys, or environment files. Remove actual secrets and use placeholders like YOUR_SECRET_HERE or example values for demonstrations.\"}"
|
|
125
|
+
exit 2
|
|
126
|
+
fi
|
|
127
|
+
done
|
|
128
|
+
|
|
129
|
+
# Check specific question for Context7
|
|
130
|
+
if [[ "$tool_name" == "mcp__context7__get-library-docs" ]]; then
|
|
131
|
+
local library_id=$(echo "$tool_args" | jq -r '.context7CompatibleLibraryID // ""' 2>/dev/null || echo "")
|
|
132
|
+
# Basic check to prevent injection attacks
|
|
133
|
+
if echo "$library_id" | grep -qE '(\$|`|;|&&|\|\||>|<)'; then
|
|
134
|
+
log_security_event "blocked" "suspicious_library_id" "$tool_name"
|
|
135
|
+
echo '{"decision": "block", "reason": "Security Alert: Detected suspicious characters in library ID that could indicate command injection. Please use only alphanumeric characters, hyphens, underscores, and forward slashes."}'
|
|
136
|
+
exit 2
|
|
137
|
+
fi
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
log_security_event "scan_completed" "no_sensitive_data_found" "$tool_name"
|
|
141
|
+
|
|
142
|
+
# All checks passed, allow the tool to continue
|
|
143
|
+
# No output needed when allowing - just exit 0
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# Run main function
|
|
147
|
+
main
|