security-mcp 1.0.3 → 1.0.5
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 +77 -21
- package/defaults/control-catalog.json +157 -0
- package/defaults/security-exceptions.json +4 -0
- package/defaults/security-tools.json +41 -0
- package/dist/ci/pr-gate.js +2 -3
- package/dist/cli/index.js +51 -16
- package/dist/cli/install.js +39 -17
- package/dist/cli/update.js +124 -0
- package/dist/gate/catalog.js +55 -0
- package/dist/gate/checks/ai.js +45 -14
- package/dist/gate/checks/dependencies.js +4 -0
- package/dist/gate/checks/scanners.js +84 -0
- package/dist/gate/checks/secrets.js +53 -26
- package/dist/gate/diff.js +2 -2
- package/dist/gate/evidence.js +116 -0
- package/dist/gate/exceptions.js +85 -0
- package/dist/gate/policy.js +110 -4
- package/dist/mcp/server.js +440 -6
- package/dist/repo/fs.js +10 -5
- package/dist/review/store.js +80 -0
- package/dist/tests/run.js +103 -0
- package/package.json +13 -3
- package/prompts/SECURITY_PROMPT.md +40 -0
- package/skills/senior-security-engineer/SKILL.md +46 -1
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ security-mcp actively hardens every surface of your software:
|
|
|
45
45
|
## Quick Start
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
npx security-mcp install
|
|
48
|
+
npx -y security-mcp@latest install
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
That's it. The tool auto-detects your editor and writes the MCP config. Restart your editor -- done.
|
|
@@ -53,15 +53,43 @@ That's it. The tool auto-detects your editor and writes the MCP config. Restart
|
|
|
53
53
|
To target a specific editor:
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
|
-
npx security-mcp install --claude-code
|
|
57
|
-
npx security-mcp install --cursor
|
|
58
|
-
npx security-mcp install --vscode
|
|
56
|
+
npx -y security-mcp@latest install --claude-code
|
|
57
|
+
npx -y security-mcp@latest install --cursor
|
|
58
|
+
npx -y security-mcp@latest install --vscode
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
Preview without writing anything:
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
npx security-mcp install --dry-run
|
|
64
|
+
npx -y security-mcp@latest install --dry-run
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Global Install
|
|
68
|
+
|
|
69
|
+
Install the package globally, then configure editors to call the global binary directly:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm install -g security-mcp@latest
|
|
73
|
+
security-mcp install-global
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Preview the global install flow without writing:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
security-mcp install-global --dry-run
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Update Behavior
|
|
83
|
+
|
|
84
|
+
- `npx -y security-mcp@latest ...` always runs the latest published npm version.
|
|
85
|
+
- Global installs (`npm install -g security-mcp`) do not auto-upgrade by themselves.
|
|
86
|
+
- The CLI now checks npm for new releases and prints an update prompt when a newer version is available.
|
|
87
|
+
|
|
88
|
+
Global update command:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npm install -g security-mcp@latest
|
|
92
|
+
security-mcp install-global
|
|
65
93
|
```
|
|
66
94
|
|
|
67
95
|
In **Claude Code**, activate the security engineer:
|
|
@@ -78,28 +106,36 @@ Your AI will now **find and fix** security issues instead of just mentioning the
|
|
|
78
106
|
|
|
79
107
|
When you invoke `/senior-security-engineer` or call any security-mcp MCP tool, your AI shifts into the role of a Senior Security Engineer. It will:
|
|
80
108
|
|
|
81
|
-
1. **
|
|
82
|
-
2. **
|
|
83
|
-
3. **
|
|
84
|
-
4. **
|
|
85
|
-
5. **
|
|
109
|
+
1. **Ask scan scope first** -- folder-by-folder, file-by-file, or recent changes
|
|
110
|
+
2. **Start a review run** -- carry a `runId` for ordered execution and attestation
|
|
111
|
+
3. **Scan your code** for vulnerabilities, misconfigurations, and security anti-patterns
|
|
112
|
+
4. **Fix what it finds** -- not just flag it; it rewrites the insecure code with the secure version
|
|
113
|
+
5. **Enforce policies** -- set up input validation, auth middleware, security headers, and rate limiting
|
|
114
|
+
6. **Block dangerous patterns** -- refuse to implement code that introduces known vulnerabilities
|
|
115
|
+
7. **Produce an attestation** -- emit a confidence summary and integrity hash for the completed review
|
|
86
116
|
|
|
87
117
|
### MCP Tools (Your AI Uses These Automatically)
|
|
88
118
|
|
|
89
119
|
| Tool | What It Does |
|
|
90
120
|
| --- | --- |
|
|
121
|
+
| `security.start_review` | Starts a stateful review run and returns the `runId` used for ordered execution and attestation |
|
|
91
122
|
| `security.get_system_prompt` | Loads the full security directive into your AI session -- activates the Senior Security Engineer mode |
|
|
92
123
|
| `security.threat_model` | Generates a complete threat model for any feature before a single line of code is written |
|
|
93
124
|
| `security.checklist` | Returns a hardened pre-ship checklist specific to your surface (web, API, mobile, AI, cloud) |
|
|
125
|
+
| `security.scan_strategy` | Forces scan mode selection (`folder_by_folder`, `file_by_file`, `recent_changes`) and builds an exhaustive review plan |
|
|
94
126
|
| `security.generate_policy` | Writes a `security-policy.json` for your project that the gate enforces on every PR |
|
|
95
|
-
| `security.
|
|
127
|
+
| `security.terraform_hardening_blueprint` | Produces an advanced Terraform hardening baseline (network, IAM, data, logging, CI controls) |
|
|
128
|
+
| `security.generate_opa_rego` | Generates preventive OPA/Rego policies for Terraform plans, CI pipelines, and Kubernetes admission (requires explicit consent) |
|
|
129
|
+
| `security.self_heal_loop` | Proposes self-healing improvements, but requires explicit human approval before any change |
|
|
130
|
+
| `security.attest_review` | Writes an auditable review attestation with integrity hash and confidence summary |
|
|
131
|
+
| `security.run_pr_gate` | Scans recent changes, selected folders, or selected files and **blocks merge** on CRITICAL/HIGH vulnerabilities; requires `runId` in MCP usage |
|
|
96
132
|
| `repo.read_file` | Reads files from your workspace for analysis |
|
|
97
133
|
| `repo.search` | Searches your codebase for vulnerable patterns |
|
|
98
134
|
|
|
99
135
|
### Security Gate (Blocks Bad Code from Shipping)
|
|
100
136
|
|
|
101
137
|
```bash
|
|
102
|
-
npx security-mcp ci:pr-gate
|
|
138
|
+
npx -y security-mcp@latest ci:pr-gate
|
|
103
139
|
```
|
|
104
140
|
|
|
105
141
|
Add this to your CI pipeline. It scans every PR and **blocks the merge** if it finds:
|
|
@@ -158,14 +194,17 @@ When your AI has security-mcp active, it will **fix these automatically** -- not
|
|
|
158
194
|
|
|
159
195
|
| Editor | Install Command | Config Location |
|
|
160
196
|
| --- | --- | --- |
|
|
161
|
-
| Claude Code | `npx security-mcp install --claude-code` | `~/.claude/settings.json` |
|
|
162
|
-
|
|
|
163
|
-
| Cursor (
|
|
164
|
-
|
|
|
197
|
+
| Claude Code | `npx -y security-mcp@latest install --claude-code` | `~/.claude/settings.json` |
|
|
198
|
+
| Claude Code (global binary) | `security-mcp install-global --claude-code` | `~/.claude/settings.json` |
|
|
199
|
+
| Cursor (global) | `npx -y security-mcp@latest install --cursor` | `~/.cursor/mcp.json` |
|
|
200
|
+
| Cursor (global binary) | `security-mcp install-global --cursor` | `~/.cursor/mcp.json` |
|
|
201
|
+
| Cursor (workspace) | `npx -y security-mcp@latest install --cursor` | `.cursor/mcp.json` |
|
|
202
|
+
| VS Code | `npx -y security-mcp@latest install --vscode` | User `settings.json` |
|
|
203
|
+
| VS Code (global binary) | `security-mcp install-global --vscode` | User `settings.json` |
|
|
165
204
|
| GitHub Copilot | Manual config (see below) | `.vscode/settings.json` |
|
|
166
205
|
| Codex | Manual config (see below) | Editor config |
|
|
167
206
|
| Replit | Manual config (see below) | `.replit` config |
|
|
168
|
-
| Any MCP-compatible | `npx security-mcp config` | Paste into editor config |
|
|
207
|
+
| Any MCP-compatible | `npx -y security-mcp@latest config` or `security-mcp config --use-global-binary` | Paste into editor config |
|
|
169
208
|
|
|
170
209
|
---
|
|
171
210
|
|
|
@@ -203,7 +242,20 @@ security-mcp applies all of them on your behalf:
|
|
|
203
242
|
"mcpServers": {
|
|
204
243
|
"security-mcp": {
|
|
205
244
|
"command": "npx",
|
|
206
|
-
"args": ["-y", "security-mcp", "serve"]
|
|
245
|
+
"args": ["-y", "security-mcp@latest", "serve"]
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Claude Code With Global Binary (`~/.claude/settings.json`)
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{
|
|
255
|
+
"mcpServers": {
|
|
256
|
+
"security-mcp": {
|
|
257
|
+
"command": "security-mcp",
|
|
258
|
+
"args": ["serve"]
|
|
207
259
|
}
|
|
208
260
|
}
|
|
209
261
|
}
|
|
@@ -216,7 +268,7 @@ security-mcp applies all of them on your behalf:
|
|
|
216
268
|
"mcpServers": {
|
|
217
269
|
"security-mcp": {
|
|
218
270
|
"command": "npx",
|
|
219
|
-
"args": ["-y", "security-mcp", "serve"]
|
|
271
|
+
"args": ["-y", "security-mcp@latest", "serve"]
|
|
220
272
|
}
|
|
221
273
|
}
|
|
222
274
|
}
|
|
@@ -229,7 +281,7 @@ security-mcp applies all of them on your behalf:
|
|
|
229
281
|
"mcp.servers": {
|
|
230
282
|
"security-mcp": {
|
|
231
283
|
"command": "npx",
|
|
232
|
-
"args": ["-y", "security-mcp", "serve"]
|
|
284
|
+
"args": ["-y", "security-mcp@latest", "serve"]
|
|
233
285
|
}
|
|
234
286
|
}
|
|
235
287
|
}
|
|
@@ -238,7 +290,8 @@ security-mcp applies all of them on your behalf:
|
|
|
238
290
|
Print the config snippet for any editor:
|
|
239
291
|
|
|
240
292
|
```bash
|
|
241
|
-
npx security-mcp config
|
|
293
|
+
npx -y security-mcp@latest config
|
|
294
|
+
security-mcp config --use-global-binary
|
|
242
295
|
```
|
|
243
296
|
|
|
244
297
|
---
|
|
@@ -250,6 +303,9 @@ Copy the default policy into your project:
|
|
|
250
303
|
```bash
|
|
251
304
|
cp node_modules/security-mcp/defaults/security-policy.json .mcp/policies/security-policy.json
|
|
252
305
|
cp node_modules/security-mcp/defaults/evidence-map.json .mcp/mappings/evidence-map.json
|
|
306
|
+
cp node_modules/security-mcp/defaults/control-catalog.json .mcp/catalog/control-catalog.json
|
|
307
|
+
cp node_modules/security-mcp/defaults/security-tools.json .mcp/scanners/security-tools.json
|
|
308
|
+
cp node_modules/security-mcp/defaults/security-exceptions.json .mcp/exceptions/security-exceptions.json
|
|
253
309
|
```
|
|
254
310
|
|
|
255
311
|
Or generate one tailored to your project:
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.0",
|
|
3
|
+
"controls": [
|
|
4
|
+
{
|
|
5
|
+
"id": "SCAN_MODE_SELECTED",
|
|
6
|
+
"description": "A review mode is selected before scanning begins.",
|
|
7
|
+
"automation": "workflow",
|
|
8
|
+
"surfaces": ["all"],
|
|
9
|
+
"frameworks": ["NIST 800-218", "OWASP SAMM"],
|
|
10
|
+
"required_steps": ["scan_strategy"]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "THREAT_MODEL_COMPLETED",
|
|
14
|
+
"description": "Threat modeling is completed before security review signoff.",
|
|
15
|
+
"automation": "workflow",
|
|
16
|
+
"surfaces": ["web", "api", "mobile", "ai", "infra"],
|
|
17
|
+
"frameworks": ["STRIDE", "PASTA", "MITRE ATT&CK"],
|
|
18
|
+
"required_steps": ["threat_model"]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "CHECKLIST_COMPLETED",
|
|
22
|
+
"description": "Surface-specific security checklist completed.",
|
|
23
|
+
"automation": "workflow",
|
|
24
|
+
"surfaces": ["all"],
|
|
25
|
+
"frameworks": ["OWASP ASVS", "NIST CSF 2.0"],
|
|
26
|
+
"required_steps": ["checklist"]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "ZERO_TRUST",
|
|
30
|
+
"description": "All services enforce authentication and authorization with no implicit trust.",
|
|
31
|
+
"automation": "evidence",
|
|
32
|
+
"surfaces": ["web", "api", "infra"],
|
|
33
|
+
"frameworks": ["NIST 800-207", "OWASP ASVS"],
|
|
34
|
+
"evidence": ["deny_by_default_authz", "service_to_service_auth"]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "SECRET_MANAGER_ONLY",
|
|
38
|
+
"description": "Secrets stored only in a dedicated secret manager.",
|
|
39
|
+
"automation": "evidence",
|
|
40
|
+
"surfaces": ["all"],
|
|
41
|
+
"frameworks": ["NIST 800-53", "SOC 2"],
|
|
42
|
+
"evidence": ["no_hardcoded_secrets", "secret_manager_refs"]
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "TLS_13",
|
|
46
|
+
"description": "TLS configuration is verified.",
|
|
47
|
+
"automation": "evidence",
|
|
48
|
+
"surfaces": ["web", "api", "infra"],
|
|
49
|
+
"frameworks": ["NIST 800-53", "PCI DSS 4.0"],
|
|
50
|
+
"evidence": ["tls_config_verified"]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "CSP_NO_INLINE",
|
|
54
|
+
"description": "CSP is configured without unsafe inline execution.",
|
|
55
|
+
"automation": "evidence",
|
|
56
|
+
"surfaces": ["web"],
|
|
57
|
+
"frameworks": ["OWASP ASVS", "CWE-79"],
|
|
58
|
+
"evidence": ["security_headers_present"]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "CSRF",
|
|
62
|
+
"description": "CSRF protections and tests are present.",
|
|
63
|
+
"automation": "evidence",
|
|
64
|
+
"surfaces": ["web", "api"],
|
|
65
|
+
"frameworks": ["OWASP Top 10", "OWASP ASVS"],
|
|
66
|
+
"evidence": ["csrf_protection_present", "csrf_tests_present"]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "SSRF",
|
|
70
|
+
"description": "SSRF guards and tests are present.",
|
|
71
|
+
"automation": "evidence",
|
|
72
|
+
"surfaces": ["web", "api", "infra"],
|
|
73
|
+
"frameworks": ["OWASP Top 10", "CWE-918"],
|
|
74
|
+
"evidence": ["ssrf_guard_present", "ssrf_tests_present"]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"id": "MOBILE_MASVS",
|
|
78
|
+
"description": "Mobile release configuration meets MASVS baseline.",
|
|
79
|
+
"automation": "evidence",
|
|
80
|
+
"surfaces": ["mobile"],
|
|
81
|
+
"frameworks": ["OWASP MASVS"],
|
|
82
|
+
"evidence": ["ios_ats_strict", "android_nsc_strict", "release_not_debuggable"]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": "AI_BOUNDED_OUTPUTS",
|
|
86
|
+
"description": "AI outputs are schema-validated and tools are allowlisted.",
|
|
87
|
+
"automation": "evidence",
|
|
88
|
+
"surfaces": ["ai"],
|
|
89
|
+
"frameworks": ["OWASP LLM Top 10", "MITRE ATLAS"],
|
|
90
|
+
"evidence": ["json_schema_validation", "tool_allowlist_router"]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"id": "SECRETS_SCANNER_READY",
|
|
94
|
+
"description": "A dedicated secrets scanner is installed and runnable.",
|
|
95
|
+
"automation": "tooling",
|
|
96
|
+
"surfaces": ["all"],
|
|
97
|
+
"frameworks": ["NIST 800-218", "SLSA"],
|
|
98
|
+
"required_scanners": ["gitleaks"]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"id": "SAST_SCANNER_READY",
|
|
102
|
+
"description": "A static analysis scanner is installed and runnable.",
|
|
103
|
+
"automation": "tooling",
|
|
104
|
+
"surfaces": ["web", "api", "ai"],
|
|
105
|
+
"frameworks": ["OWASP Top 10", "OWASP SAMM"],
|
|
106
|
+
"required_scanners": ["semgrep"]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"id": "DEPENDENCY_SCANNER_READY",
|
|
110
|
+
"description": "A dependency scanner is installed and runnable.",
|
|
111
|
+
"automation": "tooling",
|
|
112
|
+
"surfaces": ["web", "api", "ai"],
|
|
113
|
+
"frameworks": ["SLSA", "NIST 800-218"],
|
|
114
|
+
"required_scanners": ["osv-scanner"]
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"id": "IAC_SCANNER_READY",
|
|
118
|
+
"description": "Infrastructure scanners and policy-as-code tooling are installed and runnable.",
|
|
119
|
+
"automation": "tooling",
|
|
120
|
+
"surfaces": ["infra"],
|
|
121
|
+
"frameworks": ["CIS", "NIST 800-53"],
|
|
122
|
+
"required_scanners": ["checkov", "conftest"]
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"id": "CONTAINER_SCANNER_READY",
|
|
126
|
+
"description": "A container scanner is installed and runnable.",
|
|
127
|
+
"automation": "tooling",
|
|
128
|
+
"surfaces": ["infra"],
|
|
129
|
+
"frameworks": ["NIST 800-190", "CIS"],
|
|
130
|
+
"required_scanners": ["trivy"]
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"id": "DAST_SCANNER_READY",
|
|
134
|
+
"description": "A DAST scanner is installed and runnable.",
|
|
135
|
+
"automation": "tooling",
|
|
136
|
+
"surfaces": ["web", "api"],
|
|
137
|
+
"frameworks": ["OWASP Testing Guide"],
|
|
138
|
+
"required_scanners": ["zaproxy"]
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"id": "HUMAN_APPROVED_REGO",
|
|
142
|
+
"description": "OPA/Rego generation only happens after explicit human approval.",
|
|
143
|
+
"automation": "approval",
|
|
144
|
+
"surfaces": ["infra"],
|
|
145
|
+
"frameworks": ["NIST 800-218"],
|
|
146
|
+
"required_steps": ["generate_opa_rego"]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"id": "HUMAN_APPROVED_SELF_HEALING",
|
|
150
|
+
"description": "Self-healing changes only happen after explicit human approval.",
|
|
151
|
+
"automation": "approval",
|
|
152
|
+
"surfaces": ["all"],
|
|
153
|
+
"frameworks": ["NIST 800-218", "SOC 2"],
|
|
154
|
+
"required_steps": ["self_heal_loop"]
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.0",
|
|
3
|
+
"fail_closed": true,
|
|
4
|
+
"scanners": {
|
|
5
|
+
"gitleaks": {
|
|
6
|
+
"command": "gitleaks",
|
|
7
|
+
"args": ["version"],
|
|
8
|
+
"required_for": ["all"]
|
|
9
|
+
},
|
|
10
|
+
"semgrep": {
|
|
11
|
+
"command": "semgrep",
|
|
12
|
+
"args": ["--version"],
|
|
13
|
+
"required_for": ["web", "api", "ai"]
|
|
14
|
+
},
|
|
15
|
+
"osv-scanner": {
|
|
16
|
+
"command": "osv-scanner",
|
|
17
|
+
"args": ["--version"],
|
|
18
|
+
"required_for": ["web", "api", "ai"]
|
|
19
|
+
},
|
|
20
|
+
"checkov": {
|
|
21
|
+
"command": "checkov",
|
|
22
|
+
"args": ["--version"],
|
|
23
|
+
"required_for": ["infra"]
|
|
24
|
+
},
|
|
25
|
+
"trivy": {
|
|
26
|
+
"command": "trivy",
|
|
27
|
+
"args": ["--version"],
|
|
28
|
+
"required_for": ["infra"]
|
|
29
|
+
},
|
|
30
|
+
"conftest": {
|
|
31
|
+
"command": "conftest",
|
|
32
|
+
"args": ["--version"],
|
|
33
|
+
"required_for": ["infra"]
|
|
34
|
+
},
|
|
35
|
+
"zaproxy": {
|
|
36
|
+
"command": "zaproxy",
|
|
37
|
+
"args": ["-version"],
|
|
38
|
+
"required_for": ["web", "api"]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
package/dist/ci/pr-gate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { runPrGate } from "../gate/policy.js";
|
|
2
|
-
//
|
|
3
|
-
const SAFE_REF_RE = /^[a-zA-Z0-9_
|
|
2
|
+
// Allow safe git revision operators (~ and ^) plus ref/path characters. CWE-88.
|
|
3
|
+
const SAFE_REF_RE = /^[a-zA-Z0-9_./~^-]+$/;
|
|
4
4
|
function safeEnvRef(envVar, defaultValue) {
|
|
5
5
|
const val = process.env[envVar] || defaultValue;
|
|
6
6
|
if (!SAFE_REF_RE.test(val)) {
|
|
@@ -20,7 +20,6 @@ async function main() {
|
|
|
20
20
|
process.exit(2);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
// eslint-disable-next-line unicorn/prefer-top-level-await
|
|
24
23
|
main().catch((err) => {
|
|
25
24
|
console.error("security gate crashed:", err);
|
|
26
25
|
process.exit(3);
|
package/dist/cli/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import { fileURLToPath } from "url";
|
|
|
14
14
|
import { dirname, resolve } from "path";
|
|
15
15
|
import { runInstall } from "./install.js";
|
|
16
16
|
import { main as runServer } from "../mcp/server.js";
|
|
17
|
+
import { notifyIfUpdateAvailable } from "./update.js";
|
|
17
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
19
|
const require = createRequire(import.meta.url);
|
|
19
20
|
function getVersion() {
|
|
@@ -26,17 +27,33 @@ function getVersion() {
|
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
const VERSION = getVersion();
|
|
30
|
+
function getConfigSnippet(useGlobalBinary) {
|
|
31
|
+
return {
|
|
32
|
+
mcpServers: {
|
|
33
|
+
"security-mcp": useGlobalBinary
|
|
34
|
+
? {
|
|
35
|
+
command: "security-mcp",
|
|
36
|
+
args: ["serve"]
|
|
37
|
+
}
|
|
38
|
+
: {
|
|
39
|
+
command: "npx",
|
|
40
|
+
args: ["-y", "security-mcp@latest", "serve"]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
29
45
|
const HELP = `
|
|
30
46
|
security-mcp v${VERSION}
|
|
31
47
|
|
|
32
48
|
AI security MCP server and gate for Claude Code, Cursor, Copilot, Codex, Replit, and any MCP-compatible editor.
|
|
33
49
|
|
|
34
50
|
USAGE
|
|
35
|
-
npx security-mcp <command> [options]
|
|
51
|
+
npx -y security-mcp@latest <command> [options]
|
|
36
52
|
|
|
37
53
|
COMMANDS
|
|
38
54
|
serve Start the MCP server over stdio (default for editors)
|
|
39
55
|
install Auto-detect installed editors and write MCP configs
|
|
56
|
+
install-global Install using the globally installed security-mcp binary
|
|
40
57
|
config Print MCP config JSON for manual editor setup
|
|
41
58
|
|
|
42
59
|
OPTIONS (install)
|
|
@@ -44,6 +61,7 @@ OPTIONS (install)
|
|
|
44
61
|
--cursor Write config for Cursor only
|
|
45
62
|
--vscode Write config for VS Code only
|
|
46
63
|
--global Write to global editor config (default)
|
|
64
|
+
--use-global-binary Write configs that execute "security-mcp serve" instead of npx
|
|
47
65
|
--dry-run Print what would change without writing
|
|
48
66
|
|
|
49
67
|
OPTIONS (general)
|
|
@@ -52,26 +70,31 @@ OPTIONS (general)
|
|
|
52
70
|
|
|
53
71
|
EXAMPLES
|
|
54
72
|
# Start MCP server (called automatically by editors):
|
|
55
|
-
npx -y security-mcp serve
|
|
73
|
+
npx -y security-mcp@latest serve
|
|
56
74
|
|
|
57
75
|
# Install into all detected editors:
|
|
58
|
-
npx security-mcp install
|
|
76
|
+
npx -y security-mcp@latest install
|
|
77
|
+
|
|
78
|
+
# Install globally once, then configure editors to use the global binary:
|
|
79
|
+
npm install -g security-mcp@latest
|
|
80
|
+
security-mcp install-global
|
|
59
81
|
|
|
60
82
|
# Install into Claude Code only:
|
|
61
|
-
npx security-mcp install --claude-code
|
|
83
|
+
npx -y security-mcp@latest install --claude-code
|
|
62
84
|
|
|
63
85
|
# Preview install without writing:
|
|
64
|
-
npx security-mcp install --dry-run
|
|
86
|
+
npx -y security-mcp@latest install --dry-run
|
|
65
87
|
|
|
66
88
|
# Print JSON config snippet:
|
|
67
|
-
npx security-mcp config
|
|
89
|
+
npx -y security-mcp@latest config
|
|
90
|
+
security-mcp config --use-global-binary
|
|
68
91
|
|
|
69
92
|
EDITOR CONFIG (add manually if install fails):
|
|
70
93
|
{
|
|
71
94
|
"mcpServers": {
|
|
72
95
|
"security-mcp": {
|
|
73
96
|
"command": "npx",
|
|
74
|
-
"args": ["-y", "security-mcp", "serve"]
|
|
97
|
+
"args": ["-y", "security-mcp@latest", "serve"]
|
|
75
98
|
}
|
|
76
99
|
}
|
|
77
100
|
}
|
|
@@ -83,16 +106,9 @@ EDITOR CONFIG (add manually if install fails):
|
|
|
83
106
|
MORE INFO
|
|
84
107
|
https://github.com/AbrahamOO/security-mcp
|
|
85
108
|
`;
|
|
86
|
-
const CONFIG_SNIPPET = {
|
|
87
|
-
mcpServers: {
|
|
88
|
-
"security-mcp": {
|
|
89
|
-
command: "npx",
|
|
90
|
-
args: ["-y", "security-mcp", "serve"]
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
109
|
async function main() {
|
|
95
110
|
const args = process.argv.slice(2);
|
|
111
|
+
const useGlobalBinary = args.includes("--use-global-binary");
|
|
96
112
|
if (args.includes("--version") || args.includes("-v")) {
|
|
97
113
|
process.stdout.write(`security-mcp v${VERSION}\n`);
|
|
98
114
|
process.exit(0);
|
|
@@ -102,6 +118,12 @@ async function main() {
|
|
|
102
118
|
process.exit(0);
|
|
103
119
|
}
|
|
104
120
|
const command = args[0] ?? "serve";
|
|
121
|
+
if (command === "serve") {
|
|
122
|
+
void notifyIfUpdateAvailable(VERSION);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
await notifyIfUpdateAvailable(VERSION);
|
|
126
|
+
}
|
|
105
127
|
switch (command) {
|
|
106
128
|
case "serve": {
|
|
107
129
|
// MCP stdio server - never write to stdout except via MCP protocol
|
|
@@ -114,14 +136,27 @@ async function main() {
|
|
|
114
136
|
cursor: args.includes("--cursor"),
|
|
115
137
|
vscode: args.includes("--vscode"),
|
|
116
138
|
dryRun: args.includes("--dry-run"),
|
|
139
|
+
useGlobalBinary,
|
|
117
140
|
// If no editor flag specified, install to all detected
|
|
118
141
|
all: !args.includes("--claude-code") && !args.includes("--cursor") && !args.includes("--vscode")
|
|
119
142
|
};
|
|
120
143
|
await runInstall(options);
|
|
121
144
|
break;
|
|
122
145
|
}
|
|
146
|
+
case "install-global": {
|
|
147
|
+
const options = {
|
|
148
|
+
claudeCode: args.includes("--claude-code"),
|
|
149
|
+
cursor: args.includes("--cursor"),
|
|
150
|
+
vscode: args.includes("--vscode"),
|
|
151
|
+
dryRun: args.includes("--dry-run"),
|
|
152
|
+
useGlobalBinary: true,
|
|
153
|
+
all: !args.includes("--claude-code") && !args.includes("--cursor") && !args.includes("--vscode")
|
|
154
|
+
};
|
|
155
|
+
await runInstall(options);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
123
158
|
case "config": {
|
|
124
|
-
process.stdout.write(JSON.stringify(
|
|
159
|
+
process.stdout.write(JSON.stringify(getConfigSnippet(useGlobalBinary), null, 2) + "\n");
|
|
125
160
|
process.stdout.write("\nAdd the above to your editor's MCP config file.\n");
|
|
126
161
|
process.stdout.write(" Claude Code: ~/.claude/settings.json\n");
|
|
127
162
|
process.stdout.write(" Cursor: ~/.cursor/mcp.json\n");
|
package/dist/cli/install.js
CHANGED
|
@@ -3,16 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Auto-detects installed editors and writes MCP server config + Claude Code skill.
|
|
5
5
|
*/
|
|
6
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync } from "fs";
|
|
7
|
-
import { dirname, join, resolve } from "path";
|
|
8
|
-
import { homedir, platform } from "os";
|
|
9
|
-
import { fileURLToPath } from "url";
|
|
6
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync } from "node:fs";
|
|
7
|
+
import { dirname, join, resolve } from "node:path";
|
|
8
|
+
import { homedir, platform } from "node:os";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
11
|
const PKG_ROOT = resolve(__dirname, "../..");
|
|
12
|
-
const MCP_ENTRY = {
|
|
13
|
-
command: "npx",
|
|
14
|
-
args: ["-y", "security-mcp", "serve"]
|
|
15
|
-
};
|
|
16
12
|
function resolveHome(p) {
|
|
17
13
|
return p.replace(/^~/, homedir());
|
|
18
14
|
}
|
|
@@ -78,10 +74,23 @@ function readJsonSafe(filePath) {
|
|
|
78
74
|
return {};
|
|
79
75
|
}
|
|
80
76
|
}
|
|
81
|
-
function
|
|
77
|
+
function getMcpEntry(useGlobalBinary) {
|
|
78
|
+
return useGlobalBinary
|
|
79
|
+
? {
|
|
80
|
+
type: "stdio",
|
|
81
|
+
command: "security-mcp",
|
|
82
|
+
args: ["serve"]
|
|
83
|
+
}
|
|
84
|
+
: {
|
|
85
|
+
type: "stdio",
|
|
86
|
+
command: "npx",
|
|
87
|
+
args: ["-y", "security-mcp@latest", "serve"]
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function writeMcpServersJson(configPath, dryRun, useGlobalBinary) {
|
|
82
91
|
const existing = readJsonSafe(configPath);
|
|
83
92
|
const servers = existing["mcpServers"] ?? {};
|
|
84
|
-
servers["security-mcp"] =
|
|
93
|
+
servers["security-mcp"] = getMcpEntry(useGlobalBinary);
|
|
85
94
|
existing["mcpServers"] = servers;
|
|
86
95
|
const content = JSON.stringify(existing, null, 2) + "\n";
|
|
87
96
|
if (!dryRun) {
|
|
@@ -90,10 +99,10 @@ function writeMcpServersJson(configPath, dryRun) {
|
|
|
90
99
|
}
|
|
91
100
|
return configPath;
|
|
92
101
|
}
|
|
93
|
-
function writeVsCodeSettings(configPath, dryRun) {
|
|
102
|
+
function writeVsCodeSettings(configPath, dryRun, useGlobalBinary) {
|
|
94
103
|
const existing = readJsonSafe(configPath);
|
|
95
104
|
const servers = existing["mcp.servers"] ?? {};
|
|
96
|
-
servers["security-mcp"] =
|
|
105
|
+
servers["security-mcp"] = getMcpEntry(useGlobalBinary);
|
|
97
106
|
existing["mcp.servers"] = servers;
|
|
98
107
|
const content = JSON.stringify(existing, null, 2) + "\n";
|
|
99
108
|
if (!dryRun) {
|
|
@@ -107,7 +116,19 @@ function installPolicy(dryRun) {
|
|
|
107
116
|
const policyDest = join(process.cwd(), ".mcp", "policies", "security-policy.json");
|
|
108
117
|
const evidenceSrc = join(PKG_ROOT, "defaults", "evidence-map.json");
|
|
109
118
|
const evidenceDest = join(process.cwd(), ".mcp", "mappings", "evidence-map.json");
|
|
110
|
-
|
|
119
|
+
const catalogSrc = join(PKG_ROOT, "defaults", "control-catalog.json");
|
|
120
|
+
const catalogDest = join(process.cwd(), ".mcp", "catalog", "control-catalog.json");
|
|
121
|
+
const scannersSrc = join(PKG_ROOT, "defaults", "security-tools.json");
|
|
122
|
+
const scannersDest = join(process.cwd(), ".mcp", "scanners", "security-tools.json");
|
|
123
|
+
const exceptionsSrc = join(PKG_ROOT, "defaults", "security-exceptions.json");
|
|
124
|
+
const exceptionsDest = join(process.cwd(), ".mcp", "exceptions", "security-exceptions.json");
|
|
125
|
+
for (const { src, dest } of [
|
|
126
|
+
{ src: policySrc, dest: policyDest },
|
|
127
|
+
{ src: evidenceSrc, dest: evidenceDest },
|
|
128
|
+
{ src: catalogSrc, dest: catalogDest },
|
|
129
|
+
{ src: scannersSrc, dest: scannersDest },
|
|
130
|
+
{ src: exceptionsSrc, dest: exceptionsDest }
|
|
131
|
+
]) {
|
|
111
132
|
if (!existsSync(src)) {
|
|
112
133
|
process.stdout.write(` [skip] ${src} not found in package\n`);
|
|
113
134
|
continue;
|
|
@@ -144,7 +165,7 @@ export async function runInstall(opts) {
|
|
|
144
165
|
if (targets.length === 0) {
|
|
145
166
|
process.stdout.write("No supported editors detected automatically.\n" +
|
|
146
167
|
"Run with --claude-code, --cursor, or --vscode to target a specific editor.\n" +
|
|
147
|
-
'Or add the config manually (run "npx security-mcp config" for the snippet).\n\n');
|
|
168
|
+
'Or add the config manually (run "npx -y security-mcp@latest config" for the snippet).\n\n');
|
|
148
169
|
return;
|
|
149
170
|
}
|
|
150
171
|
for (const target of targets) {
|
|
@@ -152,10 +173,10 @@ export async function runInstall(opts) {
|
|
|
152
173
|
try {
|
|
153
174
|
let written;
|
|
154
175
|
if (target.type === "vscode-settings") {
|
|
155
|
-
written = writeVsCodeSettings(target.configPath, dryRun);
|
|
176
|
+
written = writeVsCodeSettings(target.configPath, dryRun, opts.useGlobalBinary);
|
|
156
177
|
}
|
|
157
178
|
else {
|
|
158
|
-
written = writeMcpServersJson(target.configPath, dryRun);
|
|
179
|
+
written = writeMcpServersJson(target.configPath, dryRun, opts.useGlobalBinary);
|
|
159
180
|
}
|
|
160
181
|
process.stdout.write(` ${dryRun ? "[dry-run] would update" : "updated"}: ${written}\n`);
|
|
161
182
|
}
|
|
@@ -175,8 +196,9 @@ export async function runInstall(opts) {
|
|
|
175
196
|
process.stdout.write(dryRun
|
|
176
197
|
? "Dry-run complete. Re-run without --dry-run to apply.\n"
|
|
177
198
|
: "Done! Restart your editor to activate the security-mcp server.\n");
|
|
199
|
+
process.stdout.write(`Install mode: ${opts.useGlobalBinary ? "global binary (security-mcp serve)" : "npx (npx -y security-mcp@latest serve)"}\n`);
|
|
178
200
|
process.stdout.write("\nNext steps:\n");
|
|
179
201
|
process.stdout.write(" 1. Restart your editor.\n");
|
|
180
202
|
process.stdout.write(' 2. In Claude Code, type /senior-security-engineer to activate the security persona.\n');
|
|
181
|
-
process.stdout.write(' 3. Ask your AI: "Run security.
|
|
203
|
+
process.stdout.write(' 3. Ask your AI: "Run security.start_review with mode=recent_changes" to begin an auditable review.\n\n');
|
|
182
204
|
}
|