mcp-sentinel 0.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/LICENSE +21 -0
- package/README.md +290 -0
- package/bin/mcp-sentinel.js +2 -0
- package/dist/aguara.d.ts +4 -0
- package/dist/aguara.d.ts.map +1 -0
- package/dist/aguara.js +105 -0
- package/dist/aguara.js.map +1 -0
- package/dist/analyzer.d.ts +9 -0
- package/dist/analyzer.d.ts.map +1 -0
- package/dist/analyzer.js +42 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +176 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +55 -0
- package/dist/config.js.map +1 -0
- package/dist/diff.d.ts +3 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +110 -0
- package/dist/diff.js.map +1 -0
- package/dist/formatter.d.ts +7 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +184 -0
- package/dist/formatter.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +165 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown.d.ts +3 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +106 -0
- package/dist/markdown.js.map +1 -0
- package/dist/policy.d.ts +5 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +162 -0
- package/dist/policy.js.map +1 -0
- package/dist/scanner.d.ts +17 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +147 -0
- package/dist/scanner.js.map +1 -0
- package/dist/types.d.ts +135 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gustavo Aragon (@oktsec)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">MCP Sentinel</h1>
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>Audit and enforce security policies on MCP servers before you trust them.</strong>
|
|
5
|
+
</p>
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="https://www.npmjs.com/package/mcp-sentinel"><img src="https://img.shields.io/npm/v/mcp-sentinel.svg" alt="npm version"></a>
|
|
8
|
+
<a href="https://github.com/oktsec/mcp-sentinel/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/mcp-sentinel.svg" alt="license"></a>
|
|
9
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/node/v/mcp-sentinel.svg" alt="node version"></a>
|
|
10
|
+
</p>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
MCP servers run third-party code with access to your files, credentials, and shell. MCP Sentinel connects to any server, shows you exactly what it exposes, and **enforces security policies** — blocking dangerous tools before your agent can call them.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx mcp-sentinel --policy .mcp-policy.yml npx @modelcontextprotocol/server-filesystem /tmp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Real Output
|
|
22
|
+
|
|
23
|
+
Scanned against the official [MCP filesystem server](https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem):
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
🔍 MCP Sentinel v0.1.0
|
|
27
|
+
|
|
28
|
+
📦 Server: secure-filesystem-server v0.2.0
|
|
29
|
+
Capabilities: tools
|
|
30
|
+
|
|
31
|
+
🔧 Tools (14) 11 read • 3 write • 0 admin
|
|
32
|
+
|
|
33
|
+
✅ read_file Read the complete contents of a file as text... (3 params)
|
|
34
|
+
* string path
|
|
35
|
+
number tail
|
|
36
|
+
number head
|
|
37
|
+
✅ read_text_file Read the complete contents of a file from... (3 params)
|
|
38
|
+
✅ read_media_file Read an image or audio file... (1 params)
|
|
39
|
+
✅ read_multiple_files Read multiple files simultaneously... (1 params)
|
|
40
|
+
✏️ write_file Create a new file or overwrite an existing... [write] (2 params)
|
|
41
|
+
* string path
|
|
42
|
+
* string content
|
|
43
|
+
✅ edit_file Make line-based edits to a text file... (3 params)
|
|
44
|
+
✏️ create_directory Create a new directory... [write] (1 params)
|
|
45
|
+
✅ list_directory Get a detailed listing of all files... (1 params)
|
|
46
|
+
✏️ move_file Move or rename files and directories... [write] (2 params)
|
|
47
|
+
✅ search_files Recursively search for files... (3 params)
|
|
48
|
+
✅ get_file_info Retrieve detailed metadata... (1 params)
|
|
49
|
+
✅ list_allowed_directories Returns the list of allowed directories
|
|
50
|
+
|
|
51
|
+
🛡️ Aguara Security Analysis
|
|
52
|
+
|
|
53
|
+
0 finding(s)
|
|
54
|
+
|
|
55
|
+
Scanned in 1706ms
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### With Policy Enforcement
|
|
59
|
+
|
|
60
|
+
Using this policy:
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
# .mcp-policy.yml
|
|
64
|
+
deny:
|
|
65
|
+
categories: [admin]
|
|
66
|
+
tools: ["write_*", "move_*"]
|
|
67
|
+
require:
|
|
68
|
+
maxTools: 10
|
|
69
|
+
allow:
|
|
70
|
+
tools: ["write_file"] # Allow write_file despite deny pattern
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
🛡️ Policy: .mcp-policy.yml
|
|
75
|
+
|
|
76
|
+
❌ secure-filesystem-server: policy FAILED (2 violations)
|
|
77
|
+
|
|
78
|
+
✖ [deny.tools] Tool 'move_file' matches denied pattern 'move_*'
|
|
79
|
+
✖ [require.maxTools] Server exposes 14 tools, policy allows max 10
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Exit code 2** — `write_file` was allowed by the exception, but `move_file` and the tool count violated the policy. Your CI pipeline stops here.
|
|
83
|
+
|
|
84
|
+
## Why MCP Sentinel
|
|
85
|
+
|
|
86
|
+
Every MCP client already shows you the tool list. MCP Sentinel goes further:
|
|
87
|
+
|
|
88
|
+
| Feature | MCP Client | MCP Sentinel |
|
|
89
|
+
|---------|-----------|---------------|
|
|
90
|
+
| See tool list | Yes | Yes |
|
|
91
|
+
| **Security policy enforcement** | No | **Yes** |
|
|
92
|
+
| **CI/CD pipeline gate** | No | **Yes** |
|
|
93
|
+
| **Drift detection** between versions | No | **Yes** |
|
|
94
|
+
| **Fleet scan** all configured servers | No | **Yes** |
|
|
95
|
+
| Deep security analysis (aguara) | No | **Yes** |
|
|
96
|
+
| Exportable reports (JSON/Markdown) | No | **Yes** |
|
|
97
|
+
|
|
98
|
+
## Policy Enforcement
|
|
99
|
+
|
|
100
|
+
Define what's allowed in `.mcp-policy.yml`:
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
deny:
|
|
104
|
+
categories:
|
|
105
|
+
- admin # No admin tools (delete, exec, shell)
|
|
106
|
+
tools:
|
|
107
|
+
- "execute_*" # Block all execution patterns
|
|
108
|
+
- "push_*" # Block push operations
|
|
109
|
+
|
|
110
|
+
require:
|
|
111
|
+
aguara: clean # Zero security findings required
|
|
112
|
+
maxTools: 20 # Limit attack surface
|
|
113
|
+
|
|
114
|
+
allow:
|
|
115
|
+
tools:
|
|
116
|
+
- "execute_query" # Allow specific tools even if pattern-denied
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Policy Rules
|
|
120
|
+
|
|
121
|
+
| Rule | Description |
|
|
122
|
+
|------|-------------|
|
|
123
|
+
| `deny.categories` | Block tools by category: `read`, `write`, `admin` |
|
|
124
|
+
| `deny.tools` | Block tools by name or glob pattern (`delete_*`, `exec*`) |
|
|
125
|
+
| `require.aguara` | Require aguara scan with zero findings (`clean`) |
|
|
126
|
+
| `require.maxTools` | Maximum number of tools a server can expose |
|
|
127
|
+
| `allow.tools` | Exception list — allow specific tools even if they match deny rules |
|
|
128
|
+
|
|
129
|
+
### Example Policies
|
|
130
|
+
|
|
131
|
+
Ready-to-use policies in [`examples/policies/`](examples/policies/):
|
|
132
|
+
|
|
133
|
+
| Policy | Use case |
|
|
134
|
+
|--------|----------|
|
|
135
|
+
| [`strict.yml`](examples/policies/strict.yml) | Production — blocks admin + write, requires aguara clean |
|
|
136
|
+
| [`standard.yml`](examples/policies/standard.yml) | Development — blocks admin + exec patterns, allows writes |
|
|
137
|
+
| [`permissive.yml`](examples/policies/permissive.yml) | Local dev — only blocks destructive patterns (delete, drop, destroy) |
|
|
138
|
+
| [`ci-pipeline.yml`](examples/policies/ci-pipeline.yml) | CI/CD — blocks admin + deploy + push, requires aguara clean |
|
|
139
|
+
|
|
140
|
+
### CI/CD Integration
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
# .github/workflows/mcp-sentinel.yml
|
|
144
|
+
name: MCP Security Audit
|
|
145
|
+
on: [pull_request]
|
|
146
|
+
|
|
147
|
+
jobs:
|
|
148
|
+
audit:
|
|
149
|
+
runs-on: ubuntu-latest
|
|
150
|
+
steps:
|
|
151
|
+
- uses: actions/checkout@v4
|
|
152
|
+
- uses: actions/setup-node@v4
|
|
153
|
+
with: { node-version: 20 }
|
|
154
|
+
- run: npx mcp-sentinel --policy .mcp-policy.yml npx ./your-mcp-server
|
|
155
|
+
# Exit code 2 = policy violations → build fails
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
See full example in [`examples/github-action.yml`](examples/github-action.yml).
|
|
159
|
+
|
|
160
|
+
## Install & Use
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# No install needed
|
|
164
|
+
npx mcp-sentinel <command> [args...]
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Quick Start
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# 1. Scan a server
|
|
171
|
+
npx mcp-sentinel npx @modelcontextprotocol/server-filesystem /tmp
|
|
172
|
+
|
|
173
|
+
# 2. Create a policy
|
|
174
|
+
cat > .mcp-policy.yml << 'EOF'
|
|
175
|
+
deny:
|
|
176
|
+
categories: [admin]
|
|
177
|
+
tools: ["execute_*", "delete_*"]
|
|
178
|
+
require:
|
|
179
|
+
aguara: clean
|
|
180
|
+
maxTools: 20
|
|
181
|
+
EOF
|
|
182
|
+
|
|
183
|
+
# 3. Enforce it
|
|
184
|
+
npx mcp-sentinel --policy .mcp-policy.yml npx @modelcontextprotocol/server-filesystem /tmp
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### More Examples
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# Scan remote servers via HTTP
|
|
191
|
+
npx mcp-sentinel http://localhost:3000/mcp
|
|
192
|
+
|
|
193
|
+
# Scan all servers from your config (Claude Desktop, Cursor, Windsurf)
|
|
194
|
+
npx mcp-sentinel --config
|
|
195
|
+
|
|
196
|
+
# Diff mode: detect changes between server versions
|
|
197
|
+
npx mcp-sentinel npx @mcp/server --json > baseline.json
|
|
198
|
+
npx mcp-sentinel npx @mcp/server --diff baseline.json
|
|
199
|
+
|
|
200
|
+
# Scan multiple servers at once
|
|
201
|
+
npx mcp-sentinel npx @mcp/server-a --- npx @mcp/server-b
|
|
202
|
+
|
|
203
|
+
# JSON output for scripting
|
|
204
|
+
npx mcp-sentinel --json npx @mcp/server
|
|
205
|
+
|
|
206
|
+
# Markdown report
|
|
207
|
+
npx mcp-sentinel --markdown report.md npx @mcp/server
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### With Aguara (recommended)
|
|
211
|
+
|
|
212
|
+
Install [Aguara](https://github.com/garagon/aguara) for deep security analysis (prompt injection, exfiltration, supply chain, credential leaks):
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
curl -fsSL https://raw.githubusercontent.com/garagon/aguara/main/install.sh | bash
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
MCP Sentinel auto-detects Aguara and runs its 177-rule engine against tool descriptions. Combine with `require.aguara: clean` in your policy to enforce zero findings.
|
|
219
|
+
|
|
220
|
+
## Options
|
|
221
|
+
|
|
222
|
+
| Flag | Description |
|
|
223
|
+
|------|-------------|
|
|
224
|
+
| `--policy <file>` | Enforce security policy (auto-detects `.mcp-policy.yml`) |
|
|
225
|
+
| `--config` | Auto-detect and scan servers from config files |
|
|
226
|
+
| `--diff <file.json>` | Compare against a previous JSON scan |
|
|
227
|
+
| `--transport <type>` | Force transport: `stdio`, `sse`, `streamable-http` |
|
|
228
|
+
| `--json` | Structured JSON output |
|
|
229
|
+
| `--markdown <file>` | Export report as Markdown |
|
|
230
|
+
| `--fail-on-findings` | Exit code 2 if aguara finds issues |
|
|
231
|
+
| `--no-color` | Disable colored output |
|
|
232
|
+
| `--timeout <ms>` | Connection timeout (default: 30000) |
|
|
233
|
+
| `-h, --help` | Show help |
|
|
234
|
+
| `-v, --version` | Show version |
|
|
235
|
+
|
|
236
|
+
## How It Works
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
┌────────────────┐
|
|
240
|
+
stdio │ MCP Server │
|
|
241
|
+
┌──────── │ (local) │
|
|
242
|
+
│ └────────────────┘
|
|
243
|
+
┌───────────┤
|
|
244
|
+
│ mcp- │ HTTP/ ┌────────────────┐
|
|
245
|
+
│ inspector │ SSE │ MCP Server │
|
|
246
|
+
│ ├──────── │ (remote) │
|
|
247
|
+
│ Scan │ └────────────────┘
|
|
248
|
+
│ Enforce │
|
|
249
|
+
│ Diff │ ┌──────────────────┐
|
|
250
|
+
│ Report │ ──────► │ Aguara (177 │
|
|
251
|
+
└───────────┘ │ security rules) │
|
|
252
|
+
│ └──────────────────┘
|
|
253
|
+
▼
|
|
254
|
+
.mcp-policy.yml
|
|
255
|
+
(deny / require / allow)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Ecosystem
|
|
259
|
+
|
|
260
|
+
MCP Sentinel is part of the [Aguara](https://github.com/garagon/aguara) security ecosystem:
|
|
261
|
+
|
|
262
|
+
| Tool | What it does |
|
|
263
|
+
|------|-------------|
|
|
264
|
+
| **[Aguara](https://github.com/garagon/aguara)** | Security scanner — 177 rules, NLP, toxic-flow analysis |
|
|
265
|
+
| **[Aguara MCP](https://github.com/garagon/aguara-mcp)** | MCP server — gives AI agents security scanning as a tool |
|
|
266
|
+
| **MCP Sentinel** | Policy enforcement — audit, enforce, and monitor MCP servers |
|
|
267
|
+
| **[Aguara Watch](https://aguarascan.com)** | Cloud platform — continuous monitoring of MCP registries |
|
|
268
|
+
|
|
269
|
+
## Roadmap
|
|
270
|
+
|
|
271
|
+
- [x] Runtime introspection (tools, resources, prompts, capabilities)
|
|
272
|
+
- [x] **Policy enforcement engine**
|
|
273
|
+
- [x] Aguara integration
|
|
274
|
+
- [x] HTTP/SSE transport support
|
|
275
|
+
- [x] Diff mode
|
|
276
|
+
- [x] Config auto-detection (Claude Desktop, Cursor, Windsurf)
|
|
277
|
+
- [ ] Per-server policy overrides
|
|
278
|
+
- [ ] Registry integration (Smithery, mcp.run)
|
|
279
|
+
- [ ] GitHub Action (reusable workflow)
|
|
280
|
+
- [ ] VS Code extension
|
|
281
|
+
|
|
282
|
+
## Contributing
|
|
283
|
+
|
|
284
|
+
Contributions welcome. Please open an issue first to discuss what you'd like to change.
|
|
285
|
+
|
|
286
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development standards.
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
[MIT](LICENSE) — Gustavo Aragon ([@oktsec](https://github.com/oktsec))
|
package/dist/aguara.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aguara.d.ts","sourceRoot":"","sources":["../src/aguara.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAiB,MAAM,YAAY,CAAC;AAIxE,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO1D;AAyDD,wBAAsB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAgD7E"}
|
package/dist/aguara.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { writeFile, unlink, mkdtemp } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
export async function isAguaraInstalled() {
|
|
8
|
+
try {
|
|
9
|
+
await execFileAsync("aguara", ["version"]);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function buildScanContent(tools) {
|
|
17
|
+
const lines = [];
|
|
18
|
+
for (const tool of tools) {
|
|
19
|
+
lines.push(`## Tool: ${tool.name}`);
|
|
20
|
+
lines.push("");
|
|
21
|
+
lines.push(tool.description);
|
|
22
|
+
lines.push("");
|
|
23
|
+
if (tool.parameters.length > 0) {
|
|
24
|
+
lines.push(`Parameters: ${tool.parameters.map((p) => p.name).join(", ")}`);
|
|
25
|
+
lines.push("");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return lines.join("\n");
|
|
29
|
+
}
|
|
30
|
+
function parseAguaraOutput(stdout) {
|
|
31
|
+
try {
|
|
32
|
+
const parsed = JSON.parse(stdout);
|
|
33
|
+
const findings = [];
|
|
34
|
+
if (Array.isArray(parsed.findings)) {
|
|
35
|
+
for (const f of parsed.findings) {
|
|
36
|
+
findings.push({
|
|
37
|
+
severity: String(f.severity ?? "UNKNOWN"),
|
|
38
|
+
ruleId: String(f.rule_id ?? ""),
|
|
39
|
+
ruleName: String(f.rule_name ?? ""),
|
|
40
|
+
matchedText: String(f.matched_text ?? ""),
|
|
41
|
+
line: typeof f.line === "number" ? f.line : undefined,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
findings,
|
|
47
|
+
summary: typeof parsed.summary === "string" ? parsed.summary : `${findings.length} finding(s)`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return { findings: [], summary: "Failed to parse aguara output" };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export async function scanWithAguara(tools) {
|
|
55
|
+
const installed = await isAguaraInstalled();
|
|
56
|
+
if (!installed) {
|
|
57
|
+
return {
|
|
58
|
+
available: false,
|
|
59
|
+
findings: [],
|
|
60
|
+
summary: "aguara not installed — install from https://github.com/garagon/aguara for deep security analysis",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const content = buildScanContent(tools);
|
|
64
|
+
const tmpDir = await mkdtemp(join(tmpdir(), "mcp-sentinel-"));
|
|
65
|
+
const tmpFile = join(tmpDir, "tools.md");
|
|
66
|
+
try {
|
|
67
|
+
await writeFile(tmpFile, content, "utf-8");
|
|
68
|
+
const { stdout } = await execFileAsync("aguara", [
|
|
69
|
+
"scan", tmpFile, "--format", "json", "--severity", "low",
|
|
70
|
+
], {
|
|
71
|
+
timeout: 30_000,
|
|
72
|
+
env: process.env,
|
|
73
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
74
|
+
});
|
|
75
|
+
const result = parseAguaraOutput(stdout);
|
|
76
|
+
return { available: true, ...result };
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
// aguara exits with code 1 when findings are found, but still outputs JSON
|
|
80
|
+
if (typeof err === "object" && err !== null && "stdout" in err) {
|
|
81
|
+
const stdout = String(err.stdout);
|
|
82
|
+
if (stdout.trim().length > 0) {
|
|
83
|
+
const result = parseAguaraOutput(stdout);
|
|
84
|
+
return { available: true, ...result };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
available: true,
|
|
89
|
+
findings: [],
|
|
90
|
+
summary: `aguara scan failed: ${err instanceof Error ? err.message : "unknown error"}`,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
try {
|
|
95
|
+
await unlink(tmpFile);
|
|
96
|
+
}
|
|
97
|
+
catch { /* cleanup */ }
|
|
98
|
+
try {
|
|
99
|
+
const { rmdir } = await import("node:fs/promises");
|
|
100
|
+
await rmdir(tmpDir);
|
|
101
|
+
}
|
|
102
|
+
catch { /* cleanup */ }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=aguara.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aguara.js","sourceRoot":"","sources":["../src/aguara.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ToolInfo, AnalyzedTool, ToolCategory } from "./types.js";
|
|
2
|
+
export declare function categorizeTool(tool: ToolInfo): ToolCategory;
|
|
3
|
+
export declare function analyzeTools(tools: ToolInfo[]): AnalyzedTool[];
|
|
4
|
+
export declare function summarize(tools: AnalyzedTool[]): {
|
|
5
|
+
read: number;
|
|
6
|
+
write: number;
|
|
7
|
+
admin: number;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EACb,MAAM,YAAY,CAAC;AAepB,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,YAAY,CAU3D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,YAAY,EAAE,CAK9D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAY/F"}
|
package/dist/analyzer.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const WRITE_HINTS = [
|
|
2
|
+
/\bwrite\b/i, /\bcreate\b/i, /\bupdate\b/i, /\bmodify\b/i,
|
|
3
|
+
/\bset\b/i, /\bput\b/i, /\bpatch\b/i, /\bpush\b/i,
|
|
4
|
+
/\binsert\b/i, /\bupload\b/i, /\bsave\b/i, /\bmove\b/i,
|
|
5
|
+
];
|
|
6
|
+
const ADMIN_HINTS = [
|
|
7
|
+
/\bdelete\b/i, /\bremove\b/i, /\bdrop\b/i, /\bdestroy\b/i,
|
|
8
|
+
/\bpurge\b/i, /\btruncate\b/i, /\bexec\b/i, /\bexecute\b/i,
|
|
9
|
+
/\bshell\b/i, /\bbash\b/i, /\bspawn\b/i, /\beval\b/i,
|
|
10
|
+
/\buninstall\b/i, /\breset\b/i, /\bkill\b/i, /\bforce\b/i,
|
|
11
|
+
];
|
|
12
|
+
export function categorizeTool(tool) {
|
|
13
|
+
const text = `${tool.name} ${tool.description}`;
|
|
14
|
+
if (ADMIN_HINTS.some((p) => p.test(text))) {
|
|
15
|
+
return "admin";
|
|
16
|
+
}
|
|
17
|
+
if (WRITE_HINTS.some((p) => p.test(text))) {
|
|
18
|
+
return "write";
|
|
19
|
+
}
|
|
20
|
+
return "read";
|
|
21
|
+
}
|
|
22
|
+
export function analyzeTools(tools) {
|
|
23
|
+
return tools.map((tool) => ({
|
|
24
|
+
tool,
|
|
25
|
+
category: categorizeTool(tool),
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
export function summarize(tools) {
|
|
29
|
+
let read = 0;
|
|
30
|
+
let write = 0;
|
|
31
|
+
let admin = 0;
|
|
32
|
+
for (const t of tools) {
|
|
33
|
+
if (t.category === "admin")
|
|
34
|
+
admin++;
|
|
35
|
+
else if (t.category === "write")
|
|
36
|
+
write++;
|
|
37
|
+
else
|
|
38
|
+
read++;
|
|
39
|
+
}
|
|
40
|
+
return { read, write, admin };
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,GAAG;IAClB,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa;IACzD,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW;IACjD,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW;CACvD,CAAC;AAEF,MAAM,WAAW,GAAG;IAClB,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc;IACzD,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc;IAC1D,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW;IACpD,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY;CAC1D,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,IAAc;IAC3C,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;IAEhD,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QAC1C,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QAC1C,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAiB;IAC5C,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC1B,IAAI;QACJ,QAAQ,EAAE,cAAc,CAAC,IAAI,CAAC;KAC/B,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAqB;IAC7C,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO;YAAE,KAAK,EAAE,CAAC;aAC/B,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO;YAAE,KAAK,EAAE,CAAC;;YACpC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAChC,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AAgD3D,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG,IAAI,CAqG3D"}
|