agent-security-scanner-mcp 1.0.1 → 1.1.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/README.md +314 -31
- package/index.js +229 -0
- package/package.json +8 -4
- package/packages/dart.txt +64721 -0
- package/packages/perl.txt +50202 -0
- package/packages/raku.txt +2626 -0
package/README.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# agent-security-scanner-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A powerful MCP (Model Context Protocol) server for real-time security vulnerability scanning. Integrates with Claude Desktop and Claude Code to automatically detect and fix security issues as you code.
|
|
4
|
+
|
|
5
|
+
**165 Semgrep-aligned security rules | 105 auto-fix templates | 100% fix coverage | Package hallucination detection**
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Real-time scanning** - Detect vulnerabilities instantly as you write code
|
|
10
|
+
- **Auto-fix suggestions** - Get actionable fixes for every security issue
|
|
11
|
+
- **Multi-language support** - JavaScript, TypeScript, Python, Java, Go, Dockerfile
|
|
12
|
+
- **Semgrep-compatible** - Rules aligned with Semgrep registry format
|
|
13
|
+
- **CWE & OWASP mapped** - Every rule includes CWE and OWASP references
|
|
14
|
+
- **Hallucination detection** - Detect AI-invented package names (Dart, Perl, Raku)
|
|
4
15
|
|
|
5
16
|
## Installation
|
|
6
17
|
|
|
@@ -17,7 +28,7 @@ npx agent-security-scanner-mcp
|
|
|
17
28
|
## Requirements
|
|
18
29
|
|
|
19
30
|
- Node.js >= 18.0.0
|
|
20
|
-
- Python 3.x (for the analyzer)
|
|
31
|
+
- Python 3.x (for the analyzer engine)
|
|
21
32
|
|
|
22
33
|
## Configuration
|
|
23
34
|
|
|
@@ -36,9 +47,13 @@ Add to your `claude_desktop_config.json`:
|
|
|
36
47
|
}
|
|
37
48
|
```
|
|
38
49
|
|
|
50
|
+
**Config file locations:**
|
|
51
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
52
|
+
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
53
|
+
|
|
39
54
|
### Claude Code
|
|
40
55
|
|
|
41
|
-
Add to your MCP settings:
|
|
56
|
+
Add to your MCP settings (`~/.claude/settings.json`):
|
|
42
57
|
|
|
43
58
|
```json
|
|
44
59
|
{
|
|
@@ -57,50 +72,318 @@ Add to your MCP settings:
|
|
|
57
72
|
|
|
58
73
|
Scan a file for security vulnerabilities and return issues with suggested fixes.
|
|
59
74
|
|
|
60
|
-
|
|
61
|
-
|
|
75
|
+
```
|
|
76
|
+
Parameters:
|
|
77
|
+
file_path (string): Absolute path to the file to scan
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
- List of security issues
|
|
81
|
+
- Severity level (ERROR, WARNING, INFO)
|
|
82
|
+
- CWE and OWASP references
|
|
83
|
+
- Line numbers and code context
|
|
84
|
+
- Suggested fixes
|
|
85
|
+
```
|
|
62
86
|
|
|
63
|
-
**
|
|
87
|
+
**Example output:**
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"file": "/path/to/file.js",
|
|
91
|
+
"language": "javascript",
|
|
92
|
+
"issues_count": 3,
|
|
93
|
+
"issues": [
|
|
94
|
+
{
|
|
95
|
+
"ruleId": "javascript.lang.security.audit.sql-injection",
|
|
96
|
+
"message": "SQL Injection detected. Use parameterized queries.",
|
|
97
|
+
"line": 15,
|
|
98
|
+
"severity": "error",
|
|
99
|
+
"metadata": {
|
|
100
|
+
"cwe": "CWE-89",
|
|
101
|
+
"owasp": "A03:2021 - Injection"
|
|
102
|
+
},
|
|
103
|
+
"suggested_fix": {
|
|
104
|
+
"description": "Use parameterized queries instead of string concatenation",
|
|
105
|
+
"original": "db.query(\"SELECT * FROM users WHERE id = \" + userId)",
|
|
106
|
+
"fixed": "db.query(\"SELECT * FROM users WHERE id = ?\", [userId])"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
```
|
|
64
112
|
|
|
65
113
|
### `fix_security`
|
|
66
114
|
|
|
67
|
-
|
|
115
|
+
Automatically fix all security issues in a file.
|
|
68
116
|
|
|
69
|
-
|
|
70
|
-
|
|
117
|
+
```
|
|
118
|
+
Parameters:
|
|
119
|
+
file_path (string): Absolute path to the file to fix
|
|
71
120
|
|
|
72
|
-
|
|
121
|
+
Returns:
|
|
122
|
+
- Number of fixes applied
|
|
123
|
+
- Details of each fix
|
|
124
|
+
- Fixed file content
|
|
125
|
+
```
|
|
73
126
|
|
|
74
127
|
### `list_security_rules`
|
|
75
128
|
|
|
76
|
-
List all available
|
|
129
|
+
List all 105 available auto-fix templates.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Package Hallucination Detection
|
|
134
|
+
|
|
135
|
+
Detect AI-hallucinated package names that don't exist in official registries. Prevents supply chain attacks where attackers register fake package names suggested by AI.
|
|
136
|
+
|
|
137
|
+
### `check_package`
|
|
138
|
+
|
|
139
|
+
Check if a single package name is legitimate or potentially hallucinated.
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
Parameters:
|
|
143
|
+
package_name (string): The package name to verify
|
|
144
|
+
ecosystem (enum): "dart", "perl", "raku", "npm", "pypi"
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
- legitimate: true/false
|
|
148
|
+
- hallucinated: true/false
|
|
149
|
+
- confidence: "high"
|
|
150
|
+
- recommendation: Action to take
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Example:**
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"package": "flutter_animations",
|
|
157
|
+
"ecosystem": "dart",
|
|
158
|
+
"legitimate": true,
|
|
159
|
+
"hallucinated": false,
|
|
160
|
+
"confidence": "high",
|
|
161
|
+
"total_known_packages": 64721,
|
|
162
|
+
"recommendation": "Package exists in registry - safe to use"
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### `scan_packages`
|
|
77
167
|
|
|
78
|
-
|
|
168
|
+
Scan a code file and detect all potentially hallucinated package imports.
|
|
79
169
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
170
|
+
```
|
|
171
|
+
Parameters:
|
|
172
|
+
file_path (string): Path to the file to scan
|
|
173
|
+
ecosystem (enum): "dart", "perl", "raku", "npm", "pypi"
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
- List of all packages found
|
|
177
|
+
- Which are legitimate vs hallucinated
|
|
178
|
+
- Recommendation
|
|
179
|
+
```
|
|
88
180
|
|
|
89
|
-
|
|
181
|
+
**Example output:**
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"file": "/path/to/main.dart",
|
|
185
|
+
"ecosystem": "dart",
|
|
186
|
+
"total_packages_found": 5,
|
|
187
|
+
"legitimate_count": 4,
|
|
188
|
+
"hallucinated_count": 1,
|
|
189
|
+
"hallucinated_packages": ["fake_flutter_pkg"],
|
|
190
|
+
"legitimate_packages": ["flutter", "http", "provider", "shared_preferences"],
|
|
191
|
+
"recommendation": "⚠️ Found 1 potentially hallucinated package(s): fake_flutter_pkg"
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### `list_package_stats`
|
|
196
|
+
|
|
197
|
+
Show statistics about loaded package lists.
|
|
90
198
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"package_lists": [
|
|
202
|
+
{ "ecosystem": "dart", "packages_loaded": 64721, "status": "ready" },
|
|
203
|
+
{ "ecosystem": "perl", "packages_loaded": 1000, "status": "ready" },
|
|
204
|
+
{ "ecosystem": "raku", "packages_loaded": 2626, "status": "ready" }
|
|
205
|
+
],
|
|
206
|
+
"total_packages": 68347
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Adding Custom Package Lists
|
|
211
|
+
|
|
212
|
+
Add your own package lists to `packages/` directory:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# Format: one package name per line
|
|
216
|
+
packages/
|
|
217
|
+
├── dart.txt # 64,721 packages
|
|
218
|
+
├── perl.txt # 1,000 packages
|
|
219
|
+
├── raku.txt # 2,626 packages
|
|
220
|
+
├── npm.txt # Add your own
|
|
221
|
+
└── pypi.txt # Add your own
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Fetching Package Lists
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# Using the included script
|
|
228
|
+
cd mcp-server
|
|
229
|
+
pip install datasets
|
|
230
|
+
python scripts/fetch-packages.py
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Package lists are sourced from:
|
|
234
|
+
- **Dart:** [dchitimalla1/dart-20250529](https://huggingface.co/datasets/dchitimalla1/dart-20250529)
|
|
235
|
+
- **Perl:** [dchitimalla1/perl-20250530](https://huggingface.co/datasets/dchitimalla1/perl-20250530)
|
|
236
|
+
- **Raku:** [dchitimalla1/raku-20250523](https://huggingface.co/datasets/dchitimalla1/raku-20250523)
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Security Rules (165 total)
|
|
241
|
+
|
|
242
|
+
### By Language
|
|
243
|
+
|
|
244
|
+
| Language | Rules | Categories |
|
|
245
|
+
|----------|-------|------------|
|
|
246
|
+
| JavaScript/TypeScript | 31 | XSS, injection, secrets, crypto |
|
|
247
|
+
| Python | 36 | Injection, deserialization, crypto, XXE |
|
|
248
|
+
| Java | 27 | Injection, XXE, crypto, deserialization |
|
|
249
|
+
| Go | 22 | Injection, crypto, race conditions |
|
|
250
|
+
| Dockerfile | 18 | Secrets, permissions, best practices |
|
|
251
|
+
| Generic (Secrets) | 31 | API keys, tokens, passwords |
|
|
252
|
+
|
|
253
|
+
### By Category
|
|
254
|
+
|
|
255
|
+
| Category | Rules | Auto-Fix |
|
|
256
|
+
|----------|-------|----------|
|
|
257
|
+
| **Injection (SQL, Command, XSS)** | 35 | Yes |
|
|
258
|
+
| **Hardcoded Secrets** | 45 | Yes |
|
|
259
|
+
| **Weak Cryptography** | 18 | Yes |
|
|
260
|
+
| **Insecure Deserialization** | 12 | Yes |
|
|
261
|
+
| **Path Traversal** | 6 | Yes |
|
|
262
|
+
| **SSRF** | 6 | Yes |
|
|
263
|
+
| **XXE** | 6 | Yes |
|
|
264
|
+
| **SSL/TLS Issues** | 8 | Yes |
|
|
265
|
+
| **CSRF** | 4 | Yes |
|
|
266
|
+
| **JWT Vulnerabilities** | 6 | Yes |
|
|
267
|
+
| **Dockerfile Security** | 18 | Yes |
|
|
268
|
+
| **Other** | 11 | Yes |
|
|
269
|
+
|
|
270
|
+
## Auto-Fix Templates (105 total)
|
|
271
|
+
|
|
272
|
+
Every detected vulnerability includes an automatic fix suggestion:
|
|
273
|
+
|
|
274
|
+
| Vulnerability | Fix Strategy |
|
|
275
|
+
|--------------|--------------|
|
|
276
|
+
| SQL Injection | Parameterized queries with placeholders |
|
|
277
|
+
| XSS (innerHTML) | Replace with `textContent` or DOMPurify |
|
|
278
|
+
| Command Injection | Use `execFile()` / `spawn()` with `shell: false` |
|
|
279
|
+
| Hardcoded Secrets | Environment variables (`process.env` / `os.environ`) |
|
|
280
|
+
| Weak Crypto (MD5/SHA1) | Replace with SHA-256 |
|
|
281
|
+
| Insecure Deserialization | Use `json.load()` or `yaml.safe_load()` |
|
|
282
|
+
| SSL verify=False | Set `verify=True` |
|
|
283
|
+
| Path Traversal | Use `path.basename()` / `os.path.basename()` |
|
|
284
|
+
| Eval/Exec | Remove or use safer alternatives |
|
|
285
|
+
| CORS Wildcard | Specify allowed origins |
|
|
286
|
+
|
|
287
|
+
## Example Usage
|
|
288
|
+
|
|
289
|
+
### Scanning a file
|
|
290
|
+
|
|
291
|
+
Ask Claude: *"Scan my app.js file for security issues"*
|
|
292
|
+
|
|
293
|
+
Claude will use `scan_security` and return:
|
|
294
|
+
- All vulnerabilities found
|
|
295
|
+
- Severity levels
|
|
296
|
+
- CWE/OWASP references
|
|
297
|
+
- Suggested fixes for each issue
|
|
298
|
+
|
|
299
|
+
### Auto-fixing issues
|
|
300
|
+
|
|
301
|
+
Ask Claude: *"Fix all security issues in app.js"*
|
|
302
|
+
|
|
303
|
+
Claude will use `fix_security` to:
|
|
304
|
+
- Apply all available auto-fixes
|
|
305
|
+
- Return the secured code
|
|
306
|
+
- List all changes made
|
|
307
|
+
|
|
308
|
+
## Supported Vulnerabilities
|
|
309
|
+
|
|
310
|
+
### Injection
|
|
311
|
+
- SQL Injection (multiple databases)
|
|
312
|
+
- NoSQL Injection (MongoDB)
|
|
313
|
+
- Command Injection (exec, spawn, subprocess)
|
|
314
|
+
- XSS (innerHTML, document.write, React dangerouslySetInnerHTML)
|
|
315
|
+
- LDAP Injection
|
|
316
|
+
- XPath Injection
|
|
317
|
+
- Template Injection (Jinja2, SpEL)
|
|
318
|
+
|
|
319
|
+
### Secrets & Credentials
|
|
320
|
+
- AWS Access Keys & Secret Keys
|
|
321
|
+
- GitHub Tokens (PAT, OAuth, App)
|
|
322
|
+
- Stripe API Keys
|
|
323
|
+
- OpenAI API Keys
|
|
324
|
+
- Slack Tokens & Webhooks
|
|
325
|
+
- Database URLs & Passwords
|
|
326
|
+
- Private Keys (RSA, SSH)
|
|
327
|
+
- JWT Secrets
|
|
328
|
+
- 25+ more token types
|
|
329
|
+
|
|
330
|
+
### Cryptography
|
|
331
|
+
- Weak Hashing (MD5, SHA1)
|
|
332
|
+
- Weak Ciphers (DES, RC4)
|
|
333
|
+
- ECB Mode Usage
|
|
334
|
+
- Insecure Random
|
|
335
|
+
- Weak RSA Key Size
|
|
336
|
+
- Weak TLS Versions
|
|
337
|
+
|
|
338
|
+
### Deserialization
|
|
339
|
+
- Python pickle/marshal/shelve
|
|
340
|
+
- YAML unsafe load
|
|
341
|
+
- Java ObjectInputStream
|
|
342
|
+
- Node serialize
|
|
343
|
+
- Go gob decode
|
|
344
|
+
|
|
345
|
+
### Network & SSL
|
|
346
|
+
- SSL Verification Disabled
|
|
347
|
+
- Certificate Validation Bypass
|
|
348
|
+
- SSRF Vulnerabilities
|
|
349
|
+
- Open Redirects
|
|
350
|
+
- CORS Misconfiguration
|
|
351
|
+
|
|
352
|
+
### Other
|
|
353
|
+
- Path Traversal
|
|
354
|
+
- XXE (XML External Entities)
|
|
355
|
+
- CSRF Disabled
|
|
356
|
+
- Debug Mode Enabled
|
|
357
|
+
- Prototype Pollution
|
|
358
|
+
- ReDoS (Regex DoS)
|
|
359
|
+
- Race Conditions
|
|
360
|
+
|
|
361
|
+
## Contributing
|
|
362
|
+
|
|
363
|
+
Contributions welcome! Please see our [GitHub repository](https://github.com/sinewaveai/agent-security-layer-fork).
|
|
364
|
+
|
|
365
|
+
### Adding New Rules
|
|
366
|
+
|
|
367
|
+
Rules are defined in YAML format in the `rules/` directory:
|
|
368
|
+
|
|
369
|
+
```yaml
|
|
370
|
+
- id: language.category.rule-name
|
|
371
|
+
languages: [javascript]
|
|
372
|
+
severity: ERROR
|
|
373
|
+
message: "Description of the vulnerability"
|
|
374
|
+
patterns:
|
|
375
|
+
- "regex_pattern"
|
|
376
|
+
metadata:
|
|
377
|
+
cwe: "CWE-XXX"
|
|
378
|
+
owasp: "Category"
|
|
379
|
+
```
|
|
99
380
|
|
|
100
381
|
## License
|
|
101
382
|
|
|
102
383
|
MIT
|
|
103
384
|
|
|
104
|
-
##
|
|
385
|
+
## Links
|
|
105
386
|
|
|
106
|
-
https://
|
|
387
|
+
- **npm:** https://www.npmjs.com/package/agent-security-scanner-mcp
|
|
388
|
+
- **GitHub:** https://github.com/sinewaveai/agent-security-layer-fork
|
|
389
|
+
- **Issues:** https://github.com/sinewaveai/agent-security-layer-fork/issues
|
package/index.js
CHANGED
|
@@ -824,6 +824,235 @@ server.tool(
|
|
|
824
824
|
}
|
|
825
825
|
);
|
|
826
826
|
|
|
827
|
+
// ===========================================
|
|
828
|
+
// PACKAGE HALLUCINATION DETECTION
|
|
829
|
+
// ===========================================
|
|
830
|
+
|
|
831
|
+
// Load legitimate package lists into memory (hash sets for O(1) lookup)
|
|
832
|
+
const LEGITIMATE_PACKAGES = {
|
|
833
|
+
dart: new Set(),
|
|
834
|
+
perl: new Set(),
|
|
835
|
+
raku: new Set(),
|
|
836
|
+
npm: new Set(),
|
|
837
|
+
pypi: new Set()
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
// Package import patterns by ecosystem
|
|
841
|
+
const IMPORT_PATTERNS = {
|
|
842
|
+
dart: [
|
|
843
|
+
/import\s+['"]package:([^\/'"]+)/g,
|
|
844
|
+
/dependencies:\s*\n(?:\s+(\w+):\s*[\^~]?[\d.]+\n)+/g
|
|
845
|
+
],
|
|
846
|
+
perl: [
|
|
847
|
+
/use\s+([\w:]+)/g,
|
|
848
|
+
/require\s+([\w:]+)/g
|
|
849
|
+
],
|
|
850
|
+
raku: [
|
|
851
|
+
/use\s+([\w:]+)/g,
|
|
852
|
+
/need\s+([\w:]+)/g
|
|
853
|
+
],
|
|
854
|
+
npm: [
|
|
855
|
+
/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
856
|
+
/from\s+['"]([^'"]+)['"]/g,
|
|
857
|
+
/import\s+['"]([^'"]+)['"]/g
|
|
858
|
+
],
|
|
859
|
+
pypi: [
|
|
860
|
+
/^import\s+([\w]+)/gm,
|
|
861
|
+
/^from\s+([\w]+)/gm
|
|
862
|
+
]
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
// Load package lists on startup
|
|
866
|
+
function loadPackageLists() {
|
|
867
|
+
const packagesDir = join(__dirname, 'packages');
|
|
868
|
+
|
|
869
|
+
for (const ecosystem of Object.keys(LEGITIMATE_PACKAGES)) {
|
|
870
|
+
const filePath = join(packagesDir, `${ecosystem}.txt`);
|
|
871
|
+
try {
|
|
872
|
+
if (existsSync(filePath)) {
|
|
873
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
874
|
+
const packages = content.split('\n').filter(p => p.trim());
|
|
875
|
+
LEGITIMATE_PACKAGES[ecosystem] = new Set(packages);
|
|
876
|
+
console.error(`Loaded ${packages.length} ${ecosystem} packages`);
|
|
877
|
+
}
|
|
878
|
+
} catch (error) {
|
|
879
|
+
console.error(`Warning: Could not load ${ecosystem} packages: ${error.message}`);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// Extract package names from code
|
|
885
|
+
function extractPackages(code, ecosystem) {
|
|
886
|
+
const packages = new Set();
|
|
887
|
+
const patterns = IMPORT_PATTERNS[ecosystem] || [];
|
|
888
|
+
|
|
889
|
+
for (const pattern of patterns) {
|
|
890
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
891
|
+
let match;
|
|
892
|
+
while ((match = regex.exec(code)) !== null) {
|
|
893
|
+
const pkg = match[1];
|
|
894
|
+
if (pkg && !pkg.startsWith('.') && !pkg.startsWith('/')) {
|
|
895
|
+
// Normalize package name (handle scoped packages, subpaths)
|
|
896
|
+
const basePkg = pkg.split('/')[0].replace(/^@/, '');
|
|
897
|
+
packages.add(basePkg);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
return Array.from(packages);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// Check if a package is hallucinated
|
|
906
|
+
function isHallucinated(packageName, ecosystem) {
|
|
907
|
+
const legitPackages = LEGITIMATE_PACKAGES[ecosystem];
|
|
908
|
+
if (!legitPackages || legitPackages.size === 0) {
|
|
909
|
+
return { unknown: true, reason: `No package list loaded for ${ecosystem}` };
|
|
910
|
+
}
|
|
911
|
+
return { hallucinated: !legitPackages.has(packageName) };
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// Register check_package tool
|
|
915
|
+
server.tool(
|
|
916
|
+
"check_package",
|
|
917
|
+
"Check if a package name is legitimate or potentially hallucinated (AI-invented)",
|
|
918
|
+
{
|
|
919
|
+
package_name: z.string().describe("The package name to verify"),
|
|
920
|
+
ecosystem: z.enum(["dart", "perl", "raku", "npm", "pypi"]).describe("The package ecosystem")
|
|
921
|
+
},
|
|
922
|
+
async ({ package_name, ecosystem }) => {
|
|
923
|
+
const legitPackages = LEGITIMATE_PACKAGES[ecosystem];
|
|
924
|
+
const totalPackages = legitPackages?.size || 0;
|
|
925
|
+
|
|
926
|
+
if (totalPackages === 0) {
|
|
927
|
+
return {
|
|
928
|
+
content: [{
|
|
929
|
+
type: "text",
|
|
930
|
+
text: JSON.stringify({
|
|
931
|
+
package: package_name,
|
|
932
|
+
ecosystem,
|
|
933
|
+
status: "unknown",
|
|
934
|
+
reason: `No package list loaded for ${ecosystem}. Add packages/${ecosystem}.txt`,
|
|
935
|
+
suggestion: "Load package list or verify manually at the package registry"
|
|
936
|
+
}, null, 2)
|
|
937
|
+
}]
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
const exists = legitPackages.has(package_name);
|
|
942
|
+
|
|
943
|
+
return {
|
|
944
|
+
content: [{
|
|
945
|
+
type: "text",
|
|
946
|
+
text: JSON.stringify({
|
|
947
|
+
package: package_name,
|
|
948
|
+
ecosystem,
|
|
949
|
+
legitimate: exists,
|
|
950
|
+
hallucinated: !exists,
|
|
951
|
+
confidence: "high",
|
|
952
|
+
total_known_packages: totalPackages,
|
|
953
|
+
recommendation: exists
|
|
954
|
+
? "Package exists in registry - safe to use"
|
|
955
|
+
: "⚠️ POTENTIAL HALLUCINATION - Package not found in registry. Verify before using!"
|
|
956
|
+
}, null, 2)
|
|
957
|
+
}]
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
);
|
|
961
|
+
|
|
962
|
+
// Register scan_packages tool
|
|
963
|
+
server.tool(
|
|
964
|
+
"scan_packages",
|
|
965
|
+
"Scan code for package imports and check for hallucinated (AI-invented) packages",
|
|
966
|
+
{
|
|
967
|
+
file_path: z.string().describe("Path to the file to scan"),
|
|
968
|
+
ecosystem: z.enum(["dart", "perl", "raku", "npm", "pypi"]).describe("The package ecosystem")
|
|
969
|
+
},
|
|
970
|
+
async ({ file_path, ecosystem }) => {
|
|
971
|
+
if (!existsSync(file_path)) {
|
|
972
|
+
return {
|
|
973
|
+
content: [{ type: "text", text: JSON.stringify({ error: "File not found" }) }]
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
const code = readFileSync(file_path, 'utf-8');
|
|
978
|
+
const packages = extractPackages(code, ecosystem);
|
|
979
|
+
const legitPackages = LEGITIMATE_PACKAGES[ecosystem];
|
|
980
|
+
const totalKnown = legitPackages?.size || 0;
|
|
981
|
+
|
|
982
|
+
if (totalKnown === 0) {
|
|
983
|
+
return {
|
|
984
|
+
content: [{
|
|
985
|
+
type: "text",
|
|
986
|
+
text: JSON.stringify({
|
|
987
|
+
file: file_path,
|
|
988
|
+
ecosystem,
|
|
989
|
+
packages_found: packages,
|
|
990
|
+
status: "unknown",
|
|
991
|
+
reason: `No package list loaded for ${ecosystem}`
|
|
992
|
+
}, null, 2)
|
|
993
|
+
}]
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
const results = packages.map(pkg => ({
|
|
998
|
+
package: pkg,
|
|
999
|
+
legitimate: legitPackages.has(pkg),
|
|
1000
|
+
hallucinated: !legitPackages.has(pkg)
|
|
1001
|
+
}));
|
|
1002
|
+
|
|
1003
|
+
const hallucinated = results.filter(r => r.hallucinated);
|
|
1004
|
+
const legitimate = results.filter(r => r.legitimate);
|
|
1005
|
+
|
|
1006
|
+
return {
|
|
1007
|
+
content: [{
|
|
1008
|
+
type: "text",
|
|
1009
|
+
text: JSON.stringify({
|
|
1010
|
+
file: file_path,
|
|
1011
|
+
ecosystem,
|
|
1012
|
+
total_packages_found: packages.length,
|
|
1013
|
+
legitimate_count: legitimate.length,
|
|
1014
|
+
hallucinated_count: hallucinated.length,
|
|
1015
|
+
known_packages_in_registry: totalKnown,
|
|
1016
|
+
hallucinated_packages: hallucinated.map(r => r.package),
|
|
1017
|
+
legitimate_packages: legitimate.map(r => r.package),
|
|
1018
|
+
all_results: results,
|
|
1019
|
+
recommendation: hallucinated.length > 0
|
|
1020
|
+
? `⚠️ Found ${hallucinated.length} potentially hallucinated package(s): ${hallucinated.map(r => r.package).join(', ')}`
|
|
1021
|
+
: "✅ All packages verified as legitimate"
|
|
1022
|
+
}, null, 2)
|
|
1023
|
+
}]
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
);
|
|
1027
|
+
|
|
1028
|
+
// Register list_package_stats tool
|
|
1029
|
+
server.tool(
|
|
1030
|
+
"list_package_stats",
|
|
1031
|
+
"List statistics about loaded package lists for hallucination detection",
|
|
1032
|
+
{},
|
|
1033
|
+
async () => {
|
|
1034
|
+
const stats = Object.entries(LEGITIMATE_PACKAGES).map(([ecosystem, packages]) => ({
|
|
1035
|
+
ecosystem,
|
|
1036
|
+
packages_loaded: packages.size,
|
|
1037
|
+
status: packages.size > 0 ? "ready" : "not loaded"
|
|
1038
|
+
}));
|
|
1039
|
+
|
|
1040
|
+
return {
|
|
1041
|
+
content: [{
|
|
1042
|
+
type: "text",
|
|
1043
|
+
text: JSON.stringify({
|
|
1044
|
+
package_lists: stats,
|
|
1045
|
+
total_packages: stats.reduce((sum, s) => sum + s.packages_loaded, 0),
|
|
1046
|
+
usage: "Use check_package or scan_packages to detect hallucinated packages"
|
|
1047
|
+
}, null, 2)
|
|
1048
|
+
}]
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
);
|
|
1052
|
+
|
|
1053
|
+
// Load package lists on module initialization
|
|
1054
|
+
loadPackageLists();
|
|
1055
|
+
|
|
827
1056
|
// Start the server with stdio transport
|
|
828
1057
|
async function main() {
|
|
829
1058
|
const transport = new StdioServerTransport();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-security-scanner-mcp",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "MCP server for security
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "MCP server for security scanning & package hallucination detection - SQL injection, XSS, secrets, and AI-invented package detection",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -21,7 +21,10 @@
|
|
|
21
21
|
"code-analysis",
|
|
22
22
|
"sql-injection",
|
|
23
23
|
"xss",
|
|
24
|
-
"secrets-detection"
|
|
24
|
+
"secrets-detection",
|
|
25
|
+
"hallucination-detection",
|
|
26
|
+
"package-verification",
|
|
27
|
+
"supply-chain-security"
|
|
25
28
|
],
|
|
26
29
|
"author": "",
|
|
27
30
|
"license": "MIT",
|
|
@@ -43,6 +46,7 @@
|
|
|
43
46
|
"files": [
|
|
44
47
|
"index.js",
|
|
45
48
|
"analyzer.py",
|
|
46
|
-
"rules/**"
|
|
49
|
+
"rules/**",
|
|
50
|
+
"packages/**"
|
|
47
51
|
]
|
|
48
52
|
}
|