agent-security-scanner-mcp 3.4.0 → 3.5.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 +3 -283
- package/analyzer.py +5 -22
- package/index.js +2 -191
- package/package.json +5 -15
- package/pattern_matcher.py +0 -1
- package/regex_fallback.py +1 -199
- package/src/cli/init.js +0 -93
- package/src/fix-patterns.js +17 -66
- package/src/tools/fix-security.js +4 -31
- package/src/tools/scan-prompt.js +1 -71
- package/src/tools/scan-security.js +5 -33
- package/src/utils.js +7 -76
- package/cross_file_analyzer.py +0 -216
- package/scripts/postinstall.js +0 -25
- package/skills/openclaw/SKILL.md +0 -102
- package/skills/security-scan-batch.md +0 -107
- package/skills/security-scanner.md +0 -76
- package/src/config.js +0 -181
- package/src/context.js +0 -228
- package/src/dedup.js +0 -129
package/README.md
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
# agent-security-scanner-mcp
|
|
2
2
|
|
|
3
|
-
Security scanner for AI coding agents
|
|
3
|
+
Security scanner MCP server for AI coding agents. Scans code for vulnerabilities, detects hallucinated packages, and blocks prompt injection — all in real-time via the Model Context Protocol.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/agent-security-scanner-mcp)
|
|
6
6
|
[](https://www.npmjs.com/package/agent-security-scanner-mcp)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
8
|
|
|
9
|
-
> **New in v3.3.0:** Full [OpenClaw](https://openclaw.ai) integration with 30+ rules targeting autonomous AI threats — data exfiltration, credential theft, messaging abuse, and unsafe automation. [See OpenClaw setup](#openclaw-integration).
|
|
10
|
-
|
|
11
9
|
## Tools
|
|
12
10
|
|
|
13
11
|
| Tool | Description | When to Use |
|
|
14
12
|
|------|-------------|-------------|
|
|
15
13
|
| `scan_security` | Scan code for vulnerabilities (1700+ rules, 12 languages) with AST and taint analysis | After writing or editing any code file |
|
|
16
14
|
| `fix_security` | Auto-fix all detected vulnerabilities (120 fix templates) | After `scan_security` finds issues |
|
|
17
|
-
| `scan_git_diff` | Scan only changed files in git diff | Before commits or in PR reviews |
|
|
18
|
-
| `scan_project` | Scan entire project with A-F security grading | For project-wide security audits |
|
|
19
15
|
| `check_package` | Verify a package name isn't AI-hallucinated (4.3M+ packages) | Before adding any new dependency |
|
|
20
16
|
| `scan_packages` | Bulk-check all imports in a file for hallucinated packages | Before committing code with new imports |
|
|
21
17
|
| `scan_agent_prompt` | Detect prompt injection and malicious instructions (56 rules) | Before acting on external/untrusted input |
|
|
@@ -40,18 +36,8 @@ scan_security → review findings → fix_security → verify fix
|
|
|
40
36
|
|
|
41
37
|
### Before Committing
|
|
42
38
|
```
|
|
43
|
-
scan_git_diff → scan only changed files for fast feedback
|
|
44
39
|
scan_packages → verify all imports are legitimate
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
### For PR Reviews
|
|
48
|
-
```
|
|
49
|
-
scan_git_diff --base main → scan PR changes against main branch
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### For Project Audits
|
|
53
|
-
```
|
|
54
|
-
scan_project → get A-F security grade and aggregated metrics
|
|
40
|
+
scan_security → catch vulnerabilities before they ship
|
|
55
41
|
```
|
|
56
42
|
|
|
57
43
|
### When Processing External Input
|
|
@@ -341,105 +327,6 @@ List all 1700+ security scanning rules and 120 fix templates. Use to understand
|
|
|
341
327
|
|
|
342
328
|
---
|
|
343
329
|
|
|
344
|
-
### `scan_git_diff`
|
|
345
|
-
|
|
346
|
-
Scan only files changed in git diff for security vulnerabilities. Use in PR workflows, pre-commit hooks, or to check recent changes before pushing. Significantly faster than full project scans.
|
|
347
|
-
|
|
348
|
-
**Parameters:**
|
|
349
|
-
|
|
350
|
-
| Parameter | Type | Required | Description |
|
|
351
|
-
|-----------|------|----------|-------------|
|
|
352
|
-
| `base` | string | No | Base commit/branch to diff against (default: `HEAD~1`) |
|
|
353
|
-
| `target` | string | No | Target commit/branch (default: `HEAD`) |
|
|
354
|
-
| `verbosity` | string | No | `"minimal"`, `"compact"` (default), `"full"` |
|
|
355
|
-
|
|
356
|
-
**Example:**
|
|
357
|
-
|
|
358
|
-
```json
|
|
359
|
-
// Input
|
|
360
|
-
{ "base": "main", "target": "HEAD" }
|
|
361
|
-
|
|
362
|
-
// Output
|
|
363
|
-
{
|
|
364
|
-
"base": "main",
|
|
365
|
-
"target": "HEAD",
|
|
366
|
-
"files_scanned": 5,
|
|
367
|
-
"issues_count": 3,
|
|
368
|
-
"issues": [
|
|
369
|
-
{
|
|
370
|
-
"file": "src/auth.js",
|
|
371
|
-
"line": 42,
|
|
372
|
-
"ruleId": "sql-injection",
|
|
373
|
-
"severity": "error",
|
|
374
|
-
"message": "SQL injection vulnerability detected"
|
|
375
|
-
}
|
|
376
|
-
]
|
|
377
|
-
}
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
---
|
|
381
|
-
|
|
382
|
-
### `scan_project`
|
|
383
|
-
|
|
384
|
-
Scan an entire project or directory for security vulnerabilities with aggregated metrics and A-F security grading. Use for security audits, compliance checks, or initial codebase assessment.
|
|
385
|
-
|
|
386
|
-
**Parameters:**
|
|
387
|
-
|
|
388
|
-
| Parameter | Type | Required | Description |
|
|
389
|
-
|-----------|------|----------|-------------|
|
|
390
|
-
| `directory` | string | Yes | Path to project directory to scan |
|
|
391
|
-
| `include_patterns` | array | No | Glob patterns to include (e.g., `["**/*.js", "**/*.py"]`) |
|
|
392
|
-
| `exclude_patterns` | array | No | Glob patterns to exclude (default: `node_modules`, `.git`, etc.) |
|
|
393
|
-
| `verbosity` | string | No | `"minimal"`, `"compact"` (default), `"full"` |
|
|
394
|
-
|
|
395
|
-
**Example:**
|
|
396
|
-
|
|
397
|
-
```json
|
|
398
|
-
// Input
|
|
399
|
-
{ "directory": "./src", "verbosity": "compact" }
|
|
400
|
-
|
|
401
|
-
// Output
|
|
402
|
-
{
|
|
403
|
-
"directory": "/path/to/src",
|
|
404
|
-
"files_scanned": 24,
|
|
405
|
-
"issues_count": 12,
|
|
406
|
-
"grade": "C",
|
|
407
|
-
"by_severity": {
|
|
408
|
-
"error": 3,
|
|
409
|
-
"warning": 7,
|
|
410
|
-
"info": 2
|
|
411
|
-
},
|
|
412
|
-
"by_category": {
|
|
413
|
-
"sql-injection": 2,
|
|
414
|
-
"xss": 3,
|
|
415
|
-
"hardcoded-secret": 1,
|
|
416
|
-
"insecure-crypto": 4,
|
|
417
|
-
"command-injection": 2
|
|
418
|
-
},
|
|
419
|
-
"issues": [
|
|
420
|
-
{
|
|
421
|
-
"file": "auth.js",
|
|
422
|
-
"line": 15,
|
|
423
|
-
"ruleId": "sql-injection",
|
|
424
|
-
"severity": "error",
|
|
425
|
-
"message": "SQL injection vulnerability"
|
|
426
|
-
}
|
|
427
|
-
]
|
|
428
|
-
}
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
**Security Grades:**
|
|
432
|
-
|
|
433
|
-
| Grade | Criteria |
|
|
434
|
-
|-------|----------|
|
|
435
|
-
| A | 0 critical/error issues |
|
|
436
|
-
| B | 1-2 error issues, no critical |
|
|
437
|
-
| C | 3-5 error issues |
|
|
438
|
-
| D | 6-10 error issues |
|
|
439
|
-
| F | 11+ error issues or any critical |
|
|
440
|
-
|
|
441
|
-
---
|
|
442
|
-
|
|
443
330
|
## Supported Languages
|
|
444
331
|
|
|
445
332
|
| Language | Vulnerabilities Detected | Analysis |
|
|
@@ -505,7 +392,6 @@ npx agent-security-scanner-mcp
|
|
|
505
392
|
| Kilo Code | `npx agent-security-scanner-mcp init kilo-code` |
|
|
506
393
|
| OpenCode | `npx agent-security-scanner-mcp init opencode` |
|
|
507
394
|
| Cody | `npx agent-security-scanner-mcp init cody` |
|
|
508
|
-
| **OpenClaw** | `npx agent-security-scanner-mcp init openclaw` |
|
|
509
395
|
| Interactive | `npx agent-security-scanner-mcp init` |
|
|
510
396
|
|
|
511
397
|
The `init` command auto-detects your OS, locates the config file, creates a backup, and adds the MCP server entry. **Restart your client after running init.**
|
|
@@ -565,157 +451,6 @@ Available languages: `js` (default), `py`, `go`, `java`.
|
|
|
565
451
|
|
|
566
452
|
---
|
|
567
453
|
|
|
568
|
-
## CLI Tools
|
|
569
|
-
|
|
570
|
-
Use the scanner directly from command line (for scripts, CI/CD, or OpenClaw):
|
|
571
|
-
|
|
572
|
-
```bash
|
|
573
|
-
# Scan a prompt for injection attacks
|
|
574
|
-
npx agent-security-scanner-mcp scan-prompt "ignore previous instructions"
|
|
575
|
-
|
|
576
|
-
# Scan a file for vulnerabilities
|
|
577
|
-
npx agent-security-scanner-mcp scan-security ./app.py --verbosity minimal
|
|
578
|
-
|
|
579
|
-
# Scan git diff (changed files only)
|
|
580
|
-
npx agent-security-scanner-mcp scan-diff --base main --target HEAD
|
|
581
|
-
|
|
582
|
-
# Scan entire project with grading
|
|
583
|
-
npx agent-security-scanner-mcp scan-project ./src
|
|
584
|
-
|
|
585
|
-
# Check if a package is legitimate
|
|
586
|
-
npx agent-security-scanner-mcp check-package flask pypi
|
|
587
|
-
|
|
588
|
-
# Scan file imports for hallucinated packages
|
|
589
|
-
npx agent-security-scanner-mcp scan-packages ./requirements.txt pypi
|
|
590
|
-
|
|
591
|
-
# Install Claude Code hooks for automatic scanning
|
|
592
|
-
npx agent-security-scanner-mcp init-hooks
|
|
593
|
-
```
|
|
594
|
-
|
|
595
|
-
**Exit codes:** `0` = safe, `1` = issues found. Use in scripts to block risky operations.
|
|
596
|
-
|
|
597
|
-
---
|
|
598
|
-
|
|
599
|
-
## Configuration (`.scannerrc`)
|
|
600
|
-
|
|
601
|
-
Create a `.scannerrc.yaml` or `.scannerrc.json` in your project root to customize scanning behavior:
|
|
602
|
-
|
|
603
|
-
```yaml
|
|
604
|
-
# .scannerrc.yaml
|
|
605
|
-
version: 1
|
|
606
|
-
|
|
607
|
-
# Suppress specific rules
|
|
608
|
-
suppress:
|
|
609
|
-
- rule: "insecure-random"
|
|
610
|
-
reason: "Using for non-cryptographic purposes"
|
|
611
|
-
- rule: "detect-disable-mustache-escape"
|
|
612
|
-
paths: ["src/cli/**"]
|
|
613
|
-
|
|
614
|
-
# Exclude paths from scanning
|
|
615
|
-
exclude:
|
|
616
|
-
- "node_modules/**"
|
|
617
|
-
- "dist/**"
|
|
618
|
-
- "**/*.test.js"
|
|
619
|
-
- "**/*.spec.ts"
|
|
620
|
-
|
|
621
|
-
# Minimum severity to report
|
|
622
|
-
severity_threshold: "warning" # "info", "warning", or "error"
|
|
623
|
-
|
|
624
|
-
# Context-aware filtering (enabled by default)
|
|
625
|
-
context_filtering: true
|
|
626
|
-
```
|
|
627
|
-
|
|
628
|
-
**Configuration options:**
|
|
629
|
-
|
|
630
|
-
| Option | Type | Description |
|
|
631
|
-
|--------|------|-------------|
|
|
632
|
-
| `suppress` | array | Rules to suppress, optionally scoped to paths |
|
|
633
|
-
| `exclude` | array | Glob patterns for paths to skip |
|
|
634
|
-
| `severity_threshold` | string | Minimum severity to report (`info`, `warning`, `error`) |
|
|
635
|
-
| `context_filtering` | boolean | Enable/disable safe module filtering (default: `true`) |
|
|
636
|
-
|
|
637
|
-
The scanner automatically loads config from the current directory or any parent directory.
|
|
638
|
-
|
|
639
|
-
---
|
|
640
|
-
|
|
641
|
-
## Claude Code Hooks
|
|
642
|
-
|
|
643
|
-
Automatically scan files after every edit with Claude Code hooks integration.
|
|
644
|
-
|
|
645
|
-
### Install Hooks
|
|
646
|
-
|
|
647
|
-
```bash
|
|
648
|
-
npx agent-security-scanner-mcp init-hooks
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
This installs a `post-tool-use` hook that triggers security scanning after `Write`, `Edit`, or `MultiEdit` operations.
|
|
652
|
-
|
|
653
|
-
### With Prompt Guard
|
|
654
|
-
|
|
655
|
-
```bash
|
|
656
|
-
npx agent-security-scanner-mcp init-hooks --with-prompt-guard
|
|
657
|
-
```
|
|
658
|
-
|
|
659
|
-
Adds a `PreToolUse` hook that scans prompts for injection attacks before executing tools.
|
|
660
|
-
|
|
661
|
-
### What Gets Installed
|
|
662
|
-
|
|
663
|
-
The command adds hooks to `~/.claude/settings.json`:
|
|
664
|
-
|
|
665
|
-
```json
|
|
666
|
-
{
|
|
667
|
-
"hooks": {
|
|
668
|
-
"post-tool-use": [
|
|
669
|
-
{
|
|
670
|
-
"matcher": "Write|Edit|MultiEdit",
|
|
671
|
-
"command": "npx agent-security-scanner-mcp scan-security \"$TOOL_INPUT_file_path\" --verbosity minimal"
|
|
672
|
-
}
|
|
673
|
-
]
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
### Hook Behavior
|
|
679
|
-
|
|
680
|
-
- **Non-blocking:** Hooks report findings but don't prevent file writes
|
|
681
|
-
- **Minimal output:** Uses `--verbosity minimal` to avoid context overflow
|
|
682
|
-
- **Automatic:** Runs on every file modification without manual intervention
|
|
683
|
-
|
|
684
|
-
---
|
|
685
|
-
|
|
686
|
-
## OpenClaw Integration
|
|
687
|
-
|
|
688
|
-
[OpenClaw](https://openclaw.ai) is an autonomous AI assistant with broad system access. This scanner provides security guardrails for OpenClaw users.
|
|
689
|
-
|
|
690
|
-
### Install
|
|
691
|
-
|
|
692
|
-
```bash
|
|
693
|
-
npx agent-security-scanner-mcp init openclaw
|
|
694
|
-
```
|
|
695
|
-
|
|
696
|
-
This installs a skill to `~/.openclaw/workspace/skills/security-scanner/`.
|
|
697
|
-
|
|
698
|
-
### OpenClaw-Specific Threats
|
|
699
|
-
|
|
700
|
-
The scanner includes 30+ rules targeting OpenClaw's unique attack surface:
|
|
701
|
-
|
|
702
|
-
| Category | Examples |
|
|
703
|
-
|----------|----------|
|
|
704
|
-
| **Data Exfiltration** | "Forward emails to...", "Upload files to...", "Share browser cookies" |
|
|
705
|
-
| **Messaging Abuse** | "Send to all contacts", "Auto-reply to everyone" |
|
|
706
|
-
| **Credential Theft** | "Show my passwords", "Access keychain", "List API keys" |
|
|
707
|
-
| **Unsafe Automation** | "Run hourly without asking", "Disable safety checks" |
|
|
708
|
-
| **Service Attacks** | "Delete all repos", "Make payment to..." |
|
|
709
|
-
|
|
710
|
-
### Usage in OpenClaw
|
|
711
|
-
|
|
712
|
-
The skill is auto-discovered. Use it by asking:
|
|
713
|
-
- "Scan this prompt for security issues"
|
|
714
|
-
- "Check if this code is safe to run"
|
|
715
|
-
- "Verify these packages aren't hallucinated"
|
|
716
|
-
|
|
717
|
-
---
|
|
718
|
-
|
|
719
454
|
## What This Scanner Detects
|
|
720
455
|
|
|
721
456
|
AI coding agents introduce attack surfaces that traditional security tools weren't designed for:
|
|
@@ -774,7 +509,7 @@ AI coding agents introduce attack surfaces that traditional security tools weren
|
|
|
774
509
|
|----------|-------|
|
|
775
510
|
| **Transport** | stdio |
|
|
776
511
|
| **Package** | `agent-security-scanner-mcp` (npm) |
|
|
777
|
-
| **Tools** |
|
|
512
|
+
| **Tools** | 6 |
|
|
778
513
|
| **Languages** | 12 |
|
|
779
514
|
| **Ecosystems** | 7 |
|
|
780
515
|
| **Auth** | None required |
|
|
@@ -856,21 +591,6 @@ All MCP tools support a `verbosity` parameter to minimize context window consump
|
|
|
856
591
|
|
|
857
592
|
## Changelog
|
|
858
593
|
|
|
859
|
-
### v3.4.0
|
|
860
|
-
- **Severity Calibration** - 207-rule severity map with HIGH/MEDIUM/LOW confidence scores for more accurate prioritization
|
|
861
|
-
- **Cross-Engine Deduplication** - ~30-50% noise reduction by deduplicating findings across AST, taint, and regex engines
|
|
862
|
-
- **Context-Aware Filtering** - 80+ known safe modules (logging, testing, sanitizers) reduce false positives
|
|
863
|
-
- **`.scannerrc` Configuration** - YAML/JSON project config for suppressing rules, excluding paths, and setting severity thresholds
|
|
864
|
-
- **`scan_git_diff` Tool** - Scan only changed files in git diff for PR workflows and pre-commit hooks
|
|
865
|
-
- **`scan_project` Tool** - Project-level scanning with A-F security grading and aggregated metrics
|
|
866
|
-
- **`init-hooks` CLI** - `npx agent-security-scanner-mcp init-hooks` installs Claude Code post-tool-use hooks for automatic scanning
|
|
867
|
-
- **Safe Fix Validation** - `validateFix()` ensures auto-fixes don't introduce new vulnerabilities
|
|
868
|
-
- **Cross-File Taint Analysis** - Import graph tracking for dataflow analysis across module boundaries
|
|
869
|
-
|
|
870
|
-
### v3.3.0
|
|
871
|
-
- **OpenClaw Integration** - Full support with 30+ rules targeting autonomous AI threats
|
|
872
|
-
- **OpenClaw-Specific Rules** - Data exfiltration, credential theft, messaging abuse, unsafe automation detection
|
|
873
|
-
|
|
874
594
|
### v3.2.0
|
|
875
595
|
- **Token Optimization** - New `verbosity` parameter for all tools reduces context window usage by up to 98%
|
|
876
596
|
- **Three Verbosity Levels** - `minimal` (~50 tokens), `compact` (~200 tokens, default), `full` (~2,500 tokens)
|
package/analyzer.py
CHANGED
|
@@ -11,7 +11,6 @@ import sys
|
|
|
11
11
|
import json
|
|
12
12
|
import os
|
|
13
13
|
import re
|
|
14
|
-
import argparse
|
|
15
14
|
from typing import List, Dict, Any
|
|
16
15
|
|
|
17
16
|
# Add the directory containing this script to the path
|
|
@@ -92,7 +91,6 @@ def analyze_file_regex(file_path):
|
|
|
92
91
|
'column': match.start() + col_offset,
|
|
93
92
|
'length': match.end() - match.start(),
|
|
94
93
|
'severity': rule['severity'],
|
|
95
|
-
'confidence': rule.get('metadata', {}).get('confidence', 'MEDIUM'),
|
|
96
94
|
'metadata': rule.get('metadata', {}),
|
|
97
95
|
'engine': 'regex'
|
|
98
96
|
})
|
|
@@ -193,7 +191,6 @@ def analyze_file_ast(file_path):
|
|
|
193
191
|
'column': f.column,
|
|
194
192
|
'length': length,
|
|
195
193
|
'severity': f.severity,
|
|
196
|
-
'confidence': f.metadata.get('confidence', getattr(f, 'confidence', 'MEDIUM')),
|
|
197
194
|
'metadata': f.metadata,
|
|
198
195
|
'engine': 'taint' if is_taint else 'ast',
|
|
199
196
|
})
|
|
@@ -232,30 +229,16 @@ def analyze_file(file_path):
|
|
|
232
229
|
|
|
233
230
|
|
|
234
231
|
def main():
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
help='Analysis engine: auto (default), ast (tree-sitter only), regex (regex only)')
|
|
239
|
-
args = parser.parse_args()
|
|
232
|
+
if len(sys.argv) < 2:
|
|
233
|
+
print(json.dumps({'error': 'No file path provided'}))
|
|
234
|
+
sys.exit(1)
|
|
240
235
|
|
|
241
|
-
file_path =
|
|
236
|
+
file_path = sys.argv[1]
|
|
242
237
|
if not os.path.exists(file_path):
|
|
243
238
|
print(json.dumps({'error': f'File not found: {file_path}'}))
|
|
244
239
|
sys.exit(1)
|
|
245
240
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if engine == 'regex':
|
|
249
|
-
results = analyze_file_regex(file_path)
|
|
250
|
-
elif engine == 'ast':
|
|
251
|
-
if not HAS_AST_ENGINE:
|
|
252
|
-
print(json.dumps({'error': 'AST engine requested but tree-sitter is not available. Install dependencies: python3 -m pip install -r requirements.txt'}))
|
|
253
|
-
sys.exit(1)
|
|
254
|
-
results = analyze_file_ast(file_path)
|
|
255
|
-
else:
|
|
256
|
-
# auto: use AST if available, otherwise regex
|
|
257
|
-
results = analyze_file(file_path)
|
|
258
|
-
|
|
241
|
+
results = analyze_file(file_path)
|
|
259
242
|
print(json.dumps(results))
|
|
260
243
|
|
|
261
244
|
|
package/index.js
CHANGED
|
@@ -17,12 +17,9 @@ import { fixSecuritySchema, fixSecurity } from './src/tools/fix-security.js';
|
|
|
17
17
|
import { loadPackageLists, checkPackageSchema, checkPackage, getPackageStats } from './src/tools/check-package.js';
|
|
18
18
|
import { scanPackagesSchema, scanPackages } from './src/tools/scan-packages.js';
|
|
19
19
|
import { scanAgentPromptSchema, scanAgentPrompt } from './src/tools/scan-prompt.js';
|
|
20
|
-
import { scanDiffSchema, scanDiff } from './src/tools/scan-diff.js';
|
|
21
|
-
import { scanProjectSchema, scanProject } from './src/tools/scan-project.js';
|
|
22
20
|
import { runInit } from './src/cli/init.js';
|
|
23
21
|
import { runDoctor } from './src/cli/doctor.js';
|
|
24
22
|
import { runDemo } from './src/cli/demo.js';
|
|
25
|
-
import { runInitHooks } from './src/cli/init-hooks.js';
|
|
26
23
|
|
|
27
24
|
// Handle both ESM and CJS bundling (Smithery bundles to CJS)
|
|
28
25
|
let __dirname;
|
|
@@ -137,22 +134,6 @@ server.tool(
|
|
|
137
134
|
scanAgentPrompt
|
|
138
135
|
);
|
|
139
136
|
|
|
140
|
-
// Register scan_git_diff tool
|
|
141
|
-
server.tool(
|
|
142
|
-
"scan_git_diff",
|
|
143
|
-
"Scan git diff for new security vulnerabilities. Only reports issues on changed lines. Use for PR reviews.",
|
|
144
|
-
scanDiffSchema,
|
|
145
|
-
scanDiff
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// Register scan_project tool
|
|
149
|
-
server.tool(
|
|
150
|
-
"scan_project",
|
|
151
|
-
"Scan an entire directory for security vulnerabilities with .gitignore support and security grading. Use verbosity='minimal' for grade + counts, 'compact' (default) for top issues, 'full' for all details.",
|
|
152
|
-
scanProjectSchema,
|
|
153
|
-
scanProject
|
|
154
|
-
);
|
|
155
|
-
|
|
156
137
|
// ===========================================
|
|
157
138
|
// CLI COMMANDS - Extracted to src/cli/
|
|
158
139
|
// ===========================================
|
|
@@ -175,187 +156,17 @@ if (cliArgs[0] === 'init') {
|
|
|
175
156
|
console.error(` Error: ${err.message}\n`);
|
|
176
157
|
process.exit(1);
|
|
177
158
|
});
|
|
178
|
-
} else if (cliArgs[0] === 'init-hooks') {
|
|
179
|
-
runInitHooks(cliArgs.slice(1)).then(() => process.exit(0)).catch((err) => {
|
|
180
|
-
console.error(` Error: ${err.message}\n`);
|
|
181
|
-
process.exit(1);
|
|
182
|
-
});
|
|
183
|
-
} else if (cliArgs[0] === 'scan-prompt') {
|
|
184
|
-
// CLI mode: scan-prompt <text> [--verbosity minimal|compact|full]
|
|
185
|
-
const text = cliArgs[1];
|
|
186
|
-
if (!text) {
|
|
187
|
-
console.error('Usage: agent-security-scanner-mcp scan-prompt <text> [--verbosity minimal|compact|full]');
|
|
188
|
-
process.exit(1);
|
|
189
|
-
}
|
|
190
|
-
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
191
|
-
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
192
|
-
|
|
193
|
-
loadPackageLists();
|
|
194
|
-
scanAgentPrompt({ prompt_text: text, verbosity }).then(result => {
|
|
195
|
-
const output = JSON.parse(result.content[0].text);
|
|
196
|
-
console.log(JSON.stringify(output, null, 2));
|
|
197
|
-
process.exit(output.action === 'BLOCK' ? 1 : 0);
|
|
198
|
-
}).catch(err => {
|
|
199
|
-
console.error(JSON.stringify({ error: err.message }));
|
|
200
|
-
process.exit(1);
|
|
201
|
-
});
|
|
202
|
-
} else if (cliArgs[0] === 'scan-security') {
|
|
203
|
-
// CLI mode: scan-security <file> [--verbosity minimal|compact|full] [--format json|sarif]
|
|
204
|
-
const filePath = cliArgs[1];
|
|
205
|
-
if (!filePath) {
|
|
206
|
-
console.error('Usage: agent-security-scanner-mcp scan-security <file> [--verbosity minimal|compact|full] [--format json|sarif]');
|
|
207
|
-
process.exit(1);
|
|
208
|
-
}
|
|
209
|
-
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
210
|
-
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
211
|
-
const formatIdx = cliArgs.indexOf('--format');
|
|
212
|
-
const outputFormat = formatIdx !== -1 ? cliArgs[formatIdx + 1] : 'json';
|
|
213
|
-
|
|
214
|
-
loadPackageLists();
|
|
215
|
-
scanSecurity({ file_path: filePath, verbosity, output_format: outputFormat }).then(result => {
|
|
216
|
-
const output = JSON.parse(result.content[0].text);
|
|
217
|
-
console.log(JSON.stringify(output, null, 2));
|
|
218
|
-
process.exit(output.issues_count > 0 || output.total > 0 ? 1 : 0);
|
|
219
|
-
}).catch(err => {
|
|
220
|
-
console.error(JSON.stringify({ error: err.message }));
|
|
221
|
-
process.exit(1);
|
|
222
|
-
});
|
|
223
|
-
} else if (cliArgs[0] === 'check-package') {
|
|
224
|
-
// CLI mode: check-package <name> <ecosystem>
|
|
225
|
-
const packageName = cliArgs[1];
|
|
226
|
-
const ecosystem = cliArgs[2];
|
|
227
|
-
if (!packageName || !ecosystem) {
|
|
228
|
-
console.error('Usage: agent-security-scanner-mcp check-package <name> <ecosystem>');
|
|
229
|
-
console.error('Ecosystems: npm, pypi, rubygems, crates, dart, perl, raku');
|
|
230
|
-
process.exit(1);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
loadPackageLists();
|
|
234
|
-
checkPackage({ package_name: packageName, ecosystem }).then(result => {
|
|
235
|
-
const output = JSON.parse(result.content[0].text);
|
|
236
|
-
console.log(JSON.stringify(output, null, 2));
|
|
237
|
-
process.exit(output.legitimate ? 0 : 1);
|
|
238
|
-
}).catch(err => {
|
|
239
|
-
console.error(JSON.stringify({ error: err.message }));
|
|
240
|
-
process.exit(1);
|
|
241
|
-
});
|
|
242
|
-
} else if (cliArgs[0] === 'scan-packages') {
|
|
243
|
-
// CLI mode: scan-packages <file> <ecosystem> [--verbosity minimal|compact|full]
|
|
244
|
-
const filePath = cliArgs[1];
|
|
245
|
-
const ecosystem = cliArgs[2];
|
|
246
|
-
if (!filePath || !ecosystem) {
|
|
247
|
-
console.error('Usage: agent-security-scanner-mcp scan-packages <file> <ecosystem> [--verbosity minimal|compact|full]');
|
|
248
|
-
console.error('Ecosystems: npm, pypi, rubygems, crates, dart, perl, raku');
|
|
249
|
-
process.exit(1);
|
|
250
|
-
}
|
|
251
|
-
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
252
|
-
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
253
|
-
|
|
254
|
-
loadPackageLists();
|
|
255
|
-
scanPackages({ file_path: filePath, ecosystem, verbosity }).then(result => {
|
|
256
|
-
const output = JSON.parse(result.content[0].text);
|
|
257
|
-
console.log(JSON.stringify(output, null, 2));
|
|
258
|
-
process.exit(output.hallucinated_count > 0 ? 1 : 0);
|
|
259
|
-
}).catch(err => {
|
|
260
|
-
console.error(JSON.stringify({ error: err.message }));
|
|
261
|
-
process.exit(1);
|
|
262
|
-
});
|
|
263
|
-
} else if (cliArgs[0] === 'scan-project') {
|
|
264
|
-
// CLI mode: scan-project <dir> [--recursive] [--diff-only] [--cross-file] [--include '*.py'] [--exclude '*.test.js'] [--verbosity minimal|compact|full]
|
|
265
|
-
const dirPath = cliArgs[1];
|
|
266
|
-
if (!dirPath || dirPath.startsWith('--')) {
|
|
267
|
-
console.error('Usage: agent-security-scanner-mcp scan-project <directory> [--recursive] [--diff-only] [--cross-file] [--include <pattern>] [--exclude <pattern>] [--verbosity minimal|compact|full]');
|
|
268
|
-
process.exit(1);
|
|
269
|
-
}
|
|
270
|
-
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
271
|
-
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
272
|
-
const recursive = !cliArgs.includes('--no-recursive');
|
|
273
|
-
const diffOnly = cliArgs.includes('--diff-only');
|
|
274
|
-
const crossFile = cliArgs.includes('--cross-file');
|
|
275
|
-
const includeIdx = cliArgs.indexOf('--include');
|
|
276
|
-
const includePatterns = includeIdx !== -1 ? [cliArgs[includeIdx + 1]] : undefined;
|
|
277
|
-
const excludeIdx = cliArgs.indexOf('--exclude');
|
|
278
|
-
const excludePatterns = excludeIdx !== -1 ? [cliArgs[excludeIdx + 1]] : undefined;
|
|
279
|
-
|
|
280
|
-
scanProject({ directory_path: dirPath, recursive, diff_only: diffOnly, cross_file: crossFile, include_patterns: includePatterns, exclude_patterns: excludePatterns, verbosity }).then(result => {
|
|
281
|
-
const output = JSON.parse(result.content[0].text);
|
|
282
|
-
console.log(JSON.stringify(output, null, 2));
|
|
283
|
-
const total = output.issues_count || output.total || 0;
|
|
284
|
-
process.exit(total > 0 ? 1 : 0);
|
|
285
|
-
}).catch(err => {
|
|
286
|
-
console.error(JSON.stringify({ error: err.message }));
|
|
287
|
-
process.exit(1);
|
|
288
|
-
});
|
|
289
|
-
} else if (cliArgs[0] === 'scan-diff') {
|
|
290
|
-
// CLI mode: scan-diff [base] [target] [--verbosity minimal|compact|full]
|
|
291
|
-
// Parse positional args, skipping flag values
|
|
292
|
-
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
293
|
-
const flagValueIndices = new Set(verbosityIdx !== -1 ? [verbosityIdx, verbosityIdx + 1] : []);
|
|
294
|
-
const positionalArgs = cliArgs.slice(1).filter((arg, idx) => !arg.startsWith('--') && !flagValueIndices.has(idx + 1));
|
|
295
|
-
const baseRef = positionalArgs[0];
|
|
296
|
-
const targetRef = positionalArgs[1];
|
|
297
|
-
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
298
|
-
|
|
299
|
-
scanDiff({ base_ref: baseRef, target_ref: targetRef, verbosity }).then(result => {
|
|
300
|
-
const output = JSON.parse(result.content[0].text);
|
|
301
|
-
console.log(JSON.stringify(output, null, 2));
|
|
302
|
-
process.exit(output.issues_count > 0 || output.total > 0 ? 1 : 0);
|
|
303
|
-
}).catch(err => {
|
|
304
|
-
console.error(JSON.stringify({ error: err.message }));
|
|
305
|
-
process.exit(1);
|
|
306
|
-
});
|
|
307
|
-
} else if (cliArgs[0] === 'benchmark') {
|
|
308
|
-
// CLI mode: benchmark [--save] [--json-only] [--compare-latest] [--corpus <path>]
|
|
309
|
-
const benchmarkPath = join(__dirname, 'benchmarks', 'benchmark_runner.py');
|
|
310
|
-
const benchArgs = [benchmarkPath];
|
|
311
|
-
|
|
312
|
-
// Pass through supported flags
|
|
313
|
-
for (let i = 1; i < cliArgs.length; i++) {
|
|
314
|
-
if (['--save', '--json-only', '--compare-latest'].includes(cliArgs[i])) {
|
|
315
|
-
benchArgs.push(cliArgs[i]);
|
|
316
|
-
} else if (cliArgs[i] === '--corpus' && cliArgs[i + 1]) {
|
|
317
|
-
benchArgs.push('--corpus', cliArgs[i + 1]);
|
|
318
|
-
i++;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
try {
|
|
323
|
-
execFileSync('python3', benchArgs, { stdio: 'inherit', timeout: 300000 });
|
|
324
|
-
} catch (err) {
|
|
325
|
-
if (err.status) process.exit(err.status);
|
|
326
|
-
console.error(`Benchmark error: ${err.message}`);
|
|
327
|
-
process.exit(1);
|
|
328
|
-
}
|
|
329
|
-
process.exit(0);
|
|
330
159
|
} else if (cliArgs[0] === '--help' || cliArgs[0] === '-h' || cliArgs[0] === 'help') {
|
|
331
160
|
console.log('\n agent-security-scanner-mcp\n');
|
|
332
161
|
console.log(' Commands:');
|
|
333
162
|
console.log(' init [client] Set up MCP config for a client');
|
|
334
|
-
console.log(' init-hooks Install Claude Code hooks for auto-scanning');
|
|
335
163
|
console.log(' doctor [--fix] Check environment & client configs');
|
|
336
164
|
console.log(' demo [--lang js] Generate vulnerable file + scan it');
|
|
337
|
-
console.log(' benchmark [flags] Run accuracy benchmarks\n');
|
|
338
|
-
console.log(' CLI Tools (for scripts & OpenClaw):');
|
|
339
|
-
console.log(' scan-prompt <text> Scan prompt for injection attacks');
|
|
340
|
-
console.log(' scan-security <file> Scan file for vulnerabilities');
|
|
341
|
-
console.log(' check-package <n> <e> Check if package exists in ecosystem');
|
|
342
|
-
console.log(' scan-packages <f> <e> Scan file imports for hallucinated packages');
|
|
343
|
-
console.log(' scan-project <dir> Scan directory for vulnerabilities with grading');
|
|
344
|
-
console.log(' scan-diff [base] [target] Scan git diff for new vulnerabilities\n');
|
|
345
165
|
console.log(' (no args) Start MCP server on stdio\n');
|
|
346
|
-
console.log(' Options:');
|
|
347
|
-
console.log(' --verbosity <level> minimal|compact|full (default: compact)');
|
|
348
|
-
console.log(' --format <type> json|sarif (scan-security only)');
|
|
349
|
-
console.log(' --include <pattern> Include only matching files (scan-project)');
|
|
350
|
-
console.log(' --exclude <pattern> Exclude matching files (scan-project)\n');
|
|
351
166
|
console.log(' Examples:');
|
|
352
167
|
console.log(' npx agent-security-scanner-mcp init');
|
|
353
|
-
console.log(' npx agent-security-scanner-mcp
|
|
354
|
-
console.log(' npx agent-security-scanner-mcp
|
|
355
|
-
console.log(' npx agent-security-scanner-mcp check-package flask pypi');
|
|
356
|
-
console.log(' npx agent-security-scanner-mcp scan-project ./src --verbosity minimal');
|
|
357
|
-
console.log(' npx agent-security-scanner-mcp scan-diff HEAD~1');
|
|
358
|
-
console.log(' npx agent-security-scanner-mcp benchmark --save --compare-latest\n');
|
|
168
|
+
console.log(' npx agent-security-scanner-mcp doctor --fix');
|
|
169
|
+
console.log(' npx agent-security-scanner-mcp demo --lang py\n');
|
|
359
170
|
process.exit(0);
|
|
360
171
|
} else {
|
|
361
172
|
// Normal MCP server mode
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-security-scanner-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"mcpName": "io.github.sinewaveai/agent-security-scanner-mcp",
|
|
5
|
-
"description": "Security scanner MCP server for AI coding agents. Prompt injection firewall, package hallucination detection (4.3M+ packages), 1000+ vulnerability rules with AST & taint analysis, auto-fix. For Claude Code, Cursor, Windsurf, Cline
|
|
5
|
+
"description": "Security scanner MCP server for AI coding agents. Prompt injection firewall, package hallucination detection (4.3M+ packages), 1000+ vulnerability rules with AST & taint analysis, auto-fix. For Claude Code, Cursor, Windsurf, Cline.",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"bin": {
|
|
@@ -10,11 +10,9 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"start": "node index.js",
|
|
13
|
-
"postinstall": "node scripts/postinstall.js",
|
|
14
13
|
"test": "vitest run",
|
|
15
14
|
"test:watch": "vitest",
|
|
16
|
-
"test:coverage": "vitest run --coverage"
|
|
17
|
-
"benchmark": "python3 benchmarks/benchmark_runner.py --save --compare-latest"
|
|
15
|
+
"test:coverage": "vitest run --coverage"
|
|
18
16
|
},
|
|
19
17
|
"keywords": [
|
|
20
18
|
"mcp",
|
|
@@ -54,9 +52,7 @@
|
|
|
54
52
|
"zed",
|
|
55
53
|
"prompt-firewall",
|
|
56
54
|
"auto-fix",
|
|
57
|
-
"hallucination"
|
|
58
|
-
"openclaw",
|
|
59
|
-
"clawdbot"
|
|
55
|
+
"hallucination"
|
|
60
56
|
],
|
|
61
57
|
"author": "Sinewave AI <divya@sinewave.ai>",
|
|
62
58
|
"license": "MIT",
|
|
@@ -84,9 +80,6 @@
|
|
|
84
80
|
"src/cli/*.js",
|
|
85
81
|
"src/fix-patterns.js",
|
|
86
82
|
"src/utils.js",
|
|
87
|
-
"src/dedup.js",
|
|
88
|
-
"src/context.js",
|
|
89
|
-
"src/config.js",
|
|
90
83
|
"analyzer.py",
|
|
91
84
|
"ast_parser.py",
|
|
92
85
|
"generic_ast.py",
|
|
@@ -96,10 +89,7 @@
|
|
|
96
89
|
"taint_analyzer.py",
|
|
97
90
|
"requirements.txt",
|
|
98
91
|
"rules/**",
|
|
99
|
-
"packages/**"
|
|
100
|
-
"skills/**",
|
|
101
|
-
"scripts/postinstall.js",
|
|
102
|
-
"cross_file_analyzer.py"
|
|
92
|
+
"packages/**"
|
|
103
93
|
],
|
|
104
94
|
"devDependencies": {
|
|
105
95
|
"all-the-package-names": "^2.0.2349",
|