@burtthecoder/mcp-shodan 1.0.5 → 1.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/build/index.js +61 -14
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -10,13 +10,13 @@ import fs from "fs";
|
|
|
10
10
|
import path from "path";
|
|
11
11
|
import os from "os";
|
|
12
12
|
dotenv.config();
|
|
13
|
-
// Use os.tmpdir() for the log file to ensure we have write permissions
|
|
14
13
|
const logFilePath = path.join(os.tmpdir(), "mcp-shodan-server.log");
|
|
15
14
|
const SHODAN_API_KEY = process.env.SHODAN_API_KEY;
|
|
16
15
|
if (!SHODAN_API_KEY) {
|
|
17
16
|
throw new Error("SHODAN_API_KEY environment variable is required.");
|
|
18
17
|
}
|
|
19
18
|
const API_BASE_URL = "https://api.shodan.io";
|
|
19
|
+
const CVEDB_API_URL = "https://cvedb.shodan.io";
|
|
20
20
|
// Logging Helper Function
|
|
21
21
|
function logToFile(message) {
|
|
22
22
|
try {
|
|
@@ -42,7 +42,9 @@ const SearchArgsSchema = z.object({
|
|
|
42
42
|
.describe("Maximum results to return."),
|
|
43
43
|
});
|
|
44
44
|
const VulnerabilitiesArgsSchema = z.object({
|
|
45
|
-
cve: z.string()
|
|
45
|
+
cve: z.string()
|
|
46
|
+
.regex(/^CVE-\d{4}-\d{4,}$/i, "Must be a valid CVE ID format (e.g., CVE-2021-44228)")
|
|
47
|
+
.describe("The CVE identifier to query (format: CVE-YYYY-NNNNN)."),
|
|
46
48
|
});
|
|
47
49
|
const DnsLookupArgsSchema = z.object({
|
|
48
50
|
hostnames: z.array(z.string()).describe("List of hostnames to resolve."),
|
|
@@ -62,6 +64,23 @@ async function queryShodan(endpoint, params) {
|
|
|
62
64
|
throw new Error(`Shodan API error: ${errorMessage}`);
|
|
63
65
|
}
|
|
64
66
|
}
|
|
67
|
+
// Helper Function for CVE lookups using CVEDB
|
|
68
|
+
async function queryCVEDB(cveId) {
|
|
69
|
+
try {
|
|
70
|
+
logToFile(`Querying CVEDB for: ${cveId}`);
|
|
71
|
+
const response = await axios.get(`${CVEDB_API_URL}/cve/${cveId}`);
|
|
72
|
+
return response.data;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (error.response?.status === 422) {
|
|
76
|
+
throw new Error(`Invalid CVE ID format: ${cveId}`);
|
|
77
|
+
}
|
|
78
|
+
if (error.response?.status === 404) {
|
|
79
|
+
throw new Error(`CVE not found: ${cveId}`);
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`CVEDB API error: ${error.message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
65
84
|
// Server Setup
|
|
66
85
|
const server = new Server({
|
|
67
86
|
name: "shodan-mcp",
|
|
@@ -87,7 +106,7 @@ server.setRequestHandler(InitializeRequestSchema, async (request) => {
|
|
|
87
106
|
name: "shodan-mcp",
|
|
88
107
|
version: "1.0.0",
|
|
89
108
|
},
|
|
90
|
-
instructions: "This server provides tools for querying Shodan, including IP lookups, searches, and vulnerabilities.",
|
|
109
|
+
instructions: "This server provides tools for querying Shodan, including IP lookups, searches, and vulnerabilities. For CVE lookups, use the format CVE-YYYY-NNNNN (e.g., CVE-2021-44228).",
|
|
91
110
|
};
|
|
92
111
|
});
|
|
93
112
|
// Register Tools
|
|
@@ -105,7 +124,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
105
124
|
},
|
|
106
125
|
{
|
|
107
126
|
name: "vulnerabilities",
|
|
108
|
-
description: "Retrieve vulnerability information for a CVE.",
|
|
127
|
+
description: "Retrieve vulnerability information for a CVE. Use format: CVE-YYYY-NNNNN (e.g., CVE-2021-44228)",
|
|
109
128
|
inputSchema: zodToJsonSchema(VulnerabilitiesArgsSchema),
|
|
110
129
|
},
|
|
111
130
|
{
|
|
@@ -159,17 +178,45 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
159
178
|
case "vulnerabilities": {
|
|
160
179
|
const parsedVulnArgs = VulnerabilitiesArgsSchema.safeParse(args);
|
|
161
180
|
if (!parsedVulnArgs.success) {
|
|
162
|
-
throw new Error("Invalid
|
|
181
|
+
throw new Error("Invalid CVE format. Please use format: CVE-YYYY-NNNNN (e.g., CVE-2021-44228)");
|
|
182
|
+
}
|
|
183
|
+
const cveId = parsedVulnArgs.data.cve.toUpperCase();
|
|
184
|
+
logToFile(`Looking up CVE: ${cveId}`);
|
|
185
|
+
try {
|
|
186
|
+
const result = await queryCVEDB(cveId);
|
|
187
|
+
return {
|
|
188
|
+
content: [
|
|
189
|
+
{
|
|
190
|
+
type: "text",
|
|
191
|
+
text: JSON.stringify({
|
|
192
|
+
cve_id: result.cve_id,
|
|
193
|
+
summary: result.summary,
|
|
194
|
+
cvss_v3: result.cvss_v3,
|
|
195
|
+
cvss_v2: result.cvss_v2,
|
|
196
|
+
epss: result.epss,
|
|
197
|
+
ranking_epss: result.ranking_epss,
|
|
198
|
+
kev: result.kev,
|
|
199
|
+
propose_action: result.propose_action,
|
|
200
|
+
ransomware_campaign: result.ransomware_campaign,
|
|
201
|
+
published: result.published_time,
|
|
202
|
+
references: result.references,
|
|
203
|
+
affected_products: result.cpes
|
|
204
|
+
}, null, 2),
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
return {
|
|
211
|
+
content: [
|
|
212
|
+
{
|
|
213
|
+
type: "text",
|
|
214
|
+
text: error.message,
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
isError: true,
|
|
218
|
+
};
|
|
163
219
|
}
|
|
164
|
-
const result = await queryShodan(`/shodan/cve/${parsedVulnArgs.data.cve}`, {});
|
|
165
|
-
return {
|
|
166
|
-
content: [
|
|
167
|
-
{
|
|
168
|
-
type: "text",
|
|
169
|
-
text: JSON.stringify(result, null, 2),
|
|
170
|
-
},
|
|
171
|
-
],
|
|
172
|
-
};
|
|
173
220
|
}
|
|
174
221
|
case "dns_lookup": {
|
|
175
222
|
const parsedDnsArgs = DnsLookupArgsSchema.safeParse(args);
|