@agentsid/scanner 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/README.md +205 -0
- package/action/action.yml +42 -0
- package/action/index.mjs +179 -0
- package/docs/state-of-agent-security-2026.md +377 -0
- package/examples/security-scan.yml +57 -0
- package/package.json +37 -0
- package/reports/aashari-mcp-server-atlassian-confluence.json +110 -0
- package/reports/aashari-mcp-server-atlassian-jira.json +138 -0
- package/reports/aashari-mcp-server-aws-sso.json +122 -0
- package/reports/agentdeskai-browser-tools-mcp.json +361 -0
- package/reports/ahmetkca-mcp-server-postgres.json +43 -0
- package/reports/aiondadotcom-mcp-ssh.json +166 -0
- package/reports/apify-actors-mcp-server.json +43 -0
- package/reports/azure-mcp.json +43 -0
- package/reports/boilerplate-mcp-tool.json +43 -0
- package/reports/browserstack-mcp-server.json +43 -0
- package/reports/canvas-mcp-server.json +43 -0
- package/reports/canvas-mcp-tool.json +43 -0
- package/reports/chrome-devtools-mcp.json +300 -0
- package/reports/chrome-local-mcp.json +222 -0
- package/reports/claude-flow-mcp.json +43 -0
- package/reports/cloudflare-mcp-server.json +43 -0
- package/reports/code-canvas-server.json +43 -0
- package/reports/cognitionai-metabase-mcp-server.json +43 -0
- package/reports/composio-mcp.json +43 -0
- package/reports/contentful-mcp-server.json +43 -0
- package/reports/dbhub.json +43 -0
- package/reports/desktop-commander.json +43 -0
- package/reports/dynatrace-oss-dynatrace-mcp-server.json +43 -0
- package/reports/e2b-mcp-server.json +67 -0
- package/reports/eslint-mcp.json +51 -0
- package/reports/european-parliament-mcp-server.json +1467 -0
- package/reports/exa-mcp-server.json +74 -0
- package/reports/executeautomation-playwright-mcp-server.json +418 -0
- package/reports/fast-kit-spec-kit.json +43 -0
- package/reports/felores-airtable-mcp-server.json +43 -0
- package/reports/figma-mcp.json +103 -0
- package/reports/forestadmin-mcp-server.json +43 -0
- package/reports/fullrun-mcp.json +43 -0
- package/reports/gemini-mcp-tool.json +43 -0
- package/reports/gitlab-mcp-agent-server.json +186 -0
- package/reports/grackle-ai-mcp.json +43 -0
- package/reports/heroku-mcp-server.json +333 -0
- package/reports/hisma-server-puppeteer.json +93 -0
- package/reports/hubspot-mcp-server.json +43 -0
- package/reports/hyper-mcp-shell.json +59 -0
- package/reports/iflow-mcp-server-github.json +327 -0
- package/reports/jpisnice-shadcn-ui-mcp-server.json +149 -0
- package/reports/jsonresume-mcp.json +43 -0
- package/reports/mapbox-mcp-server.json +43 -0
- package/reports/mcp-framework.json +43 -0
- package/reports/mcp-from-openapi.json +43 -0
- package/reports/mcp-handler.json +43 -0
- package/reports/mcp-proxy.json +43 -0
- package/reports/mcp-server-docker.json +59 -0
- package/reports/mcp-server-github-gist.json +108 -0
- package/reports/mcp-server-google-calendar.json +43 -0
- package/reports/mcp-server-jira-cloud.json +43 -0
- package/reports/mcp-server-kubernetes.json +43 -0
- package/reports/mcp-server-slack.json +411 -0
- package/reports/mcp-server-sqlite-npx.json +43 -0
- package/reports/mcp-server.json +43 -0
- package/reports/mcp-starter.json +59 -0
- package/reports/mcp-tool-lint.json +43 -0
- package/reports/mcporter.json +43 -0
- package/reports/mcptoolshop-mcp-tool-registry.json +43 -0
- package/reports/microsoft-devbox-mcp.json +43 -0
- package/reports/mobilenext-mobile-mcp.json +214 -0
- package/reports/modelcontextprotocol-server-brave-search.json +43 -0
- package/reports/modelcontextprotocol-server-everything.json +165 -0
- package/reports/modelcontextprotocol-server-fetch.json +43 -0
- package/reports/modelcontextprotocol-server-filesystem.json +259 -0
- package/reports/modelcontextprotocol-server-github.json +391 -0
- package/reports/modelcontextprotocol-server-memory.json +117 -0
- package/reports/modelcontextprotocol-server-postgres.json +43 -0
- package/reports/modelcontextprotocol-server-puppeteer.json +101 -0
- package/reports/modelcontextprotocol-server-sequential-thinking.json +67 -0
- package/reports/mongodb-mcp-server.json +43 -0
- package/reports/mseep-linear-mcp-server.json +43 -0
- package/reports/mseep-mcp-server-sqlite-npx.json +43 -0
- package/reports/n8n-mcp.json +123 -0
- package/reports/notepost-mcp.json +43 -0
- package/reports/notionhq-notion-mcp-server.json +220 -0
- package/reports/nx-mcp.json +59 -0
- package/reports/obsidian-mcp-server.json +43 -0
- package/reports/opengraph-io-mcp.json +130 -0
- package/reports/payloadcms-plugin-mcp.json +43 -0
- package/reports/peac-mappings-mcp.json +43 -0
- package/reports/playwright-mcp.json +236 -0
- package/reports/puppeteer-mcp-server.json +43 -0
- package/reports/railway-mcp-server.json +194 -0
- package/reports/razorpay-blade-mcp.json +182 -0
- package/reports/rekog-mcp-nest.json +43 -0
- package/reports/remotion-mcp.json +51 -0
- package/reports/rollbar-mcp-server.json +43 -0
- package/reports/sap-ux-fiori-mcp-server.json +80 -0
- package/reports/sentry-mcp-server.json +43 -0
- package/reports/server-filesystem.json +43 -0
- package/reports/server-memory.json +43 -0
- package/reports/shortcut-mcp.json +43 -0
- package/reports/supabase-mcp-server-supabase.json +43 -0
- package/reports/tavily-mcp.json +79 -0
- package/reports/thelord-mcp-server-docker-npx.json +43 -0
- package/reports/tyk-technologies-api-to-mcp.json +43 -0
- package/reports/tyk-technologies-tyk-dashboard-mcp.json +43 -0
- package/reports/ui5-mcp-server.json +157 -0
- package/reports/upstash-context7-mcp.json +82 -0
- package/reports/vantasdk-vanta-mcp-server.json +43 -0
- package/reports/winor30-mcp-server-datadog.json +43 -0
- package/reports/wonderwhy-er-desktop-commander.json +43 -0
- package/reports/xzxzzx-bilibili-mcp.json +58 -0
- package/src/grader.mjs +66 -0
- package/src/index.mjs +108 -0
- package/src/reporter.mjs +158 -0
- package/src/rules.mjs +363 -0
- package/src/scanner.mjs +208 -0
package/src/scanner.mjs
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server Scanner — connects to an MCP server, enumerates tools,
|
|
3
|
+
* and runs security analysis across all rule categories.
|
|
4
|
+
*
|
|
5
|
+
* Supports stdio and HTTP transport modes.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawn } from "child_process";
|
|
9
|
+
import {
|
|
10
|
+
scanToolDescriptions,
|
|
11
|
+
scanToolNames,
|
|
12
|
+
scanInputSchemas,
|
|
13
|
+
scanAuthIndicators,
|
|
14
|
+
scanOutputSafety,
|
|
15
|
+
scanHallucinationRisks,
|
|
16
|
+
} from "./rules.mjs";
|
|
17
|
+
import { grade } from "./grader.mjs";
|
|
18
|
+
import { formatTerminalReport, formatJsonReport } from "./reporter.mjs";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Scan an MCP server by spawning it as a subprocess (stdio transport).
|
|
22
|
+
* @param {string} command — Command to start the server (e.g., "npx @some/mcp-server")
|
|
23
|
+
* @param {object} options — { env, timeout, json }
|
|
24
|
+
*/
|
|
25
|
+
export async function scanStdio(command, options = {}) {
|
|
26
|
+
const { env = {}, timeout = 30000, json = false } = options;
|
|
27
|
+
|
|
28
|
+
const parts = command.split(/\s+/);
|
|
29
|
+
const proc = spawn(parts[0], parts.slice(1), {
|
|
30
|
+
env: { ...process.env, ...env },
|
|
31
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const result = await communicateWithServer(proc, timeout);
|
|
35
|
+
proc.kill();
|
|
36
|
+
|
|
37
|
+
return generateReport(result.serverInfo, result.tools, json);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Scan an MCP server by connecting over HTTP (streamable HTTP transport).
|
|
42
|
+
* @param {string} url — Server URL
|
|
43
|
+
* @param {object} options — { headers, timeout, json }
|
|
44
|
+
*/
|
|
45
|
+
export async function scanHttp(url, options = {}) {
|
|
46
|
+
const { headers = {}, timeout = 30000, json = false } = options;
|
|
47
|
+
|
|
48
|
+
// For HTTP transport, we send JSON-RPC over HTTP
|
|
49
|
+
const initResponse = await fetchRpc(url, "initialize", {
|
|
50
|
+
protocolVersion: "2024-11-05",
|
|
51
|
+
capabilities: {},
|
|
52
|
+
clientInfo: { name: "agentsid-scanner", version: "0.1.0" },
|
|
53
|
+
}, headers, timeout);
|
|
54
|
+
|
|
55
|
+
const serverInfo = initResponse?.result?.serverInfo || { name: "unknown", version: "?" };
|
|
56
|
+
|
|
57
|
+
// Send initialized notification
|
|
58
|
+
await fetchRpc(url, "notifications/initialized", {}, headers, timeout);
|
|
59
|
+
|
|
60
|
+
// List tools
|
|
61
|
+
const toolsResponse = await fetchRpc(url, "tools/list", {}, headers, timeout);
|
|
62
|
+
const tools = toolsResponse?.result?.tools || [];
|
|
63
|
+
|
|
64
|
+
return generateReport(serverInfo, tools, json);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Scan from a tool definition list (no server connection needed).
|
|
69
|
+
* Useful for scanning tool configs or package.json MCP definitions.
|
|
70
|
+
* @param {Array} tools — Array of tool definition objects
|
|
71
|
+
* @param {object} options — { serverName, json }
|
|
72
|
+
*/
|
|
73
|
+
export function scanToolDefinitions(tools, options = {}) {
|
|
74
|
+
const { serverName = "static-analysis", json = false } = options;
|
|
75
|
+
const serverInfo = { name: serverName, version: "static" };
|
|
76
|
+
return generateReport(serverInfo, tools, json);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── Internal ───
|
|
80
|
+
|
|
81
|
+
async function communicateWithServer(proc, timeout) {
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
let buf = "";
|
|
84
|
+
const responses = {};
|
|
85
|
+
|
|
86
|
+
proc.stdout.on("data", (data) => {
|
|
87
|
+
buf += data.toString();
|
|
88
|
+
while (buf.includes("\n")) {
|
|
89
|
+
const idx = buf.indexOf("\n");
|
|
90
|
+
const line = buf.substring(0, idx);
|
|
91
|
+
buf = buf.substring(idx + 1);
|
|
92
|
+
try {
|
|
93
|
+
const msg = JSON.parse(line);
|
|
94
|
+
if (msg.id) responses[msg.id] = msg;
|
|
95
|
+
} catch {}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
proc.stderr.on("data", () => {}); // Suppress stderr
|
|
100
|
+
|
|
101
|
+
// Send initialize
|
|
102
|
+
proc.stdin.write(JSON.stringify({
|
|
103
|
+
jsonrpc: "2.0",
|
|
104
|
+
method: "initialize",
|
|
105
|
+
params: {
|
|
106
|
+
protocolVersion: "2024-11-05",
|
|
107
|
+
capabilities: {},
|
|
108
|
+
clientInfo: { name: "agentsid-scanner", version: "0.1.0" },
|
|
109
|
+
},
|
|
110
|
+
id: 1,
|
|
111
|
+
}) + "\n");
|
|
112
|
+
|
|
113
|
+
setTimeout(() => {
|
|
114
|
+
// Send initialized notification
|
|
115
|
+
proc.stdin.write(JSON.stringify({
|
|
116
|
+
jsonrpc: "2.0",
|
|
117
|
+
method: "notifications/initialized",
|
|
118
|
+
params: {},
|
|
119
|
+
}) + "\n");
|
|
120
|
+
}, 500);
|
|
121
|
+
|
|
122
|
+
setTimeout(() => {
|
|
123
|
+
// List tools
|
|
124
|
+
proc.stdin.write(JSON.stringify({
|
|
125
|
+
jsonrpc: "2.0",
|
|
126
|
+
method: "tools/list",
|
|
127
|
+
params: {},
|
|
128
|
+
id: 2,
|
|
129
|
+
}) + "\n");
|
|
130
|
+
}, 1000);
|
|
131
|
+
|
|
132
|
+
// Wait for tools/list response
|
|
133
|
+
const check = setInterval(() => {
|
|
134
|
+
if (responses[2]) {
|
|
135
|
+
clearInterval(check);
|
|
136
|
+
clearTimeout(timer);
|
|
137
|
+
|
|
138
|
+
const serverInfo = responses[1]?.result?.serverInfo || { name: "unknown", version: "?" };
|
|
139
|
+
const tools = responses[2]?.result?.tools || [];
|
|
140
|
+
|
|
141
|
+
resolve({ serverInfo, tools });
|
|
142
|
+
}
|
|
143
|
+
}, 100);
|
|
144
|
+
|
|
145
|
+
const timer = setTimeout(() => {
|
|
146
|
+
clearInterval(check);
|
|
147
|
+
const serverInfo = responses[1]?.result?.serverInfo || { name: "unknown", version: "?" };
|
|
148
|
+
const tools = responses[2]?.result?.tools || [];
|
|
149
|
+
resolve({ serverInfo, tools });
|
|
150
|
+
}, timeout);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function fetchRpc(url, method, params, headers, timeout) {
|
|
155
|
+
const controller = new AbortController();
|
|
156
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
const response = await fetch(url, {
|
|
160
|
+
method: "POST",
|
|
161
|
+
headers: {
|
|
162
|
+
"Content-Type": "application/json",
|
|
163
|
+
...headers,
|
|
164
|
+
},
|
|
165
|
+
body: JSON.stringify({
|
|
166
|
+
jsonrpc: "2.0",
|
|
167
|
+
method,
|
|
168
|
+
params,
|
|
169
|
+
id: Math.floor(Math.random() * 10000),
|
|
170
|
+
}),
|
|
171
|
+
signal: controller.signal,
|
|
172
|
+
});
|
|
173
|
+
clearTimeout(timer);
|
|
174
|
+
return response.json();
|
|
175
|
+
} catch (err) {
|
|
176
|
+
clearTimeout(timer);
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function generateReport(serverInfo, tools, json) {
|
|
182
|
+
// Run all scan rules
|
|
183
|
+
const descriptionFindings = scanToolDescriptions(tools);
|
|
184
|
+
const { findings: nameFindings, riskProfile } = scanToolNames(tools);
|
|
185
|
+
const schemaFindings = scanInputSchemas(tools);
|
|
186
|
+
const authFindings = scanAuthIndicators(tools, serverInfo);
|
|
187
|
+
const outputFindings = scanOutputSafety(tools);
|
|
188
|
+
const hallucinationFindings = scanHallucinationRisks(tools);
|
|
189
|
+
|
|
190
|
+
// Combine all findings
|
|
191
|
+
const allFindings = [
|
|
192
|
+
...descriptionFindings,
|
|
193
|
+
...nameFindings,
|
|
194
|
+
...schemaFindings,
|
|
195
|
+
...authFindings,
|
|
196
|
+
...outputFindings,
|
|
197
|
+
...hallucinationFindings,
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
// Grade
|
|
201
|
+
const gradeResult = grade(allFindings);
|
|
202
|
+
|
|
203
|
+
// Format report
|
|
204
|
+
if (json) {
|
|
205
|
+
return formatJsonReport(serverInfo, tools, allFindings, gradeResult, riskProfile);
|
|
206
|
+
}
|
|
207
|
+
return formatTerminalReport(serverInfo, tools, allFindings, gradeResult, riskProfile);
|
|
208
|
+
}
|