agent-security-scanner-mcp 2.0.4 → 2.0.6
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 +82 -46
- package/index.js +295 -9
- package/package.json +1 -1
- package/packages/crates.txt +0 -1
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# agent-security-scanner-mcp
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/agent-security-scanner-mcp)
|
|
4
|
+
[](https://www.npmjs.com/package/agent-security-scanner-mcp)
|
|
5
|
+
[](https://www.npmjs.com/package/agent-security-scanner-mcp)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://github.com/sinewaveai/agent-security-scanner-mcp/stargazers)
|
|
8
|
+
|
|
3
9
|
A powerful MCP (Model Context Protocol) server for real-time security vulnerability scanning. Integrates with Claude Desktop, Claude Code, OpenCode.ai, Kilo Code, and any MCP-compatible client to automatically detect and fix security issues as you code.
|
|
4
10
|
|
|
5
11
|
AI coding agents like **Claude Code**, **Cursor**, **Windsurf**, **Cline**, **Copilot**, and **Devin** are transforming software development. But they introduce attack surfaces that traditional security tools weren't designed to handle:
|
|
@@ -14,22 +20,35 @@ AI coding agents like **Claude Code**, **Cursor**, **Windsurf**, **Cline**, **Co
|
|
|
14
20
|
|
|
15
21
|
**359 Semgrep-aligned security rules | 120 auto-fix templates | 6 ecosystems indexed | AI Agent prompt security**
|
|
16
22
|
|
|
17
|
-
##
|
|
23
|
+
## Installation
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
- **Code block scanning** - Detects attacks hidden inside markdown code blocks
|
|
21
|
-
- **Base64 decode-and-rescan** - Runtime decoding of encoded payloads
|
|
22
|
-
- **Security fix** - Command injection vulnerability patched (execFileSync)
|
|
23
|
-
- **Test suite** - 51 vitest tests with GitHub Actions CI
|
|
24
|
-
- **Bug fixes** - Package hallucination detection now correctly uses bloom filters
|
|
25
|
+
### Default Package (Lightweight - 2.7 MB)
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g agent-security-scanner-mcp
|
|
29
|
+
```
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
Includes hallucination detection for: **PyPI, RubyGems, crates.io, pub.dev, CPAN, raku.land** (1M+ packages)
|
|
32
|
+
|
|
33
|
+
### Full Package (With npm - 8.7 MB)
|
|
34
|
+
|
|
35
|
+
If you need **npm/JavaScript hallucination detection** (3.3M packages):
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install -g agent-security-scanner-mcp-full
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or run directly with npx:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx agent-security-scanner-mcp
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Prerequisites
|
|
48
|
+
|
|
49
|
+
- **Node.js >= 18.0.0** (required)
|
|
50
|
+
- **Python 3.x** (required for the analyzer engine)
|
|
51
|
+
- **PyYAML** (`pip install pyyaml`) — required for rule loading
|
|
33
52
|
|
|
34
53
|
### Enhanced Detection with tree-sitter (Optional)
|
|
35
54
|
|
|
@@ -44,6 +63,40 @@ The scanner works without tree-sitter using regex-based detection, but AST analy
|
|
|
44
63
|
- Taint tracking across function boundaries
|
|
45
64
|
- Language-aware pattern matching
|
|
46
65
|
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## What's New in v2.0.6
|
|
69
|
+
|
|
70
|
+
- **fix_security reliability overhaul** - Fixes now validated before applying to prevent malformed code output
|
|
71
|
+
- **Python f-string SQL injection** - Now detects AND fixes `f"SELECT...{var}"` patterns
|
|
72
|
+
- **Python .format() SQL injection** - Now fixes `"SELECT...{}".format(var)` patterns
|
|
73
|
+
- **JavaScript template literal SQL injection** - Now fixes `` `SELECT...${var}` `` patterns
|
|
74
|
+
- **Multi-pattern fix engine** - Each vulnerability type can have multiple language-specific fix patterns
|
|
75
|
+
- **Syntax validation** - Rejects fixes with unbalanced quotes, brackets, or obvious syntax errors
|
|
76
|
+
|
|
77
|
+
## What's New in v2.0.5
|
|
78
|
+
|
|
79
|
+
- **Claude Code per-project fix** - `init claude-code` now uses `claude mcp add` CLI for reliable per-project configuration
|
|
80
|
+
- **Doctor command upgrade** - Now correctly checks Claude Code config via `claude mcp list` instead of file-based check
|
|
81
|
+
- **Documentation update** - README clarifies Claude Code's per-project MCP storage (`~/.claude.json` vs `~/.claude/settings.json`)
|
|
82
|
+
|
|
83
|
+
## What's New in v2.0.2
|
|
84
|
+
|
|
85
|
+
- **Prompt injection detection overhaul** - Detection rate improved from 33% to 80%+
|
|
86
|
+
- **Code block scanning** - Detects attacks hidden inside markdown code blocks
|
|
87
|
+
- **Base64 decode-and-rescan** - Runtime decoding of encoded payloads
|
|
88
|
+
- **Security fix** - Command injection vulnerability patched (execFileSync)
|
|
89
|
+
- **Test suite** - 51 vitest tests with GitHub Actions CI
|
|
90
|
+
- **Bug fixes** - Package hallucination detection now correctly uses bloom filters
|
|
91
|
+
|
|
92
|
+
## What's New in v2.0.0
|
|
93
|
+
|
|
94
|
+
- **AST-based analysis** - tree-sitter powered parsing for 12 languages with higher accuracy
|
|
95
|
+
- **Taint analysis** - Track data flow from sources (user input) to sinks (dangerous functions)
|
|
96
|
+
- **Graceful fallback** - Works out-of-the-box with regex; enhanced detection when tree-sitter installed
|
|
97
|
+
- **Metavariable patterns** - Semgrep-style `$VAR` patterns for structural matching
|
|
98
|
+
- **Doctor command upgrade** - Now checks for AST engine availability
|
|
99
|
+
|
|
47
100
|
## What's New in v1.5.0
|
|
48
101
|
|
|
49
102
|
- **92% smaller package** - Only 2.7 MB (down from 84 MB)
|
|
@@ -76,37 +129,6 @@ The scanner works without tree-sitter using regex-based detection, but AST analy
|
|
|
76
129
|
- **CWE & OWASP mapped** - Every rule includes CWE and OWASP references
|
|
77
130
|
- **Hallucination detection** - Detect AI-invented package names across 7 ecosystems via bloom filters and text lists
|
|
78
131
|
|
|
79
|
-
## Installation
|
|
80
|
-
|
|
81
|
-
### Default Package (Lightweight - 2.7 MB)
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
npm install -g agent-security-scanner-mcp
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
Includes hallucination detection for: **PyPI, RubyGems, crates.io, pub.dev, CPAN, raku.land** (1M+ packages)
|
|
88
|
-
|
|
89
|
-
### Full Package (With npm - 8.7 MB)
|
|
90
|
-
|
|
91
|
-
If you need **npm/JavaScript hallucination detection** (3.3M packages):
|
|
92
|
-
|
|
93
|
-
```bash
|
|
94
|
-
npm install -g agent-security-scanner-mcp-full
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
Or run directly with npx:
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
npx agent-security-scanner-mcp
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Prerequisites
|
|
104
|
-
|
|
105
|
-
- **Node.js >= 18.0.0** (required)
|
|
106
|
-
- **Python 3.x** (required for the analyzer engine)
|
|
107
|
-
- **PyYAML** (`pip install pyyaml`) — required for rule loading
|
|
108
|
-
- **tree-sitter** (optional, for enhanced AST-based detection): `pip install tree-sitter tree-sitter-python tree-sitter-javascript`
|
|
109
|
-
|
|
110
132
|
## Works With All Major AI Coding Tools
|
|
111
133
|
|
|
112
134
|
| Tool | Integration | Status |
|
|
@@ -139,12 +161,14 @@ npx agent-security-scanner-mcp init cursor
|
|
|
139
161
|
npx agent-security-scanner-mcp init claude-desktop
|
|
140
162
|
npx agent-security-scanner-mcp init windsurf
|
|
141
163
|
npx agent-security-scanner-mcp init cline
|
|
142
|
-
npx agent-security-scanner-mcp init claude-code
|
|
164
|
+
npx agent-security-scanner-mcp init claude-code # Run in each project folder!
|
|
143
165
|
npx agent-security-scanner-mcp init kilo-code
|
|
144
166
|
npx agent-security-scanner-mcp init opencode
|
|
145
167
|
npx agent-security-scanner-mcp init cody
|
|
146
168
|
```
|
|
147
169
|
|
|
170
|
+
> **Claude Code users:** Run `init claude-code` in **each project folder** where you want security scanning. Claude Code uses per-project MCP configuration.
|
|
171
|
+
|
|
148
172
|
**Interactive mode** — just run `init` with no client to pick from a list:
|
|
149
173
|
|
|
150
174
|
```bash
|
|
@@ -234,7 +258,17 @@ Add to your `claude_desktop_config.json`:
|
|
|
234
258
|
|
|
235
259
|
### Claude Code
|
|
236
260
|
|
|
237
|
-
|
|
261
|
+
**Important:** Claude Code stores MCP servers **per-project** in `~/.claude.json`, not in `~/.claude/settings.json`. Use the CLI to configure:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# Run this in EACH project folder where you want security scanning:
|
|
265
|
+
claude mcp add security-scanner -- npx -y agent-security-scanner-mcp
|
|
266
|
+
|
|
267
|
+
# Verify it's configured:
|
|
268
|
+
claude mcp list
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Global configuration** (applies to new projects only) — add to `~/.claude/settings.json`:
|
|
238
272
|
|
|
239
273
|
```json
|
|
240
274
|
{
|
|
@@ -247,6 +281,8 @@ Add to your MCP settings (`~/.claude/settings.json`):
|
|
|
247
281
|
}
|
|
248
282
|
```
|
|
249
283
|
|
|
284
|
+
> **Note:** Existing projects won't automatically inherit from the global config. You must run `claude mcp add` in each project folder, or use the automated init command which handles this for you.
|
|
285
|
+
|
|
250
286
|
### OpenCode.ai
|
|
251
287
|
|
|
252
288
|
Add to your `opencode.jsonc` configuration file:
|
package/index.js
CHANGED
|
@@ -28,7 +28,73 @@ const FIX_TEMPLATES = {
|
|
|
28
28
|
// ===========================================
|
|
29
29
|
"sql-injection": {
|
|
30
30
|
description: "Use parameterized queries instead of string concatenation",
|
|
31
|
-
|
|
31
|
+
patterns: [
|
|
32
|
+
// Python f-strings: cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
|
|
33
|
+
{
|
|
34
|
+
match: /f["'].*(?:SELECT|INSERT|UPDATE|DELETE).*\{(\w+)\}.*["']/i,
|
|
35
|
+
fix: (line) => {
|
|
36
|
+
// Extract the query and variable
|
|
37
|
+
const match = line.match(/(\w+\.(?:execute|query|run))\s*\(\s*f(["'])(.*?)(?:SELECT|INSERT|UPDATE|DELETE)(.*?)\{(\w+)\}(.*?)\2/i);
|
|
38
|
+
if (match) {
|
|
39
|
+
const [, method, quote, prefix, queryStart, varName, suffix] = match;
|
|
40
|
+
// Reconstruct as parameterized query
|
|
41
|
+
const cleanPrefix = prefix.replace(/\{[^}]+\}/g, '?');
|
|
42
|
+
const cleanSuffix = suffix.replace(/\{[^}]+\}/g, '?');
|
|
43
|
+
return line.replace(
|
|
44
|
+
/f(["']).*\1/,
|
|
45
|
+
`"${cleanPrefix}${queryStart.trim().toUpperCase()}${cleanSuffix}?", (${varName},)`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
// Simpler fallback for f-strings
|
|
49
|
+
return line.replace(/f(["'])(.*?)\{(\w+)\}(.*?)\1/, '"$2?$4", ($3,)');
|
|
50
|
+
},
|
|
51
|
+
languages: ['python']
|
|
52
|
+
},
|
|
53
|
+
// Python .format(): "SELECT ... WHERE id = {}".format(user_id)
|
|
54
|
+
{
|
|
55
|
+
match: /["'].*(?:SELECT|INSERT|UPDATE|DELETE).*\{\}.*["']\.format\s*\(/i,
|
|
56
|
+
fix: (line) => {
|
|
57
|
+
return line.replace(
|
|
58
|
+
/(["'])(.*?)\{\}(.*?)\1\.format\s*\(\s*(\w+)\s*\)/,
|
|
59
|
+
'"$2?$3", [$4]'
|
|
60
|
+
);
|
|
61
|
+
},
|
|
62
|
+
languages: ['python']
|
|
63
|
+
},
|
|
64
|
+
// Python % formatting: "SELECT ... WHERE id = %s" % user_id
|
|
65
|
+
{
|
|
66
|
+
match: /["'].*(?:SELECT|INSERT|UPDATE|DELETE).*%s.*["']\s*%\s*\(/i,
|
|
67
|
+
fix: (line) => {
|
|
68
|
+
return line.replace(
|
|
69
|
+
/(["'])(.*?)%s(.*?)\1\s*%\s*\(\s*(\w+)\s*,?\s*\)/,
|
|
70
|
+
'"$2?$3", [$4]'
|
|
71
|
+
);
|
|
72
|
+
},
|
|
73
|
+
languages: ['python']
|
|
74
|
+
},
|
|
75
|
+
// JS template literals: `SELECT * FROM users WHERE id = ${userId}`
|
|
76
|
+
{
|
|
77
|
+
match: /`.*(?:SELECT|INSERT|UPDATE|DELETE).*\$\{.*\}.*`/i,
|
|
78
|
+
fix: (line) => {
|
|
79
|
+
return line.replace(
|
|
80
|
+
/`(.*?)\$\{(\w+)\}(.*?)`/,
|
|
81
|
+
'"$1?$3", [$2]'
|
|
82
|
+
);
|
|
83
|
+
},
|
|
84
|
+
languages: ['javascript', 'typescript']
|
|
85
|
+
},
|
|
86
|
+
// Simple concatenation (no quotes inside): "SELECT ... WHERE id = " + userId
|
|
87
|
+
{
|
|
88
|
+
match: /["'](?:SELECT|INSERT|UPDATE|DELETE)[^"']+["']\s*\+\s*\w+(?!\s*\+\s*["'])/i,
|
|
89
|
+
fix: (line) => {
|
|
90
|
+
return line.replace(
|
|
91
|
+
/(["'])((?:SELECT|INSERT|UPDATE|DELETE)[^"']+)\1\s*\+\s*(\w+)/i,
|
|
92
|
+
'"$2?", [$3]'
|
|
93
|
+
);
|
|
94
|
+
},
|
|
95
|
+
languages: ['javascript', 'python', 'java', 'go', 'ruby', 'php']
|
|
96
|
+
}
|
|
97
|
+
]
|
|
32
98
|
},
|
|
33
99
|
"nosql-injection": {
|
|
34
100
|
description: "Sanitize MongoDB query inputs",
|
|
@@ -765,17 +831,96 @@ function runAnalyzer(filePath) {
|
|
|
765
831
|
}
|
|
766
832
|
}
|
|
767
833
|
|
|
834
|
+
// Validate that a fix produces valid syntax
|
|
835
|
+
function validateFix(original, fixed, language) {
|
|
836
|
+
// Rule 1: Fix must be different from original
|
|
837
|
+
if (fixed === original || !fixed) {
|
|
838
|
+
return { valid: false, reason: 'no_change' };
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// Rule 2: Balanced quotes (ignore escaped quotes)
|
|
842
|
+
const unescaped = fixed.replace(/\\["'`]/g, '');
|
|
843
|
+
const singleQuotes = (unescaped.match(/'/g) || []).length;
|
|
844
|
+
const doubleQuotes = (unescaped.match(/"/g) || []).length;
|
|
845
|
+
const backticks = (unescaped.match(/`/g) || []).length;
|
|
846
|
+
if (singleQuotes % 2 !== 0 || doubleQuotes % 2 !== 0 || backticks % 2 !== 0) {
|
|
847
|
+
return { valid: false, reason: 'unbalanced_quotes' };
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// Rule 3: Balanced brackets
|
|
851
|
+
const brackets = { '(': 0, '[': 0, '{': 0 };
|
|
852
|
+
const closers = { ')': '(', ']': '[', '}': '{' };
|
|
853
|
+
for (const char of unescaped) {
|
|
854
|
+
if (brackets[char] !== undefined) brackets[char]++;
|
|
855
|
+
if (closers[char]) brackets[closers[char]]--;
|
|
856
|
+
}
|
|
857
|
+
if (Object.values(brackets).some(v => v !== 0)) {
|
|
858
|
+
return { valid: false, reason: 'unbalanced_brackets' };
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// Rule 4: No obvious syntax errors
|
|
862
|
+
const badPatterns = [
|
|
863
|
+
/""[^,\s\]);}]/, // empty string followed by unexpected char
|
|
864
|
+
/\+\s*[)\]}]/, // + followed by closing bracket
|
|
865
|
+
/,\s*\+/, // comma followed by +
|
|
866
|
+
/\(\s*\+/, // open paren followed by +
|
|
867
|
+
];
|
|
868
|
+
for (const pattern of badPatterns) {
|
|
869
|
+
if (pattern.test(fixed)) {
|
|
870
|
+
return { valid: false, reason: 'syntax_error' };
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
return { valid: true };
|
|
875
|
+
}
|
|
876
|
+
|
|
768
877
|
// Generate fix suggestion for an issue
|
|
769
878
|
function generateFix(issue, line, language) {
|
|
770
879
|
const ruleId = issue.ruleId.toLowerCase();
|
|
771
880
|
|
|
772
|
-
for (const [
|
|
773
|
-
if (ruleId.includes(
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
881
|
+
for (const [templateId, template] of Object.entries(FIX_TEMPLATES)) {
|
|
882
|
+
if (!ruleId.includes(templateId)) continue;
|
|
883
|
+
|
|
884
|
+
// New: handle patterns array
|
|
885
|
+
if (template.patterns && Array.isArray(template.patterns)) {
|
|
886
|
+
for (const pattern of template.patterns) {
|
|
887
|
+
// Skip if language doesn't match
|
|
888
|
+
if (pattern.languages && !pattern.languages.includes(language)) {
|
|
889
|
+
continue;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// Skip if pattern doesn't match the line
|
|
893
|
+
if (!pattern.match.test(line)) {
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// Try the fix
|
|
898
|
+
const candidate = pattern.fix(line, language);
|
|
899
|
+
const validation = validateFix(line, candidate, language);
|
|
900
|
+
|
|
901
|
+
if (validation.valid) {
|
|
902
|
+
return {
|
|
903
|
+
description: template.description,
|
|
904
|
+
original: line,
|
|
905
|
+
fixed: candidate
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
// If invalid, try next pattern
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Fallback: old-style single fix function (backward compatible)
|
|
913
|
+
if (template.fix && typeof template.fix === 'function') {
|
|
914
|
+
const candidate = template.fix(line, language);
|
|
915
|
+
const validation = validateFix(line, candidate, language);
|
|
916
|
+
|
|
917
|
+
if (validation.valid) {
|
|
918
|
+
return {
|
|
919
|
+
description: template.description,
|
|
920
|
+
original: line,
|
|
921
|
+
fixed: candidate
|
|
922
|
+
};
|
|
923
|
+
}
|
|
779
924
|
}
|
|
780
925
|
}
|
|
781
926
|
|
|
@@ -1724,7 +1869,10 @@ const CLIENT_CONFIGS = {
|
|
|
1724
1869
|
name: 'Claude Code',
|
|
1725
1870
|
configKey: 'mcpServers',
|
|
1726
1871
|
configPath: () => join(homedir(), '.claude', 'settings.json'),
|
|
1727
|
-
buildEntry: () => ({ ...MCP_SERVER_ENTRY })
|
|
1872
|
+
buildEntry: () => ({ ...MCP_SERVER_ENTRY }),
|
|
1873
|
+
// Claude Code stores MCP config per-project in ~/.claude.json, not in settings.json
|
|
1874
|
+
// Use the 'claude mcp add' CLI for reliable per-project configuration
|
|
1875
|
+
useCliCommand: true
|
|
1728
1876
|
},
|
|
1729
1877
|
'cursor': {
|
|
1730
1878
|
name: 'Cursor',
|
|
@@ -1843,6 +1991,91 @@ function printInitUsage() {
|
|
|
1843
1991
|
console.log(' npx agent-security-scanner-mcp init cline --force --name my-scanner\n');
|
|
1844
1992
|
}
|
|
1845
1993
|
|
|
1994
|
+
// Special init handler for clients that use CLI commands (e.g., Claude Code)
|
|
1995
|
+
async function runCliInit(client, flags) {
|
|
1996
|
+
const serverName = flags.name;
|
|
1997
|
+
const cwd = process.cwd();
|
|
1998
|
+
|
|
1999
|
+
console.log(`\n Client: ${client.name}`);
|
|
2000
|
+
console.log(` Project: ${cwd}`);
|
|
2001
|
+
console.log(` OS: ${platform()} (${process.arch})`);
|
|
2002
|
+
console.log(` Key: ${serverName}\n`);
|
|
2003
|
+
|
|
2004
|
+
// Check if claude CLI is available
|
|
2005
|
+
const claudeCheck = checkCommand('claude', ['--version']);
|
|
2006
|
+
if (!claudeCheck.ok) {
|
|
2007
|
+
console.log(' ERROR: Claude Code CLI not found.');
|
|
2008
|
+
console.log(' Please install Claude Code first: https://claude.ai/download\n');
|
|
2009
|
+
console.log(' Alternative: Use --path to write to ~/.claude/settings.json directly:\n');
|
|
2010
|
+
console.log(` npx agent-security-scanner-mcp init claude-code --path ~/.claude/settings.json\n`);
|
|
2011
|
+
process.exit(1);
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
// Check if already configured for this project
|
|
2015
|
+
const listCheck = checkCommand('claude', ['mcp', 'list']);
|
|
2016
|
+
if (listCheck.ok && listCheck.output.includes(serverName)) {
|
|
2017
|
+
if (!flags.force) {
|
|
2018
|
+
console.log(` ${serverName} is already configured for this project.`);
|
|
2019
|
+
console.log(` Use --force to reconfigure.\n`);
|
|
2020
|
+
process.exit(0);
|
|
2021
|
+
}
|
|
2022
|
+
// Remove existing entry first if --force
|
|
2023
|
+
console.log(` Removing existing ${serverName} configuration...`);
|
|
2024
|
+
try {
|
|
2025
|
+
execFileSync('claude', ['mcp', 'remove', serverName], { encoding: 'utf-8', stdio: 'pipe' });
|
|
2026
|
+
} catch {
|
|
2027
|
+
// Ignore errors - might not exist
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
// Build the CLI command
|
|
2032
|
+
const cliArgs = ['mcp', 'add', serverName, '--', 'npx', '-y', 'agent-security-scanner-mcp'];
|
|
2033
|
+
const fullCommand = `claude ${cliArgs.join(' ')}`;
|
|
2034
|
+
|
|
2035
|
+
if (flags.dryRun) {
|
|
2036
|
+
console.log(` [dry-run] Would run: ${fullCommand}`);
|
|
2037
|
+
console.log(` [dry-run] In directory: ${cwd}`);
|
|
2038
|
+
console.log(`\n No changes made.\n`);
|
|
2039
|
+
process.exit(0);
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
console.log(` Running: ${fullCommand}`);
|
|
2043
|
+
console.log(` In directory: ${cwd}\n`);
|
|
2044
|
+
|
|
2045
|
+
try {
|
|
2046
|
+
const result = execFileSync('claude', cliArgs, { encoding: 'utf-8', stdio: 'pipe', cwd });
|
|
2047
|
+
console.log(` ${result.trim()}\n`);
|
|
2048
|
+
} catch (e) {
|
|
2049
|
+
console.error(` ERROR: Failed to add MCP server.`);
|
|
2050
|
+
console.error(` ${e.message}\n`);
|
|
2051
|
+
console.log(' Alternative: Add manually to ~/.claude/settings.json:\n');
|
|
2052
|
+
console.log(` {
|
|
2053
|
+
"mcpServers": {
|
|
2054
|
+
"${serverName}": {
|
|
2055
|
+
"command": "npx",
|
|
2056
|
+
"args": ["-y", "agent-security-scanner-mcp"]
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
}\n`);
|
|
2060
|
+
process.exit(1);
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
// Verify it was added
|
|
2064
|
+
const verifyCheck = checkCommand('claude', ['mcp', 'list']);
|
|
2065
|
+
if (verifyCheck.ok && verifyCheck.output.includes(serverName)) {
|
|
2066
|
+
console.log(` ✓ Successfully configured ${serverName} for this project!\n`);
|
|
2067
|
+
} else {
|
|
2068
|
+
console.log(` ⚠ Configuration may have succeeded but verification failed.`);
|
|
2069
|
+
console.log(` Run 'claude mcp list' to check.\n`);
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
console.log(` Next steps:`);
|
|
2073
|
+
console.log(` 1. Restart Claude Code in this folder`);
|
|
2074
|
+
console.log(` 2. Verify by asking: "What MCP tools do you have?"`);
|
|
2075
|
+
console.log(` 3. Test: "Scan this file for security issues"\n`);
|
|
2076
|
+
console.log(` Note: Run this command in each project folder where you want security scanning.\n`);
|
|
2077
|
+
}
|
|
2078
|
+
|
|
1846
2079
|
async function runInit(flags) {
|
|
1847
2080
|
let clientName = flags.client;
|
|
1848
2081
|
|
|
@@ -1863,6 +2096,12 @@ async function runInit(flags) {
|
|
|
1863
2096
|
process.exit(1);
|
|
1864
2097
|
}
|
|
1865
2098
|
|
|
2099
|
+
// Special handling for clients that use CLI commands (like Claude Code)
|
|
2100
|
+
if (client.useCliCommand && !flags.path) {
|
|
2101
|
+
await runCliInit(client, flags);
|
|
2102
|
+
return;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
1866
2105
|
const configPath = flags.path || client.configPath();
|
|
1867
2106
|
const serverName = flags.name;
|
|
1868
2107
|
const entry = client.buildEntry();
|
|
@@ -2067,6 +2306,53 @@ async function runDoctor(flags) {
|
|
|
2067
2306
|
console.log('\n Client Configurations');
|
|
2068
2307
|
|
|
2069
2308
|
for (const [key, client] of Object.entries(CLIENT_CONFIGS)) {
|
|
2309
|
+
// Special handling for Claude Code - uses per-project config via CLI
|
|
2310
|
+
if (client.useCliCommand) {
|
|
2311
|
+
const claudeCheck = checkCommand('claude', ['--version']);
|
|
2312
|
+
if (!claudeCheck.ok) {
|
|
2313
|
+
console.log(` \u2014 ${client.name.padEnd(20)} not installed (claude CLI not found)`);
|
|
2314
|
+
continue;
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
// Check if configured for current project using claude mcp list
|
|
2318
|
+
const listCheck = checkCommand('claude', ['mcp', 'list']);
|
|
2319
|
+
if (listCheck.ok && listCheck.output) {
|
|
2320
|
+
const output = listCheck.output.toLowerCase();
|
|
2321
|
+
const hasScanner = output.includes('security-scanner') ||
|
|
2322
|
+
output.includes('agentic-security') ||
|
|
2323
|
+
output.includes('agent-security-scanner');
|
|
2324
|
+
if (hasScanner) {
|
|
2325
|
+
// Extract the actual server name from output
|
|
2326
|
+
let serverName = 'security-scanner';
|
|
2327
|
+
if (output.includes('agentic-security')) serverName = 'agentic-security';
|
|
2328
|
+
console.log(` \u2713 ${client.name.padEnd(20)} configured (${serverName})`);
|
|
2329
|
+
} else if (output.includes('no mcp servers configured')) {
|
|
2330
|
+
console.log(` \u2717 ${client.name.padEnd(20)} not configured for this project`);
|
|
2331
|
+
if (fix) {
|
|
2332
|
+
try {
|
|
2333
|
+
execFileSync('claude', ['mcp', 'add', 'security-scanner', '--', 'npx', '-y', 'agent-security-scanner-mcp'],
|
|
2334
|
+
{ encoding: 'utf-8', stdio: 'pipe' });
|
|
2335
|
+
console.log(` \u2713 Fixed: added security-scanner via claude mcp add`);
|
|
2336
|
+
fixed++;
|
|
2337
|
+
} catch {
|
|
2338
|
+
console.log(` \u2717 Auto-fix failed. Run: npx agent-security-scanner-mcp init claude-code`);
|
|
2339
|
+
issues++;
|
|
2340
|
+
}
|
|
2341
|
+
} else {
|
|
2342
|
+
console.log(` Fix: npx agent-security-scanner-mcp init claude-code`);
|
|
2343
|
+
issues++;
|
|
2344
|
+
}
|
|
2345
|
+
} else {
|
|
2346
|
+
console.log(` \u2717 ${client.name.padEnd(20)} entry missing from project config`);
|
|
2347
|
+
console.log(` Fix: npx agent-security-scanner-mcp init claude-code`);
|
|
2348
|
+
issues++;
|
|
2349
|
+
}
|
|
2350
|
+
} else {
|
|
2351
|
+
console.log(` \u26a0 ${client.name.padEnd(20)} could not check config (run 'claude mcp list' manually)`);
|
|
2352
|
+
}
|
|
2353
|
+
continue;
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2070
2356
|
let configPath;
|
|
2071
2357
|
try { configPath = client.configPath(); } catch { continue; }
|
|
2072
2358
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-security-scanner-mcp",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.6",
|
|
4
4
|
"mcpName": "io.github.sinewaveai/agent-security-scanner-mcp",
|
|
5
5
|
"description": "Security scanner MCP server for AI coding agents. Prompt injection firewall, package hallucination detection (4.3M+ packages), 359 vulnerability rules with auto-fix. For Claude Code, Cursor, Windsurf, Cline.",
|
|
6
6
|
"main": "index.js",
|
package/packages/crates.txt
CHANGED
|
@@ -23630,7 +23630,6 @@ cinnog
|
|
|
23630
23630
|
cint
|
|
23631
23631
|
cio
|
|
23632
23632
|
cio-api
|
|
23633
|
-
cioqLsBmIV3xEUGI6XQRx411QEIZwwaDh7c
|
|
23634
23633
|
cip
|
|
23635
23634
|
cip_rust
|
|
23636
23635
|
cipepser-bicycle-book-wordcount
|